@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
172 lines • 6.17 kB
JavaScript
import { CubicEase, EasingFunction } from "../../Animations/easing.js";
import { Animation } from "../../Animations/animation.js";
/**
* Animate camera property changes with an interpolation effect
* @see https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors
*/
export class InterpolatingBehavior {
/**
* Gets the name of the behavior.
*/
get name() {
return "Interpolating";
}
/**
* Attached node of this behavior
*/
get attachedNode() {
return this._attachedCamera;
}
/**
* Initializes the behavior
*/
constructor() {
/**
* The easing function to use for interpolation
*/
this.easingFunction = new CubicEase();
/**
* The easing mode (default is EASINGMODE_EASEINOUT)
*/
this.easingMode = EasingFunction.EASINGMODE_EASEINOUT;
/**
* Duration of the animation in milliseconds
*/
this.transitionDuration = 450;
this._attachedCamera = null;
this._animatables = new Map();
this.easingFunction.setEasingMode(this.easingMode);
}
/**
* Initializes the behavior
*/
init() {
// Nothing to do on init
}
/**
* Attaches the behavior to a camera
* @param camera The camera to attach to
*/
attach(camera) {
this._attachedCamera = camera;
}
/**
* Detaches the behavior from the camera
*/
detach() {
if (!this._attachedCamera) {
return;
}
this.stopAllAnimations();
this._attachedCamera = null;
}
get isInterpolating() {
return this._animatables.size > 0;
}
/**
* Stops and removes all animations
*/
stopAllAnimations() {
if (this._attachedCamera) {
this._animatables.forEach((animatable) => animatable.stop());
}
this._animatables.clear();
this._promiseResolve?.();
this._promiseResolve = undefined;
}
updateProperties(properties) {
properties.forEach((value, key) => {
if (value !== undefined) {
const animatable = this._animatables.get(String(key));
animatable && (animatable.target = value);
}
});
}
async animatePropertiesAsync(properties, transitionDuration = this.transitionDuration, easingFn = this.easingFunction, updateAnimation) {
const promise = new Promise((resolve) => {
this.stopAllAnimations();
this._promiseResolve = resolve;
if (!this._attachedCamera) {
this._promiseResolve = undefined;
return resolve();
}
const camera = this._attachedCamera;
const scene = camera.getScene();
const checkClear = (propertyName) => {
// Remove the associated animation from camera once the transition to target is complete so that property animations don't accumulate
for (let i = camera.animations.length - 1; i >= 0; --i) {
if (camera.animations[i].name === propertyName + "Animation") {
camera.animations.splice(i, 1);
}
}
this._animatables.delete(propertyName);
if (this._animatables.size === 0) {
this._promiseResolve = undefined;
resolve();
}
};
properties.forEach((value, key) => {
if (value !== undefined && camera[key] !== value) {
const propertyName = String(key);
const animation = Animation.CreateAnimation(propertyName, GetAnimationType(value), 60, easingFn);
// Optionally allow caller to further customize the animation
updateAnimation?.(propertyName, animation);
// Pass false for stopCurrent so that we can interpolate multiple properties at once
const animatable = Animation.TransitionTo(propertyName, value, camera, scene, 60, animation, transitionDuration, () => checkClear(propertyName), false);
if (animatable) {
this._animatables.set(propertyName, animatable);
}
}
});
});
return await promise;
}
}
// Structural type-guards (no instanceof)
function IsQuaternionLike(v) {
return v != null && typeof v.x === "number" && typeof v.y === "number" && typeof v.z === "number" && typeof v.w === "number";
}
function IsMatrixLike(v) {
return v != null && (Array.isArray(v.m) || typeof v.m === "object");
}
function IsVector3Like(v) {
return v != null && typeof v.x === "number" && typeof v.y === "number" && typeof v.z === "number";
}
function IsVector2Like(v) {
return v != null && typeof v.x === "number" && typeof v.y === "number";
}
function IsColor3Like(v) {
return v != null && typeof v.r === "number" && typeof v.g === "number" && typeof v.b === "number";
}
function IsColor4Like(v) {
return v != null && typeof v.r === "number" && typeof v.g === "number" && typeof v.b === "number" && typeof v.a === "number";
}
function IsSizeLike(v) {
return v != null && typeof v.width === "number" && typeof v.height === "number";
}
const GetAnimationType = (value) => {
if (IsQuaternionLike(value)) {
return Animation.ANIMATIONTYPE_QUATERNION;
}
if (IsMatrixLike(value)) {
return Animation.ANIMATIONTYPE_MATRIX;
}
if (IsVector3Like(value)) {
return Animation.ANIMATIONTYPE_VECTOR3;
}
if (IsVector2Like(value)) {
return Animation.ANIMATIONTYPE_VECTOR2;
}
if (IsColor3Like(value)) {
return Animation.ANIMATIONTYPE_COLOR3;
}
if (IsColor4Like(value)) {
return Animation.ANIMATIONTYPE_COLOR4;
}
if (IsSizeLike(value)) {
return Animation.ANIMATIONTYPE_SIZE;
}
// Fallback to float for numbers and unknown shapes
return Animation.ANIMATIONTYPE_FLOAT;
};
//# sourceMappingURL=interpolatingBehavior.js.map