如何:光标跟踪视差
你见过屏幕上的元素会响应鼠标移动的特效吗?它们经常用于类似视差的效果,或者用于眼睛跟随光标。我在jhey.dev上用这个特效来制作熊头上的阴影眩光。
我不知道技术名称。我们就用“光标追踪视差”吧。
好消息是,这项技术不需要花费太多时间,还能为你的设计增添一些额外的亮点。记住,细节决定成败。
让我们画个鬼脸吧!我们先从一些标记开始。
<div class="face">
<div class="face__eyes">
<div class="eye eye--left"></div>
<div class="eye eye--right"></div>
</div>
<div class="face__mouth"></div>
</div>
我们已经着手设计了💅
记住,您可以在 CodePen 中查看已编译的 HTML、CSS 和 JavaScript。使用下拉菜单打开源面板,然后点击“查看已编译的 HTML/CSS/JavaScript”。
那张脸很棒。但是,如果我们能让它更有活力就更酷了。
为此,我们可以使用 CSS 变量和“pointermove”事件监听器。
document.addEventListener('pointermove', () => {
// Make those features move by updating some CSS variables.
})
不过,我们希望限制这些特征的移动。我们不希望它们到处乱飞。我们想要“微妙”的效果。
让我们首先更新眼睛容器的 CSS。这很重要。我们不需要对每个眼睛都进行过渡。我们将在 中使用作用域 CSS 变量transform
。
.face__eyes {
transform: translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px));
}
注意我们是如何使用 的calc
值的1px
。保留一些 CSS 变量的单位并非坏习惯。这让我们可以轻松地切换到不同的单位。
不过目前还没有变化。--x
和 的--y
值会回退到0
。不过,你可以试试这个 demo,看看更新 值会对眼睛产生什么影响。
现在。那些脚本怎么样?我们需要一个函数,将光标位置映射到某个定义的范围并输出一个值。
为此,我们可以创建一个映射函数。
const mapRange = (inputLower, inputUpper, outputLower, outputUpper) => {
const INPUT_RANGE = inputUpper - inputLower
const OUTPUT_RANGE = outputUpper - outputLower
return value => outputLower + (((value - inputLower) / INPUT_RANGE) * OUTPUT_RANGE || 0)
}
我们的映射函数接受一个输入范围和一个输出范围。然后它返回一个函数,我们可以使用它来将一个值映射到另一个值。
让我们来看看这里发生了什么。我们给函数传入两个范围,分别用于输入和输出。计算范围后,我们返回一个函数。这个函数发挥了神奇的作用。
- 计算输入值相对于输入范围的分数值。
- 将其乘以输出范围。
- 将其添加到输出范围的下限。
考虑这个例子,输入范围为 0 到 100,输出范围为 500 到 1000,输入为 50。
50 => 500 + (((50 - 0) / 100) * 500))
50 => 500 + (0.5 * 500)
50 => 500 + 250
50 => 750
我们需要把它和 CSS 变量 transform 绑定起来,这样就搞定了!下面是如何将 x 轴的平移关联到我们的眼睛上。
const BOUNDS = 20
const update = ({ x, y }) => {
const POS_X = mapRange(0, window.innerWidth, -BOUNDS, BOUNDS)(x)
EYES.style.setProperty('--x', POS_X)
}
document.addEventListener('pointermove', update)
这有效!
剩下要做的就是连接其他轴和特征。注意我们如何声明要使用的“BOUNDS”。对于 y 轴,我们按照相同的步骤将其作为window.innerHeight
输入。
但是,嘴巴怎么办呢?嗯,这就是 CSS 作用域变量的威力所在。
我们不要在眼睛容器上设置样式,而是在脸部元素本身上设置它。
const FACE = document.querySelector('.face')
const update = ({ x, y }) => {
const POS_X = mapRange(0, window.innerWidth, -BOUNDS, BOUNDS)(x)
const POS_Y = mapRange(0, window.innerHeight, -BOUNDS, BOUNDS)(y)
FACE.style.setProperty('--x', POS_X)
FACE.style.setProperty('--y', POS_Y)
}
进行这些更改不会破坏任何内容。这就是 CSS 变量作用域的作用。变量值仍然会向下级联到眼睛容器。但现在嘴巴也拥有访问权限,我们可以对其使用相同的变换。起始位置translateX
用于在进行另一次平移之前将嘴巴居中。
.face__mouth {
transform: translateX(-50%) translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px));
}
现在嘴巴也动了!
但是,看起来不太对劲。它和眼睛的转动同步,感觉有点不对劲。这算是“注重细节”的一点,值得学习。比如,如果我们的脸上有耳朵,眼睛向上,耳朵应该往下?往下!照照镜子看看,我不会评判你的。我为了“细节”做过更奇怪的事情😅
那么我们该如何解决这个问题呢?嗯,还记得我calc
在一开始提到过使用无单位值吗?现在这很有用。
我们实现了 JavaScript 和 CSS 的关注点分离。这很棒!JavaScript 负责计算光标映射范围并将其传递给 CSS。它并不关心我们在 CSS 端如何处理它。事实上,“BOUNDS” 可以是一个整数100
,我们可以在 CSS 端随意使用它。
脸部的各个特征各自处理变换。目前,它们都使用系数1px
。
.face__eyes {
transform: translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px));
}
.face__mouth {
transform: translateX(-50%) translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px));
}
但是,如果我们改变嘴巴以使用系数会怎样-1px
?
.face__mouth {
transform: translateX(-50%) translate(calc(var(--x, 0) * -1px), calc(var(--y, 0) * -1px));
}
现在嘴巴的移动方向与眼睛的移动方向相反。
但是,我们只需要在 CSS 中更改系数。这就是使用作用域 CSS 变量但保持其无单位的超强功能之一。我们可以用一个变量来驱动整个场景,同时保持良好的关注点分离。
进行一些调整,我们就有了一个使用 CSS 变量的光标跟踪面!
但是,你不必只在表面上使用它。它可以用于很多东西。另一个“巧妙之处”是用它来创建类似视差的图标背景。这里的技巧是background-position
使用 CSS 变量来更新。
我在这个演示中使用了这个效果。它不是“主要内容”。但它是一个很棒的小附加功能。
⚠️ 此演示包含音频 ⚠️
这是图标背景的独立版本,可以试用一下。诀窍是创建一个带有你喜欢的图标的图块,然后依靠它background-repeat
。
在这个演示中,你可以配置系数。这体现了我们分离关注点并让 CSS 自行处理值这一原则。
就是这样!
这是使用 JavaScript 和 CSS 作用域变量实现“光标追踪视差”的一种方法。我很期待看到你运用这些技术实现的效果。你还能做出什么新东西?和往常一样,请告诉我你的想法,下次再见!
所有代码均可在此CodePen Collection中找到。
保持精彩!ʕ •ᴥ•ʔ
文章来源:https://dev.to/jh3y/how-to-cursor-tracking-parallax-52cf