以下是 34 Cross 的 Rajoshi Ghosh 和 Tanmai Gopal 的客座文章。 他们给我发了邮件,向我展示了他们的新网站以及它在拥有酷炫功能、视觉效果丰富和响应式的情况下是如何高效的。 我想,嘿,你应该写一篇关于它的文章! 这就是它。 沉浸在数学中。
更新: 本文发布后,他们更新了网站。 他们保留了本文提到的网站,地址为 old.34cross.in”,我将更改本文中的链接指向该地址。
另一次更新: 似乎“旧”网站也已失效,我们将删除指向它的所有链接。
在 34 Cross,我们希望开发新的网站,使其具有响应式、移动友好型,并且能够仅使用 HTML 和 CSS 轻松地在 2G 网络上加载。 在本文中,我们将告诉您我们在网站设计和速度优化方面遇到的挑战以及如何克服这些挑战。 首先,我们将重点关注滚动视差效果。 接下来,我们将展示一些仅通过 CSS 即可实现的交互式功能。 最后,我们将提供一份有关性能的一般性注意事项清单。

第 1 部分。 仅使用 CSS 的视差效果
我们基于 Keith Clark 的(可能是第一个)CSS 视差 概念 构建了我们的 CSS 视差。 他关于视差的关键思想非常简单。 本质上,当您滚动时,离您“更近”的元素移动得更快,离您“更远”的元素移动得更慢。 它是不同元素以不同速度移动的分层效果。
这对于网站来说很不寻常,因为网站通常将所有元素放置在同一个平面上,当您滚动时,所有元素都以相同的速度移动。 但是,如果我们将网站视为投影在我们计算机屏幕上的 3D 世界,我们可以将一些东西移动到屏幕后面很远的地方,让一些东西留在前面。 然后垂直滚动使更远的东西移动得更慢,前面的东西移动得更快。 瞧,视差! 正如它在现实世界中工作的那样。
1.1. 简要介绍 CSS 3D 几何
CSS 的 3D 变换使我们可以像这样查看网站的元素

点 C
是计算机屏幕中心,屏幕的左上角(从用户的角度来看)是原点。 网页元素可以放置在任何位置,浏览器渲染引擎将这些元素投影到我们的屏幕上,让我们查看它们。 这与在 3D 中绘图本质上相同。
第一步是定义透视
- 选择一个元素作为您的 3D 投影表面。 它不需要是整个屏幕。
- 告诉浏览器您希望投影表面离您的眼睛有多远。
这两件事都是通过在元素上定义 perspective
属性来完成的。 perspective
属性本质上定义了上图(图 1)中长度 p
。 p
的值越低,3D 效果越强烈。 值越大,效果越弱。
1.2. 实现视差
让我们定义一个元素,通过它我们可以查看我们的 3D 世界。
- 我们希望整个视窗成为我们进入 3D 世界的窗口,因此我们将创建一个
#container
元素来覆盖整个视窗。 - 让我们将
p
的值定义为1px
。 默认情况下,透视原点是元素的中心(点C
)。 - 我们将视差元素分成组。 我们创建的组将有一个元素在背景中(通过将其沿
-z
轴向后移动)以及一个元素在前景中。
这将导致以下操作
特殊注意事项
- 由于我们并不真正理解的原因,我们需要将视差效果分成组。 Chrome 实际上不需要这样做,但 Firefox 需要。
preserve-3d
是#group1
的重要指令,它要求它尊重其父元素的 3d 属性。 否则,透视指令将被忽略。
现在,我们在前景平面中从视窗顶部有 500px 的间隙。 背景看起来更小,因为它被推开了。 由于它的投影(图 1 中的 d
),它周围还有一些空白。
接下来我们要做的是让它看起来高度为 500px,以便它填补 500px 的间隙。 从图 1,投影高度为 h_1
。
基本三角学告诉我们
p/(p-z) = h_1/h
这给了我们投影高度
h_1 = h * (1 + z/(p-z))
由于我们将 z 设置为 -1px,将 p 设置为 1px,因此我们得到
h_1 = h / 2
这意味着转换会将我们的图像缩小 1/2
,因此将原始元素放大 2 倍会使它看起来高度为 500px。
让我们看看它的实际效果。 其他所有 CSS 与之前相同。
较远的元素现在高度为 500px,但似乎仍然在元素顶部有一些空白。 在新标签页中打开上面的 Pen ,然后垂直调整窗口大小。 我们假装的前景粉红色 div 顶部的空白将不断变化。 这些空白来自哪里?
这需要详细了解缩放的工作原理。 缩放应用于元素时,会从其中心缩放该元素。 让我们看看缩放前后元素的侧面视图


回到我们的 HTML,我们看到,即使我们将图像放大到感知大小相同,元素仍然距顶部有一定的偏移量。 让我们尝试通过解决顶部间隙 x
来纠正这一点
h
是背景元素的高度(EF
)s
是应用的缩放比例。 因此,s.h = E_2F_2
v/2
是视窗高度的一半e
是从透视原点到元素中心的垂直距离,e_1
是其在视窗上的投影1 + z/(p-z)
是在z
距离处任何高度乘以的因子,以得到投影高度。- 使用以下约束条件:在视窗投影上:
x + E_2'O' + e_1
是视窗高度的一半 x + s/2 * h * (1 + z/(p-z)) + e * (1 + z/(p-z)) = v/2
x = h/2*(1-s) + h*z*(1-s)/(2*(p-z)) - v*z/(2*(p-z))
- 应用我们的属性:
z = -1, p = 1, s = 2
,我们得到 x = (v-h)/4
重要的是要注意,间隙 x
取决于视窗高度。 如果我们将背景元素的顶部偏移此值,则顶部边缘将始终对齐。 但是,由于 x
是间隙的感知值,因此背景元素将向上移动 (1 + z/(p-z))^-1
倍。 这意味着我们应该将元素向上移动:((p-z)/p) * x
,在我们这种情况下为 2 . x
。
但是,我们如何应用这种公式版本的 CSS 高度? CSS calc() 来了! 对于高度为 500px 的元素,我们得到
top: calc(250px - 50vh);
让我们看看它的实际效果
完美! 现在我们只需要继续添加更多组即可。 在每个组中,我们将根据需要放置元素。 尝试调整 z-index
值以确保正确的元素位于最上面。
这是一个 Sass 模块,可以帮助您为视差设置基本的平移和缩放数学。
第 2 部分。 纯 CSS 交互性
2.1. 仅使用 CSS 的响应式菜单导航
该网站在有空间的情况下有一个平均的链接行导航栏。 但在小屏幕上,我们想要一个仅使用 CSS 的下拉式导航。 这需要一些交互性(点击以显示)。 很少有 HTML 元素可以在没有任何 JS 的情况下进行交互,例如表单元素,例如复选框。 当这些元素更改状态时,CSS 选择器允许我们 检测这些更改并执行某些操作。
工作原理
- HTML 中的复选框:
<input type="checkbox">
- CSS 中的伪状态选择器:
input:checked
- CSS 中的同级选择器,用于选择相邻的
<div>
:input:checked ~ div
,用于在状态更改时切换 div 的高度 - 使用
max-height
来转换 div 的高度,而不是height
,因为最终的高度可能是未知的。
结果
你可以发挥创意,创建可重复使用的过渡效果,比如 “阅读更多” 手风琴。你需要遵守的关键限制是,expandee
元素必须紧随 input
元素,这样才能让兄弟选择器生效。如果你想让触发元素(比如 “阅读更多” 链接或箭头按钮)看起来位于展开元素的下方,那么 position: absolute
将是你的好帮手。
2.2. 只用 CSS 动画滚动
这只是一个技巧,但有一个相当大的限制。
我们只能用 CSS 控制样式属性,而不能控制滚动条位置,这只能通过 JavaScript(以及用户交互)来实现。但我们可以使用 CSS 的 :target
选择器来缓慢地动画化容器 <div>
的 top
值到某个特定值。限制在于用户会失去滚动控制,因为 top
值已被 CSS 设置。
因此,虽然你可以动画化滚动(我们将在本文中演示),但如果你认为用户更喜欢直接滚动,那么你可能不想这样做。
工作原理
- 点击锚链接会将一个片段添加到 URL 中。(例如:http://site.com/#id)
- CSS 的
:target
伪类选择器在 URL 片段与 ID 匹配时激活(例如:#id:target
)。 - 这可以用来动画化
top
属性,从而产生滚动效果。
在下面的示例中,我们创建了一些页面内锚链接。与通常将锚链接放置在相应内容附近不同,我们创建了虚假的锚链接,它们可以帮助我们触发 CSS 动画。
第 3 部分. 优化清单
优化页面加载速度是一项永无止境的工作。你需要找到一个平衡点,在将多少时间用于改进现有基础设施,升级基础设施,以及需要投入多少精力来关心这些方面。有一些明显但非常重要的要点。
- 减少每次请求传输的数据量:只使用压缩过的最小化资产。
- 减少浏览器渲染页面所需的请求数量。每个 CSS 文件、JavaScript 文件和图像文件都是另一个请求。还要在它们上使用浏览器缓存,这样就不需要多次请求它们。
- 提高服务器响应时间。对于静态内容使用 nginx,其他情况使用 Apache。调整服务器以获得最佳性能。使用良好的应用层缓存,尤其是当你拥有经常动态生成的内容时(比如通过复杂的 SQL 连接)。
- 减少 阻塞 JavaScript 内容。
- 不要编写无关的 CSS 规则。始终计划高效地编写你的 CSS 高效。
3.1. 图片压缩
图片是导致页面加载缓慢的主要原因之一。以下是一个清单,可以帮助你充分利用图片。
3.1.1 调整图片大小和选择合适的格式
不同的图片格式有不同的优势。大多数格式对比例敏感,比如 JPG 和 PNG,而一些格式则对比例不敏感,比如 SVG。
- 原生图片尺寸应该与渲染后的图片尺寸完全一致或接近(即,最好将 400x400px 的 JPG 以 400×400 的尺寸显示)。或者使用 SVG 之类的比例不敏感格式。
- 图片类型
- 数百万种颜色(比如照片):JPG 和有损压缩
- 插图或透明度要求:PNG8,如果确实需要超过 256 种颜色,则使用 PNG 真彩色。
3.1.2 压缩图片
有很多方法可以压缩和优化图片。以下是一些接近底层的技术,可以帮助你从图片中压缩出最后几个不需要的字节。
要手动压缩 JPG,一个好的起点是这个 StackOverflow 答案。使用 ImageMagick 并运行以下命令
convert -strip -interlace Plane -gaussian-blur 0.05 -quality 85% source.jpg result.jpg
如果你不想模糊,请使用以下命令
-sampling-factor 4:2:0


要手动压缩 PNG
- 步骤 1:pngcrush 会尝试各种方法来减少调色板、丢弃无用块等。
来源:文件大小减小 - YUI 博客
- 步骤 2:pngquant 将你的 PNG 从 PNG 真彩色格式转换为 8 位格式。对于静态图片,这总是值得尝试,因为大小会减少到大约三分之一!唯一的不足之处是,你需要手动查看图片,确保质量足够好。
提示:对于 JPG 使用渐进式渲染,或者对于 PNG 使用隔行扫描,以获得更好的图片加载效果。请参阅 Coding Horror 上的 渐进式渲染。
3.1.3 雪碧图和数据 URI
图片雪碧图是一张包含许多较小图片的大图片,这些图片位于不同的位置。它们通常通过 CSS 的 background-position
来渲染,通过移动位置来显示你需要的那部分图片。关键是:雪碧图减少了对服务器的总体请求数量,正如我们之前所述,这有利于性能。
雪碧图的另一个优势是,所有图片都已加载,因此可以避免延迟加载问题。例如:当悬停过渡显示一张单独的图片时,会有一闪而过,因为图片只有在需要时才会被获取。雪碧图可以避免图片预加载问题。
雪碧图最大的缺点是,自行创建雪碧图可能很麻烦。这篇文章 介绍了大量相关技术。如果你有一组大小相似的图片,使用 ImageMagick 的 montage 来创建雪碧图是一个毫不费力的任务。
montage -mode concatenate -tile 2x10 1.jpg 2.jpg ... out.jpg
这将创建一张 2 列 10 行的图片蒙太奇,命名为 out.jpg
。
数据 URI 是一种巧妙的技术,可以将所需的图片嵌入到 HTML 或 CSS 中。与雪碧图一样,数据 URI 可以减少对服务器的总体请求数量。但是需要注意的是:数据 URI 在移动设备上会 慢 6 倍。
3.2. 缓存和最小化
关于良好最小化和缓存技术的资源已经足够多了,但这里提供一个简单易行的解决方案,值得实施:Pagespeed 的 nginx 模块。Pagespeed 的 Apache 和 nginx 模块包含了许多最佳实践。它们提供了很多过滤器和选项,值得你 详细了解。你可以 构建 自己的 nginx 并包含 Pagespeed 模块,或者使用类似 这个 的 Docker 镜像。
结论
- 只用 CSS 可以做很多事情。用 CSS 添加一些交互性本质上是一个技巧,但用声明式的方式添加交互性和过渡效果无疑有着强大的力量。
- 始终优化你的图片!在任何可以的情况下使用有损压缩,并投入时间来做好它们。将自动压缩和调整大小功能集成到你的构建流程中。
- 页面应该快速加载!可能没有什么比 Google 的 Pagespeed 模块更有效(以最小的努力)了。
很棒的文章。我从这个例子中学到了很多东西。
这就是我们要的!
真的太酷了,我喜欢这种性能。
由于该技术正在滚动溢出框,请记住
1) 此视差技术阻止了 iOS Safari 浏览器在向下滚动时后退。也许这会困扰你,也许不会。
2) 使用此技术时,请记住使用 -webkit-overflow-scrolling: touch;,以在 iOS 上获得良好的动量滚动效果。
希望 iOS Safari 能自行处理这个问题。
嗨,Mark:
感谢您的评论。我刚刚添加了您和 Chris 建议的修复程序,使滚动在 iOS 上也变得平滑。
啊哈,太丝滑了!我现在真的想试一试。您是在小尺寸下为了设计而禁用了视差吗?还是为了电池?或者其他原因?
我的一部分想尝试在移动设备上使用请求动画帧来实现视差(现在 iOS 最终在滚动期间触发滚动事件),但我没有那么强的 JS 技能,可能会使页面变得很耗能。但是我认为使用这种透视技术,我可以更安全地做一些类似的事情。
在 iPhone 上,没有流畅的垂直滚动。这是故意的吗?相比之下,更简单的“职业”页面拥有流畅的滚动。很难向下滚动水平员工图片,滚动后,页面似乎卡住了。
它只是一个登陆页面!我们期望它有多复杂?在阅读这篇文章之前,我甚至没有注意到视差。
嗨,cba:
此页面是尝试做一些不同事情的实验。所以它有时会让你感到头疼,但尝试一下还是很有趣的。
感谢 Mark 和 Chris 的帮助,我已经修复了 iOS 上的流畅滚动问题。:)
你做到了!这说明了对反馈的响应 :-)
先简单说一下视差:透视在 IE8 或 IE9 上不起作用。我认为一些企业仍在付费以支持 WinXP。
对于图像压缩,那些有损技术很酷,但还有更多无损压缩可以进行。对于 JPEG,我推荐 JPEGrescan – 我对此有贡献,需要说明一下。它可以将该示例图像压缩到 8000 字节以下,减小了 2.25%。
对于 PNG,我有几个建议。首先,如果图像看起来只有很少的颜色,请尝试在 PhotoShop 或 Gimp 中将其海报化。这可能会删除您不知道存在的无用“混合”颜色,或者如果有人将 JPEG 粘贴到您的 PNG 中,可能会删除 JPEG 伪影。
其次,有许多 PNG 优化器,包括 Google 的 Zopfli。通常可以将它们一个接一个地使用。最后,始终使用 Ben Jos Walbeehm 的 DeflOpt.exe – 由于他的网站已关闭,似乎很难找到 – 以节省更多字节。
关于:2.1. 仅使用 CSS 的响应式菜单导航
我认为这也可以通过将不可见的复选框放在上方并使用
<label for="that-checkbox">
作为触发器来解决。(可能需要调整 Tab 索引,以便键盘导航可以预测性地工作,并且您可能希望指定一些焦点效果的[type=checkbox]:focus ~ label
样式。)浪费了太多空白空间。滚动是噩梦。这些技术很好,但设计对可用性来说很糟糕……
我在 iPhone 6 上的 Safari 中测试了 34cross.in。页面滚动存在一些重大问题。
Safari 的浏览器不会消失。通常,向下滚动页面时,Safari 会将顶部浏览器(地址栏)缩小一半并隐藏底部浏览器(工具栏)。这在您的页面上不会发生。考虑到该站点有一个相当大的固定标题,可用的垂直视窗会大大减少。
滚动不会动画,而是突然结束,因此“感觉很奇怪”。固定的滚动速度使滚动变得很累人,因为无法通过快速、重复地向上/向下滑动来加速滚动。
页面的某些部分会禁用滚动。(具有水平滚动功能的部分。)这与问题 1 结合在一起,使这些部分的滚动几乎不可能。
我想知道 这样做 是否会有所帮助。我同意滚动很粗糙。我们已经习惯了这种流畅的动量风格,没有它感觉很不对劲。
嗨,Sime:
我已经修复了流畅滚动问题,但地址栏后退问题存在于所有 Chrome 浏览器中,也许与这样一个事实有关:整个视窗大小的相同内容 div 未被检测为需要后退效果的“高”页面。我认为没有办法解决这个问题。有什么想法吗?
一篇很棒的文章……学到了很多……我必须提高我的数学技能……
对于 PNG 压缩,我使用过 https://tinypng.com/
对于 JPG 压缩,我使用过 http://www.jpegmini.com/
很棒的文章。您是如何让 MathML 在以下情况下正确显示的?1. 在 WordPress 上,不会让它的 WYSIWYG 重新格式化您的标记;2. 在非 Gecko 浏览器(如 Chrome)上?
我对这一部分很感兴趣
您发现了什么?
我对您代码中 1px 的透视和 -1px 的 translate-z 感到印象深刻。我本来会花很长时间来平衡几百的透视和负数的 translate-z。您的代码更优雅。我敢说这是数学的力量。
当一些在 Z 轴上推回的内容在视差中突然消失时,我遇到了这个问题。例如,看看 Keith 的演示(在调试模式下)http://keithclark.co.uk/articles/pure-css-parallax-websites/demo3/。
在调试模式下,一旦开始向下滚动,FF 开始隐藏一些 div,而 Chrome 则保持所有 div 显示。我最初将整个页面都写在一个视差组中,但当用户向下滚动几百像素时,顶部面板会突然消失。一旦我将其移动到组中,一切就正常工作了。
为什么会出现这种情况?也许 FF 试图通过不渲染它认为不在视窗中的内容来优化渲染,但 translate + scale 扰乱了它的计算?
该网站非常时尚,但在我的移动设备(1280 x 720)横向模式下查看时,可用性很差,因为固定标题占用了太多宝贵的屏幕空间。借鉴 Google Material Design 的风格,我建议您将标题缩小为一个可折叠图标,在移动设备上以不显眼的方式放置在角落。
您好,我只想说感谢您提供如此精彩的教程!
非常有趣的文章。他们制作的网站太棒了!
很棒的文章!我在 iPhone 4 上的 2G 网络上查看您的网站,它加载速度非常快,我当时想“等等,什么?已经加载了?!:D”。关于 iPhone 4 用户体验的一点说明,恕我直言,
.mobm
的高度太高了,它覆盖了超过 25% 的视窗,这使得内容的阅读略微困难 截图在这里。除此之外,正如我所说,文章很棒,我正在保存它,并迫不及待地想在我的下一个项目中使用这些知识!:)太酷了。两点
– “应用于元素的缩放会以其中心缩放该元素”。这取决于您的原点。默认情况下,transform-origin 在中心。将其设置为 y 轴的 0。
– 透视也有原点。将其也设置为 y 轴的 0。
之后您就不再需要 calc() 了!另外,您可以使用任何 translateZ() 值,不仅仅是 1。
http://codepen.io/atirip/pen/qEjYpm
我最近在网站中使用了这种 CSS 视差效果,并发现了一些问题。
第一:缩放和 z 轴平移图层似乎存在一些舍入问题或其他问题。在所有示例中,红色图层比蓝色图层窄 4px,在两侧留下了难看的白色间隙。我已经通过比需要稍微多一点地缩放图层来解决了这个问题:2.02x 而不是 2x 等等。
第二:Windows 上的 Chrome 在滚动方面存在重大问题。您可以在 Windows 上按下鼠标滚轮并在所有方向上自由滚动。这允许您在页面侧边滚动,并且比使用滚动条时向下滚动得更远。看起来 Chrome 在计算 3D 变换层上的边界框而不是未变换的边界框。
我无法解决此问题,因此视差目前仅在网站的 FF 上处于活动状态。遗憾的是。
以下是我所指的示例。请注意,这不是“调试视图”,我只是滚出了页面
“仅使用 CSS 的响应式菜单导航”简直是我见过的最巧妙的纯 CSS 应用!而且由于它是预定义的 HTML,它比使用 JavaScript 快得多,即使禁用 JavaScript,您仍然可以访问所有链接。
我读过的关于 CSS3 主题的最有用文章之一。非常酷。
非常酷!迫不及待想看到将来更多内容。