755 lines
20 KiB
Plaintext
755 lines
20 KiB
Plaintext
<template>
|
||
<div id="SlideAlbum">
|
||
<div class="img-slide-wrapper">
|
||
<div
|
||
class="img-slide-list"
|
||
ref="slideListEl"
|
||
@touchstart.passive="touchStart"
|
||
@touchmove="touchMove"
|
||
@touchend="touchEnd"
|
||
>
|
||
<div class="img-slide-item" :key="index" v-for="(img, index) in item.imgs">
|
||
<img :ref="(e) => setItemRef(e, 'itemRefs')" :src="img + '&d=' + index" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<Icon
|
||
icon="fluent:play-28-filled"
|
||
class="pause-icon"
|
||
v-if="
|
||
state.status === SlideItemPlayStatus.Pause &&
|
||
state.operationStatus === SlideAlbumOperationStatus.Normal
|
||
"
|
||
/>
|
||
|
||
<template v-if="state.operationStatus === SlideAlbumOperationStatus.Normal">
|
||
<!-- <ItemToolbar-->
|
||
<!-- class="mb3r"-->
|
||
<!-- v-model:item="state.localItem"-->
|
||
<!-- :position="position"-->
|
||
<!-- v-bind="$attrs"-->
|
||
<!-- />-->
|
||
<!-- <ItemDesc class="mb3r" v-model:item="state.localItem" :position="position" />-->
|
||
</template>
|
||
<!--不知为啥touch事件,在下部20px的空间内不触发,加上click事件不好了 -->
|
||
<div
|
||
class="progress-bar"
|
||
v-if="!state.isPreview && state.operationStatus !== SlideAlbumOperationStatus.Zooming"
|
||
@click="null"
|
||
@touchstart="progressBarTouchStart"
|
||
@touchmove="progressBarTouchMove"
|
||
@touchend="progressBarTouchMEnd"
|
||
>
|
||
<div class="bar" :key="index" v-for="(img, index) in item.imgs">
|
||
<div class="progress" :style="getWidth(index)"></div>
|
||
</div>
|
||
</div>
|
||
<Teleport to="#home-index" v-if="state.isPreview">
|
||
<div class="preview">
|
||
<div class="preview-wrapper">
|
||
<img
|
||
:src="img + '&d=' + index"
|
||
:class="{ 'preview-img': index === state.localIndex }"
|
||
:key="index"
|
||
v-for="(img, index) in props.item.imgs"
|
||
:ref="(e) => setItemRef(e, 'previewImgs')"
|
||
/>
|
||
</div>
|
||
<div class="indicator">
|
||
<span class="index">{{ state.localIndex + 1 }}</span
|
||
> / {{ props.item.imgs.length }}
|
||
</div>
|
||
</div>
|
||
</Teleport>
|
||
<Teleport to="#home-index" v-if="state.operationStatus !== SlideAlbumOperationStatus.Normal">
|
||
<div class="album-toolbar">
|
||
<div class="left">
|
||
<Icon
|
||
icon="iconamoon:close"
|
||
@click="state.operationStatus = SlideAlbumOperationStatus.Normal"
|
||
/>
|
||
</div>
|
||
<div class="right">
|
||
<Icon icon="heroicons-outline:menu-alt-1" @click="Utils._no" />
|
||
<Icon
|
||
icon="fluent:play-28-filled"
|
||
v-if="state.status === SlideItemPlayStatus.Pause"
|
||
class="pause"
|
||
@click="startPlay"
|
||
/>
|
||
<Icon icon="bi:pause-fill" v-else class="pause" @click="stopPlay" />
|
||
<Icon icon="system-uicons:push-down" @click="_notice('已保存到系统相册')" />
|
||
</div>
|
||
</div>
|
||
</Teleport>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="jsx">
|
||
import enums from '../../utils/enums'
|
||
import Utils from '../../utils'
|
||
import GM, { _notice } from '../../utils'
|
||
import { mat4 } from 'gl-matrix'
|
||
import { Icon } from '@iconify/vue'
|
||
import {
|
||
nextTick,
|
||
onBeforeUpdate,
|
||
onMounted,
|
||
onUnmounted,
|
||
provide,
|
||
reactive,
|
||
ref,
|
||
watch
|
||
} from 'vue'
|
||
import {
|
||
getSlideOffset,
|
||
slideInit,
|
||
slideReset,
|
||
slideTouchEnd,
|
||
slideTouchMove,
|
||
slideTouchStart
|
||
} from '@/utils/slide'
|
||
import { SlideAlbumOperationStatus, SlideItemPlayStatus, SlideType } from '../../utils/const_var'
|
||
import { cloneDeep } from '@/utils'
|
||
import bus, { EVENT_KEY } from '../../utils/bus'
|
||
|
||
let out = new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||
let ov = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
|
||
let origin = cloneDeep(ov)
|
||
const rectMap = new Map()
|
||
|
||
// provide('isPlaying', computed(() => this.isPlaying))
|
||
provide('isPlaying', false)
|
||
const props = defineProps({
|
||
item: {
|
||
type: Object,
|
||
default() {
|
||
return {
|
||
type: 'imgs',
|
||
imgs: [
|
||
'https://cdn.seovx.com/?mom=302',
|
||
'https://cdn.seovx.com/?mom=302',
|
||
'https://cdn.seovx.com/?mom=302',
|
||
'https://cdn.seovx.com/?mom=302',
|
||
'https://cdn.seovx.com/?mom=302',
|
||
'https://cdn.seovx.com/?mom=302',
|
||
'https://cdn.seovx.com/?mom=302',
|
||
'https://cdn.seovx.com/?mom=302'
|
||
],
|
||
id: '034ae83b-ca0a-401a-b7c6-cf78361bae7b',
|
||
video: 'http://douyin.ttentau.top/0.mp4',
|
||
video_data_size: 26829508,
|
||
duration: 427780,
|
||
desc: '我不管我们宿舍第一好看',
|
||
allow_download: 0,
|
||
allow_duet: 0,
|
||
allow_react: 0,
|
||
allow_music: 1,
|
||
allow_douplus: 1,
|
||
allow_share: 1,
|
||
digg_count: 10480000,
|
||
comment_count: 79000,
|
||
download_count: 6,
|
||
play_count: 0,
|
||
share_count: 119000,
|
||
forward_count: 0,
|
||
collect_count: 3,
|
||
sort: 195,
|
||
is_top: 0,
|
||
city: '北京',
|
||
address: '中央戏剧学院',
|
||
musicId: '2ee213c6-3e3f-4758-ba5a-7f1c955604a4',
|
||
create_time: '1630423555',
|
||
creator_id: '93864497380',
|
||
status: 1,
|
||
topics: [
|
||
{
|
||
id: '85ceda30-898f-4b57-b891-0e58b3ab99a9',
|
||
name: '敬礼变装',
|
||
creator_id: '93864497380',
|
||
create_time: '1630423555',
|
||
status: 1
|
||
},
|
||
{
|
||
id: '85ceda30-898f-4b57-b891-0e58b3ab99a9',
|
||
name: '宿舍',
|
||
creator_id: '93864497380',
|
||
create_time: '1630423555',
|
||
status: 1
|
||
}
|
||
],
|
||
music: {
|
||
id: 'cde50af2-628c-4d28-b9c6-67237a62518e',
|
||
cover:
|
||
'https://p29.douyinpic.com/img/tos-cn-avt-0015/f4de202ff2e41b523838a4a767aebd16~c5_100x100.jpeg?from=116350172',
|
||
mp3: 'https://sf3-cdn-tos.douyinstatic.com/obj/ies-music/1658584661080088.mp3',
|
||
title: '@穷电影创作的原声-小高快起来跳舞',
|
||
creator_id: '93864497380',
|
||
create_time: '1630423555',
|
||
status: 1
|
||
},
|
||
author: {
|
||
id: '1',
|
||
unique_id_modify_time: '1630393144',
|
||
unique_id: '10040050',
|
||
favoriting_count: 143,
|
||
avatar: new URL('../../assets/img/icon/avatar/3.png', import.meta.url).href,
|
||
school: {
|
||
name: '中央戏剧学院',
|
||
department: null,
|
||
joinTime: null,
|
||
education: null,
|
||
displayType: enums.DISPLAY_TYPE.ALL
|
||
},
|
||
city: '',
|
||
province: '北京',
|
||
country: '',
|
||
location: '',
|
||
birthday: '2002-01-01',
|
||
cover: 'https://p3.douyinpic.com/obj/c8510002be9a3a61aad2?from=116350172',
|
||
following_count: 66,
|
||
follower_count: 235000,
|
||
aweme_count: 1796000,
|
||
nickname: '我是小睿耶',
|
||
certification: '',
|
||
phone: '',
|
||
sex: '',
|
||
last_login_time: '1630423555',
|
||
create_time: '1630423555',
|
||
status: 1,
|
||
desc: `一个普普通通学表演的\n看到的人都能开开心心`,
|
||
is_private: 0
|
||
}
|
||
}
|
||
}
|
||
},
|
||
position: {
|
||
type: Object,
|
||
default: () => {
|
||
return {
|
||
uniqueId: '',
|
||
index: ''
|
||
}
|
||
}
|
||
}
|
||
})
|
||
const slideListEl = ref(null)
|
||
|
||
//用于解决,touch事件触发startPlay,然后click事件又触发stopLoop的问题
|
||
let lockDatetime = 0
|
||
|
||
const state = reactive({
|
||
type: SlideType.HORIZONTAL,
|
||
judgeValue: 20,
|
||
name: 'SlideHorizontal',
|
||
localIndex: 0,
|
||
needCheck: true,
|
||
isPreview: false,
|
||
isZoom: false,
|
||
operationStatus: SlideAlbumOperationStatus.Normal,
|
||
next: false,
|
||
wrapper: { width: 0, height: 0, childrenLength: 0 },
|
||
last: {
|
||
point1: { x: 0, y: 0 },
|
||
point2: { x: 0, y: 0 }
|
||
},
|
||
start: {
|
||
x: 0,
|
||
y: 0,
|
||
point1: { x: 0, y: 0 },
|
||
point2: { x: 0, y: 0 },
|
||
center: { x: 0, y: 0 },
|
||
time: 0
|
||
},
|
||
move: { x: 0, y: 0 },
|
||
itemRefs: [],
|
||
previewImgs: [],
|
||
cycleFn: -1,
|
||
status: SlideItemPlayStatus.Play,
|
||
isAutoPlay: true,
|
||
localItem: props.item
|
||
})
|
||
|
||
function stopPlay() {
|
||
state.status = SlideItemPlayStatus.Pause
|
||
stopLoop()
|
||
}
|
||
|
||
function startPlay() {
|
||
state.isAutoPlay = true
|
||
state.status = SlideItemPlayStatus.Play
|
||
startLoop()
|
||
}
|
||
|
||
function stopLoop() {
|
||
clearInterval(state.cycleFn)
|
||
state.cycleFn = -1
|
||
}
|
||
|
||
function startLoop() {
|
||
if (state.cycleFn !== -1) return
|
||
if (!state.isAutoPlay) return
|
||
state.cycleFn = setInterval(() => {
|
||
state.status = SlideItemPlayStatus.Play
|
||
if (state.localIndex < props.item.imgs.length - 1) {
|
||
state.localIndex++
|
||
} else {
|
||
state.localIndex = 0
|
||
}
|
||
}, 2000)
|
||
}
|
||
|
||
onMounted(async () => {
|
||
await nextTick()
|
||
slideInit(slideListEl.value, state, SlideType.HORIZONTAL)
|
||
startPlay()
|
||
// setTimeout(() => {
|
||
// state.operationStatus = SlideAlbumOperationStatus.Zooming
|
||
// }, 1000)
|
||
bus.on(EVENT_KEY.SINGLE_CLICK_BROADCAST, click)
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
bus.off(EVENT_KEY.SINGLE_CLICK_BROADCAST, click)
|
||
})
|
||
|
||
function click({ uniqueId, index, type }) {
|
||
// console.log('position,', type, Date.now() - lockDatetime)
|
||
if (props.position.uniqueId === uniqueId && props.position.index === index) {
|
||
// if (type === EVENT_KEY.ITEM_TOGGLE) {
|
||
// if (state.status === SlideItemPlayStatus.Play) {
|
||
// stopLoop()
|
||
// } else {
|
||
// state.isAutoPlay = true
|
||
// startLoop()
|
||
// }
|
||
// }
|
||
if (type === EVENT_KEY.ITEM_STOP) {
|
||
stopPlay()
|
||
setTimeout(() => {
|
||
state.localIndex = 0
|
||
state.status = SlideItemPlayStatus.Play
|
||
state.operationStatus = SlideAlbumOperationStatus.Normal
|
||
}, 500)
|
||
}
|
||
if (type === EVENT_KEY.ITEM_PLAY) {
|
||
state.localIndex = 0
|
||
startPlay()
|
||
}
|
||
}
|
||
}
|
||
|
||
// 确保在每次更新之前重置ref
|
||
onBeforeUpdate(() => {
|
||
state.itemRefs = []
|
||
state.previewImgs = []
|
||
})
|
||
|
||
watch(
|
||
() => state.localIndex,
|
||
() => {
|
||
GM.$setCss(slideListEl.value, 'transition-duration', `300ms`)
|
||
GM.$setCss(
|
||
slideListEl.value,
|
||
'transform',
|
||
`translate3d(${getSlideOffset(state, slideListEl.value)}px, 0, 0)`
|
||
)
|
||
}
|
||
)
|
||
|
||
watch(
|
||
() => state.operationStatus,
|
||
(newVal) => {
|
||
if (newVal !== SlideAlbumOperationStatus.Normal) {
|
||
bus.emit(EVENT_KEY.ENTER_FULLSCREEN)
|
||
} else {
|
||
bus.emit(EVENT_KEY.EXIT_FULLSCREEN)
|
||
}
|
||
}
|
||
)
|
||
|
||
function calcCurrentIndex(e) {
|
||
state.isPreview = true
|
||
let x = e.touches[0].pageX
|
||
|
||
let current = -1
|
||
let length = state.previewImgs.length
|
||
for (let i = length - 1; i >= 0; i--) {
|
||
let rect = state.previewImgs[i].getBoundingClientRect()
|
||
if (rect.x < x) {
|
||
current = i
|
||
break
|
||
}
|
||
}
|
||
if (current > -1) {
|
||
state.localIndex = current
|
||
}
|
||
}
|
||
|
||
function progressBarTouchStart(e) {
|
||
Utils.$stopPropagation(e)
|
||
}
|
||
|
||
function progressBarTouchMove(e) {
|
||
Utils.$stopPropagation(e)
|
||
calcCurrentIndex(e)
|
||
}
|
||
|
||
function progressBarTouchMEnd(e) {
|
||
Utils.$stopPropagation(e)
|
||
state.isPreview = false
|
||
}
|
||
|
||
function touchStart(e) {
|
||
lockDatetime = Date.now()
|
||
|
||
// Utils.$showNoticeDialog('start'+e.touches.length)
|
||
console.log('start', e.touches.length)
|
||
if (e.touches.length === 1) {
|
||
slideTouchStart(e, slideListEl.value, state)
|
||
} else {
|
||
if (state.operationStatus === SlideAlbumOperationStatus.Zooming) {
|
||
// state.start.center = Utils.getCenter(state.start.point1, state.start.point2)
|
||
return
|
||
}
|
||
state.operationStatus = SlideAlbumOperationStatus.Zooming
|
||
state.itemRefs[state.localIndex].style['transition-duration'] = '0ms'
|
||
state.last.point1 = state.start.point1 = {
|
||
x: e.touches[0].pageX,
|
||
y: e.touches[0].pageY
|
||
}
|
||
state.last.point2 = state.start.point2 = {
|
||
x: e.touches[1].pageX,
|
||
y: e.touches[1].pageY
|
||
}
|
||
state.start.center = Utils.getCenter(state.start.point1, state.start.point2)
|
||
}
|
||
}
|
||
|
||
function touchMove(e) {
|
||
const s = true
|
||
if (s) return
|
||
|
||
// Utils.$showNoticeDialog('move'+e.touches.length)
|
||
// console.log('move', e.touches.length, state.operationStatus)
|
||
let current1 = { x: e.touches[0].pageX, y: e.touches[0].pageY }
|
||
stopLoop()
|
||
|
||
//单手移动
|
||
if (e.touches.length === 1) {
|
||
if (state.operationStatus === SlideAlbumOperationStatus.Zooming) {
|
||
// console.log('m1')
|
||
Utils.$stopPropagation(e)
|
||
|
||
// console.log('单手移动',)
|
||
let movementX = current1.x - state.last.point1.x
|
||
let movementY = current1.y - state.last.point1.y
|
||
// console.log(movementX, movementY)
|
||
const t = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, movementX, movementY, 0, 1])
|
||
ov = mat4.multiply(out, t, ov)
|
||
state.itemRefs[state.localIndex].style.transform = `matrix3d(${ov.toString()})`
|
||
state.last.point1 = current1
|
||
} else {
|
||
// console.log('m2')
|
||
state.isAutoPlay = false
|
||
slideTouchMove(
|
||
e,
|
||
slideListEl.value,
|
||
state,
|
||
canNext,
|
||
() => {
|
||
//TODO 这里需要这个事件吗
|
||
if (state.operationStatus !== SlideAlbumOperationStatus.Normal) {
|
||
Utils.$stopPropagation(e)
|
||
}
|
||
// console.log('move-notNextcb')
|
||
// state.operationStatus = SlideAlbumOperationStatus.Normal
|
||
},
|
||
() => {
|
||
if (state.operationStatus !== SlideAlbumOperationStatus.Normal) {
|
||
Utils.$stopPropagation(e)
|
||
}
|
||
}
|
||
)
|
||
}
|
||
} else {
|
||
// console.log('m3')
|
||
state.operationStatus = SlideAlbumOperationStatus.Zooming
|
||
Utils.$stopPropagation(e)
|
||
|
||
let rect = { x: 0, y: 0 }
|
||
if (rectMap.has(state.localIndex)) {
|
||
rect = rectMap.get(state.localIndex)
|
||
} else {
|
||
//TODO 这里去掉jquery
|
||
//getBoundingClientRect在手机上获取不到值
|
||
// let offset = $(state.itemRefs[state.localIndex]).offset()
|
||
// rect = { x: offset.left, y: offset.top }
|
||
// rectMap.set(state.localIndex, rect)
|
||
}
|
||
|
||
let current2 = { x: e.touches[1].pageX, y: e.touches[1].pageY }
|
||
|
||
// 双指缩放比例,就是对应的放大倍数
|
||
let currentRatio =
|
||
Utils.getDistance(current1, current2) /
|
||
Utils.getDistance(state.start.point1, state.start.point2)
|
||
let movementRatio = currentRatio - ov[0]
|
||
// console.log('movementRatio',movementRatio)
|
||
//如果本次比例和上次的不超过0.02。那么判定为平移
|
||
if (Math.abs(movementRatio) <= 0.02) {
|
||
let movementX = current1.x - state.last.point1.x
|
||
let movementY = current1.y - state.last.point1.y
|
||
let movement2X = current2.x - state.last.point2.x
|
||
let movement2Y = current2.y - state.last.point2.y
|
||
|
||
let minX = Math.min(movementX, movement2X)
|
||
let minY = Math.min(movementY, movement2Y)
|
||
const t1 = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, minX, minY, 0, 1])
|
||
ov = mat4.multiply(out, t1, ov)
|
||
} else {
|
||
let center = Utils.getCenter(current1, current2)
|
||
center.x -= rect.x
|
||
center.y -= rect.y
|
||
//用最新的放大倍数ratio除以之前的放大ov[0]倍数,算出本次要累加放大的倍数
|
||
let zoom = currentRatio / ov[0]
|
||
const x = center.x * (1 - zoom)
|
||
const y = center.y * (1 - zoom)
|
||
const t = new Float32Array([zoom, 0, 0, 0, 0, zoom, 0, 0, 0, 0, 1, 0, x, y, 0, 1])
|
||
//如果zoom是每次都是最后放大倍数,第三个参数用原值(即,矩阵x乘时,都是乘以单位矩阵)
|
||
//如果zoom是累加放大(比如每次都是0.15),第三个参数用ov。这里还是采用累加计算
|
||
ov = mat4.multiply(out, t, ov)
|
||
}
|
||
|
||
state.itemRefs[state.localIndex].style.transform = `matrix3d(${ov.toString()})`
|
||
state.last.point1 = current1
|
||
state.last.point2 = current2
|
||
}
|
||
}
|
||
|
||
function touchEnd(e) {
|
||
// console.log('Date.now() - lockDatetime', Date.now() - lockDatetime,)
|
||
if (
|
||
Date.now() - lockDatetime < 300 &&
|
||
state.move.x === 0 &&
|
||
state.move.y === 0 &&
|
||
state.operationStatus !== SlideAlbumOperationStatus.Zooming
|
||
) {
|
||
if (state.status === SlideItemPlayStatus.Play) {
|
||
stopPlay()
|
||
} else {
|
||
startPlay()
|
||
}
|
||
return
|
||
}
|
||
|
||
state.isPreview = false
|
||
//这里,如果是双指触控的话,会触发两次事件,第一次touches长度为1,第二次为0
|
||
//如果是单指触控的话,触发一次事件,touches长度为0
|
||
console.log('end', e.touches.length, state.operationStatus)
|
||
|
||
// e.touches.length === 1 说明,松开了第一只手指
|
||
if (e.touches.length === 1) {
|
||
//双指缩放状态下,但只松开了一只手
|
||
if (state.operationStatus === SlideAlbumOperationStatus.Zooming) {
|
||
Utils.$stopPropagation(e)
|
||
state.last.point1 = { x: e.touches[0].pageX, y: e.touches[0].pageY }
|
||
startLoop()
|
||
}
|
||
} else {
|
||
if (state.operationStatus === SlideAlbumOperationStatus.Zooming) {
|
||
Utils.$stopPropagation(e)
|
||
ov = origin
|
||
state.itemRefs[state.localIndex].style['transition-duration'] = '300ms'
|
||
state.itemRefs[state.localIndex].style.transform = `matrix3d(${origin.toString()})`
|
||
startLoop()
|
||
state.operationStatus = SlideAlbumOperationStatus.Look
|
||
} else {
|
||
slideTouchEnd(
|
||
e,
|
||
state,
|
||
canNext,
|
||
() => {
|
||
console.log('nextCb')
|
||
},
|
||
() => {
|
||
if (state.operationStatus !== SlideAlbumOperationStatus.Normal) {
|
||
state.operationStatus = SlideAlbumOperationStatus.Normal
|
||
}
|
||
console.log('doNotNextCb')
|
||
startLoop()
|
||
}
|
||
)
|
||
slideReset(e, slideListEl.value, state)
|
||
}
|
||
}
|
||
}
|
||
|
||
function getWidth(index) {
|
||
if (state.localIndex >= index) return { width: '100%' }
|
||
}
|
||
|
||
function setItemRef(el, key) {
|
||
el && state[key].push(el)
|
||
}
|
||
|
||
function canNext(state, isNext) {
|
||
let res = !(
|
||
(state.localIndex === 0 && !isNext) ||
|
||
(state.localIndex === props.item.imgs.length - 1 && isNext)
|
||
)
|
||
return res
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
#SlideAlbum {
|
||
transition: height 0.3s;
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
color: white;
|
||
font-size: 14rem;
|
||
|
||
.img-slide-wrapper {
|
||
position: relative;
|
||
height: 100%;
|
||
width: 100%;
|
||
|
||
.img-slide-list {
|
||
height: 100%;
|
||
width: 100%;
|
||
display: flex;
|
||
position: relative;
|
||
|
||
.img-slide-item {
|
||
height: 100%;
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
img {
|
||
transform-origin: 0 0;
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.progress-bar {
|
||
position: absolute;
|
||
width: 100%;
|
||
bottom: 10rem;
|
||
display: flex;
|
||
box-sizing: border-box;
|
||
padding: 0 5rem;
|
||
@h: 4rem;
|
||
//height: @h;
|
||
height: 10rem;
|
||
//background-color: red;
|
||
align-items: flex-end;
|
||
justify-content: space-between;
|
||
|
||
.bar {
|
||
border-radius: 10rem;
|
||
flex: 1;
|
||
margin: 0 2rem;
|
||
height: @h;
|
||
background: #75757580;
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
.progress {
|
||
border-radius: 10rem;
|
||
position: absolute;
|
||
left: 0;
|
||
height: @h;
|
||
background: #e5e5e587;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
<style lang="less">
|
||
.preview {
|
||
transition: opacity 0.3s;
|
||
position: fixed;
|
||
bottom: 0;
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-direction: column;
|
||
|
||
.preview-wrapper {
|
||
img {
|
||
transition: all 0.3s;
|
||
margin: 0 5rem;
|
||
width: 30rem;
|
||
height: 45rem;
|
||
background-color: black;
|
||
border-radius: 3rem;
|
||
overflow: hidden;
|
||
object-fit: cover;
|
||
|
||
&.preview-img {
|
||
margin: 0 15rem;
|
||
width: 40rem;
|
||
}
|
||
}
|
||
}
|
||
|
||
.indicator {
|
||
background: var(--footer-color);
|
||
width: 100%;
|
||
height: var(--footer-height);
|
||
color: gray;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.index {
|
||
color: white;
|
||
}
|
||
}
|
||
}
|
||
|
||
.album-toolbar {
|
||
position: absolute;
|
||
bottom: 0;
|
||
color: white;
|
||
font-size: 24rem;
|
||
background: var(--footer-color);
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
height: var(--footer-height);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 10rem;
|
||
|
||
@padding: 18rem;
|
||
|
||
.left {
|
||
height: 40rem;
|
||
background-color: rgba(71, 71, 86, 0.53);
|
||
border-radius: 10rem;
|
||
padding: 0 @padding;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.right {
|
||
.left;
|
||
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rem;
|
||
}
|
||
}
|
||
</style>
|