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. 滾動事件作節流處理
相關文章
相關標籤/搜索