纯CSS视差透视图像超出景观图像
#css #网络开发人员 #教程 #设计

我研究了许多有关“视差滚动”效果的教程和示例,但我并没有留下深刻的印象。大多数视差示例都以视觉方式和有关代码的方式都不优雅。我经常努力理解无关的样式和误导性班级名称之间隐藏的原则。一些“纯CSS”示例仍然使用JavaScript,并且大多数人都不关心可访问性,即使用户设置为减少运动,也可以使用视差运动。

这个(meta-)教程在我了解正在发生的事情以及如何编码工作,健壮,可访问和可维护的视角效果的方式上显示了一些实验和故意失败典型的“真棒景观”图像

parallax animation example gif by Wikimedia commons
资料来源:Wikimedia Commons: File:Parallax_scrolling_example_scene.gif

目录

视差滚动效果的定义

无论如何,什么是“ 视差透视效果”?看起来很简单,似乎对网站上的内容有不同的解释,该网站被推广为各种scroll-linked运动效果的伞术语。流行的视差效果简单地调整了全宽英雄图像,以使其比需要稍大,然后添加一个微妙的卷轴链接运动,使其看起来好像我们在移动时通过窗户看遥远的景观。这非常好,尽管如果图片没有显示遥远的景观,那没有多大意义。

超越令人敬畏的景观图像

将效果限制为英雄图像的另一个原因是,这避免了许多更复杂的方案的问题,正如我们在下面检查代码演示时会看到的。

Animated website scrolling example by Andrej Sharapov on dribbble
资料来源:CSS mix-blend-mode and Awesome parallax scrolling by Andrej Sharapov on dribbble

另一种改变内容的卷轴链接效果,例如将照片叠加在匹配的图形或线框模型上,例如在popular Ivy Chen example中,也已被列为视差效应,而实际上它是完全不同的东西。

根据Wikipedia, parallax的说法,

是“沿两种不同视线观察的物体的明显位置的位移或差异”,parallax scrolling在模拟卡通动画膜或计算机图形中应用了这种效果比前景图像更慢,在距离的2D场景中产生了深度的幻想:“就像经典跳跃游戏中的云一样

animated sequence of the classic 1980s computer game Giana Sisters, found on makeagif.com
资料来源:The Great Giana Sisters makeagif.com的游戏顺序。

与经典的电影或电脑游戏示例不同,在景观前,某些东西朝着水平方向运行,骑行或苍蝇,网站通常朝垂直方向滚动。

误解,过时和复杂的教程

我听到了有关透视效果的不同信息,从“仅添加perspective属性”到“ html文档 /身体必须充当视角父母 /滚动容器,因此有必要重构我们的项目和风险不良的副作用。在甚至不使用滚动效果的页面上。

我发现了基思·克拉克(Keith Clark)相对直接的编码epen,与他的2014年Pure CSS Parallax Websites相关,我对我进行了分叉和修改,以便更好地理解并减少其核心要求,以生成最小的模板,以添加可添加到不同类型的视差透视效果任何项目都不违反现有工作,也无需修改现有标记。我只成功了。

一些“纯CSS”视差示例实际上包含JavaScript,并且某些方面可能已经过时,并且在将近十年后过时。但是本质仍然一样!引用基思的2014年博客文章:

parallax类是视差魔术发生的地方。定义元素的高度和透视风格属性将锁定透视图的中心,创建固定的Origin 3D视口。设置overflow-y: auto将允许元素内部的内容以通常的方式滚动,但是现在将后代元素相对于固定的视角渲染。这是创建视差效应的关键。

使用此技术并用.parallax::after样式替换背景层div,我们应该能够在没有JavaScript,全球样式和副作用的情况下实现目标。

纯CSS和最小的其他标记

我们为什么还要这样做?尽管尾风和引导程序在各处都使用功能类,但React项目和内容管理软件会根据随机实例哈希引入难以辨认的通用类名称,为什么不像1999年一样编写HTML并添加许多新的Div元素来包装我们的虚拟层?您可能会从我的语气和以前的文章中猜测,这不是我更喜欢编码现代网站的方式。

试图记录装饰模块如何以及为什么引入全球样式和可能的副作用,我忍不住觉得自己以错误的方式实施了它。

Screenshot of my IDE with the code mentioned below and a README section about side effects and global styles

我更喜欢介绍新设计

  • 模块化/原子方式中防止副作用
  • separating design and content,在裸露的语义内容周围提供可选和可互换的皮肤,
  • 以最少的方式保留我的代码可读减少加载时间

这就是为什么我不认为我成功创建了一个最小的便携式模板,以实现简单的视差滚动效果。但是,这仍然可能是向前迈出的一步 - 在来回几步之后,正如您在此中间调试屏幕截图中看到的那样,试图理解和调试添加彩色边界并尝试一下位置,高度,溢出和Z -Index:

Screenshot trying to debug an intermediate stage adding colorful borders and experimenting with position, height, overflow, and z-index

新代码仍然具有副作用,因为我们似乎需要使其透视背景层的高度大于内容,并且用overflow: hidden进行剪辑以某种方式破坏了透视效果,因此我们需要给出相邻的无关相关的无关元素具有更高的z-index并定义背景颜色,以确保它们保持在顶部并在视觉上保持不受影响。在基思(Keith)的良好旧教程的"parallax sections" chapter中已经提到了另一个方面:

在分组元素时要牢记的一个重要规则,我们不能剪辑组的内容。将overflow: hidden设置在parallax__group上将破坏视差效果。取消的内容将导致后代元素溢出,因此我们需要对组的z-index值进行创造性,以确保在访问者通过文档滚动时正确揭示/隐藏内容。

至少从理论上讲,我们可以将观点效果限制在视差容器上,因此我们不需要应用全局html, body样式。但是,这不会打破连续的身体滚动并在各节中添加滚动条吗?

Side view of parallax demo layers in Keith Clark's demo #3

正如我们在Keith Clark's third demo的“调试模式”中看到的那样,我们实际上至少有三个透视图:前景元素,匹配的部分背景(包含背景图形)和一个“深度背景”,这可能是文档主体或另一个父母包装元素包含效果,使其与文档的其余部分独立。

包含效果,确保滚动,不要弄乱全球文档!

但是,该演示如何防止多个滚动条,并确保我们可以使用鼠标或触摸板以通常的方式滚动整个文档,即使用鼠标轮滑动,拖动滚动栏或按下箭头键?好吧,只是没有!页面顶部和底部有两个静态元素,但是深层背景已设置为height: 100vh; overflow-y: auto和文档body

我想将overflow: hidden移至我们所包含的部分,而不会破坏透视效果。但是,当视差容器已经具有相同的高度和溢出样式时,我们真的需要添加另一个包装器吗?事实证明,身体样式是多余的,无关的和过时的,因此我可以在一切工作时删除此部分。

Debugging screenshot of the same demo with disabled body styles in the developer tools

重点是,演示在深度背景之前或之后不使用未固定到绝对位置的任何其他内容部分。如果确实如此,它将与我的临时Codepen遇到相同的问题: double scrollbars 中断用户体验

Screenshot of the same demo with static elements before the deep background section resulting in double scrollbars

在这一点上,我仍然失败或拒绝理解为什么我不能简单地使深层包装器与其基础层的内容高度相匹配,并夹住溢出的装饰背景层而不会破坏视差透视效果。

了解我的中间双滚子示例

虽然原始Codepen在视差(组)容器内具有多个前景元素,但我们可以在它们周围添加另一个包装器,以使其更容易验证(并希望控制)其高度和行为。

Screenshot of my own codepen with added foreground wrapper element marked with a blue border

现在,我可以将任何布局样式(在我的最小示例中只是一些填充)移至新的前景内容包装器,然后将margin: 0; padding: 0设置为视差(组)容器。

但是,当我更改其高度以匹配前景内容包装器并设置装饰背景,或者父母视差(组)容器或两者兼而有之以夹住其溢出的内容时会发生什么?那会再次打破我令人敬畏的视差透视效果吗?它会,因为我们需要一些滚动内部的容器才能使图层移动。如果没有更多的滚动,也没有更多的滚动效果!

(DIS)全球身体高度方法的优势

这就是大多数看似过度工程的教程使用整个文档主体作为深层背景的原因,并使用z索引使无关的内容区域保持在装饰背景层的顶部,并且具有不透明的背景色,而装饰的部分具有透明的部分,以便在其内容背后看到装饰。

将文档主体用作深层背景的优势是它已经存在,而无需在我们的视差部分 /模块之外添加标记。但是,我们有可能干扰可能与身体做其他事情的其他模块,因此这种方法可能会破坏无关的代码,尤其是在大型项目或WordPress(例如WordPress)中使用时。

由于我们的装饰背景层是其视差组集装箱的孩子,其绝对定位的四个边缘设置为零偏移率的位置和大小所属的位置和大小,同时仍然允许其由于其视角和距离 / z轴转换,导致所需的卷轴视差透视效果。< / p>

(删除)老式“视差”方法的优势

但是,在接受那些骇人听闻的必需品之前,我想再次看一下看似简单的景观图像效应。也许这些与我以前的探索之间有一些实际的中间立场,这些探索仍然感觉像是一种过于复杂的方法,可以像this classic W3Schools How-To一样为文档主体设置background-attachment: fixed的结果,尽管在这种情况下,背景不会移动完全,在视差方案中,它以另一个可以进行微调的速度移动,此外,我们可以选择使用透视技术时添加多个背景层。

固定背景附件的优点:只有几行代码,非常易于理解,从理论上的立场我们甚至可能会将固定的背景视为透视场景的特殊边缘情况,该方案的距离近似无穷大,就像看月球或在改变视野时似乎根本不动的星星,这与距离较小的山脉或建筑物不同。

2D转换以接近3D透视图

CSS Sticky Parallax Sections, a codepen by Ryan Mulligan是一种简化的pseduo-perspective方法的另一个示例,而无需实际使用三维转换,但这也是将实际代码埋在美丽无关的造型和“隐藏” JavaScript中的另一个例子。

粘性背景图像运动似乎仅适用于此Codepen中的全高(100vh)部分。如果我们可以使用前景内容高度,那将是完美的。也许我们可以修改背景的顶部边距或粘性容器父母内部的位置,以明确控制其运动。

可访问性:尊重偏爱的运动

但是,还有另一个方面使瑞安的Codepen脱颖而出:他关心可访问性,并使用自定义属性来禁用视角运动,如果使用使用减少运动:

@media (prefers-reduced-motion) {
  :root {
    --scale: 0;
  }
}

更多的研究...

那时,我仍然对到目前为止的思想发现和理解都不满意。每个示例都有一些缺点,使得在其他情况下很难使用。

  • 3D透视需要全球身体样式?
  • 包含2D尺度,但需要100VH部分?
  • 2D固定附件不移动背景
  • 较旧的教程取决于昂贵过时的JS计算

如何很难找到一个总结该概念并为每个替代方案提供简单的便携式示例的教程。不应该有超出简短的perspective reference的MDN上的Great Grid和Flexbox教程吗?

It looks like there aren't many great matches for your search

继续我的搜索,谷歌搜索“ 2D视差近似CSS”,我想出了很多stackoverflow结果,以及另一本由Rob O'Leary在2022年撰写的DEV文章:

结论

我已经在这个主题上“浪费”了太多时间,这可能也不是现成的食谱的完美综述 - 除非我做了一段完整的重写 - 但是我还是会分享它并备份我的想法和发现。

有用的教程和代号

最后,务实的“超越风景”设置

我的最终,干净且最小的(足够)版本的行为与臭名昭著的“令人敬畏的风景”相似,但是它允许使用多个CSS背景或任意DOM内容,其移动比前景内容慢。

为其简单和遏制选择2D

我的Codepen主要基于Ryan Mulligan的教程,但确实有助于遵循Keith Clark的实际3D方法来理解和比较这两者的优势和缺点,并在我最终找到Rob O'Leary的Dev Post时,看到了我自己的结论,并得到了验证。 。任何视差方法都有其局限性和警告,所有最近的帖子似乎都同意,我们应该依靠纯CSS而不滚动位置听众,以避免已知的昂贵回调会导致性能问题。

z-indexsupported by every modern browsercustom properties (CSS variables)也是如此。我们甚至可以添加background-attachment: fixed来改善传统浏览器支持,只有一条代码。

适应超越英雄图像的2D方法

为了避免使用此技术具有可变内容高度段落而不是英雄风格的页面高度部分时,我试图夹住容器,并给它一个相应的余量,以补偿前景的margin-top: -50vh和背景的height: 100vh,以及我将后者更改为min-height: 100vh,使其对于任意长度的内容有用。请注意,除非我们相应地调整前景的负面边距,否则100vh以下的值可以使我们的视差部分重叠其他内容。

Screenshot of overlapping content marked by colorful debugging borders

我们不能在底部太早剪辑背景的情况下夹住外部容器的overflow-y,但是为什么呢?因为position: sticky将其从上下文中脱离,因此其自然高度降低到0â除非我们添加bottom: 0将其跨越到其父容器的两个边缘?不幸的是,在这种情况下这不起作用。

使用grid区域对齐两层(如stackoverflow在position: absolute and parent height?的stackoverflow答案中所建议的删除了透视效果,据我所知,没有本地CSS属性可以使用元素在calc()函数中使用元素的滚动位置(尚未)。 /p>

务实地,我们只能使用JavaScript一次,并且只能使用一次,以确定前景层的高度,并将其写入自定义CSS属性,我们可以在koude27公式中使用该属性来增加视频背景的min-height,以防100vh不够。为我们的部分提供绝对最小的高度也可能是一个好主意,因为视觉效果需要一定的空间才能按照预期的方式展开。

:root {
  --parallax-min-height: 20rem;
}

.parallax__layer--background {
  min-height: max(100vh, var(--parallax-min-height));

因此,由于可访问性原因,我们有两个可变的自定义属性--parallax-scale,可以将其设置为零,而--parallax-min-height则可以根据文档完成渲染后的一次性测量来增加该--parallax-min-heightdocument.addEventListener('DOMContentLoaded' – �)但这将超越纯CSS解决方案。

移动并隐藏重叠的顶部边距

我们需要一些额外的空间来使视觉效果正常工作,但是如果在普通的文本图像中使用该效果的文章上下文中,我们不希望破坏我们的布局,而没有页面高度的英雄部分。因此,让我们尝试调整容器的顶部边缘和/或隐藏上述元素下方的重叠。

我们可以通过这样的min-height计算来补偿最高的偏移:

.parallax__layer--foreground {
  margin-top: calc(-1 * max(100vh, var(--parallax-min-height)));

但这同时使前景和背景滚动在一起而没有任何偏移,直到它们到达视口的顶部,而sticky位置可以解决。

因此,我们要么在外部容器上使用相同的负面边距,并确保其内容被定义另一个负z-index并添加position: relative,以确保其内容隐藏在下面的下方 禁用任何z-index指令。

.parallax__group {
  margin-top: calc(-1 * max(100vh, var(--parallax-min-height)));
  z-index: -1;
  position: relative;

margin-top仍然不太匹配,因为它尚未补偿我们的Y转换。在不用太多的方面将整数缩放因子转换为rem或像素值的情况下,我只是用像这样的真实偏移来代码:

.parallax__group {
    3.75rem /* fixed offset adjustment */
    + 
    (-1     /* compensate background min-height */
      *
      max(100vh, var(--parallax-min-height))
    )
  );

现在我们几乎完成了:ðð

Intermediate state screenshot with overlapping content above the parallax scrolling section

要求:不透明背景和零边缘兄弟姐妹

现在存在副作用:即使有一个CSS选择器来针对先前的元素,我们的最高边距调整也引入了影响无关内容的新需求。在我们的视差滚动部分之前的任何元素都必须具有不透明的(非透明)背景,否则背景装饰将闪耀。

如果我们也调整效果部分的底端,也可能是正确的。尽管这种副作用破坏了我的理想主义模块化需求,但它只是“只有一个background-color”和零边缘(我们可以使用填充)。就像典型的body { margin: 0; padding: 0一样,这就是大多数(原子)设计系统通常设定的,因此我认为该解决方案在实践中足够好。

/* recommended other elements' opaque background etc. */
body {
  margin: 0;
  padding: 0;
}

.header, .footer, .content {
  background-color: white;
  margin:0;
  padding: 1rem;
}

与背景的高度和边缘相同:它看起来不错,并且用重复圆的模式破裂,但是在使用一组中心对象或臭名昭著的“很棒的景观”图像时,没有人会注意线性梯度,就像其他所有教程和现成的预设一样。

Screenshot of sticky parallax codepen highlighting the linear background making the background image fade out

现在,让我们删除任何调试内容,看看它是否有效,(重新)测试替代浏览器,不同的视口,并确保如果用户喜欢减少运动!

,则确实没有视差效果!

在行动中看到它:我的新Codepen

来源:codepen.io/openmindculture/pen/wvQevbd

相关代码片段:

<section class="parallax__group">
  <div class="parallax__layer parallax__layer--background">
  </div>
  <div class="parallax__layer parallax__layer--foreground">
:root {
  --parallax-scale: 0.1;
  --parallax-min-height: 44rem;
  --container-offset-adjustment: 5rem;
}

/* disable if requested for accessibility reasons */
@media (prefers-reduced-motion) {
  :root {
    --parallax-scale: 0;
  }
}

/* container around parallax layers */
.parallax__group {
  position: relative;
  z-index: -1;
  transform-origin: center top;
  transform: scaleY(calc(1 - var(--parallax-scale)));
  margin-top: calc(
    var(--container-offset-adjustment)
    + 
    (-1
      *
      max(100vh, var(--parallax-min-height))
    )
  );
  margin-bottom: calc(-1 * var(--container-offset-adjustment));
}

.parallax__group > * {
  transform-origin: center top;
  transform: scaleY(calc(1 / (1 - var(--parallax-scale))));
}

.parallax__layer--foreground {
  position: relative;
  top: 0;
}

.parallax__layer--background {
  position: sticky;
  z-index: -2;
  top: 0;
  height: 100%;
  min-height: 100vh;
  min-height: max(100vh, var(--parallax-min-height));
  width: 100vw;
  background-attachment: fixed; /* legacy fallback */
  background-image: url(…); 
}

/* recommended other elements' opaque background etc. */
body {
  margin: 0;
  padding: 0;
}

.header, .footer, .content {
  background-color: white;
  margin:0;
  padding: 1rem;
}

就是这样。这不是我希望写的完美最适合的教程,也不是我的代码。我只能说,它足够好,它帮助我更好地了解了该概念和必要的CSS样式。希望它也可以帮助和激发他人,并表明即使是高级网络开发人员也不总是提出完美的解决方案。