【问题标题】:Vue.js - improving performance of infinite list loopVue.js - 提高无限列表循环的性能
【发布时间】:2019-11-24 04:03:39
【问题描述】:

我需要一个用户列表来无限地垂直循环。 我知道我应该使用“translateY”而不是“top”来处理那种东西——但我不知道怎么做。 我已经完成了“顶级”版本并且可以正常工作。有什么想法可以改进吗?

谢谢大家!

Example in Codepen

<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>

【问题讨论】:

    标签: javascript css vue.js animation


    【解决方案1】:

    这是我的尝试:

    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>

    您不必进行我所做的所有更改,如果您愿意,可以选择性地进行大多数更改。主要变化是:

    1. key 放在列表项上,以便 Vue 移动 DOM 节点,而不是在发生随机播放时更新所有节点。
    2. 使用包装器&lt;div&gt;,这样实际上只有一个元素在移动(我去掉了绝对定位作为其中的一部分)。
    3. 根据要求使用translateY
    4. 摆脱setInterval,您应该只需要requestAnimationFrame。通过跟踪经过的时间来检查动画速度。
    5. 当一行跳转时,我只是将对象移动到数组的末尾而不是复制。
    6. 当组件被销毁时动画被取消。

    更新:

    三个进一步的变化:

    1. 我已添加 box-sizing: border-box 以修复计算中的 2px 不准确。
    2. DOM 节点重新排序现在是批处理的,仅每 400 像素发生一次。不知道这是否真的是一个好主意,对于这样一个简单的例子来说,它并没有任何区别。
    3. 我已将translateY 舍入以使用整个像素。对我来说,这看起来稍微好一些,但在像素比更高的屏幕上,我可以想象它可能看起来更糟。

    根据具体情况,还有可能适用的进一步优化。

    1. 可以省略不可见的行。
    2. 可以通过将translateY 应用于每一行来完全避免重新排序,但对于大量行可能不切实际。

    使用过渡或 CSS 动画对此进行动画处理会很棘手,因为需要将行跳回底部。如果每一行都是独立的动画,我不确定保持所有动画同步有多容易。

    【讨论】:

    • 非常优雅的解决方案,谢谢!我不得不更改while (this.offset &lt; -42) { this.rows.push(this.rows.shift()) this.offset += 42 },因为顶部和底部的 1px 边框丢失了,而且有点不稳定。我在尝试使用 css 转换为 translateY 属性设置动画时犯了错误 - 只需通过 javascript 设置它就可以了。这个版本的 CPU 密集度是否比我更改 top 属性的版本低?
    • 不幸的是,运行一段时间后,我可以听到我的 MacBook 的通风口关闭。这是不使用浏览器的 CSS 转换方法而是在 while 循环中更改属性时的警告吗?
    • @uNki 我不知道这里是否可以使用纯 CSS 解决方案。即使是这样,如果您的 CPU 风扇也这样做,我也不会感到惊讶。我希望translateY 的强度低于top,但它需要一个更复杂的示例(即更多的 DOM 节点)才能真正看到它有什么不同。性能调整动画是一根钢丝,很容易通过一个看似无害的错误撤消所有优化。
    猜你喜欢
    • 1970-01-01
    • 2020-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 2018-11-08
    • 1970-01-01
    • 2014-07-13
    相关资源
    最近更新 更多