大家好,我是梅巴哥er
。
本篇,我们介绍三个部分:
- css改变滚动条样式的方法。
- js做出滚动条效果。
- 滚动条事件在项目中的完整封装。
一、css改变滚动条样式的方法
老规矩,先来看下效果图:
直接上demo:
/*demo.html*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="demo.css">
</head>
<body>
<div>
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
</div>
<!-- <script src="demo.js"></script> -->
</body>
</html>
/*demo.css*/
*{
padding: 0;
margin: 0;
}
div {
width: 200px;
height: 300px;
border: 1px solid pink;
margin-top: 20px;
margin-left: 20px;
overflow: hidden;
overflow-y: scroll;
}
div::-webkit-scrollbar {/*设置滚动条整体属性*/
width: 5px;
}
div::-webkit-scrollbar-track { /*滚动条轨道属性*/
background-color: transparent;
}
div::-webkit-scrollbar-thumb {/*滚动条内滑块属性*/
/* background-color: pink; */
background: url(./images/w-bar.png) no-repeat center center;
background-size: 100% 100%; /*如果用背景图,这里要带上,不然背景图不能顶到头*/
/*我用的背景图尺寸是5*75的*/
}
优缺点
- 优点:简单方便,上手快。如果项目中对滚动条内滑块样式要求不高,可以优先选择该方法。
- 缺点:滚动条内的滑块,会随着内容的减少而改变自身高度,且比较明显。 可以试着把
demo.html
内的文字内容删去一半,再看下滚动条内滑块高度。可以看到明显拉长。
二、js做出滚动条效果
先看效果图:
demo代码:
/*demo.html*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>滚动条demo</title>
<link rel="stylesheet" href="demo.css">
</head>
<body>
<div class="box">
<div class="listWrap">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
</div>
<div class="barBox">
<div class="bar"></div>
</div>
</div>
<script src="demo.js"></script>
</body>
</html>
/*demo.css*/
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
ul li{
list-style-type: none;
}
.box {
position: relative;
width: 300px;
height: 400px;
border: 1px solid pink;
margin: 20px;
overflow: hidden;
}
.listWrap {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 800px;
z-index: 2;
}
.listWrap ul {
width: 100%;
}
.listWrap ul li {
width: 100%;
height: 80px;
line-height: 80px;
border-bottom: 1px solid purple;
}
.barBox {
position: absolute;
top: 0;
right: 0;
width: 5px;
height: 100%;
z-index: 5;
}
.bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 75px;
background: url(./images/w-bar.png) no-repeat center center;
background-size: 100% 100%;
}
/*demo.js*/
window.addEventListener('load', function() {
var box = document.querySelector('.box');
var listWrap = document.querySelector('.listWrap');
var barBox = document.querySelector('.barBox');
var bar = document.querySelector('.bar');
/**控制滚动速度 */
var speed = 20;
/**可视区高度 */
var box_hig = box.offsetHeight;
/**内容实际高度(包括被隐藏的内容) */
var listWrap_hig = listWrap.offsetHeight;
/**滚动条轨道高度 */
var barBox_hig = barBox.offsetHeight;
/**滚动条内滑块高度 */
var bar_hig = bar.offsetHeight;
box.addEventListener('mousewheel', function(e) {
// console.log(e);
scrollRoll(e);
})
/**兼容FireFox */
box.addEventListener('DOMMouseScroll', function(e) {
scrollRoll(e);
})
/**兼容不同浏览器的上滚下滚定义 */
function scrollRoll(e) {
e = e || window.event;
e.preventDefault && e.preventDefault(); //阻止浏览器默认事件
if (e.detail > 0) {
down();
} else if (e.detail < 0) {
up();
}
if (e.wheelDelta > 0) {
up();
} else if (e.wheelDelta < 0) {
down();
}
}
function down() {
if(bar.offsetTop >= barBox_hig - bar_hig) {
listWrap.style.top = -(listWrap_hig - box_hig) + 'px';
bar.style.top = barBox_hig - bar_hig + 'px';
} else {
listWrap.style.top = listWrap.offsetTop - speed + 'px';
// bar.style.top = bar.offsetTop + speed + 'px';
bar.style.top = - (barBox_hig - bar_hig) * listWrap.offsetTop / (listWrap_hig - box_hig) + 'px';
}
}
function up() {
if(bar.offsetTop <= 0) {
listWrap.style.top = 0;
bar.style.top = 0;
} else {
listWrap.style.top = listWrap.offsetTop + speed + 'px';
// bar.style.top = bar.offsetTop - speed + 'px';
bar.style.top = - (barBox_hig - bar_hig) * listWrap.offsetTop / (listWrap_hig - box_hig) + 'px';
}
}
})
该方法的优缺点
- 优点:修改样式多样,适用性强。便于封装,可复用度高。
- 缺点:代码量大,对业务要求不高的项目,不方便使用。
三、滚动条事件在项目中的完整封装
/*前面的html和css不变,
下面demo是封装好的js以及使用方法*/
window.addEventListener('load', function() {
/*使用方法*/
var obj = {
boxWrap: 'box', // 最外层盒子的类名
listWrap: 'listWrap', // ul外面那层的类名
barBox: 'barBox', // 滚动条类名
bar: 'bar', // 滚动条内滑块的类名
speed: 20, // 控制速度,默认是20
}
packageBar(obj)
/*封装*/
function packageBar(obj) {
var box = document.querySelector(`${'.' + obj.boxWrap}`);
var listWrap = document.querySelector(`${'.' + obj.listWrap}`);
var barBox = document.querySelector(`${'.' + obj.barBox }`);
var bar = document.querySelector(`${'.' + obj.bar}`);
var speed = obj.speed || 20;
var box_hig = box.offsetHeight;
var listWrap_hig = listWrap.offsetHeight;
var barBox_hig = barBox.offsetHeight;
var bar_hig = bar.offsetHeight;
box.addEventListener('mousewheel', function(e) {
// console.log(e);
scrollRoll(e);
})
/**兼容FireFox */
box.addEventListener('DOMMouseScroll', function(e) {
scrollRoll(e);
})
/**兼容不同浏览器的上滚下滚定义 */
function scrollRoll(e) {
e = e || window.event;
if (e.detail > 0) {
down();
} else if (e.detail < 0) {
up();
}
if (e.wheelDelta > 0) {
up();
} else if (e.wheelDelta < 0) {
down();
}
}
function down() {
if(bar.offsetTop >= barBox_hig - bar_hig) {
listWrap.style.top = -(listWrap_hig - box_hig) + 'px';
bar.style.top = barBox_hig - bar_hig + 'px';
} else {
listWrap.style.top = listWrap.offsetTop - speed + 'px';
// bar.style.top = bar.offsetTop + speed + 'px';
bar.style.top = - (barBox_hig - bar_hig) * listWrap.offsetTop / (listWrap_hig - box_hig) + 'px';
}
}
function up() {
if(bar.offsetTop <= 0) {
listWrap.style.top = 0;
bar.style.top = 0;
} else {
listWrap.style.top = listWrap.offsetTop + speed + 'px';
// bar.style.top = bar.offsetTop - speed + 'px';
bar.style.top = - (barBox_hig - bar_hig) * listWrap.offsetTop / (listWrap_hig - box_hig) + 'px';
}
}
}
})
四: 优化完整版封装
- 其实上面的这个封装,仍然存在问题。
- 比如没有禁止浏览器默认事件,当滚轮滚动时,内容区和整个浏览器都在滚动,这是明显的bug。
- 另外,当禁止了浏览器默认事件时,滚动条滚动到最顶部或者最底部,滚轮继续滚动,整个页面不会跟着动了,很影响用户体验,且整个滚动事件不是很流畅。
- 所以我对滚动条进行了重新封装。改善了滑块的运动形式,为了更容易看明白使用和封装方法,给出了更完整的demo 。消除原始滚动条的任务由js交给了css 。主要是因为js控制方式不稳定,容易出bug 。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="demo.css">
</head>
<body>
<div class="scrollItem">
<div class="scrollContent">
<div class="sCList">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
</div>
</div>
<div class="barWrap">
<div class="barBox">
<div class="bar"></div>
</div>
</div>
</div>
<script src="demo.js"></script>
</body>
</html>
//demo.css
* {
padding: 0;
margin: 0;
}
ul li {
list-style-type: none;
}
.sCList ul li {
width: 100%;
height: 80px;
}
.scrollContent::-webkit-scrollbar {
width: 0;
}
.scrollContent::-webkit-scrollbar-track {
background-color: transparent;
}
//demo.js
window.addEventListener('load', function() {
var obj = {
scrollItem: '.scrollItem', // 外层盒子类名
scrollContent: '.scrollContent', // 可视区类名
sCList: '.sCList', // 内容区类名
barWrap: '.barWrap', // 滚动条外层类名
barBox: '.barBox', // 滚动条内层类名
bar: '.bar', // 滚动条滑块类名
scrollItem_width: '300px', // 外层盒子宽度
scrollItem_height: '450px', // 外层盒子高度
sCList_height: '800px', // 内容区高度
barWrap_width: '5px', // 滚动条宽度
barWrap_bgc: 'pink', // 滚动条背景颜色,默认transparent
bar_height: '75px', // 滑块高度
bar_bgc: 'purple', // 滑块背景色,默认transparent
}
scrollFn(obj);
function scrollFn(obj) {
var $ = document.querySelector.bind(document);
var log = console.log || window.console.log;
var ds = document.styleSheets;
var scrollItem = $(`${obj.scrollItem}`); // 外层盒子
var scrollContent = $(`${obj.scrollContent}`);// 可视区
var sCList = $(`${obj.sCList}`); // 内容区
var barWrap = $(`${obj.barWrap}`); // 滚动条外层
var barBox = $(`${obj.barBox}`); // 滚动条内层
var bar = $(`${obj.bar}`); //滚动条滑块
scrollItem.style.cssText = `position: relative; width: ${obj.scrollItem_width}; height: ${obj.scrollItem_height};`;
scrollContent.style.cssText = 'position: relative; width: 100%; height: 100%; ';
scrollContent.style.overflowY = 'scroll';
sCList.style.cssText = `position: absolute; top: 0; left: 0; width: 100%; height: ${obj.sCList_height}; box-sizing: border-box;`;
// ds[0].insertRule('.scrollContent::-webkit-scrollbar { width: 0 }', 0);
// ds[0].insertRule('.scrollContent::-webkit-scrollbar-track { background-color: transparent }', 0);
// ds[0].addRule('.scrollContent::-webkit-scrollbar', 'width: 0');
// ds[0].addRule('.scrollContent::-webkit-scrollbar-track', 'background-color: transparent');
barWrap.style.cssText = `position: absolute; top: 0; right: 0; width: ${obj.barWrap_width}; height: 100%; background-color: ${obj.barWrap_bgc};`;
barBox.style.cssText = 'position: relative; width: 100%; height: 100%;';
bar.style.cssText = `position: absolute; top: 0; left: 0; width: 100%; height: ${obj.bar_height}; background-color: ${obj.bar_bgc};`;
/**可视区高度 */
var sc_h = scrollContent.offsetHeight;
/**内容区高度 */
var sCList_h = sCList.offsetHeight;
/**滚动条高度 */
var barWrap_h = barWrap.offsetHeight;
/**滑块高度 */
var bar_h = bar.offsetHeight;
/**滚动最大次数 */
var MAX_NUM ;
/**滑块滚动的最大高度 */
var bar_MAX_H = barWrap_h - bar_h;
/**滚动条单次移动高度 */
var bar_ONCE_H;
/**滚轮向下滚动次数 */
var times = 0;
scrollContent.addEventListener('mousewheel', function(e) {
MAX_NUM = Math.ceil((sCList_h - sc_h) / (Math.abs(e.deltaY)));
bar_ONCE_H = bar_MAX_H / MAX_NUM;
scrollRoll(e);
})
/**兼容FireFox */
scrollContent.addEventListener('DOMMouseScroll', function(e) {
scrollRoll(e);
})
/**兼容不同浏览器的上滚下滚定义 */
function scrollRoll(e) {
e = e || window.event;
if (e.detail > 0) {
down();
} else if (e.detail < 0) {
up();
}
if (e.wheelDelta > 0) {
up();
} else if (e.wheelDelta < 0) {
down();
}
}
function down(){
if(times < MAX_NUM) {
times++;
// bar.style.top = times * bar_ONCE_H + 'px';
// 滑块的运动形式做个改进
animate(bar, bar.offsetTop, times * bar_ONCE_H);
}
}
function up(){
if(times <= MAX_NUM && times > 0) {
times--;
// bar.style.top = times * bar_ONCE_H + 'px';
animate(bar, bar.offsetTop, times * bar_ONCE_H);
}
}
function animate(obj, start, end, unit, cb) {
var step = (end - start) / 10;
var n = 1;
var unit = unit || 'px';
clearInterval(timer);
function move() {
obj.style.top = start + step * n + unit;
if(obj.style.top === end + unit) {
clearInterval(timer);
cb && cb();
}
n++;
}
var timer = setInterval(move, 30);
}
}
})
以上。