重寫移動端滾動條[iScroll.js核心代碼]

最近寫一個popover彈窗時,發現這個滾動條是真的醜啊,決定從新擼一個滾動條:css

首先我們回顧一下移動端瀏覽器滾動條特性:html

  • 滾動條在開始滾動時漸顯,滾動結束後漸隱
  • 滾動條不佔內容區寬度,懸浮固定
  • 滾動條高度(深灰)和滾動區可視高度(淺灰)比等於滾動區可視高度和滾動目標的高度
  • 當滾動目標的高度小於滾動區可視高度的時候,滾動條不顯示,而且沒法滾動
  • 只有在滾動滾動目標時,才能觸發滾動
  • 當滾動條頂部觸頂和底部觸底的時候,不能繼續滾動
  • 只有在滾動大於一個固定值時,才被視爲滾動開始
  • 根據滾動的差值,計算是向上滾動仍是向下滾動
  • 滾動條是動態生成的

好啦,接下來我們開始一步一步實現,須要哪些知識點:node

  • 漸隱漸顯
opacity: 1; transition: opacity 500ms ease-in-out;

opacity: 0; transition: opacity 500ms ease-in-out;
複製代碼
  • 懸浮固定
position 定位
滾動條寬度width爲3px;
複製代碼
  • 聲明變量
var conHeight = contentBox.offsetHeight;        //滾動目標的總體高度
var _width = mainBox.clientWidth;               //滾動可視區的寬度
var _height = mainBox.clientHeight;             //滾動可視區的高度
var _scrollWidth = element.offsetWidth;         //滾動條的寬度
var _left = _width - _scrollWidth;              //定位滾動條應該距離左邊寬度
複製代碼

看到這裏是否是有種一目瞭然的感受,因此滾動條的寬度就是:
var _scrollHeight = parseInt(_height * (_height / conHeight))瀏覽器

  • 當滾動目標的高度小於滾動區可視高度的時候,滾動條不顯示,反之則顯示,不過透明度爲0,哈哈,是否是很賤...

切記不顯示和透明度爲0仍是不同的bash

if (_scrollHeight >= mainBox.clientHeight) {
    element.parentNode.style.display = "none";
} else {
    element.parentNode.style.opacity = "0"; //有滾動條的話先將透明度設置爲0
}
複製代碼
  • 只有在滾動滾動目標時,才能觸發滾動
//若是滾動的不是目標元素,此處只有觸摸的是a時才能滾動,不然直接return;
if (event.changedTouches[0].target.tagName !== 'A') return false;
複製代碼
  • 當滾動條頂部觸頂和底部觸底的時候,不能繼續滾動
if (elT === '0rem' && this.direction == '1') console.log('到頂了不要再向上滑了');
if (elT === parseInt(elParentH) - parseInt(elH) + 'rem' && this.direction == '0') console.log('到底了不要再往下滑了');
複製代碼
  • 只有在滾動大於一個固定值時,才被視爲滾動開始

這裏咱們暫且設置這個最小移動高度爲 minRange = 10;
app

  • 根據滾動的差值,計算是向上滾動仍是向下滾動,怎麼判斷滾動差值呢,好,clientY來了函數

  • 滾動條是動態生成的,這個好辦,直接粘代碼:學習

var _scrollBox = doc.createElement('div');
var _scroll = doc.createElement('div');
_scrollBox.appendChild(_scroll);
_scroll.className = className;
mainBox.appendChild(_scrollBox);
複製代碼

好了,接下來就是最關鍵的時候了,怎麼去把這些邏輯聯動起來呢,這時候HTML5觸摸事件就粉墨登場了:touchstart touchmove touchend三劍客 具體怎麼使用,你們就自行百度了,下面附上完整的js代碼,供你們研究學習:優化

/*
 * 優化瀏覽器滾動條
 * */
var doc = document;
var _wheelData = -1;

//移動端touch滾動事件
function load(obj, handler, element) {

  // 因爲touch滾動的特殊性,不能使用剛剛封裝的bind函數,touchstart,touchmove,touchend另外寫
  document.addEventListener('touchstart', touch, {
    passive: false
  });

  document.addEventListener('touchmove', touch, {
    passive: false
  });
  document.addEventListener('touchend', touch, {
    passive: false
  });

  function touch(event) {
    var event = event || window.event;
    var distance, clientY_start, clientY_end, minRange = 10;
    var elT = element.style.top; //目標元素css的top值
    var elH = element.style.height; //目標元素高度
    var elParentH = element.parentNode.style.height; //目標元素直屬父節點高度
    this.clientY_start;
    this.direction;
    //若是滾動的不是目標元素,此處只有觸摸的是a時才能滾動,不然直接return;
    if (event.changedTouches[0].target.tagName !== 'A') return false;
    this.callbackFun = function () {
      this.direction == '1' ? console.log('往回滑') : console.log('往下滑');
      if (elT === '0rem' && this.direction == '1') console.log('到頂了不要再向上滑了');
      if (elT === parseInt(elParentH) - parseInt(elH) + 'rem' && this.direction == '0') console.log('到底了不要再往下滑了');
    };

    // 切記pageY = clientY + 頁面滾動高度
    switch (event.type) {
      case "touchstart":
        clientY_start = event.touches[0].clientY; //觸摸點在Y軸方向的座標
        this.clientY_start = clientY_start;
        //顯示滾動條
        // element.parentNode.setAttribute('style', 'transition: opacity 500ms ease-in-out; opacity: 1');
        element.parentNode.cssText += 'transition: opacity 500ms ease-in-out;';
        setTimeout(function () {
          element.parentNode.style.opacity = '1';
        }, 500);
        break;
      case "touchend":
        this.callbackFun();
        //隱藏滾動條
        // element.parentNode.setAttribute('style', 'transition: opacity 500ms ease-in-out; opacity: 0'); //這種方法會將目標元素原來的css給重寫,故不能用setAttribute
        element.parentNode.cssText += 'transition: opacity 500ms ease-in-out';
        setTimeout(function () {
          element.parentNode.style.opacity = '0';
        }, 500);
        break;
      case "touchmove":
        event.preventDefault();
        clientY_end = event.changedTouches[0].clientY;
        //判斷移動的方向
        distance = clientY_end - this.clientY_start;
        // console.log(distance);
        handler(-distance);
        if (this.clientY_start + minRange < clientY_end) {
          this.direction = '1';
        } else if (this.clientY_start - minRange > clientY_end) {
          this.direction = '0';
        }
        break;
    }
  }
}

function addScroll() {
  this.init.apply(this, arguments);
}

addScroll.prototype = {
  init: function (mainBox, contentBox, className) {
    var mainBox = doc.getElementById(mainBox);
    var contentBox = doc.getElementById(contentBox);
    var scrollDiv = this._createScroll(mainBox, className);
    this._resizeScorll(scrollDiv, mainBox, contentBox); //調整滾動條
    this._touchScroll(scrollDiv, mainBox, contentBox); //調整滾動條
  },

  //建立滾動條
  _createScroll: function (mainBox, className) {
    var _scrollBox = doc.createElement('div');
    var _scroll = doc.createElement('div');
    _scrollBox.appendChild(_scroll);
    _scroll.className = className;
    mainBox.appendChild(_scrollBox);
    return _scroll;
  },

  //調整滾動條
  _resizeScorll: function (element, mainBox, contentBox) {
    var p = element.parentNode;
    var conHeight = contentBox.offsetHeight;
    var _width = mainBox.clientWidth;
    var _height = mainBox.clientHeight;
    var _scrollWidth = element.offsetWidth;
    var _left = _width - _scrollWidth;
    p.style.width = "3px";
    p.style.height = _height / 100 * 2 + "rem";
    p.style.left = _left / 100 * 2 + "rem";
    p.style.top = "0rem";
    p.style.position = "absolute";
    p.style.background = "#ccc";
    console.log('_width:' + _width + '---_height:' + _height + '---_scrollWith:' + _scrollWidth + '---_left:' + _left + '---conHeight:' + conHeight);
    var _scrollHeight = parseInt(_height * (_height / conHeight));
    if (_scrollHeight >= mainBox.clientHeight) {
      p.style.display = "none";
    } else {
      p.style.opacity = "0"; //有滾動條的話先將透明度設置爲0
    }
    element.style.height = _scrollHeight / 100 * 2 + "rem";
  },

  //移動端touch滾動事件
  _touchScroll: function (element, mainBox, contentBox) {
    var node = typeof mainBox == "string" ? $(mainBox) : mainBox;
    var flag = 0,
      rate = 0,
      wheelFlag = 0;
    if (node) {
      load(node, function (data) {
        wheelFlag += data;
        if (_wheelData >= 0) {
          flag = _wheelData;
          element.style.top = flag / 100 * 2 + "rem";
          wheelFlag = _wheelData * 12;
          _wheelData = -1;
        } else {
          flag = wheelFlag / 12;
        }
        if (flag <= 0) {
          flag = 0;
          wheelFlag = 0;

        }
        if (flag >= (mainBox.offsetHeight - element.offsetHeight)) {
          flag = (mainBox.clientHeight - element.offsetHeight);
          wheelFlag = (mainBox.clientHeight - element.offsetHeight) * 12;
        }
        element.style.top = flag / 100 * 2 + "rem";
        var contentBoxStyleTop = -flag * (contentBox.offsetHeight / mainBox.offsetHeight) / 100 * 2 + "rem";
        contentBox.style.top = contentBoxStyleTop;
        contentBox.style.cssText += 'transition: top 100ms ease';
        // contentBox.setAttribute('style', 'transition: top 100ms ease; top: ' + contentBoxStyleTop + ';position: absolute; right: 0; ');
      }, element);
    }
  }
};
new addScroll('scroll-wrapper', 'popover-scroll', 'scrollDiv');

複製代碼

html頁面結構:ui

<!--右上角彈出菜單-->
  <div class="top-popover popover">
    <div class="popover-arrow"></div>
    <div class="popover-wrapper">
      <div id="scroll-wrapper" class="scroll-wrapper">
        <div id="popover-scroll" class="popover-scroll">
          <ul class="popover-table-view">
            <li class="popover-table-view-cell">
              <a href="#">item1</a>
            </li>
            <li class="popover-table-view-cell">
              <a href="#">item2</a>
            </li>
            <li class="popover-table-view-cell">
              <a href="#">item3</a>
            </li>
            <li class="popover-table-view-cell">
              <a href="#">item4</a>
            </li>
            <li class="popover-table-view-cell">
              <a href="#">item5</a>
            </li>
            <li class="popover-table-view-cell">
              <a href="#">item6</a>
            </li>
          </ul>
        </div>
        <!-- 滾動條由js自由建立 -->
      </div>
    </div>
  </div>
複製代碼

有不懂或者有疑問,歡迎你們留言。

相關文章
相關標籤/搜索