Skip to content

Commit

Permalink
player options now go through a broker
Browse files Browse the repository at this point in the history
- split up the different actions on a model into their own files to make it easier to understand
- introduced a broker for dispatching actions.  this will ultimately be how the extension talks to timelines
- fixed some cyclical conditions that caused update to run two or three times in one frame in certain situations
  • Loading branch information
notoriousb1t committed Sep 16, 2017
1 parent c9e738a commit 4065e0b
Show file tree
Hide file tree
Showing 44 changed files with 379 additions and 315 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"prettier.tabWidth": 2,
"prettier.singleQuote": true,
"prettier.printWidth": 120,
"tslint.autoFixOnSave": true
"tslint.autoFixOnSave": true,
"mocha.enabled": true
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"homepage": "http://just-animate.com",
"license": "MIT",
"main": "./lib/main.js",
"jsnext:main": "./lib.es2015/main.js",
"module": "./lib.es2015/main.js",
"name": "just-animate",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion src/all.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addPlugin } from './lib/plugins'
import { addPlugin } from './lib/core/plugins'
import { waapiPlugin } from './web'

export * from './main'
Expand Down
17 changes: 17 additions & 0 deletions src/lib/actions/cancel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ITimelineModel } from '../core/types'
import { loopOff } from '../core/timeloop'
import { publish } from '../core/events'
import { CANCEL, S_INACTIVE, _ } from '../utils/constants'
import { all } from '../utils/lists'

export const cancel = (model: ITimelineModel) => {
all(model.players, effect => effect.cancel())

model.state = S_INACTIVE
model.time = _
model.round = _
model.players = _

loopOff(model.id)
publish(model.id, CANCEL, _)
}
10 changes: 10 additions & 0 deletions src/lib/actions/destroy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ITimelineModel } from '../core/types'
import { unsubscribeAll } from '../core/events'
import { destroyModel } from '../model/store'
import { cancel } from './cancel'

export const destroy = (model: ITimelineModel) => {
cancel(model)
unsubscribeAll(model.id)
destroyModel(model.id)
}
24 changes: 24 additions & 0 deletions src/lib/actions/finish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { loopOff } from '../core/timeloop'
import { publish } from '../core/events'
import { ITimelineModel } from '../core/types'
import { UPDATE, FINISH, S_FINISHED } from '../utils/constants'
import { destroy } from './destroy'
import { update } from './update'

export const finish = (model: ITimelineModel) => {
model.round = 0
model.state = S_FINISHED

if (!model.yoyo) {
model.time = model.rate < 0 ? 0 : model.duration
}

loopOff(model.id)
update(model)
publish(model.id, UPDATE, model.time)
publish(model.id, FINISH, model.time)

if (model.destroy) {
destroy(model)
}
}
13 changes: 13 additions & 0 deletions src/lib/actions/pause.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ITimelineModel } from '../core/types'
import { publish } from '../core/events'
import { loopOff } from '../core/timeloop'
import { S_PAUSED, PAUSE } from '../utils/constants'
import { update } from './update'

export const pause = (model: ITimelineModel) => {
model.state = S_PAUSED

loopOff(model.id)
update(model)
publish(model.id, PAUSE, model.time)
}
29 changes: 29 additions & 0 deletions src/lib/actions/play.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { PlayOptions, ITimelineModel } from '../core/types'
import { loopOn } from '../core/timeloop'
import { publish } from '../core/events'
import { S_PLAYING, PLAY } from '../utils/constants'
import { update } from './update'

export const play = (model: ITimelineModel, options: PlayOptions) => {
if (options) {
model.repeat = options.repeat
model.yoyo = options.alternate === true
model.destroy = !!options.destroy
}

model.repeat = model.repeat || 1
model.yoyo = model.yoyo || false
model.state = S_PLAYING

// set current time (this will automatically start playing when the state is running)
const isForwards = model.rate >= 0
if (isForwards && model.time === model.duration) {
model.time = 0
} else if (!isForwards && model.time === 0) {
model.time = model.duration
}

loopOn(model.id)
update(model)
publish(model.id, PLAY, model.time)
}
10 changes: 10 additions & 0 deletions src/lib/actions/reverse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ITimelineModel } from '../core/types'
import { publish } from '../core/events'
import { REVERSE } from '../utils/constants'
import { update } from './update'

export const reverse = (model: ITimelineModel) => {
model.rate *= -1
update(model)
publish(model.id, REVERSE, model.time)
}
25 changes: 25 additions & 0 deletions src/lib/actions/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ITimelineModel, AnimationPlayer } from '../core/types'
import { plugins } from '../core/plugins'
import { getEffects } from '../model/effects'
import { all, push } from '../utils/lists'
import { max } from '../utils/math'
import { _ } from '../utils/constants'

export const setup = (model: ITimelineModel) => {
const animations: AnimationPlayer[] = []

all(getEffects(model), effect => {
const controller = plugins[effect.plugin].animate(effect) as AnimationPlayer
if (controller) {
// controller.config = effect.config
controller.from = effect.from
controller.to = effect.to
push(animations, controller)
}
})

// change duration to max to
model.duration = max.apply(_, animations.filter(a => isFinite(a.to)).map(a => a.to))
model.time = isFinite(model.time) ? model.time : model.rate < 0 ? model.duration : 0
model.players = animations
}
63 changes: 63 additions & 0 deletions src/lib/actions/tick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ITimelineModel } from '../core/types'
import { S_STARTING, S_PLAYING, _ } from '../utils/constants'
import { inRange } from '../utils/math'
import { finish } from './finish'
import { update } from './update'

export const tick = (model: ITimelineModel, delta: number) => {
// calculate running range
const duration = model.duration
const repeat = model.repeat
const rate = model.rate
const isReversed = rate < 0

// set time use existing
let time = model.time === _ ? (rate < 0 ? duration : 0) : model.time

let iteration = model.round || 0

if (model.state === S_STARTING) {
model.state = S_PLAYING

// reset position properties if necessary
if (time === _ || (isReversed && time > duration) || (!isReversed && time < 0)) {
// if at finish, reset to start time
time = isReversed ? duration : 0
}
if (iteration === repeat) {
// if at finish reset iterations to 0
iteration = 0
}
}

time += delta * rate

// check if timeline has finished
let iterationEnded = false
if (!inRange(time, 0, duration)) {
model.round = ++iteration
time = isReversed ? 0 : duration
iterationEnded = true

// reverse direction on alternate
if (model.yoyo) {
model.rate = (model.rate || 0) * -1
}

// reset the clock
time = model.rate < 0 ? duration : 0
}

// call update
model.time = time
model.round = iteration

if (iterationEnded && repeat === iteration) {
// end the cycle
finish(model)
return
}

// if not the last iteration reprocess this tick from the new starting point/direction
update(model)
}
33 changes: 33 additions & 0 deletions src/lib/actions/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ITimelineModel } from '../core/types'
import { publish } from '../core/events'
import { loopOff } from '../core/timeloop'
import { S_PLAYING, UPDATE, _ } from '../utils/constants'
import { inRange, minMax, flr } from '../utils/math'
import { all } from '../utils/lists'
import { setup } from './setup'

export const update = (model: ITimelineModel) => {
// setup effects if required
if (model.players === _) {
setup(model)
}

// check current state
const isActive = model.state === S_PLAYING

// remove tick from loop if no timelines are active
if (!isActive) {
loopOff(model.id)
}

// update effects`
all(model.players, effect => {
const { from, to } = effect
const isAnimationActive = isActive && inRange(flr(model.time), from, to)
const offset = minMax((model.time - from) / (to - from), 0, 1)

effect.update(offset, model.rate, isAnimationActive)
})

publish(model.id, UPDATE, model.time)
}
7 changes: 7 additions & 0 deletions src/lib/actions/updateRate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ITimelineModel } from '../core/types'
import { update } from './update'

export const updateRate = (model: ITimelineModel, rate: number) => {
model.rate = rate || 1
update(model)
}
8 changes: 8 additions & 0 deletions src/lib/actions/updateTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ITimelineModel } from '../core/types'
import { update } from './update'

export const updateTime = (model: ITimelineModel, time: number) => {
const currentTime = +time
model.time = isFinite(currentTime) ? currentTime : model.rate < 0 ? model.duration : 0
update(model)
}
21 changes: 21 additions & 0 deletions src/lib/core/broker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ITimelineModel } from './types'
import { getModel } from '../model/store'

export interface ChangeHandler {
(model: ITimelineModel, data?: any): void
}

const handlers: Record<string, ChangeHandler> = {}

export function register(action: string, handler: ChangeHandler) {
handlers[action] = handler
}

export function dispatch(action: string, id: string, data?: any) {
const fn = handlers[action]
const model = getModel(id)

if (fn && model) {
fn(model, data)
}
}
2 changes: 1 addition & 1 deletion src/lib/dispatcher.ts → src/lib/core/events.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { pushDistinct, getIndex, all } from './utils/lists'
import { pushDistinct, getIndex, all } from '../utils/lists'

const handlers = {}

Expand Down
7 changes: 3 additions & 4 deletions src/lib/interpolate.ts → src/lib/core/interpolate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { cssFunction } from 'just-curves'
import { Keyframe, Interpolator } from './types'
import { isNumber, isFunction } from './utils/inspect'
// import { rnd } from './math';
import { memoize } from './utils/functional'
import { all } from './utils/lists'
import { isNumber, isFunction } from '../utils/inspect'
import { memoize } from '../utils/functional'
import { all } from '../utils/lists'

function findEndIndex(ns: number[], n: number) {
const ilen = ns.length
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions src/lib/references.ts → src/lib/core/references.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isString, isArrayLike, isFunction, isNumber, isDOM, isDefined } from './utils/inspect'
import { mapFlatten, all, push } from './utils/lists'
import { References } from './types'
import { owns } from './utils/utils'
import { isString, isArrayLike, isFunction, isNumber, isDOM, isDefined } from '../utils/inspect'
import { mapFlatten, all, push } from '../utils/lists'
import { owns } from '../utils/utils'

let refId = 0
const objNameExp = /\[object ([a-z]+)\]/i
Expand Down
8 changes: 4 additions & 4 deletions src/lib/model/timeloop.ts → src/lib/core/timeloop.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { caf, now, raf } from '../utils/utils'
import { _ } from '../utils/constants'
import { push, getIndex, includes } from '../utils/lists'
import { tick } from '../model/update'
import { _, TICK } from '../utils/constants'
import { push, getIndex, includes } from '../utils/lists'
import { dispatch } from './broker'

const active: string[] = []
const deltas: Record<string, number> = {}
Expand Down Expand Up @@ -40,7 +40,7 @@ function update() {
deltas[activeId] = updatedElapsed

// call sub with updated delta
tick(activeId, delta)
dispatch(TICK, activeId, delta)
}
}

Expand Down
File renamed without changes.
17 changes: 9 additions & 8 deletions src/lib/model/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { sortBy, all, includes, find, push, pushDistinct, indexOf, list } from '../utils/lists'
import {
PropertyKeyframe,
AddAnimationOptions,
Expand All @@ -12,16 +11,18 @@ import {
Interpolator,
PropertyValueOptions,
PropertyObject
} from '../types'
import { getModel } from './store'
import { isDefined, isObject, isArrayLike, isNumber } from '../utils/inspect'
} from '../core/types'

import { publish } from '../core/events'
import { plugins } from '../core/plugins'
import { replaceWithRefs } from '../core/references'
import { CONFIG, _ } from '../utils/constants'
import { publish } from '../dispatcher'
import { plugins } from '../plugins'
import { replaceWithRefs } from '../references'
import { resolveProperty } from '../resolve-property'
import { isDefined, isObject, isArrayLike, isNumber } from '../utils/inspect'
import { sortBy, all, includes, find, push, pushDistinct, indexOf, list } from '../utils/lists'
import { resolveProperty } from '../utils/resolve-property'
import { max, flr } from '../utils/math'
import { owns } from '../utils/utils'
import { getModel } from './store'

const propKeyframeSort = sortBy<PropertyKeyframe>('time')

Expand Down
Loading

0 comments on commit 4065e0b

Please sign in to comment.