Vue3+TS+Node打造个人博客(一键到顶和侧边弹射)

本项目代码已开源,具体见:

前端工程:vue3-ts-blog-frontend

后端工程:express-blog-backend

数据库初始化脚本:关注公众号bin不懂二进制,回复关键字"博客数据库脚本",即可获取。

前言

2023 年再写一键到顶和侧边菜单栏弹射效果显得过于简单,不过既然是目录里规划好的一篇内容,咱还是按计划把它完成。

首先通过两个动图看看具体效果,再来研究怎么实现!

  • 一键到顶:
  • 侧边菜单栏弹射效果:

一键到顶

回到网页顶部本质上就是修改scrollTop的值,所以最简单粗暴的方法就是直接修改滚动元素的scrollTop

一般来说,滚动元素就是网页的body,所以我们会习惯于修改body元素的scrollTop,将它的值设置为0

javascript 复制代码
document.body.scrollTop = 0

但是有时候,我们会发现修改bodyscrollTop并不会生效,滚动条还是在原地没动。这是因为:

  • 当页面具有 DOCTYPE,或者说指定了 DOCTYPE 时,使用document.documentElement.scrollTop
  • 当页面不具有 DOCTYPE,或者说没有指定了 DOCTYPE 时,使用document.body.scrollTop
  • 为了兼容各种情况,建议同时使用这两种写法。

所以为了保险,两种都写上就行:

javascript 复制代码
document.documentElement.scrollTop = 0
document.body.scrollTop = 0

虽然回到网页顶部的功能实现了,但是网页是一瞬间回到顶部的,缺失了过渡效果。如果我们希望有一个过渡的效果,就需要另外想办法了!

scroll-behavior

第一种方法是设置目标元素的 CSS 属性scroll-behavior,将其值设置为smooth即可。

css 复制代码
html {
  scroll-behavior: smooth;
}

此时再通过 JS 操作scrollTop就可以得到平滑的滚动效果了,而滚动的缓动效果和时长是由浏览器自身实现决定的。

这个方法也是最简单的,只需要多加一行 CSS 属性即可!

兼容性参考:

可以发现 IE 是完全不支持这个属性的,而 iOS 15 以下的 Safari 支持度也几乎可以认为是没有的。在实际项目的生产环境中使用时稍微注意一下即可,不过我们这个是个人项目,随便用也无伤大雅!

window.scrollTo

既然 CSS 可以提供平滑滚动的效果,那么 JS 行不行呢?答案是肯定的!使用 BOM 提供的window.scrollTo也可以达到 smooth 的效果,其中behavior参数也支持smooth

javascript 复制代码
window.scrollTo({
    top: 0,
    behavior: "smooth"
});

是不是也很简单,用这个 API 调用的方式来替代直接修改scrollTop属性也是一个不错的选择!

那么behavoir: "smooth"的兼容性怎么样呢,可以说和 CSS 的smooth差不太多,甚至更差!

Safari 明确表示不支持smooth

Safari does not have support for the smooth scroll behavior.

自定义缓动效果

既然上面两种方法的兼容性不是很好,那么我们可以尝试自己用 JS 实现一下。得益于 JS 的灵活性,整个滚动行为的缓动效果和时长我们都能很好地控制。

这里有两个问题要考虑,一个是总时长,一个是欢动效果。

假设网页当前滚动的距离是1000px,计划0.5秒完成滚动到顶部的效果,假设是匀速滚动,那么相当于每个px需要花0.5 / 1000秒(也就是0.5毫秒)的时间滚动。

但是0.5毫秒其实人眼是感知不到的,JS 定时器也不能处理低于 16 毫秒的逻辑。所以我们换个角度去思考,把 1 秒换算成 60 帧,那么 0.5 秒就是 30 帧,所以这个动画总共就是 30 帧,只要我把1000px的滚动分成 30 帧去实现即可。

如果是匀速,是不是意味着 1 帧滚动(1000 / 30)px,约等于33.33px。这样一分解,问题就简单了。

如果不希望是匀速呢?也就是每一帧可能滚动的距离都是不一样的,对于这种缓动效果,我们一般会用到贝塞尔曲线。不懂贝塞尔曲线的数学原理不要紧,我们只要调试到一个自己感觉舒适的效果,把曲线的值保留下来即可。

打开cubic-bezier.com在线调试贝塞尔曲线,拖动控制点调整,直到得到自己满意的曲线为止。

简单观察后我们能发现,ease 是先快后慢;linear 就不必说了,是匀速。感觉 easy 或者 ease-in-out 是比较适合一键到顶这个场景的。

拿到合适的贝塞尔曲线后,就是要将它变成代码了,这里先安装bezier-easing这个库。

首先初始化它,

javascript 复制代码
import BezierEasing from "bezier-easing";

const easingFunc = BezierEasing(0.42, 0, 1, 1);

得到的easingFunc是一个函数,它的输入是 0 ~ 1 的数值,代表在 0% ~ 100% 的各个位置应该得到的结果值。换句话理解,假设输入 0.5,则能得到在经过一半的滚动时长时,scrollTop应该是什么值,也就是说每个时间点的值都可以随着输入的比例算出来。

javascript 复制代码
const scrollTop = easingFunc(0.5)

那么每一帧的逻辑怎么做呢,有两个方法可以参考:

  • 利用window.requestAnimationFrame,它是和屏幕的刷新率有关系的,一般是达到 60 FPS 的效果。
  • requestAnimationFrame没有出现之前,setTimeout使用 16.67ms 也是一个常见的选择。

具体实现:

  • 根据总时长 duration(单位为秒)算出总帧数 total,也就是 duration * 60
  • requestAnimationFrame 中根据当前是第几帧(step),再结合贝塞尔曲线函数得到当前应该滚动到的位置,修改scrollTop,只要 step 没有达到总帧数 total,就可以使 step 加 1,递归调用上述逻辑。

代码参考:

javascript 复制代码
function animateSetScrollTop({ target = document.documentElement, start, end, stepNo = 1, stepTotal }: StepOptions) {
    const next = getNextScrollTopValue(start, end, stepNo, stepTotal);
    window.requestAnimationFrame(() => {
        setElementScrollTop({
            target,
            value: next,
        });
        if (stepNo !== stepTotal) {
            const nextStepNo = stepNo + 1;
            animateSetScrollTop({
                target,
                start,
                end,
                stepNo: nextStepNo,
                stepTotal,
            });
        }
    });
}

侧边菜单栏弹射

说完一键到顶,接着说第二个效果,侧边菜单栏的弹射效果。这个效果实现起来的关键在于:

  • 菜单栏弹出时,有一个将内容主体区域推出去的效果,而非菜单栏直接盖在内容区域之上。
  • 弹出和收回时,不能出现滚动条,否则会显得比较突兀。
  • 菜单栏展示时,滚动鼠标滚轮时不能发生滚动行为。

设计布局时可以想象一下这个过程。

在菜单隐藏时,其实菜单就是排布在视野之外的。

在菜单出现时,菜单和内容区域整体往右边推出一段距离。

最开始设计时,想过利用 flex 布局,右边内容区域占据剩余宽度,左边菜单在弹出的过程中慢慢从0px变成实际的宽度。但是操作的过程中因为有宽度的变化,很容易出现内部元素的布局变化,比如文字换行之类的。

最后还是决定右侧的内容区域占据整屏的宽度,左侧菜单则是用决定定位贴在内容区域的左侧。

css 复制代码
position: absolute;
top: 0;
width: 230px;
height: 100%;
background: #222;
// 保证向左侧再平移一个菜单的身位,正好消失在视野外
transform: translate3d(-100%, 0, 0);

菜单弹出的过程就是把整个容器往右平移230px,也就是菜单的宽度,这个过程是采用动画还是过渡效果都是可以实现的。

针对弹出和收回时,不能出现滚动条 ,主要是在translate的过程中保证overflow-x方向的hidden即可。

针对菜单栏展示时,滚动鼠标滚轮时不能发生滚动行为 ,只需要把bodyoverflow设置为hidden即可。

总的来说,样式的调试需要大量的实践去检验和调整,最终得到想要的效果,并且解法也不是唯一的。

小结

本文内容说难不难,说简单也有一些值得思考的地方,看到最后的朋友就当温习一下相关知识点吧。

相关推荐
qiyi.sky14 分钟前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~18 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
哪 吒20 分钟前
华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)
javascript·python·华为od
安冬的码畜日常27 分钟前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
杨荧1 小时前
【JAVA开源】基于Vue和SpringBoot的洗衣店订单管理系统
java·开发语言·vue.js·spring boot·spring cloud·开源
l1x1n01 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
Q_w77421 小时前
一个真实可用的登录界面!
javascript·mysql·php·html5·网站登录
昨天;明天。今天。1 小时前
案例-任务清单
前端·javascript·css
一丝晨光2 小时前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
Front思2 小时前
vue使用高德地图
javascript·vue.js·ecmascript