Files
react-native/Libraries/Animated/animations/TimingAnimation.js
T
Genki Kondo 3f49e6763e Support color animation with native driver for Android
Summary:
Adds support for Animated.Color with native driver for Android. Reads the native config for the rbga channel AnimatedNodes, and on update(), converts the values into an integer (0xaarrggbb)

Followup changes will include support for iOS and platform colors.

Changelog:
[Android][Added] - Support running animations with AnimatedColor with native driver

Reviewed By: javache

Differential Revision: D33833600

fbshipit-source-id: 2bf05c9715b603cf014ace09e9308b2bfd67f30a
2022-01-29 13:51:41 -08:00

177 lines
4.8 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const AnimatedValue = require('../nodes/AnimatedValue');
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
const AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
const Animation = require('./Animation');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type {AnimationConfig, EndCallback} from './Animation';
import type {RgbaValue} from '../nodes/AnimatedColor';
import AnimatedColor from '../nodes/AnimatedColor';
export type TimingAnimationConfig = {
...AnimationConfig,
toValue:
| number
| AnimatedValue
| {
x: number,
y: number,
...
}
| AnimatedValueXY
| RgbaValue
| AnimatedColor
| AnimatedInterpolation,
easing?: (value: number) => number,
duration?: number,
delay?: number,
};
export type TimingAnimationConfigSingle = {
...AnimationConfig,
toValue: number | AnimatedValue | AnimatedInterpolation,
easing?: (value: number) => number,
duration?: number,
delay?: number,
};
let _easeInOut;
function easeInOut() {
if (!_easeInOut) {
const Easing = require('../Easing');
_easeInOut = Easing.inOut(Easing.ease);
}
return _easeInOut;
}
class TimingAnimation extends Animation {
_startTime: number;
_fromValue: number;
_toValue: any;
_duration: number;
_delay: number;
_easing: (value: number) => number;
_onUpdate: (value: number) => void;
_animationFrame: any;
_timeout: any;
_useNativeDriver: boolean;
_platformConfig: ?PlatformConfig;
constructor(config: TimingAnimationConfigSingle) {
super();
this._toValue = config.toValue;
this._easing = config.easing ?? easeInOut();
this._duration = config.duration ?? 500;
this._delay = config.delay ?? 0;
this.__iterations = config.iterations ?? 1;
this._useNativeDriver = shouldUseNativeDriver(config);
this._platformConfig = config.platformConfig;
this.__isInteraction = config.isInteraction ?? !this._useNativeDriver;
}
__getNativeAnimationConfig(): any {
const frameDuration = 1000.0 / 60.0;
const frames = [];
const numFrames = Math.round(this._duration / frameDuration);
for (let frame = 0; frame < numFrames; frame++) {
frames.push(this._easing(frame / numFrames));
}
frames.push(this._easing(1));
return {
type: 'frames',
frames,
toValue: this._toValue,
iterations: this.__iterations,
platformConfig: this._platformConfig,
};
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
this.__active = true;
this._fromValue = fromValue;
this._onUpdate = onUpdate;
this.__onEnd = onEnd;
const start = () => {
// Animations that sometimes have 0 duration and sometimes do not
// still need to use the native driver when duration is 0 so as to
// not cause intermixed JS and native animations.
if (this._duration === 0 && !this._useNativeDriver) {
this._onUpdate(this._toValue);
this.__debouncedOnEnd({finished: true});
} else {
this._startTime = Date.now();
if (this._useNativeDriver) {
this.__startNativeAnimation(animatedValue);
} else {
this._animationFrame = requestAnimationFrame(
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.onUpdate.bind(this),
);
}
}
};
if (this._delay) {
this._timeout = setTimeout(start, this._delay);
} else {
start();
}
}
onUpdate(): void {
const now = Date.now();
if (now >= this._startTime + this._duration) {
if (this._duration === 0) {
this._onUpdate(this._toValue);
} else {
this._onUpdate(
this._fromValue + this._easing(1) * (this._toValue - this._fromValue),
);
}
this.__debouncedOnEnd({finished: true});
return;
}
this._onUpdate(
this._fromValue +
this._easing((now - this._startTime) / this._duration) *
(this._toValue - this._fromValue),
);
if (this.__active) {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this._animationFrame = requestAnimationFrame(this.onUpdate.bind(this));
}
}
stop(): void {
super.stop();
this.__active = false;
clearTimeout(this._timeout);
global.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
}
}
module.exports = TimingAnimation;