计算阅读时长、顶部阅读进度条、区域放大阅读

写在开头

哈喽,各位好呀!😀

写这篇文章的时候正是三八妇女节日,祝所有女神们,女神节快乐!「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>

通过这些值,我们可以计算出已滚动的页面百分比,再相应地同步到进度条上的 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 属性了,我们换成 topleft 属性,这样放大查看会有更好的体验效果。

最终效果如下:

动图可能效果不佳,你可以自己尝试看看,当然,如果你还有其他优化方案欢迎评论留言交流。😁


至此,本篇文章就写完啦,撒花撒花。

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。

老样子,点赞+评论=你会了,收藏=你精通了。

相关推荐
m0_74825502几秒前
前端常用算法集合
前端·算法
真的很上进14 分钟前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web1309332039820 分钟前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_23441 分钟前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1231 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~2 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语2 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport2 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg2 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全
胡西风_foxww2 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest