import { replaceWithRefs } from '../core/references' ; import { ITimelineModel , AnimationOptions , PropertyObject , PropertyValueOptions , Interpolator , PropertyResolver , TargetConfiguration , PropertyValue , PropertyKeyframe } from '../core/types' ; import { _ , CONFIG } from '../utils/constants' ; import { all , find , push , list , pushDistinct , sortBy } from '../utils/lists' ; import { flr , max } from '../utils/math' ; import { resolveProperty } from '../utils/resolve-property' ; import { isObject , isNumber , isArrayLike , isOwner , isDefined } from '../utils/inspect' ; import { plugins } from '../core/plugins' ; import { IReducer , IReducerContext } from '../core/types' ; import { calculateConfigs } from './calc-configs' ; const propKeyframeSort = sortBy < PropertyKeyframe > ( 'time' ) ; export const insert = ( model , options , ctx ) => { all ( options , opts => { if ( opts . to === _ ) { throw new Error ( 'missing duration' ) ; } opts = replaceWithRefs ( model . refs , opts , true ) as AnimationOptions ; all ( opts . targets , ( target , i , ilen ) => { const config = addPropertyKeyframes ( model , target , i , ilen , opts ) ; ctx . dirty ( config ) ; } ) ; } ) ; calculateConfigs ( model ) ; ctx . trigger ( CONFIG ) ; } ; function addPropertyKeyframes ( model , target , index , ilen , opts ) { const defaultEasing = 'ease' ; const delay = resolveProperty ( opts . delay , target , index , ilen ) || 0 ; const config = find ( model . configs , c => c . target === target ) || push ( model . configs , { from : max ( opts . from + delay , 0 ) , to : max ( opts . to + delay , 0 ) , easing : opts . easing || defaultEasing , duration : opts . to - opts . from , endDelay : resolveProperty ( opts . endDelay , target , index , ilen ) || 0 , stagger : opts . stagger || 0 , target , targetLength : ilen , propNames : [ ] , keyframes : [ ] } ) ; const staggerMs = ( opts . stagger && opts . stagger * ( index + 1 ) ) || 0 ; const delayMs = resolveProperty ( opts . delay , config , index , config . targetLength ) || 0 ; const from = max ( staggerMs + delayMs + opts . from , 0 ) ; const duration = opts . to - opts . from ; const easing = opts . easing || defaultEasing ; for ( var pluginName in plugins ) { if ( isOwner ( opts , pluginName ) ) { const props = opts [ pluginName ] ; for ( var name in props ) { var propVal = props [ name ] ; if ( isOwner ( props , name ) && isDefined ( propVal ) ) { addProperty ( config , pluginName , index , name , propVal , duration , from , easing ) ; } } } } config . keyframes . sort ( propKeyframeSort ) ; return config ; } function addProperty ( config , plugin , index , name , val , duration , from , defaultEasing ) { let defaultInterpolator ; let values ; const isValueObject = ! isArrayLike ( val ) && isObject ( val ) ; if ( isValueObject ) { const objVal = val as PropertyValueOptions ; if ( objVal . easing ) { defaultEasing = objVal . easing ; } if ( objVal . interpolate ) { defaultInterpolator = objVal . interpolate ; } values = list ( objVal . value ) ; } else { values = list ( val ) ; } const keyframes = values . map ( ( v , i , vals ) => { const valOrObj = resolveProperty ( v , config . target , index , config . targetLength ) ; const valObj = valOrObj as PropertyObject ; const isObj2 = isObject ( valOrObj ) ; const value = isObj2 ? valObj . value : ( valOrObj as string | number ) ; const offset = isObj2 && isNumber ( valObj . offset ) ? valObj . offset : i === vals . length - 1 ? 1 : i === 0 ? 0 : _ ; const interpolate = ( valObj && valObj . interpolate ) || defaultInterpolator ; const easing = ( valObj && valObj . easing ) || defaultEasing ; return { offset , value , easing , interpolate } ; } ) ; inferOffsets ( keyframes ) ; all ( keyframes , keyframe => { const { offset , value } = keyframe ; const time = flr ( duration * offset + from ) ; const frame = find ( config . keyframes , k => k . prop === name && k . time === time ) || push ( config . keyframes , { plugin , easing : keyframe . easing , index , prop : name , time , value , interpolate : keyframe . interpolate } ) ; frame . value = value ; } ) ; find ( config . keyframes , k => k . prop === name && k . time === from ) || push ( config . keyframes , { plugin , easing : defaultEasing , index , prop : name , time : from , value : _ , interpolate : defaultInterpolator } ) ; var to = from + duration ; find ( config . keyframes , k => k . prop === name && k . time === to , true ) || push ( config . keyframes , { plugin , easing : defaultEasing , index , prop : name , time : to , value : _ , interpolate : defaultInterpolator } ) ; pushDistinct ( config . propNames , name ) ; } function inferOffsets ( keyframes ) { if ( ! keyframes . length ) { return ; } const first = find ( keyframes , k => k . offset === 0 ) || keyframes [ 0 ] ; if ( ! isDefined ( first . offset ) ) { first . offset = 0 ; } const last = find ( keyframes , k => k . offset === 1 , true ) || keyframes [ keyframes . length - 1 ] ; if ( keyframes . length > 1 && ! isDefined ( last . offset ) ) { last . offset = 1 ; } for ( let i = 1 , ilen = keyframes . length ; i < ilen ; i ++ ) { const target = keyframes [ i ] ; if ( ! isDefined ( target . offset ) ) { for ( let j = i + 1 ; j < ilen ; j ++ ) { const endTime = keyframes [ j ] . offset ; if ( isDefined ( endTime ) ) { const startTime = keyframes [ i - 1 ] . offset ; const timeDelta = endTime - startTime ; const deltaLength = j - i + 1 ; for ( let k = 1 ; k < deltaLength ; k ++ ) { keyframes [ k - 1 + i ] . offset = ( k / j ) * timeDelta + startTime ; } i = j ; break ; } } } } }