JuHua-app/pages_player/list/VideoProgressBar.vue

265 lines
7.4 KiB
Vue
Raw Permalink Normal View History

2024-12-09 14:16:57 +08:00
<template>
<view class="container">
<!-- 进度条 -->
<view ref="progressBar" class="progress-bar" @click="handleClick">
<!-- 滑块及时间显示 -->
<view
class="slider-indicator-wrapper"
:style="{ top: sliderPosition + 'rpx', transition: isSliding ? 'none' : 'top 0.2s ease' }"
@touchstart="handleSlideStart"
@touchmove="handleSlideMove"
@touchend="handleSlideEnd"
>
<view class="time-label">
{{ selectedTime }}
</view>
<view class="slider-indicator"></view>
</view>
<!-- 卡片和标识展示 -->
<view v-for="(group, index) in cardGroups" :key="index" class="info-card-wrapper" :style="{ top: group.position + 'rpx', zIndex: group.showCards ? 10 : 1 }">
<!-- 判断是否需要显示悬浮标识 -->
<view v-if="group.cards.length > 1" class="hover-indicator" @click="toggleCardGroup(index)">
<image class="icon-indicator" src="../../static/more.png" mode="widthFix" />
</view>
<!-- 卡片列表展示 -->
<view v-if="group.showCards || group.cards.length === 1" class="card-list" :class="{ 'highlighted-card': group.showCards }">
<view v-for="(card, cardIndex) in group.cards" :key="cardIndex" class="info-card">
<view class="card-time">{{ card.time }}</view>
<view class="card-info">
<image class="thumbnail" src="../../static/video.png" mode="widthFix" />
<view class="info-text">{{ card.info }}</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'VideoProgressBar',
data() {
return {
selectedTime: null,
sliderPosition: 0,
isSliding: false,
cardGroups: []
};
},
mounted() {
this.updateCardGroups();
},
methods: {
handleClick(e) {
const query = uni.createSelectorQuery().in(this);
query.select('.progress-bar').boundingClientRect(data => {
if (data) {
let clickY = e.detail.y - data.top;
if (clickY < 0) clickY = 0;
if (clickY > data.height) clickY = data.height;
this.updateTime(clickY, data.height);
}
}).exec();
},
handleSlideStart() {
this.isSliding = true;
},
handleSlideMove(e) {
if (!this.isSliding) return;
const query = uni.createSelectorQuery().in(this);
query.select('.progress-bar').boundingClientRect(data => {
if (data) {
let touchY = e.touches[0].clientY - data.top;
if (touchY < 0) touchY = 0;
if (touchY > data.height) touchY = data.height;
this.sliderPosition = (touchY / data.height) * 2400;
}
}).exec();
},
handleSlideEnd(e) {
this.isSliding = false;
const query = uni.createSelectorQuery().in(this);
query.select('.progress-bar').boundingClientRect(data => {
if (data) {
let touchY = e.changedTouches[0].clientY - data.top;
if (touchY < 0) touchY = 0;
if (touchY > data.height) touchY = data.height;
this.updateTime(touchY, data.height);
}
}).exec();
},
updateTime(position, progressBarHeight) {
const totalSecondsInDay = 24 * 60 * 60;
const secondsAtPosition = (position / progressBarHeight) * totalSecondsInDay;
const hours = Math.floor(secondsAtPosition / 3600);
const minutes = Math.floor((secondsAtPosition % 3600) / 60);
const seconds = Math.floor(secondsAtPosition % 60);
this.sliderPosition = (position / progressBarHeight) * 2400;
this.selectedTime = `${this.padTime(hours)}:${this.padTime(minutes)}:${this.padTime(seconds)}`;
},
padTime(time) {
return time < 10 ? `0${time}` : time;
},
toggleCardGroup(index) {
this.cardGroups[index].showCards = !this.cardGroups[index].showCards;
},
updateCardGroups() {
const progressBarHeight = 2400;
const totalSecondsInDay = 24 * 60 * 60;
const minDistance = 50;
const timePoints = [
{ time: "03:30:00", info: "录像具体的时长1" },
{ time: "03:32:00", info: "测试1" },
{ time: "06:10:00", info: "测试2" },
{ time: "18:45:00", info: "录像具体的时长2" }
];
let cardGroups = [];
timePoints.forEach(point => {
const [hours, minutes, seconds] = point.time.split(":").map(Number);
const totalSeconds = hours * 3600 + minutes * 60 + seconds;
const position = (totalSeconds / totalSecondsInDay) * progressBarHeight;
const existingGroup = cardGroups.find(group => Math.abs(group.position - position) < minDistance);
if (existingGroup) {
existingGroup.cards.push(point);
} else {
cardGroups.push({
position,
showCards: false,
cards: [point]
});
}
});
this.cardGroups = cardGroups;
}
}
};
</script>
<style lang="scss" scoped>
.container {
padding-left: 200rpx;
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
height: 100%;
justify-content: center;
background-color: rgba(255, 255, 255, 0.2);
}
.progress-bar {
margin: 40rpx;
width: 20rpx;
height: 2400rpx;
background: linear-gradient(200deg, #d4f1f9, #fef9e7);
border-radius: 10rpx;
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.3);
}
.slider-indicator-wrapper {
position: absolute;
left: -150rpx;
display: flex;
align-items: center;
height: 5rpx;
}
.slider-indicator {
width: 35rpx;
height: 10rpx;
background-color: #0055ff;
border-radius: 20rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.25);
}
.time-label {
font-size: 24rpx;
color: #fff;
margin-right: 20rpx;
background-color: #0088ff;
padding: 8rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
}
.info-card-wrapper {
position: absolute;
left: 70rpx;
z-index: 1;
}
.hover-indicator {
width: 50rpx;
height: 50rpx;
background-color: transparent;
display: flex;
align-items: center;
justify-content: center;
}
.icon-indicator {
width: 30rpx;
height: 30rpx;
}
.card-list {
display: flex;
flex-direction: column;
background-color: transparent;
}
.highlighted-card {
background-color: #ffffff; /* 设置展开卡片的背景色 */
padding: 12rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.3);
z-index: 10; /* 提升层级 */
}
.info-card {
display: flex;
flex-direction: row;
background: radial-gradient(circle, #000000, #1b2e40, #3d4e5b, #5a6d78, #728896);
padding: 12rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.3);
min-width: 300rpx;
margin-top: 10rpx;
}
.thumbnail {
width: 100rpx;
border-radius: 6rpx;
display: flex;
margin: 30rpx;
}
.info-text {
display: flex;
flex-direction: column;
justify-content: center;
font-size: 22rpx;
color: #ffffff;
}
.card-time {
font-size: 20rpx;
font-weight: bold;
color: #fff;
}
</style>