视差 - 元素毛刺

Parallax - Element Glitching

所以我一直在努力研究这个叫做视差的巧妙效果。背景基本上比前景元素滚动得慢。

我发现这个新的 "trick" 可以正常工作。随着滚动的进行,更改 top 属性 以创建视差效果。

问题...

因此,出于性能目的并在元素不在用户视口内时减轻 CPU 的压力,我创建了一个 if 语句,它检查 top 位置超过300px。如果是,它将覆盖所有内容并将 top 属性 设置回 0,因此它不会无缘无故地增加它。

现在,滚动一下。看到当红色 div 越过白色的时候,白色的如何停滞不前?查看 DOM 检查器,我看到 if 语句被吓坏了,将 top 属性 设置为 0px,即使它不超过 300px。哈尔普

在此期间,我希望看到更多有关视差效果的建议。我已经看到了一些关于这种效果的答案,但它们似乎......对我来说过于复杂。我知道有更好的方法来做到这一点,我知道有。

此外,如果没有 jQuery 答案,我们将不胜感激。谢谢。

var txtfirst = document.getElementById("txtfirst");


window.onscroll = function(){
 var ypos = window.pageYOffset;
  txtfirst.style.top = ypos * 0.4 + "px";


if(txtfirst.style.top > '300px'){
 txtfirst.style.top = '0px';
}
}
html, body {
 margin: 0;
 padding: 0;
 width: 100%;
 height: 100%;
}

.text-first {
  display: flex;
  text-align: center;
  justify-content: center;
  align-items: center;
  font-size: 32px;
  font-family: Arial;
  color: gray;
  width: 100%;
  height: 500px;
  position: relative;
}

.foreground-red {
  width: 100%;
  height: 600px;
  background-color: red;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: Arial;
  color: gray;
  font-size: 32px;
}

.spacer { /*for scrolling purposes*/
  width: 100%;
  height: 1000px;
}
<div class="text-first" id="txtfirst">THIS IS SOME TEXT</div>
<div class="foreground-red">THIS SHOULD GO ABOVE</div>
<div class="spacer"></div>

您的应用程序很可能(我假设您只提供了选定的片段)至少不起作用,因为您在比较 text 时要比较 numbers,在你的优化尝试中:

txtfirst.style.top > '300px'

上面的行为不会像您期望的那样。 Element::style 属性 中的每个 属性(例如,在您的情况下为 txtfirst.style)是 text string, not a number. A test like "50px" < "300px" does not compare whether 50 is less than 300, it compares the text values lexicographically.

如果您真的想比较像素的数量,可以使用 parseInt 函数将 50px 之类的值转换为数字 50。您的测试将如下所示:

parseInt(txtfirst.style.top) < 300

下面是您解决此问题的方法的一些问题和建议的解决方案,因为您对建议感兴趣。

使用内联样式通常是有问题的(主观)

  • 内联样式在 CSS 中具有最高优先级,这在用户拥有自己的样式表的情况下可能会出现问题,因为在这些样式表中设置的属性将被忽略以支持属性设置内联。

  • 假设内联样式的属性是实际使用的值,这是完全错误的。内联样式对象跟踪 分配的 值,而不是计算或使用的值。另一方面,Window::getComputedStyle(element) 函数检索元素的计算样式。

解决方案?使用 getComputedStyle 读取属性并将它们直接写入首选(或空,如果需要)样式表(document.styleSheets,反映所有 link rel=stylesheetstyle 元素):

function rule(selector) {
    var sheet = document.styleSheets[0];
    return Array.prototype.find.call(sheet.cssRules, rule => rule.selectorText == selector);
}
var txtfirst = document.getElementById("txtfirst");
window.onscroll = function() {    
    var ypos = window.pageYOffset;
    var style = rule(".text-first").style;
    style.top = ypos * 0.4 + "px";
    if(parseInt(getComputedStyle(txtfirst).top) > 300) {
        style.top = "0px";
    }
}

rule 函数 return 是 CSS 规则(包含 CSS 属性集)和匹配选择器(例如 .text-firsthtml, body) 来自第一个找到的样式表(你只有一个)。规则的 style 属性 是指包含规则中设置的所有 CSS 属性的对象。它的行为与内联样式对象相同。请注意,您没有在上面的任何地方使用内联样式,而是写入样式表对象(由文档的 <style>...</style> 片段初始化)并读回计算值。

修复使用 scroll 动画事件的问题

首先,您知道旧版本的 iOS 不会在您滚动时触发 scroll 事件吗?这将阻止你的视差效果死在它的轨道上,因为 single scroll 事件将被触发 after 用户 停止 滚动。这与浏览器进行页面滚动的方式有关——使用受限移动 CPU 资源实现流畅的页面滚动动画,运行宁 JavaScript 代码由 scroll 事件提供处理程序每​​秒 60 次被认为过于慷慨,而 Apple 转而采用了有争议的解决方案,因为他们一直专注于良好的用户体验。

无论如何,如果不使用scroll事件怎么办?你可以使用旧的 setInterval:

function rule(selector) {
    var sheet = document.styleSheets[0];
    return Array.prototype.find.call(sheet.cssRules, rule => rule.selectorText == selector);
}
var txtfirst = document.getElementById("txtfirst");
var old_window_pageYOffset = window.pageYOffset;
setTimeout(function() {    
    var ypos = window.pageYOffset;
    if(ypos != old_window_pageYOffset) return;
    old_window_pageYOffset = ypos;
    var style = rule(".text-first").style;
    style.top = ypos * 0.4 + "px";
    if(parseInt(getComputedStyle(txtfirst).top) > 300) {
        style.top = "0px";
    }
}, 1000 / 60);

上面所做的是确保在页面的整个生命周期内每秒调用一个函数 60 次,但是检查每次调用是否 window 的滚动位置自上次调用以来发生了变化,只有在有旧代码时才调用旧代码,否则什么也不做。这显然根本没有使用 scroll 事件。综上所述,较新的 iOS 版本已经恢复了该行为,并且每次滚动位置发生变化时都会触发 scroll 事件。这意味着您可能只想将其用作基线并依赖于事件而不是 setInterval。后者的一个免费好处是您可以控制视差效果 运行s.

我还可以建议使用 requestAnimationFrame,它比 setInterval 更 "intelligent",因为如果用户代理认为不需要动画,则不会调用您的代码,例如,如果整个页面选项卡目前不可见或与用户交互。请放心,您的动画将 运行 "when needed":

function rule(selector) {
    var sheet = document.styleSheets[0];
    return Array.prototype.find.call(sheet.cssRules, rule => rule.selectorText == selector);
}
var txtfirst = document.getElementById("txtfirst");
var old_window_pageYOffset = window.pageYOffset;
requestAnimationFrame(function() {    
    var ypos = window.pageYOffset;
    if(ypos != old_window_pageYOffset) return;
    old_window_pageYOffset = ypos;
    var style = rule(".text-first").style;
    style.top = ypos * 0.4 + "px";
    if(parseInt(getComputedStyle(txtfirst).top) > 300) {
        style.top = "0px";
    }
});

上面的代码是对视差效果的一次不错的尝试,除了一些与视差效果无关的小问题:

  • 当我们有 addEventListener 时,我不使用 on*name* 系列函数。前者是每个处理程序一个 属性,并且不能保证您的脚本是这些属性的唯一使用者——它们可能已经由浏览器扩展设置。我们可以争论网页作者是否拥有对他们可以获得的所有属性的独占所有权和访问权,但至少我已经解释了我的理由。我知道使用 addEventListener("scroll", function() { ... }) 没有明显的缺点。

  • 您不需要同时使用、class 名称和元素 ID 来引用它。 document.querySelector(".text-field") 将 return 在其 class 名称列表中具有 "text-field" 的第一个可用元素。

我把最好的留到最后 -- Pure CSS Parallax Websites 通过实现(尽管对于有错误的浏览器并非没有一些小技巧)期望的效果,完全没有任何 JavaScript,依赖于CSS perspective 属性 和其他一些人。它还提到了我在上面警告过的一些相同的事情,我试图规避和解释的事情。

如果你不想阅读(和理解)文档,我建议你求助于使用方便的抽象——一个插件、一个框架、一个库或类似的东西,这样你就不必了解其中的复杂性。现代 CSS 和兼容的浏览器模型非常复杂,足以让这些解决方案存在并蓬勃发展。