Permalink
Browse files

player options now go through a broker

- 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 4065e0b0823f66cb40d492f8fb367fc9c16e1876
Showing with 379 additions and 315 deletions.
  1. +2 −1 .vscode/settings.json
  2. +0 −1 package.json
  3. +1 −1 src/all.ts
  4. +17 −0 src/lib/actions/cancel.ts
  5. +10 −0 src/lib/actions/destroy.ts
  6. +24 −0 src/lib/actions/finish.ts
  7. +13 −0 src/lib/actions/pause.ts
  8. +29 −0 src/lib/actions/play.ts
  9. +10 −0 src/lib/actions/reverse.ts
  10. +25 −0 src/lib/actions/setup.ts
  11. +63 −0 src/lib/actions/tick.ts
  12. +33 −0 src/lib/actions/update.ts
  13. +7 −0 src/lib/actions/updateRate.ts
  14. +8 −0 src/lib/actions/updateTime.ts
  15. +21 −0 src/lib/core/broker.ts
  16. +1 −1 src/lib/{dispatcher.ts → core/events.ts}
  17. +3 −4 src/lib/{ → core}/interpolate.ts
  18. 0 src/lib/{ → core}/plugins.ts
  19. +3 −3 src/lib/{ → core}/references.ts
  20. +4 −4 src/lib/{model → core}/timeloop.ts
  21. 0 src/lib/{ → core}/types.ts
  22. +9 −8 src/lib/model/config.ts
  23. +6 −9 src/lib/model/effects.ts
  24. +1 −1 src/lib/model/store.ts
  25. +0 −228 src/lib/model/update.ts
  26. +16 −25 src/lib/timeline.ts
  27. +9 −3 src/lib/utils/constants.ts
  28. +3 −3 src/lib/{ → utils}/get-targets.ts
  29. +2 −2 src/lib/{ → utils}/resolve-property.ts
  30. +42 −4 src/main.ts
  31. +2 −2 src/props/index.ts
  32. +1 −1 src/tools/index.ts
  33. +1 −1 src/web/animate.ts
  34. +1 −1 src/web/append-units.ts
  35. +1 −1 src/web/index.ts
  36. +2 −2 src/web/transforms.ts
  37. +1 −1 tests/lib/basic.ts
  38. +1 −1 tests/lib/resolve-property.ts
  39. +1 −1 tests/props/interpolators.ts
  40. +1 −1 tests/web/basic.ts
  41. +2 −2 tests/web/get-targets.ts
  42. +1 −1 tests/web/timeline.ts
  43. +1 −1 tests/web/transforms.ts
  44. +1 −1 tests/web/transitions.ts
View
@@ -3,5 +3,6 @@
"prettier.tabWidth": 2,
"prettier.singleQuote": true,
"prettier.printWidth": 120,
"tslint.autoFixOnSave": true
"tslint.autoFixOnSave": true,
"mocha.enabled": true
}
View
@@ -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": {
View
@@ -1,4 +1,4 @@
import { addPlugin } from './lib/plugins'
import { addPlugin } from './lib/core/plugins'
import { waapiPlugin } from './web'
export * from './main'
View
@@ -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, _)
}
View
@@ -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)
}
View
@@ -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)
}
}
View
@@ -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)
}
View
@@ -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)
}
View
@@ -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)
}
View
@@ -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
}
View
@@ -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)
}
View
@@ -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)
}
@@ -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)
}
@@ -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)
}
View
@@ -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)
}
}
@@ -1,4 +1,4 @@
import { pushDistinct, getIndex, all } from './utils/lists'
import { pushDistinct, getIndex, all } from '../utils/lists'
const handlers = {}
@@ -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
File renamed without changes.
@@ -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
@@ -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> = {}
@@ -40,7 +40,7 @@ function update() {
deltas[activeId] = updatedElapsed
// call sub with updated delta
tick(activeId, delta)
dispatch(TICK, activeId, delta)
}
}
File renamed without changes.
View
@@ -1,4 +1,3 @@
import { sortBy, all, includes, find, push, pushDistinct, indexOf, list } from '../utils/lists'
import {
PropertyKeyframe,
AddAnimationOptions,
@@ -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')
Oops, something went wrong.

0 comments on commit 4065e0b

Please sign in to comment.