当前位置: 代码迷 >> 综合 >> 重写移动端滚动条[iScroll.js核心代码]
  详细解决方案

重写移动端滚动条[iScroll.js核心代码]

热度:61   发布时间:2023-12-08 10:46:53.0

最近写一个popover弹窗时,发现这个滚动条是真的丑啊,决定重新撸一个滚动条:

首先咱们回顾一下移动端浏览器滚动条特性:

  • 滚动条在开始滚动时渐显,滚动结束后渐隐
  • 滚动条不占内容区宽度,悬浮固定
  • 滚动条高度(深灰)和滚动区可视高度(浅灰)比等于滚动区可视高度和滚动目标的高度
  • 当滚动目标的高度小于滚动区可视高度的时候,滚动条不显示,并且无法滚动
  • 只有在滚动滚动目标时,才能触发滚动
  • 当滚动条顶部触顶和底部触底的时候,不能继续滚动
  • 只有在滚动大于一个固定值时,才被视为滚动开始
  • 根据滚动的差值,计算是向上滚动还是向下滚动
  • 滚动条是动态生成的

好啦,接下来咱们开始一步一步实现,需要哪些知识点:

  • 渐隐渐显
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还是不一样的

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;

  • 根据滚动的差值,计算是向上滚动还是向下滚动,怎么判断滚动差值呢,好,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给重写,故不能用setAttributeelement.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页面结构:

  <!--右上角弹出菜单--><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>

有不懂或者有疑问,欢迎大家留言。