大漠 2018-08-09 23:11
@evilmartians的《滚动的特性》一文介绍了目前有关于滚动相关的特性。今天我想花点时间重新整理一下,时至今日,CSS中为浏览器滚动提供的相关新特性究竟能给用户带来哪些新的体验。
墨守成规的滚动条
一直以来,如果仅使用CSS来控制滚动条,我们只能借用overflow
属性,比如:
overflow: auto | scroll;
// 或者
overflow-x: auto | scroll;
// 或者
overflow-y: auto | scroll;
当元素内容溢出容器之后,就会出现滚动条,其滚动效果如下:
这样的效果大家或许已经习惯,觉得页面或者元素滚动的效果就应该如此。甚至在Modal弹框中的滚动效果,大家觉得也应该是如此:
除了滚动体验之外,视觉体验相对而言更为糟糕,不同系统的浏览器渲染的滚动条风格也不是一致:
对于大部分同学而言,这些东西就应该是如此,不应该也不会有太多的变化。但对于有追求的设计师或者工程师来说,还是不想墨守成规,想给用户带来不一样的体验,不管是视觉外观上,还是滚动流畅性。正因为如此,早期有很多优秀的JavaScript库来改变这一切。
随着技术的革新,就在现在或者未来的不久,我们可以采用纯CSS的一些特性来改变这一切,让用户有一个更好的体验。
改变滚动条外观效果
前面提到过了,滚动条外观的效果在不同系统存在不一样的效果已是事实。虽然在Github有上百个库可以让开发者实现个性化的滚动外观,但对于CSSer而言,能用纯CSS解决的问题就决不使用JavaScript来解决。
在Webkit内核提供了-webkit-scrollbar
(由七个伪元素)属性,可以轻易的帮助我们实现自定义(个性化)滚动条UI风格。在介绍这七个伪元素属性之前,先来看一下滚动条的结构:
仅从上图来看,是不是有一种似曾相识。是的,它的结构和我们平时看见的进度条或input[type="range"]
类似:
也就是说,我们可以像制作进度条一样来处理滚动条UI效果。不同的是采用的CSS属性不一样。刚才也提到了,-webkit-scrollbar
提供了七个伪元素,通过这些伪元素,我们可以来定制滚动条外观效果。这七个伪元素分别是:
::-webkit-scrollbar
:整个滚动条::-webkit-scrollbar-button
:滚动条上的按钮(下下箭头)::-webkit-scrollbar-thumb
:滚动条上的滚动滑块::-webkit-scrollbar-track
:滚动条轨道::-webkit-scrollbar-track-piece
:滚动条没有滑块的轨道部分::-webkit-scrollbar-corner
:当同时有垂直和水平滚动条时交汇的部分::-webkit-resizer
:某些元素的交汇部分的部分样式(类似textarea
的可拖动按钮)
具体使用的时候非常的简单。HTML结构和我们平时是一样的:
<div class="scrollbar">
<div class="force-overflow"></div>
</div>
有关于滚动条的UI样式风格都在.scrollbar
上设置,比如:
.scrollbar {}
.scrollbar::-webkit-scrollbar{}
.scrollbar::-webkit-scrollbar-thum{}
.scrollbar::-webkit-scrollbar-track{}
在对应的-webkit-scrollbar
属性写上你想要的UI样式风格,你就可以得到对应的滚动条UI样式风格,比如下图这样的:
具体的代码可以查看@akinjide在Codepen上写的Demo:
有关于采用-webkit-scrollbar
属性实现自定义滚动条UI效果的详细教程,可以阅读@Akinjide Bankole的教程。其他教程也可以查看下面的文章:
- Custom Scrollbars in WebKit
- Customize the Scrollbar
- How to customize the browser scrollbar with (WebKit) CSS
- 使用 CSS 自定义浏览器的滚动条
如果你不想烧脑,也可以使用在线生成器,比如@Darryl Huffman写的Scrollbar Generator:
虽然能用纯CSS搞定这一切,肯定也有不少同学会好奇,现在能否用于实际项目当中。还是来看看浏览器对其支持性吧:
顠红的较少了,或许大家更为安心了。如果你想做得更好,那么可以借助在《五个最新的CSS特性以及如何使用它们》文中介绍的@supports
特性来做降级处理。如果浏览器支持-webkit-scrollbar
,那么采用自定义属性,如果不支持,则采用默认的滚动条风格。是不是很完美。
丝滑般的滚动
视差滚动效果,大家应该不会感到陌生吧:
视差效果曾经很多品牌网站都可见。有关于什么是视差滚动效果,这里不做详细的阐述,如果你从未接触过或者想了解如何制作视差滚动效果,建议你花点时间阅读下面两篇文章:
同样的,早期也涌现很多优秀的JavaScript库来实现视差滚动效果。只不过这里我们不是着重介绍怎么制作视差滚动效果。而是来聊聊,怎么通过使用CSS提供的滚动特性,实现丝滑般的滚动效果。而视差滚动效果是一个很好的示例。
先来看两个效果,一个是普通的滚动效果,另一个是采用了新特性的滚动效果:
普通滚动效果,大家常见的滚动效果
优化后的滚动效果
怎么实现后者这样丝滑般的滚动效果,才是接下来的目的。很多同学可能首先会想到jQuery.scrollTo
方法或者类似的解决方法。但jQuery已经慢慢的淡出公众眼线。
不过也有可能立马想到原生的JavaScript,比如window.scrollTo(x, y)
方法。更优秀的程序员可能会借助window.setTimeout()
、window.setInterval()
、Web Animation API 和window.requestAnimationFrame()
让滚动效果更为平滑,也就是我们想要给用户丝滑般的滚动体验。比@pawelgrzybek提供的这段代码:
function scrollIt(destination, duration = 200, easing = 'linear', callback) {
const easings = {
linear(t) {
return t;
},
easeInQuad(t) {
return t * t;
},
easeOutQuad(t) {
return t * (2 - t);
},
easeInOutQuad(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
},
easeInCubic(t) {
return t * t * t;
},
easeOutCubic(t) {
return (--t) * t * t + 1;
},
easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
},
easeInQuart(t) {
return t * t * t * t;
},
easeOutQuart(t) {
return 1 - (--t) * t * t * t;
},
easeInOutQuart(t) {
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
},
easeInQuint(t) {
return t * t * t * t * t;
},
easeOutQuint(t) {
return 1 + (--t) * t * t * t * t;
},
easeInOutQuint(t) {
return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
}
};
const start = window.pageYOffset;
const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();
const documentHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
const windowHeight = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight;
const destinationOffset = typeof destination === 'number' ? destination : destination.offsetTop;
const destinationOffsetToScroll = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset);
if ('requestAnimationFrame' in window === false) {
window.scroll(0, destinationOffsetToScroll);
if (callback) {
callback();
}
return;
}
function scroll() {
const now = 'now' in window.performance ? performance.now() : new Date().getTime();
const time = Math.min(1, ((now - startTime) / duration));
const timeFunction = easings[easing](time);
window.scroll(0, Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start));
if (window.pageYOffset === destinationOffsetToScroll) {
if (callback) {
callback();
}
return;
}
requestAnimationFrame(scroll);
}
scroll();
}
调用方式很简单,像下面这样即可:
document.querySelector('.js-btn1').addEventListener('click', () => {
scrollIt(
document.querySelector('.js-section1'),
300,
'easeOutQuad',
() => console.log(`Just finished scrolling to ${window.pageYOffset}px`)