利用perspective和transform创建随鼠标倾斜的动画
03月16日, 2018 CSS JavaScript Tumars 3,297 views次
03月16日, 2018 3,297 views次
前言
本文系翻译自 Animate a Container on Mouse Over Using Perspective and Transform
本文首发于个人博客:(http://www.ferecord.com/animate-a-container-on-mouse-over-using-perspective-and-transform/.html)。如若转载请附上原文地址,以便更新溯源。
我正在做的项目需要给用户展示大量的图片,常见的灯箱效果(放大缩小等)略显枯燥,我决定让图片的展示效果更有互动性更有趣:当鼠标在图片上移动时让图片随鼠标的移动而倾斜。
效果如下:
在 codePen 查看: https://codepen.io/MihaiIonescu/pen/MrLo
这个效果的完成需要同时用到 CSS 与 JavaScript,下面的小教程能帮助你快速的理解。
建议各位先简单了解下 perspective 和 transform 后再阅读以下教程。 我们将会在下面的教程中充分了解这两个属性的用法。
开始
首先我们需要两个元素container
与inner
,container
元素将会用到 perspective 属性。
1 2 3 4 5 |
<div id="container"> <div id="inner"></div> </div> |
出于演示的目的,我们把这个元素作为卡片显示在屏幕中央:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
body { /* Full screen width and height */ width: 100%; min-height: 100vh; /* Centers the container in the middle of the screen */ display: flex; justify-content: center; align-items: center; margin: 0; background-color: rgb(220, 220, 220); } #container { /* This will come into play later */ perspective: 40px; } #inner { width: 20em; height: 18em; background-color: white; } |
可以看到结果是一张白色的卡片显示在灰色的背景上。
需要注意的是代码里我们让#container
的 css perspective 值为 40px
,这相当于告诉浏览器这个元素距离屏幕有 40px 的空间距离。等会儿我们通过 transforms 属性的改变便可以让它以 3D 旋转的方式在这段空间里展示出前后倾斜效果。
使用 JavaScript
先来定义一个鼠标事件的控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var container = document.getElementById('container'); var inner = document.getElementById('inner'); var onMouseEnterHandler = function(event) { update(event); }; var onMouseLeaveHandler = function() { inner.style = ""; }; var onMouseMoveHandler = function(event) { if (isTimeToUpdate()) { update(event); } }; container.onmouseenter = onMouseEnterHandler; container.onmouseleave = onMouseLeaveHandler; container.onmousemove = onMouseMoveHandler; |
这个控制器包括 3 个方面:
- Handler Functions:这些函数用来处理鼠标进入、移动、离开事件。
- Update Function:
update()
这个函数在这段代码里没有完整写出来,它的功能是根据鼠标移动的距离来改变#inner
的倾斜度,我们稍后完善这个函数。 - Time to Update Function:
isTimeToUpdate()
这个函数也没写出来,它的功能是控制update()
的执行次数,只有当它的返回值为 true 时才执行update()
。这是为了减少update()
的执行次数,以提高代码的性能。
上面的代码将:
- 在鼠标进入
#container
时,使#inner
产生 3D 旋转。 - 当鼠标在
#container
内移动时,以适当的时间间隔改变#inner
的 3D 旋转的角度。 - 当鼠标离开
#container
时,重置#inner
的样式。
Is it Time to Update?
先来看看 isTimeToUpdate 函数的内容,它可以控制 update()
调用的频率:
1 2 3 4 5 6 7 |
var counter = 0; var updateRate = 10; var isTimeToUpdate = function() { return counter++ % updateRate === 0; }; |
当 counter
值是 updateRate
的整数倍时,更新才会发生。这段代码表示 isTimeToUpdate()
每执行 10 次,更新才会发生一次。
鼠标
接下来我们创建一个对象用来记录鼠标的位置,提示一下,对于个 js 的初学这来说这段代码可能乍看之下挺复杂,但其实很容易理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Init var container = document.getElementById('container'); var inner = document.getElementById('inner'); // Mouse var mouse = { _x: 0, _y: 0, x: 0, y: 0, updatePosition: function(event) { var e = event || window.event; this.x = e.clientX - this._x; this.y = (e.clientY - this._y) * -1; }, setOrigin: function(e) { this._x = e.offsetLeft + Math.floor(e.offsetWidth/2); this._y = e.offsetTop + Math.floor(e.offsetHeight/2); }, show: function() { return '(' + this.x + ', ' + this.y + ')'; } } // 设置鼠标的中心位置 mouse.setOrigin(container); |
我们来聊一聊这段代码,它主要包括这几个函数:
show()
: 用来展示鼠标的当前位置(你可以用 console.log() 或别的什么方式)。setOrigin()
: 设置鼠标的初始坐标,即把#container
的中心位置设为(0,0)
。updatePosition()
: 获取鼠标当前相对(0,0)
的坐标位置。
代码完成后后效果如图:
在 codePen 查看: https://codepen.io/MihaiIonescu/pen/JpVPLQ
鼠标位置改变时改变样式
也就是我们上文提到过的update()
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var update = function(event) { mouse.updatePosition(event); updateTransformStyle( (mouse.y / inner.offsetHeight/2).toFixed(2), (mouse.x / inner.offsetWidth/2).toFixed(2) ); }; var updateTransformStyle = function(x, y) { var style = "rotateX(" + x + "deg) rotateY(" + y + "deg)"; inner.style.transform = style; inner.style.webkitTransform = style; inner.style.mozTransform = style; inner.style.msTransform = style; inner.style.oTransform = style; }; |
update()
:在鼠标位置改变时更新#inner
的样式。updateTransformStyle()
: 更新元素样式的 transform 属性。
经过以上代码,我们似乎已经王城了,鼠标移动时卡片也会以相同的角度 3D 转动,但看起来不太丝滑:
知道为什么吗?
因为我们为了性能设置了 isTimeToUpdate()
每执行 10 次,更新才会发生一次!这导致了每次更新之间会发生卡顿。
在 codePen 查看:https://codepen.io/MihaiIonescu/pen/VQWWgj
如何解决?使用 CSS transitions 就可以让这段动画变的丝滑。
使用 Transitions
1 2 3 4 5 |
#inner { transition: transform 0.5s; } |
你可以以自己的喜好来设置 perspective 的值和 transition 的持续时间。来看下最终结果:
在 codePen 查看: https://codepen.io/MihaiIonescu/pen/MQoodL
总结
通过这种简单的方法我们让一个图片更有交互性,你可以把这种方法应用到表单、弹框等等任何元素上。
另外这里有个纯 css 实现相同效果的方法,同学有兴趣可以了解一下:https://codepen.io/onediv/pen/BprVzp
xg4mq0yn
cfs79t6x
fb0ioo
vblvll
36o6pvy
uazc775
yl5fpbg0
i75syp4
txoy4o
1212