稀土掘金技术社区 04月22日 10:06
数据可视化大屏列表循环滚动动画Vue指令封装(requestAnimationFrame)
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了一种使用Vue指令实现列表循环滚动动画的方法,解决了大屏项目中列表动画重复编写的问题。通过requestAnimationFrame实现动画,并结合鼠标事件控制动画的暂停与恢复。文章提供了详细的代码示例和解释,包括如何根据需求调整滚动速度。此外,作者还分享了使用requestAnimationFrame的原因以及其他动画实现的思路,并提及了额外功能扩展的可能性,最终目标是让列表动画的实现更加优雅和易于维护。

🖱️通过Vue指令封装列表滚动动画,可以避免重复编写动画代码,提高代码的复用性和可维护性。

💨使用requestAnimationFrame实现动画,利用浏览器自身的刷新机制,保证动画的流畅性和性能,优于setTimeout或setInterval。

⏸️通过监听鼠标事件(点击、移入、移出、滚轮)来控制动画的暂停与恢复,实现用户交互。

⏱️通过控制帧数差距(gap)来调整列表滚动速度,满足不同场景的需求。

💡提供了在浏览器不支持requestAnimationFrame时的降级方案,增加了代码的兼容性。

原创 赛博丁真Damon 2025-04-22 08:32 重庆

点击关注公众号,“技术干货” 及时达!

关注更多AI编程资讯请去AI Coding专区:https://juejin.cn/aicoding

背景及需求

背景: 在大屏项目中,经常会存在列表循环滚动的动画。 当存在很多个列表都需要动画的时候, 重复代码既不够优雅,也不便于维护(当甲方觉得速度快慢调整或其他调整时候,需要重复修改)。

需求:

    列表上下滚动动画循环播放
    当鼠标点击列表时候,滚动暂停
    当鼠标移出列表元素时候,重新触发滚动
    可调整列表滚动的速度(甲方屁事多的情况下)

代码效果展示

列表滚动动画vue指令网址:

https://code.juejin.cn/pen/7400691681794162728

详解

    通过requestAnimationFrame进行列表动画滚动动画,并通过回调接口的timeStamp时间戳来控制动画进行的速度。
    通过vue指令进行封装,并且在指令mounted生命周期定义变量isUsing来辨别当前用户是否正在与元素交互中(点击,鼠标滚动, 移入等,根据业务需求), 为元素增添监听鼠标点击、移入移出事件来控制交互期间的动画状态.

一些思考

为什么使用requestAnimationFrame而不是使用setTimeoutsetInterval实现? 在数据可视化需要定制动画的情况下,requestAnimationFrame的稳定性、交互性、流畅度和易用性都是优优于其他二者的,是与浏览器刷新都挂钩的

在当前浏览器的支持度也十分良好

当然在极限的情况下,也需要额外适配,在下面代码中我也有预留当浏览器不支持时候可以使用setTimeoutsetInterval实现。

核心代码

vue 指令实现

    {    arr: {},    mounted(el, binding = { value: 200 }, vnode, prevVnode) {        const gap = 1000 / 45;  // 帧数差距, 浏览器大概1s 60帧, 数字 > 60 效果应该一致,数字越小越慢        if (window.requestAnimationFrame) {            let isUsing = false;    // 是否介入操作;介入操作时暂停动画;            // 鼠标移出时,继续动画            el.addEventListener('mouseleave'() => {                isUsing && scrollAnimation(); // 如果动画已暂停,重置动画                isUsing = false;            }, { passivetrue } )            // 鼠标滚动时,继续动画            el.addEventListener('wheel'() => {                isUsing = true;            }, { passivetrue } )            // 鼠标点击时, 暂停动画            el.addEventListener('click'() => {                isUsing = true;            }, { passivetrue } )            // 动画方法            const scrollAnimation = () => {                if (window.requestAnimationFrame) {                    /**                     * @Author: Damon Liu                     * @Date: 2024-04-29 10:28:37                     * @LastEditors: Damon Liu                     * @LastEditTime                     * @Description                     * @param {*} timeStamp 当前时间戳                     * @param {*} preTimeStamp 上一帧时间戳                     * @param {*} diff  累计的时间差                     */                    let animationFun = (timeStamp, preTimeStamp = 0, diff = 0) => {                        if (isUsing) {                            return                        }                        let currentDiff = preTimeStamp === 0 ? 0 : timeStamp - preTimeStamp;    // 当前时间差                        let n_diff = currentDiff + diff;    // 总时间差                        // 当总时间差比小于帧数差距时,不执行动画,申请下一帧执行                        if(n_diff < gap) {                            window.requestAnimationFrame((_timeStamp) => animationFun(_timeStamp, timeStamp, n_diff))                            return ;                        }                        let scrollTop = el.scrollTop;   // 滚动条顶部                        let clientHeight = el.clientHeight// 内容高度                        let scrollHeight = el.scrollHeight// 滚动内容高度                        // 当没有滚动至底部时                        if (scrollTop + clientHeight < scrollHeight) {                            el.scrollTop = scrollTop + 1;                            window.requestAnimationFrame((_timeStamp) => animationFun(_timeStamp, timeStamp, 0));                        }                        // 滚动至底部重置动画                        else {                            el.scrollTop = 0;                            scrollAnimation();                        }                    }                    // 开始动画                    window.requestAnimationFrame(animationFun);                }            }            scrollAnimation()        }        else {        }    },    unmounted(el, binding, vnode, prevVnode) {    }}


    额外拓展

    存在额外需求, 如需要手动去控制列表是否滚动时候,可通过指令传入额外变量去进行控制。

    列表滚动动画也可以更改为其他动画,如颜色的变化以及形状,位移等。总体的思路一致。


    💰即日起至5.11发文瓜分万元奖金
    📃主赛道推好文,AI命题叠福利
    🔎点击上方活动图了解详情

    阅读原文

    跳转微信打开

    Fish AI Reader

    Fish AI Reader

    AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

    FishAI

    FishAI

    鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

    联系邮箱 441953276@qq.com

    相关标签

    Vue 动画 requestAnimationFrame 指令 列表滚动
    相关文章