使用js實現拖動緩動的效果

話不多說,先上效果,一個體驗非常好的拖拽緩動的效果,讓頁面提升一個檔次。
緩動效果
這個效果看似很簡單,到也困惑了很長時間,爲什麼別人寫出來的拖拽體驗爲什麼這麼好?
直到我自己實現了以後,才發現,原來我想的實現方式不對。接下來,我通過簡短的幾句話,來提供這個功能的實現思路。

首先,我們要明白,我們鼠標拖拽是在一個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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章