封装一个支持长按屏幕选择加速倍率的视频组件

701 阅读5分钟

说在前面

在短视频兴起的时代,很多的视频软件都有视频加速的功能,只需轻轻长按屏幕,视频便会根据预设的速度模式进行倍速播放,这是很常用而且很实用的一个功能,今天就让我们来简单实现一个组件,支持长按屏幕加速,且可以滑动选择加速倍率。

效果展示

pc端

移动端

体验地址

jyeontu.xyz/jvuewheel/#…

组件实现

1、组件属性(props)

width

视频播放器的宽度,可传入如 "300px""50%" 等合法的 CSS 宽度值。默认值为"300px"

height

视频播放器的高度,接受合法的 CSS 高度值。默认为"200px"

videoUrl

视频的源地址,需为有效的视频文件 URL,本地视频或线上链接都行。

videoUrl: require("../../assets/video/密码箱.mp4"),
videoUrl: "http://jyeontu.xyz/video/202112250058.mp4",

loop

是否循环播放视频,默认为false

timeIntervalLimit

长按事件的时间间隔限制(单位:毫秒),用于判断长按操作的触发条件,默认为1500,即长按屏幕1.5就会触发长按加速事件。

speedChooseList

可选的播放速度列表,数组元素为数字,表示倍速,默认为 [2, 3, 4]

2、获取video位置尺寸

获取视频元素和组件容器的位置和尺寸信息,存储在 videoContentInfo 中,用于后续的长按位置计算和速度调节。

initVideoContentInfo() {
    const JVideoSpeedBox = this.$refs[this.uid + "-JVideoSpeed"];
    const videoContent = this.$refs[this.uid + "-video"];
    const videoContentInfo = {
        offSetTop: videoContent.offsetTop || JVideoSpeedBox.offsetTop,
        offsetLeft:
            videoContent.offsetLeft || JVideoSpeedBox.offsetLeft,
        height:
            videoContent.offsetHeight || JVideoSpeedBox.offsetHeight,
        width: videoContent.offsetWidth || JVideoSpeedBox.offsetWidth,
    };
    this.videoContentInfo = videoContentInfo;
},

3、video点击事件

处理视频的点击事件,实际上调用 videoMouseUp 方法,用于统一处理视频播放状态的切换逻辑,避免在点击和其他操作(如长按后松开)时出现不一致的播放状态。

videoClick(e) {
    this.videoMouseUp(e);
},

4、鼠标按下事件

处理鼠标按下事件,记录按下时间 downTime,并在 timeIntervalLimit 后判断是否为长按操作,如果视频未暂停,则执行 videoLongPress 方法展示速度选择面板并进行速度调节。

videoMouseDown(e) {
    this.downTime = new Date().getTime();
    setTimeout(() => {
        const videoContent = this.$refs[this.uid + "-video"];
        if (videoContent.paused) {
            this.downTime = null;
            return;
        }
        if (!this.downTime) return;
        this.videoLongPress(e);
    }, this.timeIntervalLimit);
},

5、长按结束事件

在长按停止时,将播放速度恢复为正常速度(1 倍速),并在视频暂停时恢复播放,确保视频播放状态的正确切换和速度的正常恢复。

videoLongPressStop(e) {
    this.speedRate = 1;
    const target = e.target;
    if (target.className !== "j-video-speed-content") return;
    const videoContent = this.$refs[this.uid + "-video"];
    setTimeout(() => {
        if (videoContent.paused) {
            videoContent.play();
        }
    }, 50);
},

6、选中速度设置

更新速度选择面板中当前选中速度选项的样式,通过添加或移除特定的类名(j-video-speed-rates-rate-acitvity)来实现视觉上的选中反馈,增强用户交互体验。如果获取元素失败,会尝试重新获取和更新样式,以应对可能的加载延迟或其他异常情况。

  • 参数
    • rate:要设置为选中状态的播放速度值,用于确定对应的速度选项元素并更新其样式。
    • times(可选):重试次数,默认为 1,用于递归调用时控制重试逻辑,避免无限循环。
videoSpeedRateTextChange(rate, times = 1) {
    try {
        const rateContents = document.getElementsByClassName(
            "j-video-speed-rates-rate-acitvity"
        );
        for (const item of rateContents) {
            item.classList.remove("j-video-speed-rates-rate-acitvity");
        }
        if (!this.speedChooseList.includes(rate)) return;
        const rateContent =
            this.$refs[this.uid + "speedRate" + rate][0];
        rateContent.classList.add("j-video-speed-rates-rate-acitvity");
    } catch (err) {
        if (!times) return;
        setTimeout(() => {
            this.videoSpeedRateTextChange(rate, times - 1);
        }, 100);
    }
},

7、视频播放速度修改

直接设置视频的播放速度,通过修改视频元素的 playbackRate 属性来实现,并更新 speedRate 数据,以便组件内部记录和后续操作使用。

  • 参数
    • rate:要设置的播放速度值,需为数字类型,如 2、3 等,表示倍速。
videoSpeedRateChange(rate) {
    const videoContent = this.$refs[this.uid + "-video"];
    videoContent.playbackRate = rate;
    this.speedRate = rate;
},

8、视频长按事件

在长按视频时触发,展示速度选择面板(showRagePanel 设为 true),计算长按位置对应的播放速度值,并调用 videoSpeedRateChangevideoSpeedRateTextChange 方法来设置和显示选中的速度选项,实现快速的播放速度调节。

videoLongPress(e) {
    this.showRagePanel = true;
    const rate = this.calcSpeedRate(e);
    if (!rate) return;
    this.videoSpeedRateChange(rate);
    this.videoSpeedRateTextChange(rate);
},

9、计算加速倍率

根据长按位置(layerY)和速度选择列表,计算出对应的播放速度值。如果长按位置超出视频区域或不符合计算条件,则返回 null。通过将长按位置与每个速度选项对应的区域高度进行比较,确定用户选择的速度值,为速度调节提供准确的数据支持。

calcSpeedRate(e) {
    const videoContentInfo = this.videoContentInfo;
    let layerY = e.layerY;
    if (!e.layerY && e.touches) {
        layerY = e.touches[0].pageY - videoContentInfo.offSetTop;
    }
    if (layerY < 0) return;
    const preSpeedRateHeight =
        videoContentInfo.height / this.speedChooseList.length;
    const speedRate =
        this.speedChooseList[Math.floor(layerY / preSpeedRateHeight)];
    return speedRate;
},

10、鼠标(触屏)移动事件

处理视频的移动事件(在长按过程中),根据时间间隔判断是否满足长按条件,如果满足则执行 videoLongPress 方法继续速度调节操作,确保在长按拖动过程中能实时响应速度变化。同时,通过 e.preventDefault() 阻止默认的滚动行为,避免页面滚动干扰视频操作。

videoMove(e) {
    if (!this.downTime) return;
    const videoContent = this.$refs[this.uid + "-video"];
    if (videoContent.paused) {
        this.downTime = null;
        return;
    }
    e.preventDefault();
    const nowTime = new Date().getTime();
    const timeDif = nowTime - this.downTime;
    if (timeDif >= this.timeIntervalLimit) {
        this.videoLongPress(e);
    }
},

11、切换视频的播放状态

长按结束后恢复视频原始播放状态,在一定延迟(50 毫秒)后切换视频的播放状态,即如果视频当前暂停,则恢复播放;如果正在播放,则暂停播放。通过 setTimeout 实现延迟操作,确保播放状态的切换在合适的时机进行,避免与其他操作冲突。

changeVideoPlayStaus() {
    const videoContent = this.$refs[this.uid + "-video"];
    setTimeout(() => {
        if (videoContent.paused) {
            videoContent.play();
        } else {
            videoContent.pause();
        }
    }, 50);
},

12、鼠标抬起(触屏结束)事件

处理鼠标松开事件,将播放速度恢复为正常速度(1 倍速),根据按下和松开的时间间隔判断是否为长按操作,如果是长按操作,则执行 videoLongPressStop 方法进行后续处理;否则,如果是触摸事件且时间间隔较短,则调用 changeVideoPlayStaus 方法切换视频播放状态。同时,隐藏速度选择面板(showRagePanel 设为 false),完成一次完整的视频操作交互流程。

videoMouseUp(e) {
    this.videoSpeedRateChange(1);
    if (!this.downTime) return;
    const upTime = new Date().getTime();
    const timeDif = upTime - this.downTime;
    this.downTime = null;
    this.showRagePanel = false;
    if (timeDif >= this.timeIntervalLimit) {
        this.videoLongPressStop(e);
        return;
    } else {
        if (e.touches) this.changeVideoPlayStaus();
    }
},

组件使用

<template>
    <div class="content">
        <div class="video-list">
            <JVideoSpeed
                class="video"
                :videoUrl="videoUrl"
                videoWidth: "768px",
                videoHeight: "412px",
                :speedChooseList="speedChooseList"
            >
            </JVideoSpeed>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            videoUrl: require("../../assets/video/202112250058.mp4"),
            speedChooseList: [2, 3, 4, 5],
            videoWidth: "768px",
            videoHeight: "412px",
        }
    },
    created() {
        this.initVideoBox();
    },
    methods:{
        initVideoBox() {
            const videoWidth = parseInt(this.videoWidth);
            const videoHeight = parseInt(this.videoHeight);
            const rate = videoWidth / videoHeight;
            const width = window.innerWidth;
            const height = window.innerHeight;
            if (width < height) {
                this.videoWidth = width * 0.95 + "px";
                this.videoHeight = (width * 0.95) / rate + "px";
            }
        },
    }
}
</script>

组件库

组件文档

目前该组件也已经收录到我的组件库,组件文档地址如下: jyeontu.xyz/jvuewheel/#…

组件内容

组件库中还有许多好玩有趣的组件,如:

  • 评论组件
  • 词云组件
  • 瀑布流照片容器
  • 视频动态封面
  • 3D轮播图
  • web桌宠
  • 贡献度面板
  • 拖拽上传
  • 自动补全输入框
  • 图片滑块验证

等等……

组件库源码

组件库已开源到gitee,有兴趣的也可以到这里看看:gitee.com/zheng_yongt…

🌟觉得有帮助的可以点个star~

🖊有什么问题或错误可以指出,欢迎pr~

📬有什么想要实现的组件或想法可以联系我~

公众号

关注公众号『前端也能这么有趣』,获取更多有趣内容。

发送『组件库』获取源码

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。