eyasliu / blog

博客文章
179 stars 13 forks source link

基于flex的order实现 carousel 轮播图 #21

Open eyasliu opened 6 years ago

eyasliu commented 6 years ago

原因

项目里需要使用轮播图,electron + vue 技术栈,项目应用一旦启动会持续运行24小时,并且机器性能较差,所以很关注两个点

  1. 内存泄漏
  2. 性能

目前社区的轮播组件,大多只是适用于常规 web 应用,经过内部测试后,并不能满足内存和性能方面的要求,所以需要自己实现轮播组件

思路

最开始找到了这篇文章,里面讲解了传统的轮播图实现思路和作者原创的轮播思路,并在文末给出了性能较高的原创方案。

作者的原创方案性能是很高了,但是我注意到每次执行轮播都需要移动一个 DOM 节点,这会触发浏览器重排重绘,性能依旧不够高,还可以继续优化。

首先想到了 flex 布局的 order 属性:https://developer.mozilla.org/zh-CN/docs/Web/CSS/order

兼容性

可以看到只有现代浏览器才支持,如果要兼容老久浏览器就不用考虑本方案了,我的环境是electron 2.0,集成的chrome 61,可放心使用。

实现方案

本文章只记录实现方案与伪代码,不会给出 demo。

基本功能实现

html结构

<div class="carousel">
  <div class="carousel-container" style="transition-duration: 0ms; transform: translate3d(0px, 0px, 0px);">
    <!-- 轮播列表元素 -->
    <div class="carousel-item" style="order: 0;"></div>
    <div class="carousel-item" style="order: 1;"></div>
    <div class="carousel-item" style="order: 2;"></div>
    <div class="carousel-item" style="order: 3;"></div>
    <div class="carousel-item" style="order: 4;"></div>
  </div>
</div>
<style>
  .carousel {
    width: 100%;
  }
  .carousel-container {
    width: 100%;
    display: flex;
    transition-property: transform;
  }
  .carousel-item {
    width: 100%;
  }
</style>

从里面元素开始解释

  1. 父级设置 display: flex ,子级可以通过 order 属性实现排序,这种排序虽然依然会引发重排和重绘,但是开销更小
  2. 外围一层元素,使用 transition 实现 动画,使用 transform 的 translate3d 实现硬件加速与显示范围。在非动画状态,X轴的位置永远都是 0,在动画状态,才给 X轴 赋值,所以整个组件其实就是在做两件事: 顺序X轴位置(也就是动画)
    1. 顺序:非动画状态需要 X 轴一直为0,那么就要保证当前要显示的轮播元素的 order 值最小,我暂时约定最小为 0,因为动画涉及到下一张,所以当前轮播的order 为 0,下一张为 1,其他的只要大于1 即可。
    2. 动画,如果需求是切换的时候不需要动画,那么保证顺序就已经完成了轮播切换了,但需求通常需要动画。动画的实现由三部分,起始状态结束状态重置状态
    3. 起始状态:动画一开始,就是要在当前轮播元素开始,对应的X轴是0,起就是静止状态,所以起止状态不需要设置,默认就是了,所以通常其实状态无需处理
    4. 结束状态:结束的状态是下一张轮播元素完全显示,也就是X增加一个 轮播元素的宽度。动画时间 transition-duration 赋值 500ms,就能实现动画。
    5. 重置状态:动画完成后,重新计算各个元素的 order 值,把 X 轴重设为0,动画时间重设为0

到此就完成了轮播组件的基本功能

功能扩展

自动轮播

先实现一个函数 next() 方法,定时调用

拖动滚动

  1. 记录开始拖动时鼠标位置的 X轴
  2. 移动过程中获取鼠标位置X轴,减去开始拖动时的X轴位置,得到X轴移动的距离,再把这个距离数字赋值给 translate3d 的X轴

反向动画与拖动

通常的轮播都是 从右往左 滚动的,但是有时需要兼容 从左往右,实现方案:

非动画状态无需调整,主要关注动画状态。

  1. 排序:要反过来排序,当前显示的元素 order 为0,下一张为 -1,其他的小于 -1即可
  2. 动画的不同状态都需要调整
    1. 起始状态:X轴位置:-1 * (轮播条数 - 1) * 轮播宽度
    2. 结束状态:X轴位置:-1 * (轮播条数 - 与上条间隔数量) * 轮播宽度
    3. 重置状态:X轴位置:0,排序重置为正向

反向拖动,如果拖动的时候拖动的距离是个正数,则马上更新顺序为反向,如果为负数,马上更新顺序为正向

总结

该方案的性能很高,但是兼容性不太好。而且实现过程中,对元素的排序计算如果涉及到反向动画的话会比较复杂