Files
ks-app-employment-service/components/TikTok/TikTok.vue
2025-06-26 08:56:42 +08:00

313 lines
8.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<swiper
class="m-tiktok-video-swiper"
circular
@change="swiperChange"
:current="state.current"
:vertical="true"
duration="300"
>
<swiper-item v-for="(item, index) in state.displaySwiperList" :key="index">
<view class="swiper-item" @click="(e) => handleClick(index, e)">
<video
:id="`video__${index}`"
:controls="controls"
:autoplay="false"
:loop="loop"
@ended="ended"
@controlstoggle="controlstoggle"
@play="onPlay"
@error="emits('error')"
class="m-tiktok-video-player"
:src="item.src || item.explainUrl"
v-if="index === 0 || !state.isFirstLoad"
></video>
<view class="cover-triangle" v-if="pause"></view>
<image
v-if="item.poster && state.displayIndex != index"
:src="item.poster"
class="m-tiktok-video-poster"
mode="aspectFit"
></image>
<slot :item="item"></slot>
</view>
</swiper-item>
</swiper>
</template>
<script setup>
import { ref, reactive, getCurrentInstance, watch, nextTick } from 'vue';
import { onLoad, onUnload } from '@dcloudio/uni-app';
const _this = getCurrentInstance();
const emits = defineEmits(['play', 'error', 'loadMore', 'change', 'controlstoggle', 'click', 'ended']);
const lastTapTime = ref(0);
const pause = ref(false);
const props = defineProps({
/**
* 视频列表
*/
videoList: {
type: Array,
default: () => [],
},
/**
* 是否循环播放一个视频
*/
loop: {
type: Boolean,
default: true,
},
/**
* 显示原生控制栏
*/
controls: {
type: Boolean,
default: true,
},
/**
* 是否自动播放
*/
autoplay: {
type: Boolean,
default: true,
},
/**
* 是否自动滚动播放
*/
autoChange: {
type: Boolean,
default: false,
},
/**
* 滚动加载阈值(即播放到剩余多少个之后触发加载更多
*/
loadMoreOffsetCount: {
type: Number,
default: 2,
},
/**
* 暂停 1 单机暂停; 2 双击暂停; 0关闭
*/
pauseType: {
type: Number,
default: 2,
},
});
const state = reactive({
originList: [], // 源数据
displaySwiperList: [], // swiper需要的数据
displayIndex: 0, // 用于显示swiper的真正的下标数值只有012。
originIndex: 0, // 记录源数据的下标
current: 0,
oid: 0,
showControls: '',
toggleShow: true, // 显示面板
videoContexts: [],
isFirstLoad: true,
});
const initVideoContexts = () => {
state.videoContexts = [
uni.createVideoContext('video__0', _this),
uni.createVideoContext('video__1', _this),
uni.createVideoContext('video__2', _this),
];
};
const onPlay = (e) => {
emits('play', e);
};
const setVideoRef = (el, index) => {
if (el) {
videoRefs.value[index] = el;
}
};
function handleClick(index, e) {
const now = Date.now();
switch (props.pauseType) {
case 1:
if (pause.value) {
state.videoContexts[index].play();
pause.value = false;
} else {
state.videoContexts[index].pause();
pause.value = true;
}
break;
case 2:
if (now - lastTapTime.value < 300) {
if (pause.value) {
state.videoContexts[index].play();
pause.value = false;
} else {
state.videoContexts[index].pause();
pause.value = true;
}
}
break;
}
lastTapTime.value = now;
state.toggleShow = !state.toggleShow;
emits('click', e);
}
function ended() {
// 自动切换下一个视频
if (props.autoChange) {
if (state.displayIndex < 2) {
state.current = state.displayIndex + 1;
} else {
state.current = 0;
}
}
emits('ended');
}
/**
* 初始一个显示的swiper数据
* @originIndex 从源数据的哪个开始显示默认0如从其他页面跳转进来要显示第n个这个参数就是他的下标
*/
function initSwiperData(originIndex = state.originIndex) {
const originListLength = state.originList.length; // 源数据长度
const displayList = [];
displayList[state.displayIndex] = state.originList[originIndex];
displayList[state.displayIndex - 1 == -1 ? 2 : state.displayIndex - 1] =
state.originList[originIndex - 1 == -1 ? originListLength - 1 : originIndex - 1];
displayList[state.displayIndex + 1 == 3 ? 0 : state.displayIndex + 1] =
state.originList[originIndex + 1 == originListLength ? 0 : originIndex + 1];
state.displaySwiperList = displayList;
if (state.oid >= state.originList.length) {
state.oid = 0;
}
if (state.oid < 0) {
state.oid = state.originList.length - 1;
}
// 暂停所有视频
state.videoContexts.map((item) => item?.stop());
setTimeout(() => {
// 当前视频
if (props.autoplay) {
uni.createVideoContext(`video__${state.displayIndex}`, _this).play();
}
}, 500);
// 数据改变
emits('change', {
index: originIndex,
detail: state.originList[originIndex],
});
// 加载更多
var pCount = state.originList.length - props.loadMoreOffsetCount;
if (originIndex == pCount) {
emits('loadMore');
}
}
/**
* swiper滑动时候
*/
function swiperChange(event) {
const { current } = event.detail;
state.isFirstLoad = false;
const originListLength = state.originList.length; // 源数据长度
// 向后滚动
if (state.displayIndex - current == 2 || state.displayIndex - current == -1) {
state.originIndex = state.originIndex + 1 == originListLength ? 0 : state.originIndex + 1;
state.displayIndex = state.displayIndex + 1 == 3 ? 0 : state.displayIndex + 1;
state.oid = state.originIndex - 1;
initSwiperData(state.originIndex);
}
// 如果两者的差为-2或者1则是向前滑动
else if (state.displayIndex - current == -2 || state.displayIndex - current == 1) {
state.originIndex = state.originIndex - 1 == -1 ? originListLength - 1 : state.originIndex - 1;
state.displayIndex = state.displayIndex - 1 == -1 ? 2 : state.displayIndex - 1;
state.oid = state.originIndex + 1;
initSwiperData(state.originIndex);
}
state.toggleShow = true;
}
function controlstoggle(e) {
state.showControls = e.detail.show;
emits('controlstoggle', e);
}
watch(
() => props.videoList,
() => {
if (props.videoList?.length) {
state.originList = props.videoList;
if (state.isFirstLoad || !state.videoContexts?.length) {
initSwiperData();
initVideoContexts();
}
}
},
{
immediate: true,
}
);
let loadTimer = null;
onLoad(() => {
// 为了首次只加载一条视频(提高首次加载性能),延迟加载后续视频
loadTimer = setTimeout(() => {
state.isFirstLoad = false;
clearTimeout(loadTimer);
}, 5000);
});
onUnload(() => {
clearTimeout(loadTimer);
});
defineExpose({
initSwiperData,
});
</script>
<style lang="stylus" scoped>
.m-tiktok-video-swiper,
.m-tiktok-video-player {
width: 100%;
height: 100%;
background-color: #000;
}
.m-tiktok-video-swiper {
.swiper-item {
position: relative;
height: 100%;
}
.m-tiktok-video-poster {
background-color: #000;
position: absolute;
width: 100%;
height: 100%;
}
}
.cover-triangle{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%)
width: 132rpx
height: 132rpx
border-radius: 50%;
background: rgba(0,0,0,0.3)
}
.cover-triangle::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-40%, -50%) rotate(90deg);
width: 0;
height: 0;
border-left: 40rpx solid transparent;
border-right: 40rpx solid transparent;
border-bottom: 60rpx solid #fff;
}
</style>