/
githubmirror
/
anime
Обзор
Документация
Войти
/
githubmirror
/
anime
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v4.1.4
src/compositions.js
279 строк
9 KB
Julian Garnier
v4.1.3
12 авг 2025, 12:53
12 авг 2025, 12:53
5f0b1ee
Код
Авторство
О чём код?
/// <reference path='./types.js' /> import { compositionTypes, minValue, } from './consts.js'; import { cloneArray, addChild, removeChild, forEachChildren, round, } from './helpers.js'; import { additive, addAdditiveAnimation, } from './additive.js'; const lookups = { /** @type {TweenReplaceLookups} */ _rep: new WeakMap(), /** @type {TweenAdditiveLookups} */ _add: new Map(), } /** * @param {Target} target * @param {String} property * @param {String} lookup * @return {TweenPropertySiblings} */ export const getTweenSiblings = (target, property, lookup = '_rep') => { const lookupMap = lookups[lookup]; let targetLookup = lookupMap.get(target); if (!targetLookup) { targetLookup = {}; lookupMap.set(target, targetLookup); } return targetLookup[property] ? targetLookup[property] : targetLookup[property] = { _head: null, _tail: null, } } /** * @param {Tween} p * @param {Tween} c * @return {Number|Boolean} */ const addTweenSortMethod = (p, c) => { return p._isOverridden || p._absoluteStartTime > c._absoluteStartTime; } /** * @param {Tween} tween */ export const overrideTween = tween => { tween._isOverlapped = 1; tween._isOverridden = 1; tween._changeDuration = minValue; tween._currentTime = minValue; } /** * @param {Tween} tween * @param {TweenPropertySiblings} siblings * @return {Tween} */ export const composeTween = (tween, siblings) => { const tweenCompositionType = tween._composition; // Handle replaced tweens if (tweenCompositionType === compositionTypes.replace) { const tweenAbsStartTime = tween._absoluteStartTime; addChild(siblings, tween, addTweenSortMethod, '_prevRep', '_nextRep'); const prevSibling = tween._prevRep; // Update the previous siblings for composition replace tweens if (prevSibling) { const prevParent = prevSibling.parent; const prevAbsEndTime = prevSibling._absoluteStartTime + prevSibling._changeDuration; // Handle looped animations tween if ( // Check if the previous tween is from a different animation tween.parent.id !== prevParent.id && // Check if the animation has loops prevParent.iterationCount> 1 && // Check if _absoluteChangeEndTime of last loop overlaps the current tween prevAbsEndTime + (prevParent.duration - prevParent.iterationDuration) > tweenAbsStartTime ) { // TODO: Find a way to only override the iterations overlapping with the tween overrideTween(prevSibling); let prevPrevSibling = prevSibling._prevRep; // If the tween was part of a set of keyframes, override its siblings while (prevPrevSibling && prevPrevSibling.parent.id === prevParent.id) { overrideTween(prevPrevSibling); prevPrevSibling = prevPrevSibling._prevRep; } } const absoluteUpdateStartTime = tweenAbsStartTime - tween._delay; if (prevAbsEndTime > absoluteUpdateStartTime) { const prevChangeStartTime = prevSibling._startTime; const prevTLOffset = prevAbsEndTime - (prevChangeStartTime + prevSibling._updateDuration); // Rounding is necessary here to minimize floating point errors when working in seconds const updatedPrevChangeDuration = round(absoluteUpdateStartTime - prevTLOffset - prevChangeStartTime, 12); prevSibling._changeDuration = updatedPrevChangeDuration; prevSibling._currentTime = updatedPrevChangeDuration; prevSibling._isOverlapped = 1; // Override the previous tween if its new _changeDuration is lower than minValue // TODO: See if it's even neceseeary to test against minValue, checking for 0 might be enough if (updatedPrevChangeDuration < minValue) { overrideTween(prevSibling); } } // Pause (and cancel) the parent if it only contains overlapped tweens let pausePrevParentAnimation = true; forEachChildren(prevParent, (/** @type Tween */t) => { if (!t._isOverlapped) pausePrevParentAnimation = false; }); if (pausePrevParentAnimation) { const prevParentTL = prevParent.parent; if (prevParentTL) { let pausePrevParentTL = true; forEachChildren(prevParentTL, (/** @type JSAnimation */a) => { if (a !== prevParent) { forEachChildren(a, (/** @type Tween */t) => { if (!t._isOverlapped) pausePrevParentTL = false; }); } }); if (pausePrevParentTL) { prevParentTL.cancel(); } } else { prevParent.cancel(); // Previously, calling .cancel() on a timeline child would affect the render order of other children // Worked around this by marking it as .completed and using .pause() for safe removal in the engine loop // This is no longer needed since timeline tween composition is now handled separately // Keeping this here for reference // prevParent.completed = true; // prevParent.pause(); } } } // let nextSibling = tween._nextRep; // // All the next siblings are automatically overridden // if (nextSibling && nextSibling._absoluteStartTime >= tweenAbsStartTime) { // while (nextSibling) { // overrideTween(nextSibling); // nextSibling = nextSibling._nextRep; // } // } // if (nextSibling && nextSibling._absoluteStartTime < tweenAbsStartTime) { // while (nextSibling) { // overrideTween(nextSibling); // console.log(tween.id, nextSibling.id); // nextSibling = nextSibling._nextRep; // } // } // Handle additive tweens composition } else if (tweenCompositionType === compositionTypes.blend) { const additiveTweenSiblings = getTweenSiblings(tween.target, tween.property, '_add'); const additiveAnimation = addAdditiveAnimation(lookups._add); let lookupTween = additiveTweenSiblings._head; if (!lookupTween) { lookupTween = { ...tween }; lookupTween._composition = compositionTypes.replace; lookupTween._updateDuration = minValue; lookupTween._startTime = 0; lookupTween._numbers = cloneArray(tween._fromNumbers); lookupTween._number = 0; lookupTween._next = null; lookupTween._prev = null; addChild(additiveTweenSiblings, lookupTween); addChild(additiveAnimation, lookupTween); } // Convert the values of TO to FROM and set TO to 0 const toNumber = tween._toNumber; tween._fromNumber = lookupTween._fromNumber - toNumber; tween._toNumber = 0; tween._numbers = cloneArray(tween._fromNumbers); tween._number = 0; lookupTween._fromNumber = toNumber; if (tween._toNumbers) { const toNumbers = cloneArray(tween._toNumbers); if (toNumbers) { toNumbers.forEach((value, i) => { tween._fromNumbers[i] = lookupTween._fromNumbers[i] - value; tween._toNumbers[i] = 0; }); } lookupTween._fromNumbers = toNumbers; } addChild(additiveTweenSiblings, tween, null, '_prevAdd', '_nextAdd'); } return tween; } /** * @param {Tween} tween * @return {Tween} */ export const removeTweenSliblings = tween => { const tweenComposition = tween._composition; if (tweenComposition !== compositionTypes.none) { const tweenTarget = tween.target; const tweenProperty = tween.property; const replaceTweensLookup = lookups._rep; const replaceTargetProps = replaceTweensLookup.get(tweenTarget); const tweenReplaceSiblings = replaceTargetProps[tweenProperty]; removeChild(tweenReplaceSiblings, tween, '_prevRep', '_nextRep'); if (tweenComposition === compositionTypes.blend) { const addTweensLookup = lookups._add; const addTargetProps = addTweensLookup.get(tweenTarget); if (!addTargetProps) return; const additiveTweenSiblings = addTargetProps[tweenProperty]; const additiveAnimation = additive.animation; removeChild(additiveTweenSiblings, tween, '_prevAdd', '_nextAdd'); // If only one tween is left in the additive lookup, it's the tween lookup const lookupTween = additiveTweenSiblings._head; if (lookupTween && lookupTween === additiveTweenSiblings._tail) { removeChild(additiveTweenSiblings, lookupTween, '_prevAdd', '_nextAdd'); removeChild(additiveAnimation, lookupTween); let shouldClean = true; for (let prop in addTargetProps) { if (addTargetProps[prop]._head) { shouldClean = false; break; } } if (shouldClean) { addTweensLookup.delete(tweenTarget); } } } } return tween; }