来, vue3实现一个高性能的弹幕组件

先有问题再有答案

  1. vue中如何获取dom的宽高属性?
  2. 100条数据就要设置100个dom嘛?
  3. 每个弹幕动画的运行效果 具体是如何实现的?
  4. 每个弹幕的长度是不固定的 如何正确获取长度值 给动画设置正确的结束位置?
  5. 如何利用raf实现高性能的动画效果?
  6. vue中的插槽究竟要如何使用?
  7. 插槽的本质是什么?

效果

组件功能

  1. 多行显示:row指定某条弹幕随机出现在第[1,N]行。
  2. 动态延迟:每条弹幕的显示延迟可以随机生成,使得弹幕的出现更加自然。
  3. 可复用实例:通过复用实例来优化性能,减少频繁创建和销毁 DOM 元素的开销。
  4. 自定义样式 :可以通过插槽(slot)来自定义每个弹幕项的样式和内容 长度不限
  5. 响应式更新:当传入的数据列表发生变化时,组件会自动更新并重新安排弹幕的显示。

使用方式

javascript 复制代码
<DanmuView :count="8" :row="4" :row-h="30" :delays="[500, 3000]" :frames="[0.5, 1.2]" :list-data="list">
                <template #default="{ data }">
                    <div class="item" @click="onItem(data)">
                        <img :src="data.icon" :width="18" class="icon" />
                        <div class="name">
                            {{ data.content }}
                        </div>
                    </div>
                </template>
            </DanmuView>
  • count:设置可复用的实例化数量。
  • row:设置弹幕轨道行数。
  • row-h:设置每行的高度。
  • delays:设置每条弹幕的随机延迟范围。
  • frames:设置每帧移动的距离范围。
  • list-data:传入弹幕数据列表。

源码:

github: vue3-dan-mu-view 伸手党同学 如果好用 还请点赞收藏哈 感谢

实现方案

这里可以分为两部分:

单条弹幕如何实现,需要注意哪些知识点。

多条弹幕实现 如何设计数据的缓存队列 dom池的复用逻辑。

单条弹幕实现

初始态:

结束态:

整体在X轴方向移动了 容器的宽度 + 自身宽度 的距离。

在vue中想要获取dom的width 可以通过ref实现。

位移动画 可以使用transform实现。

javascript 复制代码
<div ref="container" :style="[props.innerStyle, _style]" class="scroll-row">
        <div ref="content" class="scroll-content" :style="{ transform: `translateX(${translateX}px)` }">
            <slot :data="_item">
                {{ _item }}
            </slot>
        </div>
    </div>
javascript 复制代码
containerWidth = container.value?.offsetWidth || 0;
contentWidth = content.value?.offsetWidth || 0;
translateX.value = containerWidth;

内容自定义

每个item具体的样式 我们通过Vue 提供的插槽能力来实现

插槽 (slot) 特性的设计主要是为了实现组件内容的可重用和内容分发。这是一种让父组件可以向子组件动态插入内容的特性。

  1. 内容分发 (Content Projection) :插槽可以让开发者在使用一个组件的时候,向组件内部动态传入任意的 DOM 结构。这在开发类似于 Dialog、Card 这样的容器类型组件的时候非常有用。
  2. 作用域插槽 (Scoped Slot) :通过作用域插槽,父组件可以获取到子组件内部的数据,达到更复杂的定制和控制

对于插槽的应用不熟悉的同学 可以参考这篇文章 面试官:vue插槽有什么用?插槽的本质是什么?

item长度不定 如何解决长度问题?

因为内容是通过插槽用户自定义的 高度一般是固定的 但是长度都是不定的

我们要如何计算出每一个弹幕的长度 然后实现动画 移动到终态的效果呢?

很简单 设置translateX=-contentWidth 即可。

过渡效果

从初始态 移动到 结束态的中间过渡的过程 我们可以通过不断的修改translateX的值来实现。

这个过程会频繁的调用 这里我们使用 raf 来实现。 以保证动画和渲染频率一致。

window.requestAnimationFrame() 告诉浏览器------你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。rAF是浏览器提供的和渲染帧率保持同步的一个api.

具体参考 浏览器: 深入理解requestAnimationFrame优化js运行时

javascript 复制代码
 const step = () => {
            translateX.value -= _frame.value;
            if (translateX.value < -contentWidth) {
                nextTick(() => {
                    emit('onEnd');
                });
            } else {
                animationFrameId = requestAnimationFrame(step);
            }
        };
        step();

当运行结束 我们向外抛出一个onEnd事件 让调用方可以感知到 以便实现组件数据清空并重复使用。

多条弹幕实现

数据缓存队列

因为数据一般是源源不断从服务器拉取的 新数据到达后 因为Vue的响应性 数据发生改变后 视图会自动更新 这可能会导致视图闪烁 带来较差的视觉体验 所以需要在服务端数据和实际应用的数据中间加一个缓存队列 服务端数据不断向这个队列累加 当视图中某个view动画运行结束 在依次从缓存队列中取新的数据。

dom缓存池

在初始化组件时传入的count属性指定了 这个dom缓存池的数量 组件最多同时出现的DOM数量即为count值。

当一个dom动画运行结束 且没有更多数据可以运行时 这个dom会被加入到空闲队列中 等下次有新的数据入队时 在继续运行动画。 并不会重复的创建销毁DOM 组件通过这个缓存池实现DOM复用 以达到性能优化的目的。

javascript 复制代码
const handleScrollEnd = (instance: ItemIns) => {
  const item = list.value.shift();
  if (item) {
    instance.update(item);
  } else {
    idleList.push(instance);
  }
};

注意:如果初始化指定的count=10 实际传入的数据长度是5 那么组件依然会实例化10个item并且这10个item会正常的执行动画 只是会将多余的5个设置为重复的数据 并且设置一个较大的位移 在视图上不可见

建议根据视图的高度和弹幕的轨道高度 保证每个轨道有1~2个弹幕即可。

相关推荐
fmdpenny21 分钟前
Vue3初学之商品的增,删,改功能
开发语言·javascript·vue.js
小美的打工日记33 分钟前
ES6+新特性,var、let 和 const 的区别
前端·javascript·es6
helianying5542 分钟前
云原生架构下的AI智能编排:ScriptEcho赋能前端开发
前端·人工智能·云原生·架构
@PHARAOH1 小时前
HOW - 基于master的a分支和基于a的b分支合流问题
前端·git·github·分支管理
涔溪1 小时前
有哪些常见的 Vue 错误?
前端·javascript·vue.js
Again_acme1 小时前
20250118面试鸭特训营第26天
服务器·面试·php
程序猿online1 小时前
前端jquery 实现文本框输入出现自动补全提示功能
前端·javascript·jquery
2401_897579652 小时前
ChatGPT接入苹果全家桶:开启智能新时代
前端·chatgpt
DoraBigHead2 小时前
JavaScript 执行上下文:一场代码背后的权谋与博弈
前端
HappyAcmen2 小时前
Java中List集合的面试试题及答案解析
java·面试·list