etro 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/CONTRIBUTING.md +23 -20
- package/README.md +3 -2
- package/dist/custom-array.d.ts +10 -0
- package/dist/effect/base.d.ts +10 -1
- package/dist/effect/shader.d.ts +11 -1
- package/dist/effect/stack.d.ts +6 -2
- package/dist/etro-cjs.js +1156 -575
- package/dist/etro-iife.js +1156 -575
- package/dist/event.d.ts +10 -5
- package/dist/layer/audio-source.d.ts +9 -4
- package/dist/layer/audio.d.ts +15 -2
- package/dist/layer/base.d.ts +49 -3
- package/dist/layer/image.d.ts +15 -1
- package/dist/layer/text.d.ts +3 -0
- package/dist/layer/video.d.ts +13 -1
- package/dist/layer/visual.d.ts +6 -2
- package/dist/movie/effects.d.ts +6 -0
- package/dist/movie/index.d.ts +1 -0
- package/dist/movie/layers.d.ts +6 -0
- package/dist/movie/movie.d.ts +260 -0
- package/dist/object.d.ts +9 -6
- package/dist/util.d.ts +4 -10
- package/eslint.conf.js +2 -2
- package/karma.conf.js +4 -7
- package/package.json +8 -7
- package/src/custom-array.ts +43 -0
- package/src/effect/base.ts +23 -22
- package/src/effect/gaussian-blur.ts +11 -6
- package/src/effect/pixelate.ts +3 -3
- package/src/effect/shader.ts +33 -27
- package/src/effect/stack.ts +43 -30
- package/src/effect/transform.ts +14 -7
- package/src/event.ts +111 -21
- package/src/layer/audio-source.ts +60 -20
- package/src/layer/audio.ts +25 -3
- package/src/layer/base.ts +79 -26
- package/src/layer/image.ts +26 -2
- package/src/layer/text.ts +7 -0
- package/src/layer/video.ts +31 -4
- package/src/layer/visual-source.ts +43 -1
- package/src/layer/visual.ts +50 -28
- package/src/movie/effects.ts +26 -0
- package/src/movie/index.ts +1 -0
- package/src/movie/layers.ts +26 -0
- package/src/movie/movie.ts +855 -0
- package/src/object.ts +9 -6
- package/src/util.ts +68 -89
- package/dist/movie.d.ts +0 -201
- package/src/movie.ts +0 -744
- /package/scripts/{gen-effect-samples.html → effect/gen-effect-samples.html} +0 -0
- /package/scripts/{save-effect-samples.js → effect/save-effect-samples.js} +0 -0
package/src/object.ts
CHANGED
|
@@ -2,17 +2,20 @@ import { Movie } from './movie'
|
|
|
2
2
|
|
|
3
3
|
/** A movie, layer or effect */
|
|
4
4
|
export default interface EtroObject {
|
|
5
|
+
currentTime: number
|
|
5
6
|
/** Used in etro internals */
|
|
6
7
|
type: string
|
|
7
|
-
/**
|
|
8
|
-
* Which properties to not watch for changes, for `Movie#autoRefresh`
|
|
9
|
-
*
|
|
10
|
-
* @deprecated `Movie#autoRefresh` is deprecated
|
|
11
|
-
*/
|
|
12
|
-
publicExcludes: string[]
|
|
13
8
|
/** Map of property name to function to run on result of `val` */
|
|
14
9
|
propertyFilters: Record<string, <T>(value: T) => T>
|
|
10
|
+
/**
|
|
11
|
+
* `true` if this object is ready to be played/rendered/applied, `false`
|
|
12
|
+
* otherwise
|
|
13
|
+
*/
|
|
14
|
+
ready: boolean
|
|
15
15
|
movie: Movie
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
19
|
+
*/
|
|
17
20
|
getDefaultOptions(): object // eslint-disable-line @typescript-eslint/ban-types
|
|
18
21
|
}
|
package/src/util.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import EtroObject from './object'
|
|
6
|
-
import { publish } from './event'
|
|
7
6
|
import { Movie } from './movie'
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -15,8 +14,9 @@ import { Movie } from './movie'
|
|
|
15
14
|
function getPropertyDescriptor (obj: unknown, name: string | number | symbol): PropertyDescriptor {
|
|
16
15
|
do {
|
|
17
16
|
const propDesc = Object.getOwnPropertyDescriptor(obj, name)
|
|
18
|
-
if (propDesc)
|
|
17
|
+
if (propDesc) {
|
|
19
18
|
return propDesc
|
|
19
|
+
}
|
|
20
20
|
|
|
21
21
|
obj = Object.getPrototypeOf(obj)
|
|
22
22
|
} while (obj)
|
|
@@ -27,6 +27,10 @@ function getPropertyDescriptor (obj: unknown, name: string | number | symbol): P
|
|
|
27
27
|
* Merges `options` with `defaultOptions`, and then copies the properties with
|
|
28
28
|
* the keys in `defaultOptions` from the merged object to `destObj`.
|
|
29
29
|
*
|
|
30
|
+
* @deprecated Each option should be copied individually, and the default value
|
|
31
|
+
* should be set in the constructor. See
|
|
32
|
+
* {@link https://github.com/etro-js/etro/issues/131} for more info.
|
|
33
|
+
*
|
|
30
34
|
* @return
|
|
31
35
|
*/
|
|
32
36
|
// TODO: Make methods like getDefaultOptions private
|
|
@@ -34,10 +38,12 @@ export function applyOptions (options: object, destObj: EtroObject): void { // e
|
|
|
34
38
|
const defaultOptions = destObj.getDefaultOptions()
|
|
35
39
|
|
|
36
40
|
// Validate; make sure `keys` doesn't have any extraneous items
|
|
37
|
-
for (const option in options)
|
|
41
|
+
for (const option in options) {
|
|
38
42
|
// eslint-disable-next-line no-prototype-builtins
|
|
39
|
-
if (!defaultOptions.hasOwnProperty(option))
|
|
43
|
+
if (!defaultOptions.hasOwnProperty(option)) {
|
|
40
44
|
throw new Error("Invalid option: '" + option + "'")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
41
47
|
|
|
42
48
|
// Merge options and defaultOptions
|
|
43
49
|
options = { ...defaultOptions, ...options }
|
|
@@ -46,8 +52,9 @@ export function applyOptions (options: object, destObj: EtroObject): void { // e
|
|
|
46
52
|
for (const option in options) {
|
|
47
53
|
const propDesc = getPropertyDescriptor(destObj, option)
|
|
48
54
|
// Update the property as long as the property has not been set (unless if it has a setter)
|
|
49
|
-
if (!propDesc || propDesc.set)
|
|
55
|
+
if (!propDesc || propDesc.set) {
|
|
50
56
|
destObj[option] = options[option]
|
|
57
|
+
}
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
|
|
@@ -55,14 +62,16 @@ export function applyOptions (options: object, destObj: EtroObject): void { // e
|
|
|
55
62
|
const valCache = new WeakMap()
|
|
56
63
|
function cacheValue (element: EtroObject, path: string, value: unknown) {
|
|
57
64
|
// Initiate movie cache
|
|
58
|
-
if (!valCache.has(element.movie))
|
|
65
|
+
if (!valCache.has(element.movie)) {
|
|
59
66
|
valCache.set(element.movie, new WeakMap())
|
|
67
|
+
}
|
|
60
68
|
|
|
61
69
|
const movieCache = valCache.get(element.movie)
|
|
62
70
|
|
|
63
|
-
//
|
|
64
|
-
if (!movieCache.has(element))
|
|
71
|
+
// Initiate element cache
|
|
72
|
+
if (!movieCache.has(element)) {
|
|
65
73
|
movieCache.set(element, {})
|
|
74
|
+
}
|
|
66
75
|
|
|
67
76
|
const elementCache = movieCache.get(element)
|
|
68
77
|
|
|
@@ -107,15 +116,18 @@ export class KeyFrame<T> {
|
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
evaluate (time: number): T {
|
|
110
|
-
if (this.value.length === 0)
|
|
119
|
+
if (this.value.length === 0) {
|
|
111
120
|
throw new Error('Empty keyframe')
|
|
121
|
+
}
|
|
112
122
|
|
|
113
|
-
if (time === undefined)
|
|
123
|
+
if (time === undefined) {
|
|
114
124
|
throw new Error('|time| is undefined or null')
|
|
125
|
+
}
|
|
115
126
|
|
|
116
127
|
const firstTime: number = this.value[0][0] as number
|
|
117
|
-
if (time < firstTime)
|
|
128
|
+
if (time < firstTime) {
|
|
118
129
|
throw new Error('No keyframe point before |time|')
|
|
130
|
+
}
|
|
119
131
|
|
|
120
132
|
// I think reduce are slow to do per-frame (or more)?
|
|
121
133
|
for (let i = 0; i < this.value.length; i++) {
|
|
@@ -126,7 +138,7 @@ export class KeyFrame<T> {
|
|
|
126
138
|
if (i + 1 < this.value.length) {
|
|
127
139
|
const endTime = this.value[i + 1][0] as number
|
|
128
140
|
const endValue = this.value[i + 1][1] as T
|
|
129
|
-
if (startTime <= time && time < endTime)
|
|
141
|
+
if (startTime <= time && time < endTime) {
|
|
130
142
|
// No need for endValue if it is flat interpolation
|
|
131
143
|
// TODO: support custom interpolation for 'other' types?
|
|
132
144
|
if (!(typeof startValue === 'number' || typeof endValue === 'object')) {
|
|
@@ -142,6 +154,7 @@ export class KeyFrame<T> {
|
|
|
142
154
|
percentProgress, this.interpolationKeys
|
|
143
155
|
) as unknown as T
|
|
144
156
|
}
|
|
157
|
+
}
|
|
145
158
|
} else {
|
|
146
159
|
// Repeat last value forever
|
|
147
160
|
return startValue
|
|
@@ -169,26 +182,29 @@ export type Dynamic<T> = T | KeyFrame<T> | ((element: EtroObject, time: number)
|
|
|
169
182
|
// TODO: Is this function efficient?
|
|
170
183
|
// TODO: Update doc @params to allow for keyframes
|
|
171
184
|
export function val (element: EtroObject, path: string, time: number): any { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
172
|
-
if (hasCachedValue(element, path))
|
|
185
|
+
if (hasCachedValue(element, path)) {
|
|
173
186
|
return getCachedValue(element, path)
|
|
187
|
+
}
|
|
174
188
|
|
|
175
189
|
// Get property of element at path
|
|
176
190
|
const pathParts = path.split('.')
|
|
177
191
|
let property = element[pathParts.shift()]
|
|
178
|
-
while (pathParts.length > 0)
|
|
192
|
+
while (pathParts.length > 0) {
|
|
179
193
|
property = property[pathParts.shift()]
|
|
194
|
+
}
|
|
180
195
|
|
|
181
196
|
// Property filter function
|
|
182
197
|
const process = element.propertyFilters[path]
|
|
183
198
|
|
|
184
199
|
let value
|
|
185
|
-
if (property instanceof KeyFrame)
|
|
200
|
+
if (property instanceof KeyFrame) {
|
|
186
201
|
value = property.evaluate(time)
|
|
187
|
-
else if (typeof property === 'function')
|
|
188
|
-
value = property(element, time)
|
|
189
|
-
else
|
|
202
|
+
} else if (typeof property === 'function') {
|
|
203
|
+
value = property(element, time)
|
|
204
|
+
} else {
|
|
190
205
|
// Simple value
|
|
191
206
|
value = property
|
|
207
|
+
}
|
|
192
208
|
|
|
193
209
|
return cacheValue(element, path, process ? process.call(element, value) : value)
|
|
194
210
|
}
|
|
@@ -202,17 +218,20 @@ export function val (element: EtroObject, path: string, time: number): any { //
|
|
|
202
218
|
} */
|
|
203
219
|
|
|
204
220
|
export function linearInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
|
|
205
|
-
if (typeof x1 !== typeof x2)
|
|
221
|
+
if (typeof x1 !== typeof x2) {
|
|
206
222
|
throw new Error('Type mismatch')
|
|
223
|
+
}
|
|
207
224
|
|
|
208
|
-
if (typeof x1 !== 'number' && typeof x1 !== 'object')
|
|
225
|
+
if (typeof x1 !== 'number' && typeof x1 !== 'object') {
|
|
209
226
|
// Flat interpolation (floor)
|
|
210
227
|
return x1
|
|
228
|
+
}
|
|
211
229
|
|
|
212
230
|
if (typeof x1 === 'object') { // to work with objects (including arrays)
|
|
213
231
|
// TODO: make this code DRY
|
|
214
|
-
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
232
|
+
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
|
|
215
233
|
throw new Error('Prototype mismatch')
|
|
234
|
+
}
|
|
216
235
|
|
|
217
236
|
// Preserve prototype of objects
|
|
218
237
|
const int = Object.create(Object.getPrototypeOf(x1))
|
|
@@ -221,8 +240,9 @@ export function linearInterp (x1: number | object, x2: number | object, t: numbe
|
|
|
221
240
|
for (let i = 0; i < keys.length; i++) {
|
|
222
241
|
const key = keys[i]
|
|
223
242
|
// eslint-disable-next-line no-prototype-builtins
|
|
224
|
-
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
243
|
+
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
|
|
225
244
|
continue
|
|
245
|
+
}
|
|
226
246
|
|
|
227
247
|
int[key] = linearInterp(x1[key], x2[key], t)
|
|
228
248
|
}
|
|
@@ -232,16 +252,19 @@ export function linearInterp (x1: number | object, x2: number | object, t: numbe
|
|
|
232
252
|
}
|
|
233
253
|
|
|
234
254
|
export function cosineInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
|
|
235
|
-
if (typeof x1 !== typeof x2)
|
|
255
|
+
if (typeof x1 !== typeof x2) {
|
|
236
256
|
throw new Error('Type mismatch')
|
|
257
|
+
}
|
|
237
258
|
|
|
238
|
-
if (typeof x1 !== 'number' && typeof x1 !== 'object')
|
|
259
|
+
if (typeof x1 !== 'number' && typeof x1 !== 'object') {
|
|
239
260
|
// Flat interpolation (floor)
|
|
240
261
|
return x1
|
|
262
|
+
}
|
|
241
263
|
|
|
242
264
|
if (typeof x1 === 'object' && typeof x2 === 'object') { // to work with objects (including arrays)
|
|
243
|
-
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
265
|
+
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
|
|
244
266
|
throw new Error('Prototype mismatch')
|
|
267
|
+
}
|
|
245
268
|
|
|
246
269
|
// Preserve prototype of objects
|
|
247
270
|
const int = Object.create(Object.getPrototypeOf(x1))
|
|
@@ -250,8 +273,9 @@ export function cosineInterp (x1: number | object, x2: number | object, t: numbe
|
|
|
250
273
|
for (let i = 0; i < keys.length; i++) {
|
|
251
274
|
const key = keys[i]
|
|
252
275
|
// eslint-disable-next-line no-prototype-builtins
|
|
253
|
-
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
276
|
+
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
|
|
254
277
|
continue
|
|
278
|
+
}
|
|
255
279
|
|
|
256
280
|
int[key] = cosineInterp(x1[key], x2[key], t)
|
|
257
281
|
}
|
|
@@ -345,12 +369,22 @@ export class Font {
|
|
|
345
369
|
*/
|
|
346
370
|
toString (): string {
|
|
347
371
|
let s = ''
|
|
348
|
-
if (this.style !== 'normal')
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (this.
|
|
372
|
+
if (this.style !== 'normal') {
|
|
373
|
+
s += this.style + ' '
|
|
374
|
+
}
|
|
375
|
+
if (this.variant !== 'normal') {
|
|
376
|
+
s += this.variant + ' '
|
|
377
|
+
}
|
|
378
|
+
if (this.weight !== 'normal') {
|
|
379
|
+
s += this.weight + ' '
|
|
380
|
+
}
|
|
381
|
+
if (this.stretch !== 'normal') {
|
|
382
|
+
s += this.stretch + ' '
|
|
383
|
+
}
|
|
352
384
|
s += `${this.size}${this.sizeUnit} `
|
|
353
|
-
if (this.lineHeight !== 'normal')
|
|
385
|
+
if (this.lineHeight !== 'normal') {
|
|
386
|
+
s += this.lineHeight + ' '
|
|
387
|
+
}
|
|
354
388
|
s += this.family
|
|
355
389
|
|
|
356
390
|
return s
|
|
@@ -403,66 +437,11 @@ export function mapPixels (
|
|
|
403
437
|
width = width || canvas.width
|
|
404
438
|
height = height || canvas.height
|
|
405
439
|
const frame = ctx.getImageData(x, y, width, height)
|
|
406
|
-
for (let i = 0, l = frame.data.length; i < l; i += 4)
|
|
440
|
+
for (let i = 0, l = frame.data.length; i < l; i += 4) {
|
|
407
441
|
mapper(frame.data, i)
|
|
408
|
-
|
|
409
|
-
if (flush)
|
|
410
|
-
ctx.putImageData(frame, x, y)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* <p>Emits "change" event when public properties updated, recursively.
|
|
415
|
-
* <p>Must be called before any watchable properties are set, and only once in
|
|
416
|
-
* the prototype chain.
|
|
417
|
-
*
|
|
418
|
-
* @deprecated Will be removed in the future (see issue #130)
|
|
419
|
-
*
|
|
420
|
-
* @param target - object to watch
|
|
421
|
-
*/
|
|
422
|
-
export function watchPublic (target: EtroObject): EtroObject {
|
|
423
|
-
const getPath = (receiver, prop) =>
|
|
424
|
-
(receiver === proxy ? '' : (paths.get(receiver) + '.')) + prop
|
|
425
|
-
const callback = function (prop, val, receiver) {
|
|
426
|
-
// Public API property updated, emit 'modify' event.
|
|
427
|
-
publish(proxy, `${target.type}.change.modify`, { property: getPath(receiver, prop), newValue: val })
|
|
428
442
|
}
|
|
429
|
-
const canWatch = (receiver, prop) => !prop.startsWith('_') &&
|
|
430
|
-
(receiver.publicExcludes === undefined || !receiver.publicExcludes.includes(prop))
|
|
431
|
-
|
|
432
|
-
// The path to each child property (each is a unique proxy)
|
|
433
|
-
const paths = new WeakMap()
|
|
434
|
-
|
|
435
|
-
const handler = {
|
|
436
|
-
set (obj, prop, val, receiver) {
|
|
437
|
-
// Recurse
|
|
438
|
-
if (typeof val === 'object' && val !== null && !paths.has(val) && canWatch(receiver, prop)) {
|
|
439
|
-
val = new Proxy(val, handler)
|
|
440
|
-
paths.set(val, getPath(receiver, prop))
|
|
441
|
-
}
|
|
442
443
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
let objProto = obj
|
|
446
|
-
while ((objProto = Object.getPrototypeOf(objProto))) {
|
|
447
|
-
const propDesc = Object.getOwnPropertyDescriptor(objProto, prop)
|
|
448
|
-
if (propDesc && propDesc.set) {
|
|
449
|
-
// Call setter, supplying proxy as this (fixes event bugs)
|
|
450
|
-
propDesc.set.call(receiver, val)
|
|
451
|
-
break
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (!objProto)
|
|
455
|
-
// Couldn't find setter; set value on instance
|
|
456
|
-
obj[prop] = val
|
|
457
|
-
|
|
458
|
-
// Check if the property isn't blacklisted in publicExcludes.
|
|
459
|
-
if (canWatch(receiver, prop))
|
|
460
|
-
callback(prop, val, receiver)
|
|
461
|
-
|
|
462
|
-
return true
|
|
463
|
-
}
|
|
444
|
+
if (flush) {
|
|
445
|
+
ctx.putImageData(frame, x, y)
|
|
464
446
|
}
|
|
465
|
-
|
|
466
|
-
const proxy = new Proxy(target, handler)
|
|
467
|
-
return proxy
|
|
468
447
|
}
|
package/dist/movie.d.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module movie
|
|
3
|
-
*/
|
|
4
|
-
import { Dynamic, Color } from './util';
|
|
5
|
-
import { Base as BaseLayer } from './layer/index';
|
|
6
|
-
import { Base as BaseEffect } from './effect/index';
|
|
7
|
-
declare global {
|
|
8
|
-
interface Window {
|
|
9
|
-
webkitAudioContext: typeof AudioContext;
|
|
10
|
-
}
|
|
11
|
-
interface HTMLCanvasElement {
|
|
12
|
-
captureStream(frameRate?: number): MediaStream;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export declare class MovieOptions {
|
|
16
|
-
/** The html canvas element to use for playback */
|
|
17
|
-
canvas: HTMLCanvasElement;
|
|
18
|
-
/** The audio context to use for playback, defaults to a new audio context */
|
|
19
|
-
actx?: AudioContext;
|
|
20
|
-
/** @deprecated Use <code>actx</code> instead */
|
|
21
|
-
audioContext?: AudioContext;
|
|
22
|
-
/** The background color of the movie as a cSS string */
|
|
23
|
-
background?: Dynamic<Color>;
|
|
24
|
-
repeat?: boolean;
|
|
25
|
-
/** Call `refresh` when the user changes a property on the movie or any of its layers or effects */
|
|
26
|
-
autoRefresh?: boolean;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The movie contains everything included in the render.
|
|
30
|
-
*
|
|
31
|
-
* Implements a pub/sub system.
|
|
32
|
-
*/
|
|
33
|
-
export declare class Movie {
|
|
34
|
-
type: string;
|
|
35
|
-
/**
|
|
36
|
-
* @deprecated Auto-refresh will be removed in the future (see issue #130).
|
|
37
|
-
*/
|
|
38
|
-
publicExcludes: string[];
|
|
39
|
-
propertyFilters: Record<string, <T>(value: T) => T>;
|
|
40
|
-
repeat: boolean;
|
|
41
|
-
/**
|
|
42
|
-
* Call `refresh` when the user changes a property on the movie or any of its
|
|
43
|
-
* layers or effects
|
|
44
|
-
*
|
|
45
|
-
* @deprecated Auto-refresh will be removed in the future. If you want to
|
|
46
|
-
* refresh the canvas, call `refresh`. See issue #130.
|
|
47
|
-
*/
|
|
48
|
-
autoRefresh: boolean;
|
|
49
|
-
/** The background color of the movie as a cSS string */
|
|
50
|
-
background: Dynamic<Color>;
|
|
51
|
-
/** The audio context to which audio output is sent during playback */
|
|
52
|
-
readonly actx: AudioContext;
|
|
53
|
-
readonly effects: BaseEffect[];
|
|
54
|
-
readonly layers: BaseLayer[];
|
|
55
|
-
private _canvas;
|
|
56
|
-
private _cctx;
|
|
57
|
-
private _effectsBack;
|
|
58
|
-
private _layersBack;
|
|
59
|
-
private _currentTime;
|
|
60
|
-
private _paused;
|
|
61
|
-
private _ended;
|
|
62
|
-
private _renderingFrame;
|
|
63
|
-
private _recordEndTime;
|
|
64
|
-
private _mediaRecorder;
|
|
65
|
-
private _lastPlayed;
|
|
66
|
-
private _lastPlayedOffset;
|
|
67
|
-
/**
|
|
68
|
-
* Creates a new movie.
|
|
69
|
-
*/
|
|
70
|
-
constructor(options: MovieOptions);
|
|
71
|
-
/**
|
|
72
|
-
* Plays the movie
|
|
73
|
-
* @return fulfilled when the movie is done playing, never fails
|
|
74
|
-
*/
|
|
75
|
-
play(): Promise<void>;
|
|
76
|
-
/**
|
|
77
|
-
* Plays the movie in the background and records it
|
|
78
|
-
*
|
|
79
|
-
* @param options
|
|
80
|
-
* @param frameRate
|
|
81
|
-
* @param [options.video=true] - whether to include video in recording
|
|
82
|
-
* @param [options.audio=true] - whether to include audio in recording
|
|
83
|
-
* @param [options.mediaRecorderOptions=undefined] - options to pass to the <code>MediaRecorder</code>
|
|
84
|
-
* @param [options.type='video/webm'] - MIME type for exported video
|
|
85
|
-
* constructor
|
|
86
|
-
* @return resolves when done recording, rejects when internal media recorder errors
|
|
87
|
-
*/
|
|
88
|
-
record(options: {
|
|
89
|
-
frameRate: number;
|
|
90
|
-
duration?: number;
|
|
91
|
-
type?: string;
|
|
92
|
-
video?: boolean;
|
|
93
|
-
audio?: boolean;
|
|
94
|
-
mediaRecorderOptions?: Record<string, unknown>;
|
|
95
|
-
}): Promise<Blob>;
|
|
96
|
-
/**
|
|
97
|
-
* Stops the movie, without reseting the playback position
|
|
98
|
-
* @return the movie (for chaining)
|
|
99
|
-
*/
|
|
100
|
-
pause(): Movie;
|
|
101
|
-
/**
|
|
102
|
-
* Stops playback and resets the playback position
|
|
103
|
-
* @return the movie (for chaining)
|
|
104
|
-
*/
|
|
105
|
-
stop(): Movie;
|
|
106
|
-
/**
|
|
107
|
-
* @param [timestamp=performance.now()]
|
|
108
|
-
* @param [done=undefined] - called when done playing or when the current frame is loaded
|
|
109
|
-
* @private
|
|
110
|
-
*/
|
|
111
|
-
private _render;
|
|
112
|
-
private _updateCurrentTime;
|
|
113
|
-
private _renderBackground;
|
|
114
|
-
/**
|
|
115
|
-
* @return whether or not video frames are loaded
|
|
116
|
-
* @param [timestamp=performance.now()]
|
|
117
|
-
* @private
|
|
118
|
-
*/
|
|
119
|
-
private _renderLayers;
|
|
120
|
-
private _applyEffects;
|
|
121
|
-
/**
|
|
122
|
-
* Refreshes the screen (only use this if auto-refresh is disabled)
|
|
123
|
-
* @return - resolves when the frame is loaded
|
|
124
|
-
*/
|
|
125
|
-
refresh(): Promise<null>;
|
|
126
|
-
/**
|
|
127
|
-
* Convienence method
|
|
128
|
-
*/
|
|
129
|
-
private _publishToLayers;
|
|
130
|
-
/**
|
|
131
|
-
* If the movie is playing, recording or refreshing
|
|
132
|
-
*/
|
|
133
|
-
get rendering(): boolean;
|
|
134
|
-
/**
|
|
135
|
-
* If the movie is refreshing current frame
|
|
136
|
-
*/
|
|
137
|
-
get renderingFrame(): boolean;
|
|
138
|
-
/**
|
|
139
|
-
* If the movie is recording
|
|
140
|
-
*/
|
|
141
|
-
get recording(): boolean;
|
|
142
|
-
/**
|
|
143
|
-
* The combined duration of all layers
|
|
144
|
-
*/
|
|
145
|
-
get duration(): number;
|
|
146
|
-
/**
|
|
147
|
-
* Convienence method for <code>layers.push()</code>
|
|
148
|
-
* @param layer
|
|
149
|
-
* @return the movie
|
|
150
|
-
*/
|
|
151
|
-
addLayer(layer: BaseLayer): Movie;
|
|
152
|
-
/**
|
|
153
|
-
* Convienence method for <code>effects.push()</code>
|
|
154
|
-
* @param effect
|
|
155
|
-
* @return the movie
|
|
156
|
-
*/
|
|
157
|
-
addEffect(effect: BaseEffect): Movie;
|
|
158
|
-
/**
|
|
159
|
-
*/
|
|
160
|
-
get paused(): boolean;
|
|
161
|
-
/**
|
|
162
|
-
* If the playback position is at the end of the movie
|
|
163
|
-
*/
|
|
164
|
-
get ended(): boolean;
|
|
165
|
-
/**
|
|
166
|
-
* The current playback position
|
|
167
|
-
*/
|
|
168
|
-
get currentTime(): number;
|
|
169
|
-
set currentTime(time: number);
|
|
170
|
-
/**
|
|
171
|
-
* Sets the current playback position. This is a more powerful version of
|
|
172
|
-
* `set currentTime`.
|
|
173
|
-
*
|
|
174
|
-
* @param time - the new cursor's time value in seconds
|
|
175
|
-
* @param [refresh=true] - whether to render a single frame
|
|
176
|
-
* @return resolves when the current frame is rendered if
|
|
177
|
-
* <code>refresh</code> is true, otherwise resolves immediately
|
|
178
|
-
*
|
|
179
|
-
*/
|
|
180
|
-
setCurrentTime(time: number, refresh?: boolean): Promise<void>;
|
|
181
|
-
/**
|
|
182
|
-
* The rendering canvas
|
|
183
|
-
*/
|
|
184
|
-
get canvas(): HTMLCanvasElement;
|
|
185
|
-
/**
|
|
186
|
-
* The rendering canvas's context
|
|
187
|
-
*/
|
|
188
|
-
get cctx(): CanvasRenderingContext2D;
|
|
189
|
-
/**
|
|
190
|
-
* The width of the rendering canvas
|
|
191
|
-
*/
|
|
192
|
-
get width(): number;
|
|
193
|
-
set width(width: number);
|
|
194
|
-
/**
|
|
195
|
-
* The height of the rendering canvas
|
|
196
|
-
*/
|
|
197
|
-
get height(): number;
|
|
198
|
-
set height(height: number);
|
|
199
|
-
get movie(): Movie;
|
|
200
|
-
getDefaultOptions(): MovieOptions;
|
|
201
|
-
}
|