js slow animation function package

js slow animation function package

​ A section of the process of learning pink teacher's webAPIs course, now take a note and summarize my thoughts. Finally, do a little personal development.

demand:

  • Encapsulate a function animateX(obj, target, callback), which can realize the animation effect of the horizontal movement of the element object.
  • The formal parameter obj receives the element object to be moved, target receives the target position of the element object, and callback is the callback function executed after the element reaches the target position.
  • Register the events that make the box reach the two target positions for the two buttons to verify the function of the function.
  • The speed of the element will become slower and slower as it moves (ease animation).
  • The element object can move back and forth between two or more target positions, and if an event of another target is triggered before reaching the last target position, the element can turn to reach another position in the middle.

Realization principle

1. Animation realization principle
  • Core principle: The position of the box is constantly moved through the timer setInterval().
  • Implementation steps:
    • Get the current position of the box (obj.offsetLeft).
    • Let the box add 1 distance to the current position (set the style.left value of the positioning box).
    • Use the timer to keep repeating this movement operation.
    • Set a condition to end the timer, the target position has been reached or the animation function is triggered again. (Not clearing the previous timer when triggered again will cause multiple timers to exist at the same time, which will cause bugs to be discussed below).
2. The principle of realizing the easing effect
  • Core principle: The distance between the element and the target position is decreasing during movement. By dividing the distance by 10 as the distance of the element movement, the goal of continuously reducing the step size (moving speed) is achieved.
  • Implementation steps:
    • Trigger the timer to retrieve the position of the current element.
    • Subtract the current position from the target position to get the remaining total distance of movement.
    • Core algorithm: (target value-current position) / 10 as the step size (step ).
    • The step should be rounded, otherwise the target position cannot be reached, and there will be a decimal problem.
3. The principle that rounding can make the box finally reach the target position
  • If the step size is an integer, it is rounded up.
  • If the step size is negative, it is rounded down.
  • principle:
    • Assuming that the element finally reaches the position 10px~20px away from the target position, the step size will be 2px after rounding up each time, and the step size will become 1px when it is less than or equal to 10px, and finally only 1px will be left. The step size 1px /10 will be rounded up to 1px .
    • The principle of rounding down negative numbers is the same as that of positive numbers.

Final implementation code and results

Code
<div>
    <button class="btn1">点击到第一个位置100</button>
    <button class="btn2">点击到第二个位置300</button>
    <span></span>
</div>
<script>
    function animateX(obj, target, callback) {
     
     
        // 先清除以前的定时器,只保留当前的一个定时器执行
        clearInterval(obj.timer);
        obj.timer = setInterval(function() {
     
     
            // 步长值写到定时器的里面
            // 把我们步长值改为整数 不要出现小数的问题
            var step = (target - obj.offsetLeft) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            if (obj.offsetLeft == target) {
     
     
                // 停止动画 本质是停止定时器
                clearInterval(obj.timer);
                // 如果传入回调函数则执行,如果没有则不执行
                callback && callback()
            }
            // 把每次加固定值的步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
            obj.style.left = obj.offsetLeft + step + 'px';

        }, 15);
    }
    var span = document.querySelector('span');
    var btn1 = document.querySelector('.btn1');
    var btn2 = document.querySelector('.btn2');

    btn1.addEventListener('click', function() {
     
     
        animateX(span, 30);
    })
    btn2.addEventListener('click', function() {
     
     
        animateX(span, 300, function() {
     
     
            span.style.backgroundColor = 'red';
        });
    })
    // 匀速动画 就是 盒子是当前的位置 +  固定的值 10 
    // 缓动动画就是  盒子当前的位置 + 变化的值(目标值 - 现在的位置) / 10)
</script>

result

Note that when I press button 2 before the element reaches the position of 300 (it turns red when it arrives), I click button 1 again, and the element is directly turned back to the position of 100, so it meets the demand.

Detail processing

1. Clear the timer
  • Clear the timer after the element reaches the target position to prevent the element from no longer moving (step is 0) after the element arrives, but the timer is still running, occupying memory resources.
  • Clear the timer before the timer is declared. This is to prevent us from the last click event is still running, that is, when the timer in the last animation function is still running, we click the button again or click another button to trigger another animation function. At this time, there will be multiple timers running at the same time on this element object. Affect the movement state of the element. As shown below.

When the previous timer for rightward movement has not ended, another leftward movement timer is started, and the situation shown in the figure above appears.

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-mE26qa4z-1611173022840)(https://s3.ax1x.com/2021/01/11/sG3yCQ.gif )]

Keep tapping the timer in the same direction to speed up the animation.

2. Timer statement issue
  • When declaring the timer, the teacher has not mentioned clearing the timer before running the declared timer, so the teacher will use var timer = setInterval() to change the way of declaring the timer to the reason and reason for adding the timer as an attribute of the obj object. The idea is that if multiple elements use this animation function, the timer must be declared with var every time (occupying memory). We can use different timers for different elements (we use our own timers so that we can easily distinguish them.). Core principle: Using JS is a dynamic language that can easily add attributes to the current object.
  • But when I looked at the code, I also thought of one thing, that is, when we trigger the click event, a function scope will be generated, and if we use var to declare the timer, the timer variable is a local variable. So the code to clear the timer above is actually equivalent to nothing. That is, the timer in another animation function cannot be cleared in another animation function . Therefore, we need to use the above method to add timers.

Some small attempts of my own

Insert picture description here

I have added a speed control function to this animation function, which is divided into three levels. Fast, medium speed, slow speed. Of course, it will still move with the speed characteristics of slow animation during the process (without changing the characteristics of step size reduction). A process timing effect has also been added, so the element cannot be turned before it reaches the target position (because I don't know how to change the timing, haha). So I used the method of adding a throttle valve mentioned later by the teacher. There is a flaw that the three speed control buttons can still be pressed when the elements are in motion, and they can be changed later.

<script>
    var btn1 = document.querySelector('.btn1');
    var btn2 = document.querySelector('.btn2');
    var div1 = document.querySelector('.div1');
    var timeCount = document.querySelector('.time_count'); // 计时的span盒子
    var speed = 0; // 默认速度中等
    var span = document.querySelector('.speed')
    var spans = span.querySelectorAll('span'); // 获取三个速度按键元素

    function animateX(obj, target, timingFunction, callback) {
     
      // speed 可以传入的参数自定义有fast slow medium(默认)
        var init = obj.offsetLeft; // 获得该元素对象初始的位置
        var distance = target - init; // 获得移动的距离
        var stepLength = distance / 10; // 计算步长
        var starTime = +new Date(); // 开始的时间
        var flag = 1;
        var counts = 0;
        if (distance != 0) {
     
     
            var t = setInterval(function(f) {
     
      /* 第一次将flag以参数形式传进计时函数发现里面的定时器会在执行完一次后就清除了即计时只停在100ms */
                f = flag;
                if (!f) {
     
     
                    clearInterval(t);
                }
                var nowTime = +new Date();
                counts = nowTime - starTime;
                timeCount.innerHTML = counts + 'ms';
            }, 200);
        }


        if (timingFunction == 2) {
     
     
            time = 180;
        } else if (timingFunction == 0) {
     
     
            time = 60;
        } else {
     
     
            time = 90;
        } // 处理速度的问题
        // clearInterval(obj.timer); 此时也不需要清除定时器了,因为有函数锁只需要在下面清除即可
        // timesCount(starTime, flag);
        obj.timer = setInterval(function() {
     
     
            if (distance == 0) {
     
      // 移动距离为0时移除定时器并执行回调函数
                window.clearInterval(obj.timer);
                callback && callback();
                flag = 0;
            }
            // 如果移动距离不为0则进行移动
            // 处理不能精确到达目标位置和当目标位置在左边时的情况
            stepLength = stepLength > 0 ? Math.ceil(stepLength) : Math.floor(stepLength);
            obj.style.left = init + stepLength + 'px'; // 使定位的盒子的left值发生改变实现移动效果
            init = obj.offsetLeft; // 重新获取对象的初始位置和还需要移动的距离,为计时器下次执行作准备。
            distance = target - init;
            stepLength = distance / 10;
        }, time);
    }
    // 给三个设置速度的按钮注册事件
    for (var i = 0; i < spans.length; i++) {
     
     
        // 使用立即执行函数
        (function(j) {
     
     
            spans[j].setAttribute('data-speed', j);

        })(i);
        spans[i].addEventListener('click', function() {
     
     
            for (var j = 0; j < spans.length; j++) {
     
     
                spans[j].className = '';
            }
            this.className = 'current';
            speed = this.getAttribute('data-speed');
        })
    }
    // 定义一个计时函数,参数是开始的时间,因为只有动画开始时才计时所以只能在开始时再传参

    var lock = 1; // 设置函数锁防止快速点击出现计时问题
    btn1.onclick = function() {
     
     
        if (lock) {
     
     
            lock = 0;
            animateX(div1, 500, speed, function() {
     
     
                div1.style.backgroundColor = 'black';
                lock = 1;
            })
        }


    }
    btn2.onclick = function() {
     
     
        if (lock) {
     
     
            lock = 0;
            animateX(div1, 0, speed, function() {
     
     
                div1.style.backgroundColor = 'green';
                lock = 1;
            })
        }

    }
</script>

Guess you like

Origin blog.csdn.net/TKOP_/article/details/112503938
Recommended