1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
|
# Tagged Animations
The user can tag and control the runner for any animation
```js
var animation = element
.loop(300, true)
.tag('first')
.rotate(360)
.translate(50)
.animate(300, 200)
.tag('second')
.scale(3)
element.timeline.finish()
element.timeline.pause()
element.timeline.stop()
element.timeline.play()
```
# Absolute Timeline Times
The user can specify their time which is relative to the timelines time.
```js
var animation = element.animate(2000).move(200, 200)
// after 1000 ms
animation.animate(1000, 500, 'absolute').scale(2)
var runner = elemenet.move(0, 0).animate(1000)
// after 500ms
runner.move(200, 200)
```
This block of code would:
- Spend the first 1000ms moving the element
- At this time, it will snap the scale to 1.5 (halfway to 2)
- After this time, the scale and the move should go together
# Rotating While Scaling
The user may want to run multiple animations concurrently and have
control over each animation that they define.
```js
let animationA = element.loop(300, ">").rotate(360)
let animationB = element.loop(200, "><").scale(2)
// Maybe they want to disable a runner - which acts like pausing
animationB.active(false)
// Maybe they want to remove an animation matching a tag
animationB.tag('B')
element.timeline().remove('B')
// They can move around a runner as well
element.timeline()
.schedule('B', 300, 'absolute') // Moves a runner to start at 300
// time(currentAbsolute - newAbsolute)
.shift('B', 300) // Shifts the runner start time by 300
// which is sugar to
.schedule('B', 300, 'relative')
// seek(shiftTime)
```
Lets demonstrate the difference between the schedule and shift
```
Given this:
--------
--------------
----------------
Schedule:
--------
--------------
----------------
Shift:
--------
--------------
----------------
```
# A Sequenced Animation
The user might want to be able to run a long sequenced animation that they have
predesigned as they please.
```js
let timeline = element.loop(300, "><").scale(2)
.animate(300).rotate(30)
.animate(300, 200).fill(blue)
// They might want to move forwards or backwards
timeline.seek(-300)
// They might want to set a specific time
timeline.time(200)
// Or a specific position
timeline.position(0.3)
// Maybe they want to clear the timeline
timeline.reset()
```
# User wants to Loop Something
If the user wants to loop something, they should be able to call the loop
method at any time, and it will just change the behaviour of the current
runner. If we are running declaratively, we will throw an error.
## Correct Usages
They can invoke this from the timeline
```js
element.loop(duration, times, swing)
```
If they want to work with absolute times, they should animate first
```js
element.animate(300, 200, true)
.loop(Infinity, true)
```
Or alternatively, they could equivalently do this:
```js
element.loop({
now: true,
times: Infinity,
delay: 200,
duration: 300,
swing: true,
wait: [200, 300]
})
```
## Error Case
# Declarative Animations
The user might want to have something chase their mouse around. This would
require a declarative animation.
```js
el.animate((curr, target, dt, ctx) => {
// Find the error and the value
let error = target - current
ctx.speed = (ctx.error - error) / dt
ctx.error = error
return newPos
})
SVG.on(document, 'mousemove', (ev) => {
el.timeline(controller)
.move(ev.pageX, ev.pageY)
})
```
## Springy Mouse Chaser
Pretend we gave the user a springy controller that basically springs to a
target in 300ms for example. They might be constantly changing the target with:
```js
el.animate(Spring(500), 200)
.tag('declarative')
.persist()
.move(10, 10)
el.animate('declarative')
.move(300, 200)
SVG.on(document, 'mousemove', function (ev) {
el.animate(springy, 200)
.tag('declarative')
.move(ev.pageX, ev.pageY)
})
```
# Repeated Animations
The user might want to duplicate an animation and have it rerun a few times
```js
// User makes two copies of an animation
let animA = el.animate(300, 300, 'now')...(animation)...
let animB = animA.clone() // Deep copy
// Now let the user attach and reschedule their animations
el.timeline()
.schedule(animA, 500, 'absolute')
.schedule(animB, 2000, 'absolute')
```
Then the user can loop the timeline, by changing its play mode
```js
el.timeline()
.loop(times, swing, waits)
```
# Advanced Animations
The user can create their own runners and then attach it to the timeline
themselves if they like.
```js
// They declare their animation
let rotation = () => new SVG.Runner().rotate(500)
// They attach an element, and schedule the runner
let leftAnimation = rotation().element(leftSquare).reverse()
// They might want to animate another
let rightAnimation = rotation().element(rightSquare)
// They can schedule these two runners to a master element
timelineElement.timeline()
.schedule(leftAnimation, 300, 'absolute')
.schedule(rightAnimation, 500, 'now')
.schedule(rightAnimation, 300, 'end')
// Or they can schedule it to a timeline as well
let timeline = new SVG.Timeline()
.schedule(leftAnimation, 300, 'absolute')
.schedule(rightAnimation, 500, 'now')
```
# Modifying Controller Parameters
Some user might want to change the speed of a controller, or how the controller
works in the middle of an animation. For example, they might do:
```js
var pid = PID(30, 20, 40)
let animation = el.animate(pid).move(.., ..)
// Some time later, the user slides a slider, and they can do:
slider1.onSlide( v => pid.p(v) )
```
# Bidirectional Scheduling **(TODO)**
We would like to schedule a runner to a timeline, or to do the opposite
```js
// If we have a runner and a timeline
let timeline = new Timeline()...
let runner = new Runner()...
// Since the user can schedule a runner onto a timeline
timeline.schedule(runner, ...rest)
// It should be possible to do the opposite
runner.schedule(timeline, ...rest)
// It could be Implemented like this
runner.schedule = (t, duration, delay, now) {
this._timeline.remove(this) // Should work even if its not scheduled
t.schedule(this, duration, delay, now)
return this
}
// The benefit would be that they could call animate afterwards: eg:
runner.schedule(timeline, ...rest)
.animate()...
```
# Binding Events
The user might want to react to some events that the runner might emit. We will
emit the following events from the runner:
- start - when a runner first initialises
- finish - when a runner finishes
- step - on every step
Maybe they also want to react to timeline events as well
|