国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

基于vue.js 2.x的虛擬滾動條的示例代碼

2019-11-19 14:29:15
字體:
來源:轉載
供稿:網友

前言

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

設計

好, 現在開始我們的設計滾動條的步驟:

設計滾動條dom

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

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

最后:我們設計了scrollPanel、vBar、hBar, 我們需要一個父div來把他們包裝起來,然后加個樣式: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('mousemove', move);      document.addEventListener('mouseup', function(e) {        vm.mousedown = false;        vm.hideVBar();        document.removeEventListener('mousemove', move);      });    }    this.listeners.push({      dom: vm.vScrollBar.el,      event: t,      type: "mousedown"    });    vm.vScrollBar.el.addEventListener('mousedown', 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('touchmove', move);        pannel.addEventListener('touchend', function(e) {          vm.mousedown = false;          vm.hideBar();          pannel.removeEventListener('touchmove', move);        });      }    }    pannel.addEventListener('touchstart', 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, 'height').replace('px', "");    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('vscroll', 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的了解更多了,最后,附上github項目地址

以上部分就是這個組件的核心源碼了。希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 聂荣县| 凌云县| 崇义县| 怀来县| 皮山县| 马山县| 革吉县| 遵义市| 太白县| 尉氏县| 宁都县| 扎鲁特旗| 普陀区| 灵石县| 阿克苏市| 巍山| 潞城市| 布拖县| 屏东县| 新建县| 平定县| 汤原县| 壤塘县| 莫力| 岳普湖县| 苍山县| 闻喜县| 阳东县| 天水市| 陆川县| 红河县| 台江县| 万年县| 嘉峪关市| 乐亭县| 房山区| 宁晋县| 多伦县| 资中县| 夏河县| 武功县|