話不多說,先上效果,一個體驗非常好的拖拽緩動的效果,讓頁面提升一個檔次。
這個效果看似很簡單,到也困惑了很長時間,爲什麼別人寫出來的拖拽體驗爲什麼這麼好?
直到我自己實現了以後,才發現,原來我想的實現方式不對。接下來,我通過簡短的幾句話,來提供這個功能的實現思路。
首先,我們要明白,我們鼠標拖拽是在一個2d平面上拖拽
2d平面只有x軸和y軸,而且獲取的拖拽值也是基於平面的像素獲取的。所以,我們第一步,先通過鼠標事件來獲取到當前的拖拽的長度像素。
首先,綁定鼠標按下事件,來獲取到鼠標基於瀏覽器窗口左上角的xy平面二維座標。
然後,綁定move事件,在move事件回調內獲取到鼠標拖拽的座標,和按下座標相減,求出拖拽的距離。
然後,我們需要通過一定比例,將拖拽的像素轉換爲旋轉角度
我這裏設置的比例是,
鼠標橫向拖拽10像素,那模型沿3d的Y軸座標就旋轉5度,
鼠標縱向拖拽10像素,模型沿3d世界的X軸座標旋轉1度,並且還設置了範圍,即沿x軸旋轉再-45度到45度之間
function onDocumentMouseMove(event) {
mouseX = event.clientX;
mouseY = event.clientY;
targetRotationX = targetRotationOnMouseDownX + (mouseX - mouseXOnMouseDownX) * 0.5;
targetRotationY = Math.min(Math.max((targetRotationOnMouseDownY - (mouseY - mouseXOnMouseDownY) * 0.1), -45), 45); //拖拽後的目標位置
}
上面獲取到目標角度,重點來了,如何實現惰性旋轉呢?
通過上面思路,我們知道了目標角度,那麼直接設置目標角度,肯定就沒有這種想要的效果了,那麼如何實現這種惰性效果呢?
接下來,我們需要一個專門實現動畫的requestAnimationFrame方法,這個方法是閒時運行,最大根據性能能夠達到60幀每秒,有好多小夥伴感覺一直遞歸運行會不會卡頓,或者影響性能。那是你多慮了,這個方法會根據當前頁面性能進行減幀,保證頁面流暢運行。
我們有了這個以後,然後做什麼呢,就是用來實現緩動,在每一幀裏面,獲取到目標角度和當前角度的角度差,然後每一次只選擇總進度的百分之10 ,然後你會發現選擇離目標角度越近,越慢,體驗效果也是非常的棒。
而且在運行中,角度也會無限制的接近目標角度,當前demo是通過css3d來實現的:
function animate() {
requestAnimationFrame(animate);
rotateY += (targetRotationX - rotateY) * 0.1;
rotateX += (targetRotationY - rotateX) * 0.1;
box.style.transform = 'rotateY(' + rotateY + 'deg)';
item.style.transform = 'rotateX(' + rotateX + 'deg)';
}
案例全部代碼
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>css3d翻轉</title>
<style>
* {
padding: 0;
margin: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
perspective: 1000px;
}
.item {
width: 50vw;
height: 50vh;
transform: rotateX(-50deg);
perspective: 5000px;
transform-style: preserve-3d;
}
.box {
background: #abb9c5;
width: 100%;
height: 100%;
transform-style: preserve-3d;
position: relative;
}
.font,
.back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
line-height: 50vh;
background: #4cae4c;
backface-visibility: hidden;
}
.back {
background: #62ebff;
transform: rotateY(180deg);
}
</style>
</head>
<body>
<!--item 可以觸發翻轉的區域-->
<div class="item">
<!--box 可以翻轉的容器-->
<div class="box">
<!--font 默認顯示的正面-->
<div class="font">正面</div>
<!--back 背面-->
<div class="back">背面</div>
</div>
</div>
</body>
<script>
var targetRotationX = 0;
var targetRotationY = 0;
var targetRotationOnMouseDownX = 0;
var targetRotationOnMouseDownY = 0;
var mouseX = 0;
var mouseY = 0;
var mouseXOnMouseDownX = 0;
var mouseXOnMouseDownY = 0;
var box = document.querySelector('.box');
var item = document.querySelector('.item');
var rotateY = 0;
var rotateX = 0;
init();
animate();
function init() {
// EVENTS
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('touchstart', onDocumentTouchStart, false);
document.addEventListener('touchmove', onDocumentTouchMove, false);
}
function onDocumentMouseDown(event) {
event.preventDefault();
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
mouseXOnMouseDownX = event.clientX;
mouseXOnMouseDownY = event.clientY;
targetRotationOnMouseDownX = targetRotationX;
targetRotationOnMouseDownY = targetRotationY;
}
function onDocumentMouseMove(event) {
mouseX = event.clientX;
mouseY = event.clientY;
targetRotationX = targetRotationOnMouseDownX + (mouseX - mouseXOnMouseDownX) * 0.5;
targetRotationY = Math.min(Math.max((targetRotationOnMouseDownY - (mouseY - mouseXOnMouseDownY) * 0.1), -45), 45); //拖拽後的目標位置
}
function onDocumentMouseUp() {
document.removeEventListener('mousemove', onDocumentMouseMove, false);
document.removeEventListener('mouseup', onDocumentMouseUp, false);
}
function onDocumentTouchStart(event) {
event.preventDefault();
if (event.touches.length === 1) {
mouseXOnMouseDownX = event.touches[0].pageX;
mouseXOnMouseDownY = event.touches[0].pageY;
targetRotationOnMouseDownX = targetRotationX;
targetRotationOnMouseDownY = targetRotationY;
}
}
function onDocumentTouchMove(event) {
event.preventDefault();
if (event.touches.length === 1) {
mouseX = event.touches[0].pageX;
mouseY = event.touches[0].pageY;
targetRotationX = targetRotationOnMouseDownX + (mouseX - mouseXOnMouseDownX) * 0.5;
targetRotationY = Math.min(Math.max((targetRotationOnMouseDownY - (mouseY - mouseXOnMouseDownY) * 0.1), -45), 45); //拖拽後的目標位置
}
}
function animate() {
requestAnimationFrame(animate);
rotateY += (targetRotationX - rotateY) * 0.1;
rotateX += (targetRotationY - rotateX) * 0.1;
box.style.transform = 'rotateY(' + rotateY + 'deg)';
item.style.transform = 'rotateX(' + rotateX + 'deg)';
}
</script>
</html>