javascript动画计时:结束时速度应较慢
我在尝试制作Javascript动画时遇到了一个问题。我希望你们能帮助我,因为我在Stackoverflow或其他网站上找不到类似的问题。警告:我喜欢写作,所以这个话题可能有点长,对此我很抱歉。我只是想把一切都说清楚。TL;DR version位于本文底部 我想做什么?javascript动画计时:结束时速度应较慢,javascript,css,animation,requestanimationframe,Javascript,Css,Animation,Requestanimationframe,我在尝试制作Javascript动画时遇到了一个问题。我希望你们能帮助我,因为我在Stackoverflow或其他网站上找不到类似的问题。警告:我喜欢写作,所以这个话题可能有点长,对此我很抱歉。我只是想把一切都说清楚。TL;DR version位于本文底部 我想做什么? 我正在构建自己的Javascript库(出于学习目的,因此我不想使用jQuery或任何其他库来解决这个问题)。这个库有很多功能,动画就是其中之一。动画是对特定元素的CSS的更改,但以平滑的方式进行。假设您有一个元素的宽度为200
我正在构建自己的Javascript库(出于学习目的,因此我不想使用jQuery或任何其他库来解决这个问题)。这个库有很多功能,动画就是其中之一。动画是对特定元素的CSS的更改,但以平滑的方式进行。假设您有一个元素的宽度为
200px
,并且您想要使其400px
,如果它不突然改变,而是温和地改变,它看起来会更好。我设法制作了动画,所以这不是问题所在
我的问题是什么?动画有特定的时间范围。在此时间范围内,css从一个值更改为另一个值。时间框架,就像我做的,有下一个计算:
let timeFraction = (time - start) / this.duration;
其中start
是调用animate()
方法的开始时间(稍后您将看到代码)。时间是动画运行的时间,当时间和开始时间与持续时间的数量不同时,动画即完成。
timeFraction
是自调用动画以来经过的时间量。此amount与要更改的css值相乘。我知道这听起来有点奇怪,所以让我举个例子:
我想再次将amount从200px
更改为400px
。这两个值之间的差值为200px
。因此元素必须更改此值。
动画时间范围为3000毫秒。因此,动画必须在3秒内完成。1.5秒过去(3秒的一半),然后将200px
的一半加上200px
的现有宽度
let timeFraction = (1500 - 0) / 3000; // is 0.5
let cssAdded = timeFraction * 200; // 100px
因此,当3秒过去时,timeFraction
应该是1
,这将是完整的200px
这很有魅力。动画运行平稳,但即将结束时,动画速度会慢很多。以下是一个例子:
而计算结果仍然是一样的。我只是希望它能慢慢改变,直到时间过去
我的代码是什么?
我正在使用EcmaScript 6和vanilla JS
动画制作方法:
animate(css) {
if(this !== undefined && css) {
this.css = css;
}
let start = performance.now();
requestAnimationFrame(function animate(time) {
let timeFraction = (time - start) / this.duration;
if( timeFraction > 1 ) timeFraction = 1;
let timing = this.timer(timeFraction);
this.draw(timing);
if ( timing < 1 ) {
requestAnimationFrame(animate.bind(this));
}
}.bind(this));
}
绘制方法:
draw(timing) {
for(let property in this.css) {
let newValue = window.getComputedStyle(this.element)[property];
let match = /\d+(px|em|%|rem)/.exec(newValue);
if(match !== null) {
newValue = match[0].replace(match[1], '');
this.element.style[property] = Math.floor(parseInt(window.getComputedStyle(this.element)[property].replace('px', '')) - (timing * (newValue - parseInt(this.css[property].replace('px', ''))))) + match[1];
}
else {
this.element.style[property] -= timing * (window.getComputedStyle(this.element)[property] - this.css[property]);
}
}
}
draw(timing) {
for(let property in this.cssDiff) {
this.element.style[property] = this.cssDiff[property].start + (timing * this.cssDiff[property].value) + this.cssDiff[property].unit;
}
}
我尝试了什么?
首先,我尝试搜索制作javascript动画的人的例子。我尝试了他们在动画时间范围内计算css的方法,但问题没有解决。我尝试将performance.now()
更改为new Date().getTime()
,并从中进一步计算,但仍然出现了相同的问题。所以我不知道为什么动画的速度不总是一样的,而它应该是一样的。你们能帮忙吗
TL;DR版本
我正在用Javascript制作动画。动画是线性的(总是以相同的速度),但在动画结束时动画速度较慢(请参阅本主题中的GIF)
我的具体问题
为什么动画在最后会变慢?这有什么关系?我上面解释的计算还是别的?还是我错过了一些重要的事情?请帮忙,因为我不知道是什么原因造成的 我明白了为什么动画最后比开始慢
正如我在问题中所述,每次调用draw
方法时都会进行计算。同样,此计算检查了要设置动画的现有css属性之间的差异,并将定时
变量相乘(变量timing
保存动画的时间帧。因此,当动画时间的一半过去时,timing
等于0.5,当动画完成时,timing
为1)
但是,由于每次调用draw
方法时都会计算差异,因此每次的差异都是不同的。最后,当动画几乎完成时,差异非常小。虽然差异越来越小,计时*差异也越来越小。
这将导致每次添加的差异更小(因此即使从一开始它也会变慢,但您不会注意到)
在动画开始时,我更改了代码以计算差异,现在,计时
与该差异相乘,而不是在每个循环中再次获取差异。(此外,代码变得更清晰,更易于阅读/理解。)
动画制作方法:
animate(css) {
if(typeof css === 'object') {
for(let property in css) {
this.cssDiff[property] = this.calculateDifference(property, css[property])
}
}
else if(arguments.length === 2) {
this.cssDiff[arguments[0]] = this.calculateDifference(arguments[0], arguments[1]);
}
else {
return;
}
//The rest of the code, same as in my question...
}
绘制方法:
draw(timing) {
for(let property in this.css) {
let newValue = window.getComputedStyle(this.element)[property];
let match = /\d+(px|em|%|rem)/.exec(newValue);
if(match !== null) {
newValue = match[0].replace(match[1], '');
this.element.style[property] = Math.floor(parseInt(window.getComputedStyle(this.element)[property].replace('px', '')) - (timing * (newValue - parseInt(this.css[property].replace('px', ''))))) + match[1];
}
else {
this.element.style[property] -= timing * (window.getComputedStyle(this.element)[property] - this.css[property]);
}
}
}
draw(timing) {
for(let property in this.cssDiff) {
this.element.style[property] = this.cssDiff[property].start + (timing * this.cssDiff[property].value) + this.cssDiff[property].unit;
}
}
我添加了一种计算差异的方法:
calculateDifference(property, cssValue) {
let existingCSS = window.getComputedStyle(this.element)[property];
let match = /(\d+)(px|em|%|rem)/.exec(existingCSS);
let differenceMap = {value: 0, unit: 0, start: 0};
if(match !== null) {
differenceMap.start = Number(existingCSS.replace(/px|em|%|rem/g, ''));
differenceMap.value = Number(cssValue.replace(/px|em|%|rem/g, '')) - Number(match[1]);
differenceMap.unit = match[2];
}
else {
differenceMap.start = Number(existingCSS);
differenceMap.value = (typeof cssValue === 'string' ? Number(cssValue.replace(/px|em|%|rem/g, '')) : cssValue) - Number(existingCSS);
}
return differenceMap;
}
这段代码将为每个要设置动画的css属性调用一次,因此此代码将比计算每个动画帧的差异更快
对于其他人来说,这是计时器方法(作为对@Blindman67问题的回答)
我希望其他想要制作JS动画的人会觉得这个问题+答案很有用。感谢大家和我一起思考,想出了一个解决方案它看起来像是在用(time-start)重新计算每一帧的时间/this.duration
当您调用timer
时,随着time
距离start
越来越远,每次的数字都越来越大。只要动画帧及时运行,这看起来是线性的。可能您的问题在于处理CSS值的逻辑。您是否尝试在中使用时间线开发工具