javascript - Vue.js - 提高无限列表循环的性能
问题描述
我需要一个用户列表来无限地垂直循环。我知道我应该使用“translateY”而不是“top”来处理那种东西——但我不知道怎么做。我已经完成了“顶级”版本并且可以正常工作。有什么想法可以改善吗?
多谢你们!
<div id="app">
<div id="rows">
<div class="row" v-for="row in rows" v-bind:style="{ top: row.top + 'px' }">
{{row.id}}
</div>
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data() {
return {
rows: []
}
},
created() {
for (let i = 0; i < 30; i++) {
this.rows.push({
id: i,
top: i * 40
})
}
setInterval(() => {
window.requestAnimationFrame(this.update);
}, 16);
},
methods: {
update() {
this.rows.forEach(row => {
row.top -= 0.5
});
if (this.rows[0].top <= -40) {
this.rows.push({
id: this.rows[0].id,
top: (this.rows.length - 1) * 40
})
this.rows.shift();
}
}
}
})
</script>
<style>
#rows {
position: relative;
}
.row {
position: absolute;
width: 100%;
height: 40px;
border: 1px solid black;
}
</style>
解决方案
这是我的尝试:
new Vue({
el: '#app',
data() {
const rows = []
for (let i = 0; i < 30; i++) {
rows.push({
id: i
})
}
return {
offset: 0,
rows
}
},
mounted () {
this.frameTime = Date.now()
const animate = () => {
this.animationId = requestAnimationFrame(() => {
this.update()
animate()
})
}
animate()
},
beforeDestroy () {
cancelAnimationFrame(this.animationId)
},
methods: {
update() {
const now = Date.now()
const elapsed = now - this.frameTime
this.offset -= elapsed / 16
this.frameTime = now
if (this.offset < -400) {
while (this.offset < -40) {
this.rows.push(this.rows.shift())
this.offset += 40
}
}
}
}
})
* {
box-sizing: border-box;
}
#rows {
border: 1px solid #f00;
height: 200px;
overflow: hidden;
}
.row {
width: 100%;
height: 40px;
border: 2px solid black;
}
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<div id="app">
<div id="rows">
<div :style="{ transform: `translateY(${Math.round(offset)}px)` }">
<div
v-for="row in rows"
:key="row.id"
class="row"
>
{{row.id}}
</div>
</div>
</div>
</div>
您不一定要进行我所做的所有更改,如果您愿意,可以有选择地进行大多数更改。主要变化是:
- 将 a
key
放在列表项上,以便 Vue 在 shuffle 发生时移动 DOM 节点而不是更新所有节点。 - 使用包装器
<div>
,这样实际上只有一个元素在移动(我摆脱了绝对定位作为其中的一部分)。 - 根据
translateY
要求使用 。 - 摆脱
setInterval
,你应该只需要requestAnimationFrame
这个。通过跟踪已经过去的时间来检查动画速度。 - 当一行跳转时,我只是将对象移动到数组的末尾而不是制作副本。
- 当组件被销毁时动画被取消。
更新:
三个进一步的变化:
- 我已添加
box-sizing: border-box
以修复计算中的 2px 不准确性。 - DOM 节点重新排序现在批处理,每 400 像素发生一次。不知道这是否真的是一个好主意,对于这样一个简单的例子来说,它并没有什么区别。
- 我已经四舍五入
translateY
以使用整个像素。对我来说,这看起来稍微好一些,但在像素比更高的屏幕上,我可以想象它可能看起来更糟。
根据具体情况,还有可能适用的进一步优化。
- 可以省略不可见的行。
- 通过将 应用于每一行可以完全避免重新排序
translateY
,尽管对于大量行可能不切实际。
由于需要行跳回底部,因此使用过渡或 CSS 动画对此进行动画处理会很棘手。如果每一行都是独立动画的,我不确定保持所有动画同步有多容易。
推荐阅读
- python - 如何使用 struct.pack 转换可变长度的整数元组?
- google-sheets - 引用包含特定日期的单元格
- android - React Native 0.61.5 应用程序从 0.61.4 升级后一直停止
- google-apps-script - 谷歌照片API添加照片不起作用,上传似乎工作
- javascript - 我的页面出现问题,因为 onclick 音频不工作
- javascript - 使用 Lodash 从嵌套数组中删除对象
- visual-studio - 升级后Unity编译错误
- laravel - 我可以在通过 laravel 发送电子邮件时将隐藏的令牌放入电子邮件中吗
- json - 如何修复将 yaml 转换为 JSON 的 YAML 文件中的错误?在这种情况下不允许映射值”
- c# - 如何在鼠标路径上生成对象?统一 3D