时间javascript动画:最后慢,然后它应该

时间:2017-06-08 18:50:59

标签: javascript css animation requestanimationframe

我在尝试制作Javascript动画时遇到问题。我希望你们可以帮助我,因为我在Stackoverflow或其他网站上找不到类似的问题。警告:我喜欢写作,所以也许这个主题有点长,我很抱歉。我只想让一切都清楚。 TL; DR版本位于本文的底部。

我想做什么?
我正在构建自己的Javascript库(出于学习目的,因此我不想使用jQuery或任何其他库来解决此问题)。该库具有许多功能,动画就是其中之一。动画是特定元素的CSS的变化,但是以平滑的方式。想象一下,你有一个元素的宽度为200px而你想要它400px,如果它不会发生变化,而是相当温和地看起来好多了。我设法制作动画,所以这不是问题。

我的问题是什么?
动画具有特定的时间范围。在此时间范围内,css从一个值变为另一个值。像我做的那样,时间框架有下一个计算:

let timeFraction = (time - start) / this.duration;

其中start是调用animate()方法的开始时间(稍后会看到代码)。时间是动画运行的时间,当时间和开始时间与duration的数量不同时,动画结束。
timeFraction是自调用动画以来传递的时间量。这个ammount乘以要改变的css值。这听起来有点奇怪,我知道,所以让我举一个例子:

我想再次从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。 这就像一个魅力。动画运行流畅,但是当它即将结束时,动画会慢得多。这是一个例子:

See a GIF

虽然计算结果仍然相同。我只是希望它能够顺利改变,直到时间框架结束。

我的代码是什么?
我正在使用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));
}

计时器方法:

timer(timeFraction) {
    return timeFraction;
}

绘制方法:

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]);
        }
    }
}

我尝试了什么?
首先,我试图搜索制作javascript动画的人的例子。我尝试了在动画时间范围内计算css的方法,但问题没有解决。我尝试将performance.now()更改为new Date().getTime()并从那里进一步计算,但仍然出现同样的问题。所以我不知道为什么动画并不总是以相同的速度,而应该是。你能帮忙吗?

TL; DR版
我在Javascript中制作动画。动画是线性的(总是以相同的速度),但是动画结束时动画的速度较慢(参见本主题中的GIF)。

我的具体问题
为什么动画最后会变慢?那与此有什么关系?我上面解释的计算还是其他什么?还是我错过了重要的事情?请帮忙,因为我不知道是什么导致了这一点。

1 个答案:

答案 0 :(得分:0)

我弄清楚为什么动画最终比起初要慢。

正如我在我的问题中所述,每次调用draw方法时都会进行计算。同样,此计算检查了要动画的现有css属性与timing变量相乘的差异(timing变量保存动画的时间帧。因此,当动画时间的一半通过时,{ {1}}等于0.5,当动画完成时,timing为1)。

但是因为差异计算时间调用timing方法,所以差异每次都不同。最后,当动画几乎完成时,这种差异非常小。虽然差异变小,但draw也变小了 这导致每次添加较小的差异。 (所以即使从一开始它变慢,但你不会注意到它)。

我更改了代码以计算动画开头的差异,timing * difference现在乘以该差异,而不是每次循环再次获取差异。 (除此之外,代码变得更清晰,更容易阅读/理解。

动画方法:

timing

绘制方法:

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.cssDiff) {
        this.element.style[property] = this.cssDiff[property].start + (timing * this.cssDiff[property].value) + this.cssDiff[property].unit;
    }
}

对于每个动画css属性,这段代码被调用一次,因此这段代码将比计算每个动画帧的差异更快。

对于其他人来说,这是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; } 方法(作为@ Blindman67问题的答案)

timer

我希望其他想要制作JS动画的人发现这个问题+答案很有用。感谢大家与我一起思考提出解决方案