详细资料

您多久浏览一次404页? 通常,它们没有样式设置并保留默认设置。 最近,我找到了test.do.am ,它的交互式字符引起了人们的注意,并活跃了错误页面。

可能只有一张猫的照片,然后他们想到了眼睛的移动,开发人员实施了这个主意。 图片 现在,用户访问页面并检查效果。 它是一个很酷且令人愉悦的小功能,可以捕获,然后用户与同事或朋友讨论,甚至重复该功能。 如果不是这样,可能会很容易:

  1. 用户调整窗口大小时,不会更新中心点。 使用较小宽度的视口打开浏览器窗口,然后将其调整为全屏大小,这只猫看起来不在光标处。
  2. 中心点位于左眼,而不是圆的双目中心。
  3. 当用户将光标悬停在眼睛之间时,眼睛的苹果不会聚集在一起,也不会聚焦。 眼睛正注视着无穷远,这就是为什么猫不注视用户,而是注视用户的原因。
  4. 眼睛的动作是即时的,它们需要保持平稳。
  5. 苹果的移动是由于左边距/顶部距的变化而发生的。 不正确,请在下面找到说明。
  6. 如果光标位于页脚下方,则眼睛不会移动。

我的建议

首先,让我们实现完美的眼睛运动。

1.准备标记

<div class="cat"> <div class="cat__eye _left"></div> <div class="cat__eye _right"></div> </div> 

2.获取到眼睛元素的链接

 const cat = document.querySelector('.cat'); const eyes = cat.querySelectorAll('.cat__eye'); const eye_left = eyes[0]; const eye_right = eyes[1]; 

3.注册mousemove事件侦听器并获取光标坐标:

 let mouseX; let mouseY; window.addEventListener('mousemove', e => { mouseX = e.clientX; mouseY = e.clientY; }) 

我在窗口对象而不是文档主体上添加了mousemove侦听器,因为我需要使用所有屏幕来获取鼠标坐标。

4.运动
由于要平滑运动,因此无法在mousemove处理程序中进行管理。

添加将由requestAnimationFrame获取的更新方法,该方法与浏览器更新同步。 通常,续订每秒发生60次,因此,我们每16.6毫秒每秒就会看到60张图片。

如果开发人员假设用户的浏览器不支持requestAnimationFrame,则开发人员可以使用setTimeout后备或现成的polyfill

 window.requestAnimationFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })(); 

为了及时更新或稳定获取更新,我注册了启动变量

 let started = false; let mouseX; let mouseY; window.addEventListener('mousemove', e => { mouseX = e.clientX; mouseY = e.clientY; if(!started){ started = true; update(); } }) function update(){ // Here comes eyes movement magic requestAnimationFrame(update); } 

这样,我就不断获取更新方法和光标坐标。 然后,我需要获取眼睛内部苹果运动的值。

我尝试将两只眼睛作为单个元素移动

 let dx = mouseX - eyesCenterX; let dy = mouseY - eyesCenterY; let angle = Math.atan2(dy, dx); let distance = Math.sqrt(dx * dx + dy * dy); distance = distance > EYES_RADIUS ? EYES_RADIUS : distance; let x = Math.cos(angle) * distance; let y = Math.sin(angle) * distance; eye_left.style.transform = 'translate(' + x + 'px,' + y + 'px)'; eye_right.style.transform = 'translate(' + x + 'px,' + y + 'px)'; 

非常简单:使用Math.cos和Math.sin方法,找到dx和dy,它们是眼睛中心和鼠标之间的坐标差,找到从中心到光标的角度,获得水平和垂直方向的运动值。 使用三元运算符并限制眼睛移动区域。


首先为Math.atan2方法给出Y值,然后是x值。 结果,用户注意到眼​​睛运动不自然并且没有聚焦。

让每只眼睛移动,观看时不要互相参照。

 // left eye let left_dx = mouseX - eyesCenterX + 48; let left_dy = mouseY - eyesCenterY; let left_angle = Math.atan2(left_dy, left_dx); let left_distance = Math.sqrt(left_dx * left_dx + left_dy * left_dy); left_distance = left_distance > EYES_RADIUS ? EYES_RADIUS : left_distance; let left_x = Math.cos(left_angle) * left_distance; let left_y = Math.sin(left_angle) * left_distance; eye_left.style.transform = 'translate(' + left_x + 'px,' + left_y + 'px)'; // right eye let right_dx = mouseX - eyesCenterX - 48; let right_dy = mouseY - eyesCenterY; let right_angle = Math.atan2(right_dy, right_dx); let right_distance = Math.sqrt(right_dx * right_dx + right_dy * right_dy); right_distance = right_distance > EYES_RADIUS ? EYES_RADIUS : right_distance; let right_x = Math.cos(right_angle) * right_distance; let right_y = Math.sin(right_angle) * right_distance; eye_right.style.transform = 'translate(' + right_x + 'px,' + right_y + 'px)'; 


有趣但比以前的结果差的是,眼睛独立地上下移动。 因此,我将第一个演示用作基本的运动机制,并在光标位于字符中心时使眼睛的苹果聚在一起。

我不会描述完整的代码,请在此查找结果:


通过反复试验,我为眼睛的移动和聚焦匹配了所需的参数。 所以现在我需要平滑。

平滑处理

链接TweenMax库并编写类似这样的代码?

 TweenMax.to( eye, 0.15, {x: x, y: y}); 

为简单任务链接整个lib没有意义,因此,我从头开始进行平滑处理。

假设页面上只有一只眼睛元素,并且其位移区域完全没有限制。 为了使鼠标坐标值更平滑,我使用以下机制:

 const SMOOTHING = 10; x += (needX - x) / SMOOTHING; y += (needY - y) / SMOOTHING; eye.style.transform = 'translate3d(' + x + 'px,' + y + 'px,0)'; 

我使用translate3d将眼睛分开到另一个渲染流并加快它们的速度。

诀窍是每隔16.6毫秒(每秒60张图片)的变量x和y趋向于需要的值。 每次续签会将价值关闭为所需差额的1/10。

 let x = 0; let needX = 100; let SMOOTHING = 2; function update(){ x += (needX - x) / SMOOTHING; console.log(x); } 

然后每隔16.6毫秒更新一次,我们将获得简单的平滑和下一个x值(大约):

 50 75 87.5 93.75 96.875 98.4375 99.21875 99.609375 100 

还有一些不明显的技巧:

-开始这项检查以优化工作量

 if(x != needX || y != needY){ eye.style.transform = 'translate3d(' + x + 'px,' + y + 'px,0)'; } 

但是当它们接近眼睛位置几乎相同时,必须将x等同于needX

 if(Math.abs(x - needX) < 0.25){ x = needX; } if(Math.abs(y - needY) < 0.25){ y = needY; } 

否则,x和y值将达到needX和needY的时间太长; 不会有视觉差异,但是每次屏幕更改都会影响眼睛样式。 顺便说一句,你可以自己弄弄它。

 let x = 0; let needX = 100; let smoothing = 2; function update(){ x += (needX - x) / smoothing; if( Math.abs(x - needX) > 0.25 ){ // replace 0.25 with anything else and check number of x renewals. window.requestAnimationFrame(update); } else { x = needX; } console.log( x.toString(10) ); } update(); 

-如果上面的技巧很明确,则可以创建更复杂的效果,例如弹簧。 最简单的平滑和光标近似如下所示:

 x += (mouseX - x) / smoothing; y += (mouseY - y) / smoothing; 


添加所需和当前坐标值之间的差异。


有时近似限制是有意义的。 上面有一个示例,其中值从0变为100,因此在第一个迭代值达到“ 50”时,这是1步的巨大数字。 这种机制有点让人联想到阿喀琉斯和乌龟的悖论


眨眼

每隔2-3秒隐藏并显示苹果的眼睛。 最简单的方法是动态值y-scale的“ display:none;”,“ transform:scaleY(N)”要复杂一些。

创建2个const

const BLINK_COUNTER_LIMIT = 180; -闪烁开始前的续订次数,
const BLINKED_COUNTER_LIMIT = 6; -一眨眼期间的续订次数。

还有2个变量,这些值将在每次续订时更改。

 let blinkCounter = 0; let blinkedCounter = 0; 

眨眼代码

 let blinkTransform = ''; blinkCounter++; if(blinkCounter > BLINK_COUNTER_LIMIT){ blinkedCounter++ if(blinkedCounter > BLINKED_COUNTER_LIMIT){ blinkCounter = 0; } else { blinkTransform = ' scaleY(' + (blinkedCounter / BLINKED_COUNTER_LIMIT) + ')'; } } else { blinkedCounter = 0; } 

BlinkTransform是笔触变量,在眨眼和眨眼之间的跟随之间具有空值

 ' scaleY(0.17)' ' scaleY(0.33)' ' scaleY(0.50)' ' scaleY(0.67)' ' scaleY(0.83)' ' scaleY(1.00)' 

所有计算都给出变量眨眼变换,其值应添加到眼睛位置变换的CSS代码中。 因此,如果在3秒的停机时间内添加了空字符串,并且对眼睛的缩放没有影响,则在闪烁期间添加了css值。

 eye_left.style.transform = 'translate(' + xLeft + 'px,' + y + 'px)' + blinkTransform; eye_right.style.transform = 'translate(' + xRight + 'px,' + y + 'px)' + blinkTransform; 

故事的教训

每天我们遇到看起来简单明了的事物,甚至不了解这种外部简单性掩盖了大量的问题和改进。 我认为魔鬼是构成整个最终结果的细节。 穆罕默德·阿里(Muhammad Ali)是20世纪最好的拳击手,在直拳的时候举起了后脚后跟。 这个动作增加了打击的有效距离,并给了他更多获胜的机会。 一直有效。

附言:我对网站没有任何影响,希望其所有者不会对我的评论表示冒犯。 为了方便起见,我在代码中将“ apple of eye = eye”命名为。

Source: https://habr.com/ru/post/zh-CN443720/


All Articles