写在开头
哈喽,各位好呀!😀
写这篇文章的时候正是三八妇女节日,祝所有女神们,女神节快乐!「Happy women's/fairy's Day」🎇🎇🎇
舔狗日志🤡:
今天女神发了一个朋友圈,"立个flag,明天开始减肥"。
为了评论回复能吸引女神的注意,我苦思冥想、绞尽脑汁、字斟句酌,你猜猜最后我都评论了什么❓
扯远了,回归正题,本章分享三个页面阅读体验提升的小技巧,如题,诸君按需食用。
计算阅读时间
很多时候我们在看一篇文章的时候,顶部一般能看到阅读所需时长,如掘金的文章:
那么,这玩意是如何实现的呢?
其实也挺简单,我们"计算文章的字数"然后往"阅读速度"一除即可。
根据研究调查,大多数成年人的阅读速度约为每分钟200-300字(WPM),不过,这可能因年龄、语言或阅读理解等因素而异。
javascript
const content = document.querySelector('.container');
const wordsLength = content.innerText.length;
// 取 250WPM 作为平均阅读速度
const readingSpeed = 250;
// 向上取整,取到最接近的分钟
const readingTime = Math.ceil(wordsLength / readingSpeed) + '分钟';
顶部阅读进度条
"页面顶部的进度条"是一个常见的网页元素,老演员了🎃,相信各位也不陌生。
它在两种主要场景下发挥着重要作用:
- 页面跳转时的加载指示器,这种用法在后台管理系统中特别普遍。目前,一个流行的库是 NProgress,它能够轻松实现这种Loading效果。
- 另一种就是我们本次要提到的顶部阅读进度条,这种进度条能够显示用户在页面内容上的阅读进度,为用户提供直观的导航辅助。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>顶部阅读进度条</title>
<style>
.grogress {
position: fixed;
top: 0;
left: 0;
width: 0;
height: 4px;
background-color: #ff4293;
transition: width 0.2s ease-in-out;
}
</style>
</head>
<body>
<div class="container">自己放点长内容......</div>
<div class="grogress"></div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const grogress = document.querySelector('.grogress');
document.addEventListener('scroll', () => {
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const scrollTop = document.documentElement.scrollTop || window.scrollY;
const scrolled = (scrollTop / (scrollHeight - clientHeight)) * 100;
grogress.style.width = `${scrolled}%`;
});
});
</script>
</body>
</html>
- document.documentElement.scrollHeight:网页的总高度。
- document.documentElement.clientHeight:网页可见部分的高度。
- scrollTop:可见部分上边缘相对于整个网页的当前垂直位置。
通过这些值,我们可以计算出已滚动的页面百分比,再相应地同步到进度条上的 width
属性,就能完成啦😉。
最后看看效果:
(不知道为什么动图里面看起来是不连贯,将就看吧😂)
区域放大阅读
这个功能的完整需求大概是这样的:有时用户希望专注查看页面的某特定元素内容,要求点击某一按钮能将该特定元素的内容放大查看,当然,也可以点击关闭,恢复原样。😲
这个功能乍一听是不是很简单?😌就操作一个 class
添加和删除的过程就解决了,真是这样么?我们下面来尝试看看。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>区域放大阅读</title>
<style>
.box {
width: 20rem; /** 为了动画效果,需要预先设置宽度 **/
border: 1px solid rebeccapurple;
box-sizing: border-box;
/** 动画效果 **/
transition: width 0.2s, height 0.2s;
}
.full-page {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
z-index: 9999;
}
</style>
</head>
<body>
<div>
很长很长的内容.......
<div class="box">
这里是需要放大查看的某特定元素内容。。。。。。<br />
<button class="button">点击放大</button>
</div>
很长很长的内容.......
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const box = document.querySelector('.box');
const button = document.querySelector('.button');
button.addEventListener('click', () => {
const isFullPage = box.classList.contains('full-page');
if (isFullPage) {
box.classList.remove('full-page');
button.innerHTML = '点击放大';
} else {
box.classList.add('full-page');
button.innerHTML = '关闭';
}
});
})
</script>
</body>
</html>
如我们所料,过程就是如此的简单😁。 可能唯一需要注意的是 width
属性需要预先设置宽度,否则无动画效果。
到这里,你可以选择就此结束该功能开发,毕竟都是打工人,能满足业务就行,也不是用不了,是吧。👻
但是,如果你有空闲时间,可以继续往下瞧瞧,将会继续介绍该功能的两个优化点。
- 其一,页面整体布局被破坏,我们知道特定元素被我们设置成了绝对定位(
position: absolute;
),整体布局肯定是会被破坏,只是可能被覆盖在下面看不到,所以问题不大。 - 其二,动画效果依赖宽度(
width
),上面我们说过必须要预先设置宽度,否则没有动画效果。这也没办法,动画效果是使用CSS
来实现的,并且这个效果可能还不是最优的,它只是最简单最方便的方案而已。
围绕这两个问题,我们来看看是如何来解决优化的:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>区域放大阅读</title>
<style>
.box {
/** width可以加也可以不加 **/
border: 1px solid rebeccapurple;
box-sizing: border-box;
}
.full-page {
position: absolute;
/** 删除 top 与 left **/
width: 100%;
height: 100%;
background: #fff;
z-index: 9999;
}
</style>
</head>
<body>
<div>
很长很长的内容.......
<div class="box">
这里是需要放大查看的某特定元素内容。。。。。。<br />
<button class="button">点击放大</button>
</div>
很长很长的内容.......
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const box = document.querySelector('.box');
const button = document.querySelector('.button');
button.addEventListener('click', () => {
const isFullPage = box.classList.contains('full-page');
if (isFullPage) {
button.innerHTML = '点击放大';
// 删除克隆元素
const cloned = box.nextElementSibling;
const rect = cloned.getBoundingClientRect();
box.classList.remove('full-page');
box.style.position = 'absolute';
const collapseAnimation = box.animate([{
top: `${rect.top}px`,
left: `${rect.left}px`,
}], {
duration: 280,
easing: 'ease-in-out',
});
collapseAnimation.addEventListener('finish', () => {
cloned.remove();
box.style.removeProperty('position');
box.style.removeProperty('top');
box.style.removeProperty('left');
});
} else {
button.innerHTML = '关闭';
const rect = box.getBoundingClientRect();
// 克隆一个元素来占位
const cloned = box.cloneNode(true);
cloned.style.visibility = 'hidden';
box.insertAdjacentElement('afterend', cloned);
box.style.top = `${rect.top}px`;
box.style.left = `${rect.left}px`;
box.classList.add('full-page');
// 通过JS来实现动画
box.animate([{
top: 0,
left: 0,
}], {
duration: 280,
easing: 'ease-in-out',
fill: 'forwards',
});
}
});
})
</script>
</body>
</html>
为了解决上面提到的布局被破坏的问题,我们可以克隆一个与原生目标具有相同内容和位置的占位元素,我们将使用 cloneNode API进行克隆,它会连同子元素也进行克隆,并且我们需要将克隆元素设置成不可见的状态(visibility: hidden
),这样子解决我们第一个问题。
还有一个动画问题,既然 CSS
动画不好用,那就考虑使用 JS
动画实现。这里小编使用了 Element.animate API来优化动画部分,对它不熟的小伙伴可以上MDN看看它的示例,应该比较好理解哈。这里动画我们就不再依赖 width
或者 height
属性了,我们换成 top
与 left
属性,这样放大查看会有更好的体验效果。
最终效果如下:
动图可能效果不佳,你可以自己尝试看看,当然,如果你还有其他优化方案欢迎评论留言交流。😁
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。