正如 Sarah 在她之前关于 使用 Vue.js 进行页面过渡的文章 中提到的,设计师和开发人员有大量的动机来构建页面过渡。让我们考虑一下移动应用程序。虽然移动应用程序正在不断发展,但越来越多的注意力被放在动画体验上,而网页基本上保持不变。为什么是这样?
也许是因为原生应用程序开发人员花费更多时间来处理这些动画。也许是因为用户表示这就是他们想要的。也许是因为他们更了解应用程序将运行的环境。所有这些都有助于随着时间的推移改善体验。总的来说,移动应用程序开发人员似乎更了解或更关心用户体验。
如果我们看看当今移动应用程序的设计方式,通常在状态之间会有一些动画过渡。即使是现成的原生组件在状态之间也具有一些简单的动画。开发人员和设计师意识到,这种小动画可以帮助用户理解应用程序中发生了什么。它使应用程序的导航更容易,并告诉用户他们在应用程序中将要前往的位置。
例如,当您导航到子类别时,它通常会从屏幕右侧滑入,我们都以某种方式知道这意味着什么。

动画很重要。它可以用来改善用户体验。
在开发网站时,很容易花费数小时来确保用户通过一流的过渡(例如画廊图片之间的移动或花哨的悬停效果)看到整个故事……但一旦用户点击链接,所有这些体验都会崩溃,您将从头开始。这是因为页面重新加载,并且在这种情况下我们没有简单/明显/原生的方法来处理动画/过渡。
在网络上,大多数用于改善体验的努力都集中在网站的结构、视觉设计甚至性能上。您可以在这里和那里滑动一些元素,但仅此而已。这是网络只是用于浏览一堆文本页面(后来升级了一些滑动文本)的时代的乏味残余。
有一些非常花哨的网站,背景中充满了动画或令人难以置信的 WebGL 象形文字。不幸的是,它们通常难以导航,并且您的笔记本电脑电池在大约 15 分钟内就会耗尽。但它们当然看起来很不错。这些网站充满了动画,但其中大部分是为了给您留下深刻印象,而不是帮助您四处导航,或者使体验更快,或者让您更容易浏览网站。
问题的根源
所有这些都提出了一个重要的问题。我们拥有所有可以执行此页面过渡操作的技术。为什么我们不做呢?是因为我们不想做吗?是因为我们认为这不是我们工作的一部分吗?这是否很难,以至于我们不得不为项目收取双倍费用,而客户又不买账?
让我们看看另一个可能的原因。通常,公司会选择对其所有项目通用的技术。作为前端开发人员,您对服务器上实现的内容没有太多控制权,因此您可能无法依靠某些 JSX 的服务器端渲染。
也许您必须选择一些稳定且通常可用于任何类型的解决方案的东西(这通常意味着代码库越少,灵活性越高),因此您被迫避免在开发堆栈中使用某些框架。不幸的是,这是有道理的。您不希望仅因为您决定某个框架是此特定项目的良好基础,就在 10 个不同的框架中实现联系表单。而且不要让我开始讨论安全性,安全性多年来一直在改进。您不能仅仅因为您希望用户在浏览您的网站时获得更多乐趣而将其抛弃。
还有另一个可能的原因。也许您想在 WordPress 上构建,因为您想利用多年来人们为您准备的所有开源好东西。您会用所有这些“免费”功能换取更好的体验吗?可能不会……
目标
除了像去除所有图像和视频这样极端且不切实际的更改之外,我们网站从服务器加载的时间就是它本身。我们有一个服务器渲染的页面,除了更快的服务器或更好的缓存之外,没有太多可以做的事情来改善这一点。
因此,提高加载时间的方法是稍微作弊!通过让用户认为加载时间更短来作弊,因为他们会被屏幕上发生的事情分散注意力。通过提前加载内容并设置动画过渡来作弊。
让我们介绍一些可用于执行过渡的效果。即使是简单的淡入/淡出也能让您获得完全不同的感觉,尤其是在某些元素指示加载的情况下。让我们看一些示例。
注意:以下所有示例都是基于 PHP 构建的网站,并且与任何其他网站一样,几乎可以在没有 JavaScript 的情况下工作。
简单的淡出/淡入无缝地改善了用户在点击链接之前的体验,并且不像正常的浏览器重新加载那样令人不安。菜单图标上的加载指示确保用户在页面加载时间稍长时不会恐慌。
通过以不同的方式为不同的元素设置动画可以实现一些有趣的动画,这也可以与淡入淡出相结合。
或者如何用其他元素覆盖页面?
这几个小时的工作使网站焕然一新,将您的静态页面变成了动画页面。而且无需任何额外的准备。
但是,如果您在设计阶段已经了解您的意图,则可以根据您的需要调整设计。常见的元素可以提前准备。
以气泡形式引入的主要导航元素在加载下一页内容时与用户互动。其影响是用户不会感到无聊,并且知道正在发生某些事情。由于动画在点击链接时开始,因此用户甚至没有意识到他们一直在等待某些东西。
解决方案
我们已经确定了我们实际上想做什么,以及为什么我们想这样做……让我们看看我们如何才能实现这一点。
虽然后端没有太多可以做的,但您可以在浏览器端做很多事情。一个很好的例子是 Turbolinks,由 Basecamp 人员开发,它采用您的网站并使其更像应用程序,方法是使用 JavaScript 启用内容加载。它只是避免了难看的浏览器页面到页面重新加载跳转。Turbolinks 处理浏览器历史记录、加载以及所有其他通常发生在浏览器后台的事情。这里的关键是控制加载和替换内容,因为只要浏览器不重新加载,屏幕上发生的一切都在我们的控制之下。
让我们思考一些可以用来改善我们体验的概念。加载时间是我们最大的敌人,那么我们如何在浏览器中改善它呢?
预加载
在大多数情况下,加载下一页的请求不必仅在用户点击链接时才开始。还有另一个事件正在发生,可以指示用户打算访问该页面,那就是将鼠标悬停在链接上。链接悬停和点击之间始终有几百毫秒的时间,为什么不利用这段时间来发挥我们的优势呢?哎呀,如果您的大多数用户最终都到达了您已知的下一页,并且您有统计数据可以证明这一点,那么为什么不在初始渲染后的任何时间预加载它,而用户正在尝试浏览当前页面时呢?
下图说明了多少事情可以并行发生并节省我们一些宝贵的时间,而不是一个接一个地完成。在人们为自己制作的页面过渡的自定义解决方案中,一个非常常见的错误是请求的开始,这通常是在页面卸载/隐藏后触发的。

缓存
虽然这对一些动态网站来说可能是个问题,但静态网站是缓存的完美候选者。既然您可以控制内容的加载和替换,为什么还要加载两次页面呢?您可以保存内容,并在用户访问同一页面或使用浏览器后退按钮返回时使用它。
或者,您可以使用缓存来预加载悬停时的页面,并定期清空缓存(也许在每次页面访问后?),以便在动态网站上始终拥有页面的最新版本,同时仍然启用预加载功能。
有趣的是,渐进式 Web 应用也“投资”于这个领域,尽管方法略有不同。
动画
所有这些概念都很棒,但我们仍然需要将动画引入其中。
页面加载和替换的控制权可以由我们掌握,因此我们可以随时处理页面内容。例如,在页面加载时向其添加一个类非常简单。这使我们能够为页面定义另一个状态,或者换句话说,隐藏/卸载它。当动画完成并且页面加载完毕后,我们可以自由替换内容并删除状态类以让页面再次动画显示。
另一种选择是使用 JavaScript 使页面“淡出”,替换内容,然后使其“淡入”。
有很多方法可以解决这个问题,但无论如何,这里的关键功能都是不让浏览器重新加载。
准备就绪
这三个主要概念将产生很大的影响。重点是,只需付出一点额外的努力,自己动手做这件事并不难,我鼓励您这样做。
这将是一条崎岖的道路,可能会有很多麻烦,因为不破坏浏览器原生功能并不总是那么容易。
但是,如果您更喜欢“加载并运行”类型的人,我编写了一个名为 swup 的小软件包,它解决了 Turbolinks 所做的一切,以及我们在这里介绍的所有三个概念。 这是一个小演示。
swup 的优势是什么?
- 它完全在前端运行,因此不需要服务器设置。尽管您可以根据请求标头中的
X-Requested-With
轻松实现仅传输所需数据的操作(根据您的特定解决方案,需要对 swup 进行少量修改)。 - 使用浏览器历史记录 API 来确保网站的正确功能,就像在没有 swup 的情况下一样(后退/前进按钮和 URL 地址栏中的正确路由)。
- 它不必是初始加载批处理的一部分,可以在初始渲染后加载并启用。
- 处理时机,这意味着它会自动检测 CSS 中定义的过渡结束,同时处理加载下一页。整个过程基于 Promise,因此不会浪费一毫秒。
- 如果在选项中启用,swup 可以在悬停链接或在加载内容中遇到链接时(带有
data-swup-preload
属性)预加载页面。 - 如果在选项中启用,swup 还会缓存页面的内容,并且不会重复加载同一页面。
- Swup 发出大量事件,您可以在代码中使用这些事件来启用您的 JavaScript、分析等。
- 您可以定义任意数量要替换的元素,只要它们对所有页面都是通用的即可。这使得能够在页面上为通用元素设置动画,同时仍然替换其部分内容成为可能。我们上面早期示例中的气泡菜单使用了此功能,其中气泡上的勾选标记取自加载的页面,但其余气泡保留在页面上,因此可以对其设置动画。
- 使您能够为不同页面之间的过渡使用不同的动画。
对于那些喜欢使用 JavaScript 完成动画的人,还有一个名为 swupjs 的版本。虽然在 JavaScript 中实现动画可能更难,但它绝对可以让您更好地控制。
结论
动画和无缝体验不应严格限于原生应用程序或最新技术。只需付出一点努力、时间和少量代码,即使您的 WordPress 网站也能获得类似原生的体验。虽然我们已经喜欢网络的现状,但我认为我们始终可以尝试使其变得更好。
为了好玩 => 您可能需要看看 IE4(是的,您没看错)。它引入了一些用于页面过渡的元数据: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ms532847(v=vs.85)#Interpage_Transition ;)
为什么您在 swup 演示中没有利用更改 URL 的功能?如果没有唯一的 URL,您的 Web 应用不适合 SEO,并且页面无法共享。
我四年前一直在尝试使用 History API,并在我的演示中使用了 https://github.com/devote/HTML5-History-API,但我没有使用任何缓存或预加载系统,因为我只是在尝试一下 pushState。虽然演示的主菜单页面对 SEO 友好且可共享。
演示: https://projects.gentle.media/artisan/
由于 swup 仅在浏览器中运行,因此它无法以任何方式影响您的页面是否对 SEO 友好。
但是,我理解您关于页面共享的观点。Swup 始终在 URL 地址栏中保留当前 URL。演示旨在展示在页面之间进行过渡的功能,并且 URL 实际上在包含启用 swup 的页面的 iframe 中发生了更改(页面左侧)。我尝试在页面顶部的地址栏中演示 URL 的更改。
啊……我现在明白了您做了什么。对不起,我一开始没有注意到!
出于某种原因,在您的演示中滚动不起作用……不确定是否是浏览器的问题,但当我检查控制台时,它显示“未捕获的 TypeError:无法读取未定义的属性‘msie’”。
无法重现此问题。您尝试过其他浏览器吗?
如果您认为这是一个错误,请随时在 Github 上提交工单。
他指的是我的演示 :)
大多数页面没有滚动条,但在单击按钮时具有滚动到锚点的动画,并且该控制台错误是由于我当时进行的一些糟糕的用户代理嗅探以使某些东西在旧版 IE 中正常工作。
哦,我明白了
哇,这太棒了!我同意。拥有一个漂亮的动画网页真是太好了。感觉更像是原生应用。这是我使用 vue/nuxt 创建网站的主要原因之一。但正如您所说,您并不总是可以使用它们。我一直在绞尽脑汁寻找一个合适的解决方案。我会尝试您的建议!让网络动起来!!
只有我一个人无法理解 node 模块以及如何在简单的旧 HTML 文件中使用它们吗?安装说明有点简略。
npm install swup
好的,已经完成了;现在怎么办?我得到了一个包含所有文件的 node_modules 文件夹,但是如何在示例 HTML 页面中包含它们呢?我已在 Google 上搜索过解决方案,但无法轻松找到一个,因此也许对这条评论的回复可以帮助其他人 :)
您好,感谢您的反馈!据我所知,如果没有其他步骤,就无法在浏览器中加载 NPM 包。NPM 是一个包管理器,它加载您定义的依赖项,以及依赖项的依赖项,依此类推……所有这些包导入能够实现,背后有很多逻辑在运行。为了使 swup 能够作为文件在浏览器中加载,它必须使用 webpack 或 Browserify 等工具捆绑,因此它包含所有代码,包括依赖项。
话虽如此,我很久以前就开始在我的所有代码中使用 webpack 了。我没想到有人会想把它作为一个独立的文件加载。我今天晚些时候会尝试将这些文件添加到仓库中。
给你 https://github.com/gmrchk/swup/tree/master/dist。
如果你想查看 webpack 如何将 swup 打包成一个独立的文件,你可以在这里找到配置 https://github.com/gmrchk/swup/blob/master/webpack.config.js。
非常感谢!
很棒的文章,感谢分享!
我想补充一点,需要考虑那些希望减少动画体验的用户。全屏页面过渡是常见的触发因素 前庭疾病,如眩晕。使用 减少运动媒体查询 创建替代的低动画或无动画体验,可以满足他们的需求,而无需完全放弃应用般的效果。
虽然媒体查询 目前仅受 Safari 支持,但它计划成为跨浏览器 5级媒体查询。
与 barba.js 及其他现有的类似库相比,它的优势和区别是什么?
我见过几个库,其中 Barba.js 绝对是最先进的,所以我将专注于这个库。
事实上,如果你将其与 swupjs 进行比较,它在工作方式上几乎相同。我认为 swupjs 稍微方便一些,因为它为你完成了选择过渡的逻辑,你不需要编写 promise 来等待过渡完成,只需简单地调用
next
函数即可。另一方面,我自己从未使用过 Barba.js,但它看起来很干净利落。对于 swup,差异更大一些。你可以使用 CSS 创建相当复杂的过渡,而无需进行太多工作。我开始做这个项目的初衷是能够在有限的时间内实现页面过渡,因为商业项目通常都是这样。
这太棒了。我想知道在更改页面状态时,会如何考虑屏幕阅读器。是否会对新页面视图的标题标签进行某种 focus() 操作?我想这完全取决于每个开发人员,如果提供了“渲染后”函数可以使用的话。
这很酷。我注意到的一件事,不确定这是不是故意的,就是 swup 不会评估已加载页面上的脚本。这是预期的行为吗?
Swup 将下一页的内容放入你的 DOM 中。看起来这对于浏览器加载 script 标签中的脚本来说还不够。
当然,它可以遍历已加载内容中的所有脚本并将其手动放入你的页面中。我不确定如果你访问同一个页面两次会发生什么(将相同的脚本放入你的 DOM 两次)。如果浏览器再次加载脚本,那么你的页面上就会出现两次相同的代码,如果不加载,那么你的页面就不会像正常加载时那样附加相同的代码。
所有这些看起来都像是非常笨拙的解决方案,我宁愿避免它。你可以做两件事:将所有代码放在一起并在需要时运行,或者使用某种代码分割,这样可以自动为每个页面加载脚本。
我明白了。我的测试实际上非常简单。我只是在每个加载的页面上发布了一个控制台日志,并检查它们在切换页面时是否会运行。它只在第一次页面加载时运行。Turbolinks 似乎处理了这个问题,因此我询问 swup 是否故意不这样做。
我自己从未需要过这样的功能,而且我有点反对它……但是如果你不同意我的观点,请随时提出问题。在我看来,对于没有页面重新加载的网站来说,这通常是一种不好的方法。
你可以尝试类似这样的方法,并在新页面准备好时执行它。
只需在要加载的 script 标签中添加一个名为 'js-inline' 的类。
你知道如何在 WordPress 中使用它吗?
我不是 WordPress 专家,但如果你能够修改你的模板,你应该也能够像其他任何网站一样集成它。查看 文档 以获取更多信息。
很棒的文章。感谢你嵌入视频而不是 GIF!