Suppose there is such an animation function requirement: change the width of a div from 100px to 200px. The code written might look like this:
The code copy is as follows:
<div id="test1"></div>
function animate1(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width),
step = 1;
var timerId = setInterval(function() {
var nextValue = parseInt(element.style.width) + step;
element.style.width = nextValue + 'px';
if (nextValue >= endValue) {
clearInterval(timerId);
// Time-consuming to display animation
element.innerHTML = new Date - startTime;
}
}, duration / (endValue - startValue) * step);
}
animate1(document.getElementById('test1'), 200, 1000);
The principle is to increase 1px every certain time until 200px. However, the display time after the animation is over is more than 1s (usually around 1.5s). The reason is that setInterval cannot strictly guarantee the execution interval.
Is there any better way to do it? Let’s first look at a primary school mathematics problem:
The code copy is as follows:
Building A and Building B were 100 meters apart. A man walked from Building A to Building B at a constant speed. After walking for 5 minutes, he arrived at his destination. How far was he from Building A in the third minute?
The calculation formula for calculating a distance of a certain time during constant speed is: distance * current time/time. So the answer should be 100 * 3 / 5 = 60 .
The inspiration from this question is that the journey of a certain moment can be calculated through a specific formula. Similarly, the value of a certain moment during the animation process can also be calculated through formulas instead of being accumulated:
The code copy is as follows:
<div id="test2"></div>
function animate2(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width);
var timerId = setInterval(function() {
var percentage = (new Date - startTime) / duration;
var stepValue = startValue + (endValue - startValue) * percentage;
element.style.width = stepValue + 'px';
if (percentage >= 1) {
clearInterval(timerId);
element.innerHTML = new Date - startTime;
}
}, 13);
}
animate2(document.getElementById('test2'), 200, 1000);
After this improvement, you can see that the animation execution time will only take up to 10 ms. However, the problem has not been completely solved. If you check the test2 element in the browser development tool, you will find that the final width of test2 may be more than 200px. If you carefully check the code of the animate2 function, you will find:
1. The value of percentage may be greater than 1, which can be solved by limiting the maximum value by Math.min.
2. Even if the value of percentage is guaranteed to be no more than 1, as long as the endValue or startValue is a decimal, the value of (endValue - startValue) * percentage may cause errors, because the Javascript decimal operation is not accurate enough. In fact, what we need to ensure is the accuracy of the final value, so when percentage is 1, just use endValue directly.
So, the code of the animate2 function is modified to:
The code copy is as follows:
function animate2(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width);
var timerId = setInterval(function() {
// Ensure that the percentage is not greater than 1
var percentage = Math.min(1, (new Date - startTime) / duration);
var stepValue;
if (percentage >= 1) {
// Ensure the accuracy of the final value
stepValue = endValue;
} else {
stepValue = startValue + (endValue - startValue) * percentage;
}
element.style.width = stepValue + 'px';
if (percentage >= 1) {
clearInterval(timerId);
element.innerHTML = new Date - startTime;
}
}, 13);
}
There is another last question: Why is the interval of setInterval set to 13ms? The reason is that the refresh rate of the monitor at present generally does not exceed 75Hz (that is, it is refreshed 75 times per second, which means it is refreshed every 13ms), and synchronizing the interval with the refresh rate is better.