UNPKG

animare

Version:

Advanced animation library for modern JavaScript.

24 lines 11.2 kB
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=timeline;var _types=require("../types.js");var _EventManager=_interopRequireDefault(require("../utils/EventManager.js"));var _helpers=require("../utils/helpers.js");var _utils=require("../utils/utils.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}function timeline(animations,callback){let globalValues=arguments.length>2&&arguments[2]!==undefined?arguments[2]:{};const timelineOptions=(0,_helpers.prepareTimelineValues)(globalValues);const preparedValues=(0,_helpers.prepareAnimationsValues)(animations,globalValues);const eventManager=new _EventManager.default();const timelineInfo={__startTime:0,__pauseTime:0,__lastFrameTime:0,__animations:[],__requestAnimationId:null,__startProgress:0,progress:0,duration:0,elapsedTime:0,speed:timelineOptions.timelineSpeed,isPlaying:false,isPaused:false,isFinished:false,isFirstFrame:true,playCount:1,fps:60,isProgressAt(progress){let tolerance=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0.001;return Math.abs(this.progress-progress)<tolerance;},isTimeAt(time){let tolerance=arguments.length>1&&arguments[1]!==undefined?arguments[1]:5;return Math.abs(this.elapsedTime-time)<tolerance;}};// calculate timeline timelineInfo.__animations=(0,_helpers.calculateTimeline)(preparedValues);timelineInfo.duration=(0,_helpers.calculateTimelineDuration)(timelineInfo.__animations);/** * Syncs the frame time to account for browser behavior that may pause animations in inactive tabs. */const visibilitychange={isRegistered:false,hiddenTime:0,add(){if(this.isRegistered)return;document.addEventListener('visibilitychange',this.handle);this.isRegistered=true;},remove(){document.removeEventListener('visibilitychange',this.handle);this.isRegistered=false;},handle(){if(document.visibilityState==='hidden'){visibilitychange.hiddenTime=performance.now();return;}if(document.visibilityState==='visible'){timelineInfo.__startTime+=performance.now()-visibilitychange.hiddenTime;visibilitychange.hiddenTime=0;}}};const callbackAnimationInfo=Object.create(null);callbackAnimationInfo.length=animations.length;// fill `callbackAnimationInfo` with initial values for(let i=0;i<timelineInfo.__animations.length;i++){const info=timelineInfo.__animations[i].info;callbackAnimationInfo[info.name]=info;callbackAnimationInfo[info.index]=info;}const executePerFrame=(now,oneFrame)=>{now*=timelineInfo.speed;timelineInfo.elapsedTime=now-timelineInfo.__startTime+timelineInfo.__startProgress*timelineInfo.duration;// Time passed since the start timelineInfo.progress=(0,_utils.normalizePercentage)(timelineInfo.elapsedTime/timelineInfo.duration);timelineInfo.fps=Math.round(1000/(now-timelineInfo.__lastFrameTime)*timelineInfo.speed);if(!isFinite(timelineInfo.fps))timelineInfo.fps=60;timelineInfo.__lastFrameTime=now;for(let i=0;i<timelineInfo.__animations.length;i++){const animation=timelineInfo.__animations[i];animation.Update(timelineInfo.elapsedTime);const info=animation.info;callbackAnimationInfo[info.name]=info;callbackAnimationInfo[info.index]=info;}// didn't reach the end? -> continue if(timelineInfo.progress!==1){callback(callbackAnimationInfo,timelineInfo);if(oneFrame)return;// stop. we play only one frame timelineInfo.__requestAnimationId=requestAnimationFrame(executePerFrame);return;}// reached the end what to do next? // finished? -> stop if(timelineInfo.playCount===timelineOptions.timelinePlayCount){timelineInfo.isFinished=true;timelineInfo.isPlaying=false;visibilitychange.remove();callback(callbackAnimationInfo,timelineInfo);eventManager.emit(_types.Event.Complete);timelineInfo.__requestAnimationId=null;return;}if(oneFrame)return;// stop. we play only one frame // repeat? -> restart callback(callbackAnimationInfo,timelineInfo);eventManager.emit(_types.Event.Repeat);timelineInfo.__requestAnimationId=requestAnimationFrame(next=>{timelineInfo.__startTime=next*timelineInfo.speed;timelineInfo.__lastFrameTime=next*timelineInfo.speed;timelineInfo.playCount++;timelineInfo.__startProgress=0;executePerFrame(next);});};const seek=function(seekTo){let playCount=arguments.length>1&&arguments[1]!==undefined?arguments[1]:timelineInfo.playCount;// disabled timeline if(timelineOptions.timelinePlayCount===0||playCount===0){console.warn('[seek] Cannot seek the timeline because the `playCount` is set to 0.');return;}if(timelineOptions.timelinePlayCount>0&&typeof playCount==='number'&&playCount>timelineOptions.timelinePlayCount){console.warn('[seek] Cannot seek the timeline because the param `playCount` is greater than the `timelinePlayCount`.');return;}// timeline duration is 0 if(timelineInfo.duration===0){console.warn('[seek] Cannot seek the timeline because the `duration` is 0.');return;}// startFrom is a time if(typeof seekTo==='number'){if(seekTo<0){seekTo=0;console.warn('[seek] The `startFrom` param cannot be a negative value.');}if(seekTo>timelineInfo.duration){seekTo=timelineInfo.duration;console.warn('[seek] The `startFrom` param cannot be greater than the duration of the timeline.');}// time to percentage seekTo=(0,_utils.clamp)(seekTo/timelineInfo.duration,0,1);}// string percentage to percentage number if(typeof seekTo==='string'){seekTo=(0,_utils.percentageStringToNumber)(seekTo);if(seekTo<0){seekTo=0;console.warn('[seek] The `startFrom` param cannot be a negative percentage.');}if(seekTo>1){seekTo=1;console.warn('[seek] The `startFrom` param percentage cannot be greater than 1.');}}if(timelineInfo.isPlaying){const now=performance.now()*timelineInfo.speed;timelineInfo.__startTime=now;timelineInfo.__lastFrameTime=now;}timelineInfo.playCount=playCount;timelineInfo.__startProgress=seekTo;};const play=function(){let startFrom=arguments.length>0&&arguments[0]!==undefined?arguments[0]:0;let playCount=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;// timeline is already playing? -> reset if(timelineInfo.isPlaying&&timelineInfo.__requestAnimationId!==null){cancelAnimationFrame(timelineInfo.__requestAnimationId);timelineInfo.__requestAnimationId=null;}// reset all animations for(let i=0;i<callbackAnimationInfo.length;i++){timelineInfo.__animations[i].Setup();}seek(startFrom,playCount);// sets the start progress and play count timelineInfo.__requestAnimationId=requestAnimationFrame(now=>{timelineInfo.__startTime=now*timelineInfo.speed;timelineInfo.__lastFrameTime=now*timelineInfo.speed;timelineInfo.progress=timelineInfo.__startProgress;timelineInfo.isPlaying=timelineInfo.progress!==1;timelineInfo.isFinished=timelineInfo.progress===1;timelineInfo.isPaused=false;timelineInfo.isFirstFrame=true;visibilitychange.add();// add if not already added eventManager.emit(_types.Event.Play);executePerFrame(now);timelineInfo.isFirstFrame=false;});};const playOneFrame=()=>{// timeline is already playing? -> reset if(timelineInfo.isPlaying&&timelineInfo.__requestAnimationId!==null){console.warn('[playOneFrame] The timeline is already playing.');return;}const now=performance.now();timelineInfo.__startTime=now*timelineInfo.speed;timelineInfo.__lastFrameTime=now*timelineInfo.speed;timelineInfo.progress=timelineInfo.__startProgress;timelineInfo.isPlaying=false;timelineInfo.isFinished=timelineInfo.progress===1;timelineInfo.isPaused=false;timelineInfo.isFirstFrame=false;executePerFrame(now,true);};const pause=()=>{if(!timelineInfo.isPlaying){console.warn('[pause] The timeline is not playing.');return;}if(timelineInfo.isPaused){console.warn('[pause] The timeline is already paused.');return;}if(!timelineInfo.__requestAnimationId){console.error('[pause] `__requestAnimationId` is null.');return;}cancelAnimationFrame(timelineInfo.__requestAnimationId);timelineInfo.__requestAnimationId=null;timelineInfo.__pauseTime=performance.now();timelineInfo.isPaused=true;timelineInfo.isPlaying=false;visibilitychange.remove();eventManager.emit(_types.Event.Pause);};const resume=()=>{if(timelineInfo.isPlaying){console.warn('[resume] The timeline is already playing.');return;}if(!timelineInfo.isPaused){console.warn('[resume] The timeline is not paused, playing from the start.');play();return;}timelineInfo.__startTime+=(performance.now()-timelineInfo.__pauseTime)*timelineInfo.speed;timelineInfo.__pauseTime=0;timelineInfo.isPaused=false;timelineInfo.isPlaying=true;visibilitychange.add();eventManager.emit(_types.Event.Resume);timelineInfo.__requestAnimationId=requestAnimationFrame(executePerFrame);};const stop=function(){let stopAt=arguments.length>0&&arguments[0]!==undefined?arguments[0]:timelineInfo.duration;let playCount=arguments.length>1&&arguments[1]!==undefined?arguments[1]:timelineOptions.timelinePlayCount;// timeline is already playing? -> cancel if(timelineInfo.isPlaying&&timelineInfo.__requestAnimationId!==null){cancelAnimationFrame(timelineInfo.__requestAnimationId);timelineInfo.__requestAnimationId=null;timelineInfo.isPlaying=false;}seek(stopAt,playCount);playOneFrame();eventManager.emit(_types.Event.Stop);};const updateValues=newValues=>{for(let i=0;i<newValues.length;i++){const newValuesItem=newValues[i];if(!newValuesItem.name)throw new Error('[updateValues] Animation name is required.');const animIndex=timelineInfo.__animations.findIndex(a=>a.animationRef.name===newValuesItem.name);if(animIndex===-1)throw new Error(`[updateValues] Animation with name '${newValuesItem.name}' not found.`);const prepared=(0,_helpers.prepareAnimationsPartialOptions)(newValuesItem,animIndex);timelineInfo.__animations[animIndex].Set(prepared);}for(let i=0;i<callbackAnimationInfo.length;i++){timelineInfo.__animations[i].Setup();}// to make a smooth transition if the duration was changed if(timelineInfo.isPlaying){const currentProgress=timelineInfo.progress;timelineInfo.duration=(0,_helpers.calculateTimelineDuration)(timelineInfo.__animations);seek(timelineInfo.duration*currentProgress);return;}timelineInfo.duration=(0,_helpers.calculateTimelineDuration)(timelineInfo.__animations);};const updateTimelineOptions=newOptions=>{if(newOptions.timelinePlayCount===0){console.warn('The `timelinePlayCount` with the value `0` will make the timeline not play.');}if(typeof newOptions.timelineSpeed==='number'&&(newOptions.timelineSpeed===0||newOptions.timelineSpeed<0)){throw new Error('The `timelineSpeed` value cannot be a negative value or a zero.');}Object.assign(timelineOptions,newOptions);const currentProgress=timelineInfo.progress;timelineInfo.speed=timelineOptions.timelineSpeed;if(timelineInfo.isPlaying)seek(currentProgress*timelineInfo.duration);};if(timelineOptions.autoPlay)play();const returnObj={timelineInfo,animationsInfo:callbackAnimationInfo,updateValues,updateTimelineOptions,play,playOneFrame,resume,pause,stop,seek,on:eventManager.on.bind(eventManager),once:eventManager.once.bind(eventManager),onCompleteAsync:eventManager.onCompleteAsync.bind(eventManager),onPlayAsync:eventManager.onPlayAsync.bind(eventManager),onResumeAsync:eventManager.onResumeAsync.bind(eventManager),onPauseAsync:eventManager.onPauseAsync.bind(eventManager),onStopAsync:eventManager.onStopAsync.bind(eventManager),onRepeatAsync:eventManager.onRepeatAsync.bind(eventManager),clearEvents:eventManager.clear.bind(eventManager)};return returnObj;}