• Jump To … +
    ./source/core/animationloop.js ./source/core/component.js ./source/core/document.js ./source/core/events.js ./source/core/init.js ./source/core/library.js ./source/core/userInteraction.js ./source/core/utilities.js ./source/factory/action.js ./source/factory/anchor.js ./source/factory/animation.js ./source/factory/bezier.js ./source/factory/block.js ./source/factory/canvas.js ./source/factory/cell.js ./source/factory/cog.js ./source/factory/color.js ./source/factory/coordinate.js ./source/factory/element.js ./source/factory/emitter.js ./source/factory/filter.js ./source/factory/fontAttributes.js ./source/factory/gradient.js ./source/factory/grid.js ./source/factory/group.js ./source/factory/imageAsset.js ./source/factory/line.js ./source/factory/loom.js ./source/factory/mesh.js ./source/factory/net.js ./source/factory/noise.js ./source/factory/oval.js ./source/factory/palette.js ./source/factory/particle.js ./source/factory/particleForce.js ./source/factory/particleHistory.js ./source/factory/particleSpring.js ./source/factory/particleWorld.js ./source/factory/pattern.js ./source/factory/phrase.js ./source/factory/picture.js ./source/factory/polygon.js ./source/factory/polyline.js ./source/factory/quadratic.js ./source/factory/quaternion.js ./source/factory/radialGradient.js ./source/factory/rectangle.js ./source/factory/renderAnimation.js ./source/factory/shape.js ./source/factory/spiral.js ./source/factory/spriteAsset.js ./source/factory/stack.js ./source/factory/star.js ./source/factory/state.js ./source/factory/tetragon.js ./source/factory/ticker.js ./source/factory/tracer.js ./source/factory/tween.js ./source/factory/unstackedElement.js ./source/factory/vector.js ./source/factory/videoAsset.js ./source/factory/wheel.js ./source/mixin/anchor.js ./source/mixin/asset.js ./source/mixin/assetConsumer.js ./source/mixin/base.js ./source/mixin/cascade.js ./source/mixin/delta.js ./source/mixin/displayShape.js ./source/mixin/dom.js ./source/mixin/entity.js ./source/mixin/filter.js ./source/mixin/mimic.js ./source/mixin/path.js ./source/mixin/pattern.js ./source/mixin/pivot.js ./source/mixin/position.js ./source/mixin/shapeBasic.js ./source/mixin/shapeCurve.js ./source/mixin/shapePathCalculation.js ./source/mixin/styles.js ./source/mixin/tween.js ./source/worker/filter-string.js ./source/worker/filter.js
  • ¶

    Ticker factory

    Ticker objects represent a timeline against which Tween and Action objects will run.

    • A Ticker is an animation (but not a Scrawl-canvas Animation) object; it defines a fn function internally which will check through all Tween and Action objects subscribing to it and, where appropriate, trigger their update functions.
    • This module defines and launches a coreTickersAnimation Animation object; all Ticker objects get added to this object when their run or resume functions are triggered.
    • The coreTickersAnimation object runs in the Scrawl-canvas animationLoop which is tied to the browser/device’s requestAnimationFrame (RAF) functionality.
    • coreTickersAnimation runs before other Animation objects, thus Tween/Action updates happen before any Display cycle functionality.
    • Unlike Animation objects, Ticker objects do not run automatically as soon as they have been created.
    • To trigger a Ticker object, invoke its run or resume functions.

    Ticker objects have an effective duration - a set number of milliseconds for which they will run.

    • We can set this value directly, using the duration attribute.
    • We can also ask the Ticker to calculate its own effective duration, taking into accout the start times and duration of its currently subscribed Tweens and Actions
    • By default a Ticker will run once, then terminate (remove itself from the coreTickersAnimation function). This counts as 1 cycle.
    • We can get the Ticker to run multiple times before terminating, by setting its cycles attribute to a positive integer Number value.
    • Setting the cycles attribute to 0 causes the Ticker to run continuously once started, until told to stop.
    • Note that Tickers always run forwards, never backwards. Tweens and Actions can be reversed by setting the appropriate flags on them.

    Tickers (unlike Animations) take part in Scrawl-canvas packet functionality; they can be saved, restored and cloned.

    Tickers can be controlled through a set of trigger functions: run, halt, reverse, resume, seekTo, seekFor, complete, reset.

    • We can add Ticker hook functions to each of these trigger functions.

    A Ticker can be made to dispatch a DOM Custom Event at regular intervals as it runs, by setting its eventChoke attribute to a Number value (representing milliseconds) greater than 0.

    • The Custom Event is named tickerupdate.
    • Its detail object includes the following attributes: name (Ticker name-String); type (Ticker); tick (milliseconds since Ticker started running); reverseTick (milliseconds remaining until Ticker completes its current cycle).
    • The Event bubbles, and is cancelable.

    Tickers are very closely associated with Tweens.

    • Each Ticker can have more than one Tween (and 0 or more Actions) subscribed to it.
    • Tweens and Actions can be told to subscribe to a Ticker when they are created; they can change their Ticker subscription at any time.
    • A Tween can make its own Ticker when it is created.
  • ¶

    TODO: basic packet and kill functionality tested in Demo DOM-004, but there’s a lot of Ticker/Tween/Action functionality that needs to be explored and tested further:

    • If we kill a Ticker but leave associated Tweens untouched, will running those Tweens crash the script?
    • Can we successfully clone a Tween whose Ticker has been killed?
    • A Ticker can have more than one subscribed Tween/Action; can (should?) Tweens/Actions be able to subscribe to more than one Ticker? Initial thoughts: no - if such functionality required, clone the Tween/Action
    • Can Tickers be nested? How could we use nested Tickers? Initial thoughts: yes to nesting, possibly use an Action on one ticker to run/halt a second Ticker; one use case may be to break up a complex animation into smaller, more discrete parts? But this all begs the question: if we halt a nested Ticker (via user interaction), how would we cascade that through to parent/sibling Tickers?
    • Possible additional demo - tie a more complex Ticker/Tween/Action sequence to a graphical timeline - see blog post Adding some canvas love to Hexo.
    • Would be a Big Win if we can tether Ticker/Tween/Actions to progress while a video plays - opens up the world of interactive video.
  • ¶

    Demos:

    • All Tween and Action-related Demos necessarily include Tickers
    • Canvas-005 - Cell-locked, and Entity-locked, gradients; animating gradients by delta, and by tween
    • Canvas-006 - Canvas tween stress test
    • DOM-004 - Limitless rockets (clone and destroy elements, tweens, tickers)
    • DOM-005 - DOM tween stress test
    • DOM-006 - Tween actions on a DOM element; tracking tween and ticker activity (analytics)
    • Component-001 - Scrawl-canvas DOM element components
  • ¶

    Imports

    import { constructors, animationtickers, tween } from '../core/library.js';
    import { mergeOver, pushUnique, removeItem, xt, xtGet, isa_obj, convertTime } from '../core/utilities.js';
    
    import { makeAnimation } from './animation.js';
    
    import baseMix from '../mixin/base.js';
  • ¶

    Ticker constructor

    const Ticker = function (items = {}) {
    
        this.makeName(items.name);
        this.register();
    
        this.subscribers = [];
        this.subscriberObjects = [];
    
        this.set(this.defs);
        this.set(items);
    
        this.cycleCount = 0;
        this.active = false;
        this.effectiveDuration = 0;
        this.startTime = 0;
        this.currentTime = 0;
        this.tick = 0;
        this.lastEvent = 0;
    
        if (items.subscribers) this.subscribe(items.subscribers);
    
        this.setEffectiveDuration();
        return this;
    };
  • ¶

    Ticker prototype

    let P = Ticker.prototype = Object.create(Object.prototype);
    P.type = 'Ticker';
    P.lib = 'animationtickers';
    P.isArtefact = false;
    P.isAsset = false;
  • ¶

    Mixins

    P = baseMix(P);
  • ¶

    Ticker attributes

    • Attributes defined in the base mixin: name.
    let defaultAttributes = {
  • ¶

    order - positive integer Number - determines the order in which each Ticker animation object will be actioned before the Display cycle starts.

    • Higher order Tickers will be processed after lower order Tickers.
    • Tickers with the same order value will be processed in the order in which they were defined in code.
        order: 1,
  • ¶

    duration - can accept a variety of values:

    • Number, representing milliseconds.
    • String time value, for example '500ms', '0.5s'.
    • (% String values cannot be used with Ticker objects - they have nothing to measure such a relative value against).
        duration: 0,
  • ¶

    subscribers - Array of Tween and Action name-Strings. Use subscribe and unsubscribe functions rather than the set function to add/remove Tweens and/or Actions to/from the Ticker

        subscribers: null,
  • ¶

    killOnComplete - Boolean flag. When set, the Ticker will kill both itself and all Tweens and Actions associated with it at the end of its run

        killOnComplete: false,
  • ¶

    cycles - positive integer Number representing the number of cycles the Ticker will run before it completes.

    • A value of 0 indicates that the Ticker should repeat itself forever, until its halt, seekTo, seekFor, complete or reset functions are triggered.
    • Note that Tween and Action animation direction is determined by those objects (via their reverseOnCycleEnd and reversed flags). Tickers always repeat in a forwards direction - they loop back to their start; they never reverse time.
        cycles: 1,
  • ¶

    eventChoke - positive Number representing the time to elapse before the Ticker creates and emits another event. A value of 0 stops the Ticker emitting events as it runs.

        eventChoke: 0,
  • ¶

    The Ticker object supports some hook functions:

    • onRun - triggers each time the Ticker’s run function is invoked
    • onHalt - triggers each time the Ticker’s halt function is invoked
    • onReverse - triggers each time the Ticker’s reverse function is invoked
    • onResume - triggers each time the Ticker’s resume function is invoked
    • onSeekTo - triggers each time the Ticker’s seekTo function is invoked
    • onSeekFor - triggers each time the Ticker’s seekFor function is invoked
    • onComplete - triggers each time the Ticker’s complete function is invoked
    • onReset - triggers each time the Ticker’s reset function is invoked
        onRun: null,
        onHalt: null,
        onReverse: null,
        onResume: null,
        onSeekTo: null,
        onSeekFor: null,
        onComplete: null,
        onReset: null,
    };
    P.defs = mergeOver(P.defs, defaultAttributes);
  • ¶

    Packet management

    P.packetExclusions = pushUnique(P.packetExclusions, ['subscribers']);
    P.packetFunctions = pushUnique(P.packetFunctions, ['onRun', 'onHalt', 'onReverse', 'onResume', 'onSeekTo', 'onSeekFor', 'onComplete', 'onReset']);
  • ¶

    Clone management

    No additional clone functionality required.

  • ¶

    Kill management

    kill - remove Ticker from Scrawl-canvas system.

    P.kill = function () {
    
        if (this.active) this.halt();
    
        removeItem(tickerAnimations, this.name);
        tickerAnimationsFlag = true;
    
        this.deregister();
    
        return true;
    };
  • ¶

    killTweens - remove a Ticker’s subscribed Tweens from Scrawl-canvas system.

    • If the function is invoked with a truthy argument, the Ticker will also be removed from the system.
    P.killTweens = function(autokill = false) {
    
        let i, iz, sub;
    
        for (i = 0, iz = this.subscribers.length; i < iz; i++) {
    
            sub = tween[this.subscribers[i]];
            sub.completeAction();
            sub.kill();
        }
    
        if (autokill) {
    
            this.kill();
            return true;
        }
    
        return this;
    };
  • ¶

    Get, Set, deltaSet

    let G = P.getters,
        S = P.setters;
  • ¶

    subscribers - see also the subscribe and unsubscribe functions below

    • getter returns a copy of the subscribers Array, containing Tween and Action object name-Strings.
    • setter accepts a Tween or Action name-String, or an Array of such Strings. Will replace the existing subscribers Array with this new data.
    G.subscribers = function () {
    
        return [].concat(this.subscribers);
    };
    S.subscribers = function (item) {
    
        this.subscribers = [];
        this.subscribe(item);
    };
  • ¶

    order

    S.order = function (item) {
    
        this.order = item;
        
        if (this.active) tickerAnimationsFlag = true;
    };
  • ¶

    cycles

    S.cycles = function (item) {
    
        this.cycles = item;
        
        if (!this.cycles) this.cycleCount = 0;
    };
  • ¶

    duration - changes to the duration (and as a consequence effectiveDuration) attributes will be cascaded down to subscribed Tweens and Actions immediately.

    S.duration = function (item) {
    
        let i, iz, target,
            subscribers = this.subscribers;
    
        this.duration = item;
        this.setEffectiveDuration();
    
        if(xt(subscribers)){
    
            for (i = 0, iz = subscribers.length; i < iz; i++) {
            
                target = tween[subscribers[i]];
    
                if (target) {
    
                    target.calculateEffectiveTime();
    
                    if (target.type === 'Tween') target.calculateEffectiveDuration();
                }
            }
        }
    };
  • ¶

    Subscription management

  • ¶

    subscribe - accepts a Tween or Action name-String, or an Array of such Strings.

    P.subscribe = function (items) {
    
        let myItems = [].concat(items),
            i, iz, item, name;
    
        for (i = 0, iz = myItems.length; i < iz; i++) {
    
            item = myItems[i];
            
            if(item != null){
    
                if (item.substring) name = item;
                else name = (isa_obj(item) && item.name) ? item.name : false;
    
                if (name) pushUnique(this.subscribers, name);
            }
        }
    
        if (myItems.length) {
    
            this.sortSubscribers();
            this.recalculateEffectiveDuration();
        }
        return this;
    };
  • ¶

    unsubscribe - accepts a Tween or Action name-String, or an Array of such Strings.

    P.unsubscribe = function (items) {
    
        var myItems = [].concat(items),
            i, iz, item, name;
    
        for (i = 0, iz = myItems.length; i < iz; i++) {
    
            item = items[i];
    
            if (item.substring) name = item;
            else name = (isa_obj(item) && item.name) ? item.name : false;
    
            if (name) removeItem(this.subscribers, name);
        }
    
        if (myItems.length) {
    
            this.sortSubscribers();
            this.recalculateEffectiveDuration();
        }
        return this;
    };
  • ¶

    repopulateSubscriberObjects

    P.repopulateSubscriberObjects = function () {
    
        let arr = this.subscriberObjects,
            subs = this.subscribers,
            t;
    
        arr.length = 0;
    
        subs.forEach(sub => {
    
            t = tween[sub];
    
            if (t) arr.push(t);
        });
    };
  • ¶

    getSubscriberObjects

    P.getSubscriberObjects = function () {
    
        if (this.subscribers.length && !this.subscriberObjects.length) this.repopulateSubscriberObjects();
    
        return this.subscriberObjects;
    };
  • ¶

    sortSubscribers - internal Helper function called by subscribe and unsubscribe

    P.sortSubscribers = function () {
    
        let mysubscribers = this.subscribers;
    
        if(mysubscribers.length > 1) {
    
            let subs = [].concat(mysubscribers),
                floor = Math.floor,
                buckets = [];
    
            subs.forEach(obj => {
    
                let effectiveTime = floor(obj.effectiveTime) || 0;
    
                if (!buckets[effectiveTime]) buckets[effectiveTime] = [];
    
                buckets[effectiveTime].push(obj);
            });
    
            this.subscribers = buckets.reduce((a, v) => a.concat(v), []);
        }
        this.repopulateSubscriberObjects();
    };
  • ¶

    updateSubscribers - internal function called by the run, reset and complete functions below.

    • First argument is an object that gets applied as the argument to each Tween/Action object’s set function.
    • Second argument is a Boolean; when set, subscribed Tween/Actions will be told to reverse their current direction.
    P.updateSubscribers = function(items, reversed) {
        
        reversed = (xt(reversed)) ? reversed : false;
    
        let subs = this.getSubscriberObjects(),
            i, iz;
    
        if (reversed) {
    
            for (i = subs.length - 1; i >= 0; i--) {
    
                subs[i].set(items);
            }
        }
        else{
    
            for (i = 0, iz = subs.length; i < iz; i++) {
    
                subs[i].set(items);
            }
        }
        return this;
    };
  • ¶

    changeSubscriberDirection - internal function - when invoked, Tween/Actions will be told to reverse their current direction.

    P.changeSubscriberDirection = function () {
    
        let subs = this.getSubscriberObjects();
    
        subs.forEach(sub => sub.reversed = !sub.reversed);
        
        return this;
    };
  • ¶

    Events

  • ¶

    makeTickerUpdateEvent - internal function - generates a new CustomEvent object.

    P.makeTickerUpdateEvent = function() {
    
        return new CustomEvent('tickerupdate', {
            detail: {
                name: this.name,
                type: 'Ticker',
                tick: this.tick,
                reverseTick: this.effectiveDuration - this.tick
            },
            bubbles: true,
            cancelable: true
        });
    };
  • ¶

    Animation

  • ¶

    recalculateEffectiveDuration - where a Ticker has not been given a duration value, it needs to consult its subscribed Tween/Action objects to calculate an effectiveDuration value with sufficient time allocated for each Tween to run to completion, and each Action to trigger.

    • Tween/Actions with a relative time attribute - eg: 30% - will not be included in the calculation.
    • Tweens can overlap - they do not all have to start and end at the same time, nor do they need to run sequentially.
    P.recalculateEffectiveDuration = function() {
    
        let durationValue, 
            subs = this.getSubscriberObjects(),
            duration = 0;
    
        if (!this.duration) {
    
            subs.forEach(sub => {
    
                durationValue = sub.getEndTime();
    
                if (durationValue > duration) duration = durationValue;
            });
            this.effectiveDuration = duration;
        }
  • ¶

    Shouldn’t cause an infinite loop …

        else this.setEffectiveDuration();
    
        return this;
    };
  • ¶

    setEffectiveDuration - internal helper function - convert duration value into effectiveDuration value.

    P.setEffectiveDuration = function() {
    
        let temp;
    
        if (this.duration) {
        
            temp = convertTime(this.duration);
  • ¶

    Cannot use %-String values for Ticker duration attribute

            if (temp[0] === '%') {
    
                this.duration = 0
                this.recalculateEffectiveDuration();
            }
            else this.effectiveDuration = temp[1];
        }
        return this;
    };
  • ¶

    fn - internal - the animation function will trigger once per RequestAnimationFrame (RAF) tick - approximately 60 times a second, depending on other calculation work.

    • Only triggers when the Ticker is running in a qualifying state.
    • reverseOrder argument is a Boolean value; when set, subscribed Tween/Action objects will be processed in reverse order.
    • Note that this function is synchronous - it does not return a Promise. This will need to change if we ever move some Ticker/Tween/Action functionality over to a web worker.
    P.fn = function (reverseOrder) {
  • ¶

    Request a result object from the pool.

        let result = requestResultObject();
  • ¶

    Determine the order in which subscribed objects will be processed

        reverseOrder = xt(reverseOrder) ? reverseOrder : false;
    
        let i, iz, subs, eTime, now, e,
            active = this.active,
            startTime = this.startTime,
            currentTime, tick,
            cycles = this.cycles,
            cycleCount = this.cycleCount,
            effectiveDuration = this.effectiveDuration,
            eventChoke = this.eventChoke;
  • ¶

    Process only if the Ticker is currently active and has a startTime value assigned to it.

        if (active && startTime) {
  • ¶

    Process only if the Ticker’s cycles attribute has been set to 0, or if the Ticker has not yet completed all its cycles.

            if (!cycles || cycleCount < cycles) {
    
                currentTime = this.currentTime = Date.now();
                tick = this.tick = currentTime - startTime;
  • ¶

    Update the results object

    • Functionality performed if the Tween is not on its final cycle.
                if (!cycles || cycleCount + 1 < cycles) {
    
                    if (tick >= effectiveDuration) {
    
                        tick = this.tick = 0;
                        this.startTime = this.currentTime;
                        result.tick = effectiveDuration;
                        result.reverseTick = 0;
                        result.willLoop = true;
    
                        if (cycles) {
    
                            cycleCount++;
                            this.cycleCount = cycleCount;
                        }
                    }
                    else {
    
                        result.tick = tick;
                        result.reverseTick = effectiveDuration - tick;
                    }
                    result.next = true;
                }
  • ¶
    • Functionality performed only when the Tween is on its final cycle.
                else {
    
                    if (tick >= effectiveDuration) {
    
                        result.tick = effectiveDuration;
                        result.reverseTick = 0;
                        active = this.active = false;
                        
                        if (cycles) {
    
                            cycleCount++
                            this.cycleCount = cycleCount;
                        }
                    }
                    else {
    
                        result.tick = tick;
                        result.reverseTick = effectiveDuration - tick;
                        result.next = true;
                    }
                }
  • ¶

    Invoke the update function on each subscribed Tween/Action

                subs = this.getSubscriberObjects();
    
                if (reverseOrder) {
    
                    for (i = subs.length - 1; i >= 0; i--) {
    
                        subs[i].update(result);
                    }
                }
                else{
    
                    for (i = 0, iz = subs.length; i < iz; i++) {
    
                        subs[i].update(result);
                    }
                }
  • ¶

    Dispatch the Ticker Event, if required.

                if (eventChoke) {
    
                    eTime = this.lastEvent + eventChoke;
                    now = Date.now();
    
                    if (eTime < now) {
    
                        e = this.makeTickerUpdateEvent();
                        window.dispatchEvent(e);
                        this.lastEvent = now;
                    }
                }
  • ¶

    If this invocation of the function has completed the Ticker’s run, switch it off.

                if (!active) this.halt();
  • ¶

    If the Ticker’s run is completed and the killOnComplete flag is set, kill everything.

                if (this.killOnComplete && cycleCount >= cycles) this.killTweens(true);
            }
        }
  • ¶

    Release the result object back to the pool.

        releaseResultObject(result);
    };
  • ¶

    Animation control

  • ¶

    run

    • Start the Ticker from time 0.
    • Trigger the object’s onRun function.
    P.run = function () {
    
        if (!this.active) {
    
            this.startTime = this.currentTime = Date.now();
            this.cycleCount = 0;
    
            this.updateSubscribers({
                reversed: false
            });
    
            this.active = true;
    
            pushUnique(tickerAnimations, this.name);
            tickerAnimationsFlag = true;
    
            if (typeof this.onRun === 'function') this.onRun();
        }
    
        return this;
    };
  • ¶

    isRunning - check to see if Ticker is in a running state.

    P.isRunning = function () {
    
        return this.active;
    };
  • ¶

    reset

    • Halt the Ticker, if it is running.
    • Set all attributes to their initial values.
    • Update subscribed Tween/Actions.
    • Trigger the object’s onReset function.
    P.reset = function () {
    
        if (this.active) this.halt();
    
        this.startTime = this.currentTime = Date.now();
        this.cycleCount = 0;
    
        this.updateSubscribers({
            reversed: false
        });
    
        this.active = true;
    
        this.fn(true);
        this.active = false;
    
        if (typeof this.onReset === 'function') this.onReset();
    
        return this;
    };
  • ¶

    complete

    • Halt the Ticker, if it is running.
    • Set all attributes to their initial values.
    • Update subscribed Tween/Actions.
    • Trigger the object’s onComplete function.
    P.complete = function () {
    
        if (this.active) this.halt();
    
        this.startTime = this.currentTime = Date.now();
        this.cycleCount = 0;
    
        this.updateSubscribers({
            reversed: true
        });
    
        this.active = true;
    
        this.fn();
        this.active = false;
    
        if (typeof this.onComplete === 'function') this.onComplete();
    
        return this;
    };
  • ¶

    reverse - simulates a reversal in the Ticker’s animation.

    • Halt the Ticker, if it is running.
    • Trigger the object’s onReverse function.
    • after recalculation, resume the Ticker - if required.
    • Function accepts a Boolean argument - if true, Ticker will start playing “in reverse”.
    • Directionality is determined by Tween/Action object attribute settings, not the Ticker.
    P.reverse = function (resume = false) {
    
        let timePlayed;
    
        resume = xtGet(resume, false);
    
        if (this.active) {
            this.halt();
        }
    
        timePlayed = this.currentTime - this.startTime;
        this.startTime = this.currentTime - (this.effectiveDuration - timePlayed);
        this.changeSubscriberDirection();
        this.active = true;
    
        this.fn();
        this.active = false;
        
        if (typeof this.onReverse === 'function') this.onReverse();
    
        if (resume) this.resume();
    
        return this;
    };
  • ¶

    halt

    • Stop the Ticker at its current point in time
    • Trigger the object’s onHalt function.
    P.halt = function () {
    
        this.active = false;
        pushUnique(tickerAnimations, this.name);
        tickerAnimationsFlag = true;
    
        if (typeof this.onHalt === 'function') this.onHalt();
    
        return this;
    };
  • ¶

    resume - this function can also be triggered by the reverse, seekTo and seekFor functions

    • Start the Ticker from its current point in time
    • Trigger the object’s onResume function.
    P.resume = function () {
    
        let now, current, start;
    
        if (!this.active) {
    
            now = Date.now(),
            current = this.currentTime,
            start = this.startTime;
            this.startTime = now - (current - start);
            this.currentTime = now;
            this.active = true;
            pushUnique(tickerAnimations, this.name);
            tickerAnimationsFlag = true;
    
            if (typeof this.onResume === 'function') this.onResume();
    
        }
        return this;
    };
  • ¶

    seekTo

    • First argument - Number representing the millisecond time to move to on the Ticker’s timeline
    • Second argument - Boolean - if true, Ticker will resume playing from new point
    • Halt the Ticker, if it is running.
    • Update the Ticker’s currentTime, startTime attributes
    • Trigger the object’s onSeekTo function.
    • Resume the Ticker - if required.
    P.seekTo = function (milliseconds, resume = false) {
    
        let backwards = false;
    
        milliseconds = xtGet(milliseconds, 0);
    
        if (this.active) this.halt();
    
        if (this.cycles && this.cycleCount >= this.cycles) this.cycleCount = this.cycles - 1;
    
        if (milliseconds < this.tick) backwards = true;
    
        this.currentTime = Date.now();
        this.startTime = this.currentTime - milliseconds;
        this.active = true;
    
        this.fn(backwards);
        this.active = false;
    
        if (typeof this.onSeekTo === 'function') this.onSeekTo();
    
        if (resume) this.resume();
    
        return this;
    };
  • ¶

    seekFor

    • First argument - Number representing the number of milliseconds to move along the Ticker’s timeline (forwards or backwards)
    • Second argument - Boolean - if true, Ticker will resume playing from new point
    • Halt the Ticker, if it is running.
    • Update the Ticker’s currentTime, startTime attributes
    • Trigger the object’s onSeekFor function.
    • Resume the Ticker - if required.
    P.seekFor = function (milliseconds, resume = false) {
    
        let backwards = false;
    
        milliseconds = __xtGet(milliseconds, 0);
    
        if (this.active) this.halt();
    
        if (this.cycles && this.cycleCount >= this.cycles) this.cycleCount = this.cycles - 1;
    
        this.startTime -= milliseconds;
    
        if (milliseconds < 0) backwards = true;
    
        this.active = true;
    
        this.fn(backwards);
        this.active = false;
    
        if (typeof this.onSeekFor === 'function') this.onSeekFor();
    
        if (resume) this.resume();
    
        return this;
    };
  • ¶

    Ticker animation controller

    let tickerAnimations = [];
    let tickerAnimationsFlag = true;
  • ¶

    coreTickersAnimation

    const coreTickersAnimation = makeAnimation({
    
        name: 'coreTickersAnimation',
        order: 0,
        fn: function () {
    
            return new Promise((resolve) => {
                
                let i, iz, t;
  • ¶

    We only sort active Ticker objects when absolutely necessary.

    • Sorted using a bucket sort algorithm.
                if (tickerAnimationsFlag) {
    
                    tickerAnimationsFlag = false;
    
                    let tans = [].concat(tickerAnimations),
                        floor = Math.floor,
                        buckets = [];
    
                    tans.forEach(name => {
    
                        let obj = animationtickers[name];
    
                        if (xt(obj)) {
    
                            let order = floor(obj.order) || 0;
    
                            if (!buckets[order]) buckets[order] = [];
    
                            buckets[order].push(obj.name);
                        }
                    });
                    tickerAnimations = buckets.reduce((a, v) => a.concat(v), []);
                }
  • ¶

    Invoke each Ticker’s fn function.

    • It’s up to the Ticker object to decide whether it’s active
    • Ticker fn functions are (currently) synchronous functions; they do not return Promises.
                tickerAnimations.forEach(name => {
    
                    let obj = animationtickers[name];
    
                    if (obj && obj.fn) obj.fn();
    
                });
  • ¶

    Will only resolve the Promise when all Tickers (and their subscribed Tween/Actions) have processed their fn/update functions.

                resolve(true);
            });
        }
    });
  • ¶

    ResultObject pool

    … Because we generate so many of them.

    • To use a pool result object, request it using requestResultObject function.
    • It is imperative that requested result objects are released - releaseResultObject - once work with them completes.
    const resultObjectPool = [];
  • ¶

    requestResultObject

    const requestResultObject = function () {
    
        if (!resultObjectPool.length) {
    
            resultObjectPool.push({
                tick: 0,
                reverseTick: 0,
                willLoop: false,
                next: false
            });
        }
    
        return resultObjectPool.shift();
    };
  • ¶

    releaseResultObject

    const releaseResultObject = function (r) {
    
        if (r) {
    
            r.tick = 0;
            r.reverseTick = 0;
            r.willLoop = false;
            r.next = false;
            resultObjectPool.push(r);
        }
    };
  • ¶

    Factory

    const makeTicker = function (items) {
        return new Ticker(items);
    };
    
    constructors.Ticker = Ticker;
  • ¶

    Exports

    export {
        makeTicker,
    };