backTop组件(element vs iview)

总体结构

<transition name="el-fade-in">
    <div v-if="visible" @click.stop="handleClick" :style="{ 'right': styleRight, 'bottom': styleBottom }" class="el-backtop">
      <slot>
        <el-icon name="caret-top"></el-icon>
      </slot>
    </div>
  </transition>
复制代码

能够看到,backtop组件的结构是比较简单的。div > icon
icon是写在slot里面,这样子的写法有一个好处就是若是没有传递slot,那么这个icon将会显示出来,若是传递slot,那么icon将不会渲染,详见官网:cn.vuejs.org/v2/guide/co…javascript

主要功能

backTop组件有两个重要的点html

  1. visible: 什么时候显示
  2. click事件: 点击返回顶部
    若是咱们完成这两个功能 ,backTop基本就算完成了。
1. visible
mounted() {
    this.init();
    this.throttledScrollHandler = throttle(300, this.onScroll);
    this.container.addEventListener('scroll', this.throttledScrollHandler);
  },
  // methods:
    init() {
      this.container = document;
      this.el = document.documentElement;
      if (this.target) {
        this.el = document.querySelector(this.target);
        if (!this.el) {
          throw new Error(`target is not existed: ${this.target}`);
        }
        this.container = this.el;
      }
    },
复制代码

mounted时,获取proptarget(触发滚动的对象),将target元素赋值给this.elthis.container。监听this.containerscroll事件。同时,这里作了节流的处理。每300毫秒内重复触发scroll事件都会被认为只触发一次 。vue

我以为有两个节流与防抖的比喻,很是形象!!!
节流比如地铁限流,过一段时间才会放人进去
防抖比如坐电梯,只有没有人在上来了,电梯门才会关闭。不然持续有人上电梯,电梯是不会关门的。java

当滚动的高度大于visibilityHeight(backtop组件可见时的高度), visible才会为true。git

2. click

click事件除了emit以外,还要作的就是返回顶部。若是直接将this.el的滚动高度设置为0,固然是最简单最容易达到目的。可是不要忘了,element-ui的设计原则: 一致,反馈,效率,可控。feedback(反馈)这一点,明确的说到:github

反馈 Feedback
控制反馈:经过界面样式和交互动效让用户能够清晰的感知本身的操做; 页面反馈:操做后,经过页面元素的变化清晰地展示当前状态。element-ui

若是咱们可以让滚动条慢慢的滚回到顶部,而不是一会儿跳到顶部,是比较符合咱们的习惯的。show code:bash

scrollToTop() {
      let el = this.el;
      let step = 0;
      let interval = setInterval(() => {
        if (el.scrollTop <= 0) {
          clearInterval(interval);
          return;
        }
        step += 10;
        el.scrollTop -= step;
      }, 20);
    }
复制代码

attention please:iview

  • 这里并非匀速往上滑动,而是愈来愈快,每隔20ms,scrollTop减去的step都会加10。其实也很好理解,若是咱们的页面很是长,滚动条处于底部,咱们不但愿它像老人车过斑马线同样慢吞吞的过去。
  • 这样子减会有一个问题,若是减到scrollTop < 0呢? 这个时候就要请出咱们的MDN规范老师了

scrollTop 能够被设置为任何整数值,同时注意:async

  • 若是一个元素不能被滚动(例如,它没有溢出,或者这个元素有一个"non-scrollable"属性), scrollTop将被设置为0。
  • 设置scrollTop的值小于0,scrollTop 被设为0
  • 若是设置了超出这个容器可滚动的值, scrollTop 会被设为最大值.

测试

it('create', async() => {
    vm = createVue({
      template: `
        <div ref="scrollTarget" class="test-scroll"  style="height: 100px; overflow: auto">
          <div style="height: 10000px; width: 100%">
            <el-backtop target=".test-scroll">
              <span>test_up_text</span>
            </el-backtop>
          </div>
        </div>
      `
    }, true);
    expect(vm.$el).to.exist;
    expect(vm.$el.innerText).to.be.equal('');
    vm.$refs.scrollTarget.scrollTop = 2000;
    await wait();
    expect(vm.$el.innerText).to.be.equal('test_up_text');
  });
复制代码

测试用例中,对visible进行了测试。这个就比较简单,就无需多讲啦。

iview的异同

iview的代码
不一样 element iview
scroll事件 节流 -
滚动对象 prop(target)决定 window
显示隐藏 v-if display
动画 setInterval,逐渐加快 requestAnimationFrame, 匀速
动画的实现

提供了一个prop: duration,是滚动动画持续时间,单位毫秒

// scrollTop animation
export function scrollTop(el, from = 0, to, duration = 500, endCallback) {
    // `requestAnimationFrame `降级处理处理,代码省略
    const difference = Math.abs(from - to);
    const step = Math.ceil(difference / duration * 50);

    function scroll(start, end, step) {
        if (start === end) {
            endCallback && endCallback();
            return;
        }

        let d = (start + step > end) ? end : start + step;
        if (start > end) {
            d = (start - step < end) ? end : start - step;
        }

        if (el === window) {
            window.scrollTo(d, d);
        } else {
            el.scrollTop = d;
        }
        window.requestAnimationFrame(() => scroll(d, end, step));
    }
    scroll(from, to, step);
}
复制代码
  • requestAnimationFrame降级处理
  • step = Math.ceil(difference / duration * 50)计算每帧须要滚动的距离

为何是50
我也不知道 = = 我以为是算错了TAT, 应该是*(1000/60)才对

  • 设置每一帧的scrollTop
  1. 获取滚动的高度方式不一样
    iview: window.pageYOffset
    element-ui: el.scrollTop

如图,是在ie8中的使用结果,该用谁我想你们内心清楚吧^_^

总结

结合两个组件库的特色,总结以下:

  1. 使用requestAnimationFrame处理动画
  2. 传递target,指定触发滚动的对象,咱们要的backTop组件并不是必定是返回document.documentElement的顶部
  3. 滚动事件作节流处理
相关文章
相关标签/搜索