首頁 >web前端 >js教程 >在vue.js中有關2.x的虛擬捲軸

在vue.js中有關2.x的虛擬捲軸

亚连
亚连原創
2018-06-09 17:25:551573瀏覽

本篇文章主要介紹了基於vue.js 2.x的虛擬捲軸的範例程式碼,現在分享給大家,也給大家做個參考。

前言

記得以前偶然有一次瀏覽過一個開源的cms項目,發現這個項目的左邊的選單已經超出了windows的寬度,我就好奇為什麼沒出捲軸呢?然後我仔細一看,發現它左側有一個小的p,然後我嘗試著拖動它,發現竟能和原生的滾動條一樣!可以透過查看它的源碼,發現了這款滾動條的叫做slimScroll,然後我去它的github倉庫 看了下,研究了一下源碼,給我的感覺是我也能做出來一樣的滾動條!透過vue實現!

設計

好,現在開始我們的設計捲軸的步驟:

設計捲軸dom

#首先要思考的是: 如果要讓你需要滾動的內容滾動的話,首先一點是它的父dom必須為固定長寬,即超出部分要隱藏掉,即加了個樣式: overflow: hidden ,所以,我們為所要滾動的內容加個包裝,使它的長寬和父dom相等,然後有一個樣式叫: overflow: hidden ,這個包裝的元素就叫scrollPanel

其次:我們知道,我們要做到與原生捲軸一樣強大!就必須設計水平滾動條和垂直滾動條,滾動條和scrollPanel屬於兄弟節點之間的關係,因為滾動條的存在不能使原本的樣式排版錯誤,並且支持top、left來控制其位置,所以滾動條的position必須是absolute,好了,我們叫水平滾動條為:hBar,垂直滾動條為:vBar

最後:我們設計了scrollPanel、vBar、hBar, 我們需要一個父p來把他們包裝起來,然後加個樣式:position: relative

實踐

#設計元件結構

##首先,我們的外掛一共是4個組件,其中3個是子組件,1個是父組件,分別是: vueScroll (父組件)、 scrollPanel (包裹需要滾動內容的子組件)、 vBar (垂直滾動條)、 hBar (水平滾動條)

其次,讓我們設計一下各元件所分管的功能。這裡的組件分為控制層組件和展示組件(熟悉react的同學應該有所了解),展示層組件只完成展示的功能: vBar 、 hBar 、 scrollPanel ,控制層組件有點類似於cpu,可以控制子組件的各個狀態,如寬、高、顏色、透明度、位置等等。控制層元件就是: vueScroll 。

具體實作

hBar/vBar

#hBar/vBar 這兩個分別為水平捲軸和垂直捲軸,所實現的功能大致是一樣的,所以舊放在一起說了,這裡以vBar 為例。

props 接收父元件傳過來的屬性,具體為:

{
  height: vm.state.height + 'px', //滚动条的高度
  width: vm.ops.width, // 滚动条的宽度
  position: 'absolute', 
  background: vm.ops.background, // 滚动条背景色
  top: vm.state.top + 'px', // 滚动条的高度
  transition: 'opacity .5s', // 消失/显示 所用的时间
  cursor: 'pointer', //
  opacity: vm.state.opacity, // 透明度
  userSelect: 'none' 
 }

2 事件,主要是當滑鼠移動的時候,顯示捲軸。

...
render(_c){
  return _c(
    // ...
    {
      mouseenter: function(e) {
        vm.$emit('showVBar'); // 触发父组件事件,显示滚动条
      }
    }
    // ...
  )
}

其中 state 表示狀態,是在執行時可改變的,而 ops 則是設定參數,是使用者傳過來的。

scrollPanel

包裹捲動內容的元件,樣式需設定為: overflow: hidden 。

1、樣式

var style = vm.scrollContentStyle;
 style.overflow = 'hidden';
 // ...
 {
   style: style
 }
 // ...

2、事件

// ...
  render(_c) {
    // ...
      on: {
        mouseenter: function() {
          vm.$emit('showBar');
        },
        mouseleave: function() {
          vm.$emit('hideBar');
        }
      }
    // ...
  }
 // ...

#vuescroll

控制元件。控制子組件顯示的狀態,新增各種監聽事件等。

1、取得子元件的dom元素,用來取得dom的即時資訊。

// ...
   initEl() {
    this.scrollPanel.el = this.$refs['vueScrollPanel'] && this.$refs['vueScrollPanel'].$el;
    this.vScrollBar.el = this.$refs['vScrollBar'] && this.$refs['vScrollBar'].$el;
    this.hScrollBar.el = this.$refs['hScrollBar'] && this.$refs['hScrollBar'].$el;
  }
  // ...

2、顯示捲軸

顯示捲軸,包括顯示水平捲軸和顯示垂直滾動條,這裡以顯示垂直滾動條為例:

// ...
    var temp;
    var deltaY = {
      deltaY: this.vScrollBar.ops.deltaY // 获取用户配置的deltaY
    };
    if(!this.isMouseLeavePanel || this.vScrollBar.ops.keepShow){
      if ((this.vScrollBar.state.height = temp = this.getVBarHeight(deltaY))) { // 判断条件
        // 重新设置滚动条的状态
        this.vScrollBar.state.top = this.resizeVBarTop(temp);
        this.vScrollBar.state.height = temp.height;
        this.vScrollBar.state.opacity = this.vScrollBar.ops.opacity;
      }
    }
  // ...

3 、取得滾動條的高度

因為dom元素的高度不是固定的,所以你要即時地取得dom 真實的高度,滾動條的高度計算公式如下:

var height = Math.max(
      scrollPanelHeight / 
      (scrollPanelScrollHeight / scrollPanelHeight), 
      this.vScrollBar.minBarHeight
      );

即: 捲動條的高度:scrollPanel的高度== scrollPanel的高度:dom元素高度

4、resizeVBarTop ,為了防止誤差,並且可以求出滾動條距離父元素的高度。

resizeVBarTop({height, scrollPanelHeight, scrollPanelScrollHeight, deltaY}) {
  // cacl the last height first
  var lastHeight = scrollPanelScrollHeight - scrollPanelHeight - this.scrollPanel.el.scrollTop;
  if(lastHeight < this.accuracy) {
    lastHeight = 0;
  }
  var time = Math.abs(Math.ceil(lastHeight / deltaY));
  var top = scrollPanelHeight - (height + (time * this.vScrollBar.innerDeltaY));
  return top;
}

5、監聽滾輪滾動的事件。

// ...
  on: {
    wheel: vm.wheel
  }
  // ...
   wheel(e) {
    var vm = this;
    vm.showVBar();
    vm.scrollVBar(e.deltaY > 0 ? 1 : -1, 1);
    e.stopPropagation();
  }
  // ...

6、監聽捲軸拖曳事件

listenVBarDrag: function() {
    var vm = this;
    var y;
    var _y;
    function move(e) {
      _y = e.pageY;
      var _delta = _y - y;
      vm.scrollVBar(_delta > 0 ? 1 : -1, Math.abs(_delta / vm.vScrollBar.innerDeltaY));
      y = _y;
    }
    function t(e) {
      var deltaY = {
        deltaY: vm.vScrollBar.ops.deltaY
      };
      if(!vm.getVBarHeight(deltaY)) {
        return;
      }
      vm.mousedown = true;
      y = e.pageY; // 记录初始的Y的位置
      vm.showVBar();
      document.addEventListener(&#39;mousemove&#39;, move);
      document.addEventListener(&#39;mouseup&#39;, function(e) {
        vm.mousedown = false;
        vm.hideVBar();
        document.removeEventListener(&#39;mousemove&#39;, move);
      });
    }
    this.listeners.push({
      dom: vm.vScrollBar.el,
      event: t,
      type: "mousedown"
    });
    vm.vScrollBar.el.addEventListener(&#39;mousedown&#39;, t); // 把事件放到数组里面,等销毁之前移除掉注册的时间。
  }

7、適配行動端,監聽 touch 事件。原理跟拖曳事件差不多,無非就是多了個判斷,來判斷當前方向是x還是y。

listenPanelTouch: function() {
    var vm = this;
    var pannel = this.scrollPanel.el;
    var x, y;
    var _x, _y;
    function move(e) {
      if(e.touches.length) {
        var touch = e.touches[0];
        _x = touch.pageX;
        _y = touch.pageY;
        var _delta = void 0;
        var _deltaX = _x - x;
        var _deltaY = _y - y;
        if(Math.abs(_deltaX) > Math.abs(_deltaY)) {
          _delta = _deltaX;
          vm.scrollHBar(_delta > 0 ? -1 : 1, Math.abs(_delta / vm.hScrollBar.innerDeltaX));
        } else if(Math.abs(_deltaX) < Math.abs(_deltaY)){
          _delta = _deltaY;
          vm.scrollVBar(_delta > 0 ? -1 : 1, Math.abs(_delta / vm.vScrollBar.innerDeltaY));
        }
        x = _x;
        y = _y;
      }
    }
    function t(e) {
      var deltaY = {
        deltaY: vm.vScrollBar.ops.deltaY
      };
      var deltaX = {
        deltaX: vm.hScrollBar.ops.deltaX
      };
      if(!vm.getHBarWidth(deltaX) && !vm.getVBarHeight(deltaY)) {
        return;
      }
      if(e.touches.length) {
        e.stopPropagation();
        var touch = e.touches[0];
        vm.mousedown = true;
        x = touch.pageX;
        y = touch.pageY;
        vm.showBar();
        pannel.addEventListener(&#39;touchmove&#39;, move);
        pannel.addEventListener(&#39;touchend&#39;, function(e) {
          vm.mousedown = false;
          vm.hideBar();
          pannel.removeEventListener(&#39;touchmove&#39;, move);
        });
      }
    }
    pannel.addEventListener(&#39;touchstart&#39;, t);
    this.listeners.push({
      dom: pannel,
      event: t,
      type: "touchstart"
    });
  }

8、滾動內容

滾動內容的原則無非是改變 scrollPanel 的 scrollTop/scrollLeft 來達到控制內容上下左右移動的目的。

scrollVBar: function(pos, time) {
    // >0 scroll to down <0 scroll to up
     
    var top = this.vScrollBar.state.top; 
    var scrollPanelHeight = getComputed(this.scrollPanel.el, &#39;height&#39;).replace(&#39;px&#39;, "");
    var scrollPanelScrollHeight = this.scrollPanel.el.scrollHeight;
    var scrollPanelScrollTop = this.scrollPanel.el.scrollTop;
    var height = this.vScrollBar.state.height;
    var innerdeltaY = this.vScrollBar.innerDeltaY;
    var deltaY = this.vScrollBar.ops.deltaY;
    if (!((pos < 0 && top <= 0) || (scrollPanelHeight <= top + height && pos > 0) || (Math.abs(scrollPanelScrollHeight - scrollPanelHeight) < this.accuracy))) {
      var Top = top + pos * innerdeltaY * time;
      var ScrollTop = scrollPanelScrollTop + pos * deltaY * time;
      if (pos < 0) {
        // scroll ip
        this.vScrollBar.state.top = Math.max(0, Top);
        this.scrollPanel.el.scrollTop = Math.max(0, ScrollTop);
      } else if (pos > 0) {
        // scroll down
        this.vScrollBar.state.top = Math.min(scrollPanelHeight - height, Top);
        this.scrollPanel.el.scrollTop = Math.min(scrollPanelScrollHeight - scrollPanelHeight, ScrollTop);
      }
    }
    // 这些是传递给父组件的监听滚动的函数的。
    var content = {};
    var bar = {};
    var process = "";
    content.residual = (scrollPanelScrollHeight - scrollPanelScrollTop - scrollPanelHeight);
    content.scrolled = scrollPanelScrollTop;
    bar.scrolled = this.vScrollBar.state.top;
    bar.residual = (scrollPanelHeight - this.vScrollBar.state.top - this.vScrollBar.state.height);
    bar.height = this.vScrollBar.state.height;
    process = bar.scrolled/(scrollPanelHeight - bar.height);
    bar.name = "vBar";
    content.name = "content";
    this.$emit(&#39;vscroll&#39;, bar, content, process);
  },

9、銷毀註冊的事件。

剛才我們已經把註冊事件放到listeners陣列裡面了,我們可以在beforedestroy鉤子裡將他們進行銷毀。

// remove the registryed event.
  this.listeners.forEach(function(item) {
    item.dom.removeEventListener(item.event, item.type);
  });

執行截圖

PC端運行截圖如下圖所示:

註冊監聽事件以後如下圖所示:

在手機上運行截圖:

#

可以看出,跟原生捲軸表現效果一致。

結語&感想

以上就基本把我設計的滾動條設計完了,首先很感激掘金給了我這麼一個分享平台,然後感謝slimScroll的作者給了我這麼一個想法。做完這個插件, 我對dom元素的scrollWidth、scrollHeigh、scrollTop、scrollLeft的了解更多了

上面是我整理給大家的,希望今後會對大家有幫助。

相關文章:

vue路由傳參3種的基本模式(詳細教學)

在JavaScript中有關的trick

在webpack中實作動態引入檔案

#

以上是在vue.js中有關2.x的虛擬捲軸的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn