本文系翻譯自 Animate a Container on Mouse Over Using Perspective and Transformjavascript
本文首發於我的博客:www.ferecord.com/animate-a-c…。如若轉載請附上原文地址,以便更新溯源。css
我正在作的項目須要給用戶展現大量的圖片,常見的燈箱效果(放大縮小等)略顯枯燥,我決定讓圖片的展現效果更有互動性更有趣:當鼠標在圖片上移動時讓圖片隨鼠標的移動而傾斜。html
效果以下:java
在 codePen 查看: https://codepen.io/MihaiIonescu/pen/MrLoweb
這個效果的完成須要同時用到 CSS 與 JavaScript,下面的小教程能幫助你快速的理解。瀏覽器
建議各位先簡單瞭解下 perspective 和 transform 後再閱讀如下教程。 咱們將會在下面的教程中充分了解這兩個屬性的用法。函數
首先咱們須要兩個元素container
與inner
,container
元素將會用到 perspective 屬性。性能
<div id="container">
<div id="inner"></div>
</div>
複製代碼
出於演示的目的,咱們把這個元素做爲卡片顯示在屏幕中央:flex
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 旋轉的方式在這段空間裏展現出先後傾斜效果。動畫
先來定義一個鼠標事件的控制器:
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 個方面:
update()
這個函數在這段代碼裏沒有完整寫出來,它的功能是根據鼠標移動的距離來改變 #inner
的傾斜度,咱們稍後完善這個函數。isTimeToUpdate()
這個函數也沒寫出來,它的功能是控制 update()
的執行次數,只有當它的返回值爲 true 時才執行update()
。這是爲了減小 update()
的執行次數,以提升代碼的性能。上面的代碼將:
#container
時,使#inner
產生 3D 旋轉。#container
內移動時,以適當的時間間隔改變#inner
的 3D 旋轉的角度。#container
時,重置#inner
的樣式。先來看看 isTimeToUpdate 函數的內容,它能夠控制 update()
調用的頻率:
var counter = 0;
var updateRate = 10;
var isTimeToUpdate = function() {
return counter++ % updateRate === 0;
};
複製代碼
當 counter
值是 updateRate
的整數倍時,更新纔會發生。這段代碼表示 isTimeToUpdate()
每執行 10 次,更新纔會發生一次。
接下來咱們建立一個對象用來記錄鼠標的位置,提示一下,對於 js 的初學者來講這段代碼可能乍看之下挺複雜,但其實很容易理解。
// 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()
函數:
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 就可讓這段動畫變的絲滑。
#inner {
transition: transform 0.5s;
}
複製代碼
你能夠以本身的喜愛來設置 perspective 的值和 transition 的持續時間。來看下最終結果:
在 codePen 查看: https://codepen.io/MihaiIonescu/pen/MQoodL
經過這種簡單的方法咱們讓一個圖片更有交互性,你能夠把這種方法應用到表單、彈框等等任何元素上。
另外這裏有個純 css 實現相同效果的方法,同窗有興趣能夠了解一下:https://codepen.io/onediv/pen/BprVzp