年前单位需要搞一个类似抖音的需求,这本应是客户端的任务,然而,不知天高地厚的我却接了下来,然而下细致调研之下,发现网上并没有成熟的方案,但是却又很多需求,各大论坛全是提问的帖子,却少有人回答和解决。
这一瞬间,俺慌了,毕竟单位的活,排期都是定死的,这时候临阵退缩,实乃下下策。于是只能撸起袖子加油干。毕竟自己揽的事,含着泪也要干完,这就是男人,一个吐沫一个钉!
大家知道,web端比起客户端的劣势有几点,想要做出类似客户端的复杂的交互效果,需要考虑几个问题:
而在我调研了抖音的web端、git上的一些开源的相关项目、以及一些零零散散的回答之后,发现都不太匹配 他们在实现上,那么只能集几百家之长自己来了,既然自己来就需要针对当前三个问题来寻找既能解决问题,又能快速实现的方案(毕竟有排期)
在实现的初步设想中,我们不只需要解决问题,其实也需要考虑一些架构设计,也就是你怎样去将关注度分离,怎样将组件的颗粒度拆的细致,能将每一个组件独立出来,外部单独引用,怎样将每一个组件做通用,方便日后维护,并且还能快速开发,不耽误排期,这其实就是你在这做也无需求之初需要去想的一些问题,总结如下
首先,来说毋庸置疑的是:要想实现滑动的效果,现成的方案最快的就是swiper,swiper在web端的地位也是不可动摇。
其次,原生video标签体验大家也都知道,一塌糊涂,那么这一块也就需要自己实现,比如进度条,拖动,暂停播放,缓冲中等等内容。并且类似抖音中的视频上方的一些元素,比如点赞,分享等功能需要外部传入,让别的开发者在使用时自己定制
最后,怎样将组件的的结构拆分出来,能单独打包上传npm 供大家使用。
组件设计的设想俺才疏学浅也就能想到这了,接下来就该解决在调研中发现的三个问题:
工程构建为了装逼上了最新的vite ,体验了一把,开发体验确实是丝滑快速。由于vite天生支持库的开发,只需要在vite.config.ts 添加build内容即可。
build: {
lib: {
entry: path.resolve(__dirname, 'src/components/index.ts'),
name: 'videoSlide',
fileName: (format) => `index.${format}.js`
},
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ['vue'],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: 'Vue'
}
}
}
},
由于库可能给ts大佬使用,需要安装vite-plugin-dts 插件,来生成d.ts文件。
由于视频内容和轮播部分的处理是两个独立的逻辑,所以将代码拆分为两个组件video.vue以及slide.vue。
video的实现的基本思路就是重写原生video 标签默认ui来达到自定义的目的,样式就不在赘述,主要就是video提供的一些事件重写video默认行为,这里简述下重点的函数。
// vue
//js
setup({ autoplay }) {
// 是否是暂停状态
const paused = ref(true);
// 视频总时间
const endTime = ref(second(0));
//播放的时间
const startTime = ref(second(0));
// 是否是按下状态
const isPress = ref(false);
//缓冲进度
const percentageBuffer = ref(0);
// 播放进度
const percentage = ref(0);
// 保存计算后的播放时间
const calculationTime = ref(0);
// 拿到video 实例
const video = ref(null);
// 是否展示封面图
const showImg = ref(true);
// 是否处于缓冲中
const loading = ref(false);
// 播放
function play() {
video.value.play();
paused.value = false;
}
// 暂停
function pause() {
if (paused.value) return;
video.value.pause();
paused.value = true;
loading.value = false;
}
// 获取缓冲进度
function progress() {
if (!video.value) return;
percentageBuffer.value = Math.floor(
(video.value.buffered.length
? video.value.buffered.end(video.value.buffered.length - 1) /
video.value.duration
: 0) * 100
);
}
// 时间改变
function durationchange() {
endTime.value = second(video.value.duration);
console.log("时间改变触发");
}
// 首帧加载触发,为了获取视频时长
function loadeddata() {
console.log("首帧渲染触发");
showImg.value = false;
autoplay && play();
}
//当播放准备开始时(之前被暂停或者由于数据缺乏被暂缓)被触发
function playing() {
console.log("缓冲结束");
loading.value = false;
}
//缓冲的时候触发
function waiting() {
console.log("处于缓冲中");
loading.value = true;
}
// 时间改变触发
function timeupdate() {
// 如果是按下状态不能走进度,表示需要执行拖动
if (isPress.value || !video.value) return;
startTime.value = second(Math.floor(video.value.currentTime));
percentage.value = Math.floor(
(video.value.currentTime / video.value.duration) * 100
);
}
// 按下开始触发
function touchstart() {
isPress.value = true;
}
//松开按钮触发
function touchend() {
isPress.value = false;
video.value.currentTime = calculationTime.value;
}
// 拖动的时候触发
function touchmove(e) {
const width = window.screen.width;
const tx = e.clientX || e.changedTouches[0].clientX;
if (tx < 0 || tx > width) {
return;
}
calculationTime.value = video.value.duration * (tx / width);
startTime.value = second(Math.floor(calculationTime.value));
percentage.value = Math.floor((tx / width) * 100);
}
//点击进度条触发
function handleProgress(e) {
touchmove(e);
touchend();
}
// 播放结束时触发
function ended() {
play();
}
onMounted(() => {});
return {
video,
paused,
pause,
play,
progress,
durationchange,
loadeddata,
endTime,
startTime,
playing,
percentage,
waiting,
timeupdate,
percentageBuffer,
touchstart,
touchend,
touchmove,
isPress,
ended,
handleProgress,
loading,
showImg,
};
},
需要注意的是,需要自定义内容交给了使用者去自定义,全部通过插槽传入当前组件,这样就方便了根据内容自定义样式了。
slide.vue 就是处理滑动内容的组件,他包含了常用的上拉刷新,预加载等内容核心代码如下:
// vue
direction="vertical"
@transitionStart="transitionStart"
>
:item="item"
:index="index"
:activeIndex="activeIndex"
v-if="activeIndex >= index - 1 && activeIndex <= index + 1"
>
//js
setup({ list }, { emit }) {
const activeIndex = ref(0);
function transitionStart(swiper) {
//表示没有滑动,不做处理
if (activeIndex.value === swiper.activeIndex) {
// 表示是第一个轮播图
if (swiper.swipeDirection === "prev" && swiper.activeIndex === 0) {
// 表示上拉刷新
emit("refresh");
} else if (
swiper.swipeDirection === "next" &&
swiper.activeIndex === list.length - 1
) {
// 滑动到底部
emit("toBottom");
}
} else {
activeIndex.value = swiper.activeIndex;
// 为了预加载视频,提前load 数据
if (swiper.activeIndex === list.length - 1) {
emit("load");
}
}
}
return {
transitionStart,
activeIndex,
};
},
需要注意的是有两点:
组合使用其实就非常简单了。
//vue
:list="data"
v-slot="{ item, index, activeIndex }"
@refresh="refresh"
@toBottom="toBottom"
@load="load"
>
:src="item.entStoreVO.video"
:poster="item.entStoreVO.videoImg"
:index="index"
:activeIndex="activeIndex"
autoplay
>
点赞
评论
收藏
分享
在组合使用中,我将video通过插槽的方式传入silide内部,这样做的原因是,为了用户能自定义传入内容,这也是很多插件库惯用的伎俩,增加了组件的灵活性,又增加了组件的独立性。
在web浏览器中你经常会看到DOMException: play() failed because the user didn't interact with the document first 这个问题。
首先可以肯定的是在web浏览器中在与浏览器没有交互的情况下是不允许自动播放的,目前暂时还无法突破这个限制
如果你要嵌入app中,webview 可以突破,具体方法大家可自行查询,网上教程数不胜数。
将插件地址奉上,供大佬们参考,如有需求可直接引用,也可,克隆下来自行修改,如有问题请提issuesgithub.com/yixinagqing…
本文标题:实现一个Vue3版抖音滑动插件踩坑指南!
当前地址:http://www.csdahua.cn/qtweb/news27/452577.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网