基于 uniapp 开发的仿抖音小程序组件(超高性能)
注:组件使用 vue3+typescript 开发
全局仅渲染 3 个 swiper-item
实测,不管加载多少数据也能丝滑滚动
适用于 vue3,vue2 请自行修
自动预加载视频
首次渲染优化
安装 nodejs: https://nodejs.org/en/
安装依赖: npm i
运行项目: npm run dev:mp-weixin
构建项目资源: npm run build:mp-weixin
打开小程序开发工具导入dist/dev/mp-weixin
即可
真机预览,请点小程序开发工具上的预览,扫码真机预览即可
属性
类型
默认值
说明
videoList
Array
-
视频列表,数组对象 {src: string, poster?: string, objectFit?: string}
autoObjectFit
Boolean
true
是否开启视频自动自适应平铺模式,竖屏全屏覆盖,横屏自适应居中, 此参数优先级低于 videoList 中的 objectFit
loop
Boolean
true
是否循环播放视频
controls
Boolean
false
显示原生控制栏
autoplay
Boolean
true
是否自动播放
autoChange
Boolean
false
是否自动滚动播放
loadMoreOffsetCount
Number
2
滚动加载阈值(即播放到剩余多少个之后触发加载更多
@play
EventHandle
-
当开始/继续播放时触发 play 事件
@error
EventHandle
-
视频播放出错时触发
@ended
EventHandle
-
当播放到末尾时触发 ended 事件
@loadMore
EventHandle
-
当滚动到最后第 N 条数据后,需要加载更多时触发
@change
EventHandle
-
切换视频时触发
@click
EventHandle
-
点击整个视频区域触发
@controlstoggle
EventHandle
-
控制栏状态变化触发
属性
默认值
说明
default
-
自定义内容,覆盖到视频上方的所有自定义内容 v-slot="data" 为当前渲染数据,请参照使用示例
// 播放第几个视频
mTikTokRef . value ?. initSwiperData ( index ) ;
// 播放与暂停
mTikTokRef . value ?. togglePlay ( ) ;
// 播放跳转到指定位置,单位 s
mTikTokRef . value ?. playSeeked ( 8 ) ;
< template >
< div class = "video-container" >
< mTikTok
ref = "mTikTokRef"
:video-list = "state.videoList"
@loadMore = "loadMore"
@change = "change"
>
< ! -- 此处为用户完全自定义 data 中的数据为当前渲染的数据 -- >
< template v-slot = "data" >
< ! -- active修复视频悬浮层消失和点击的问题 -- >
< view
class = "video-side-right"
:class = "{ active: state.cutVideo.id === data.item.id }"
>
< view class = "action-item action-item-user" >
< image
class = "shop-logo"
src = "https://examples-1251000004.cos.ap-shanghai.myqcloud.com/sample.jpeg?imageMogr2/crop/180x180/gravity/center"
/ >
< view class = "action-btn" >
< text class = "iconfont" > +< / text >
< / view >
< text class = "action-item-text" > < / text >
< / view >
< view class = "action-item" >
< text class = "iconfont icon-star11beifen" > ❤< / text >
< text class = "action-item-text" > { { data. item . id } } < / text>
< / view >
< view class = "action-item" >
< text class = "iconfont icon-share" > ☝< / text >
< text class = "action-item-text" > 分享< / text >
< / view >
< / view>
< ! -- active修复视频悬浮层消失和点击的问题 -- >
< view
class = "video-bottom-area"
:class = "{ active: state.cutVideo.id === data.item.id }"
>
< view class = "shop-name" > @{ { data. item . name } } < / view >
< view class = "shop-card" > { { data . item . desc } } < / view>
< / view >
< / template >
< / mTikTok >
< / div>
< / template>
< script lang = "ts" setup >
import { onMounted , reactive , ref } from "vue";
// 导入组件
import mTikTok from "@/components/mTikTok.vue";
const mTikTokRef = ref< InstanceType < typeof mTikTok > > ( ) ;
const state = reactive ( {
cutVideo : { } as AnyObject,
videoList: [
{
src : "https://xjc.demo.hongcd.com/uploads/20230214/84e165388f5bfdb1550522f50f5a57bb.mp4" ,
id : "1" ,
name : "开玩笑的鸡毛" ,
desc : "这里是简介内容" ,
} ,
{
src : "https://xjc.demo.hongcd.com/uploads/20230214/3f26d950ac286eecedba49f5295f0819.mp4" ,
id : "2" ,
name : "开玩笑的鸡毛" ,
desc : "这里是简介内容" ,
} ,
{
src : "https://xjc.demo.hongcd.com/uploads/20230215/8b5ac0420fe61e2f9660d7b8af998f7b.mp4" ,
id : "3" ,
name : "开玩笑的鸡毛" ,
desc : "这里是简介内容" ,
} ,
{
src : "https://xjc.demo.hongcd.com/uploads/20210128/d932b2d78cebb0a8cb8f9a6216790dfb.mp4" ,
id : "4" ,
name : "开玩笑的鸡毛" ,
desc : "这里是简介内容" ,
} ,
{
src : "https://xjc.demo.hongcd.com/uploads/20210128/0c64cbeea28b10c06eee8728c762449e.mp4" ,
id : "5" ,
name : "开玩笑的鸡毛" ,
desc : "这里是简介内容" ,
} ,
{
src : "https://xjc.demo.hongcd.com/uploads/20210327/1b72e1b6153cd29df07f5449991e8083.mp4" ,
id : "6" ,
name : "开玩笑的鸡毛" ,
desc : "这里是简介内容" ,
} ,
{
src : "https://xjc.demo.hongcd.com/uploads/20230214/7e1a0baaebc4e656bbbfbc44d7a55a60.mp4" ,
id : "7" ,
name : "开玩笑的鸡毛" ,
desc : "这里是简介内容" ,
} ,
] ,
} );
const loadMore = () => {
// 触发加载更多
console . log ( "加载更多" ) ;
} ;
const change = (e: any) => {
state . cutVideo = e . detail ;
console . log ( "🚀 ~ file: index.vue:53 ~ change ~ data:" , e ) ;
} ;
// 播放第几个
const playIndex = (index: number) => {
mTikTokRef . value ?. initSwiperData ( index ) ;
} ;
onMounted(() => {
// 直接播放第3个
// playIndex(3);
} );
< / script >
< style lang = "scss" >
$zIndex: 99;
.video-layer {
position : absolute ;
right : 12 px ;
bottom : 120 px ;
color : #fff ;
}
.video-bottom-area {
position : absolute ;
left : 20 px ;
bottom : 40 px ;
opacity : 0 ;
transition : all 250 ms ;
z - index : 0 ;
& . active {
opacity : 1 ;
z - index : $zIndex ;
transition - delay : 200 ms ;
}
. shop - name {
color : #fff ;
margin - bottom : 6 px ;
}
. shop - card {
width : 160 px ;
height : 80 px ;
background - color : rgba ( 255 , 255 , 255 , 0.5 ) ;
border - radius : 4 px ;
}
}
.video-side-right {
position : absolute ;
right : 12 px ;
bottom : 120 px ;
color : #fff ;
opacity : 0 ;
transition : all 250 ms ;
z - index : 0 ;
& . active {
opacity : 1 ;
z - index : $zIndex ;
transition - delay : 200 ms ;
}
. action - item {
position : relative ;
margin - bottom : 20 px ;
text - align : center ;
. shop - logo {
width : 40 px ;
height : 40 px ;
border - radius : 50 % ;
overflow : hidden ;
}
. iconfont {
display : block ;
font - size : 28 px ;
}
. action - item - text {
display : block ;
font - size : 12 px ;
}
. action - btn {
position : absolute ;
left : 50 % ;
transform : translateX ( - 50 % ) ;
bottom : - 8 px ;
width : 20 px ;
height : 20 px ;
border - radius : 50 % ;
display : flex ;
align - items : center ;
justify - content : center ;
background - color : #f60 ;
. iconfont {
font - size : 16 px ;
}
}
}
. action - item - user {
margin - bottom : 40 px ;
}
}
< / style>