etro 0.8.0 → 0.8.3
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/.github/workflows/nodejs.yml +3 -1
- package/.github/workflows/shipjs-trigger.yml +29 -0
- package/CHANGELOG.md +36 -13
- package/CODE_OF_CONDUCT.md +5 -5
- package/CONTRIBUTING.md +22 -72
- package/README.md +2 -2
- package/dist/effect/base.d.ts +14 -1
- package/dist/etro-cjs.js +189 -230
- package/dist/etro-iife.js +189 -230
- package/dist/layer/base.d.ts +13 -0
- package/eslint.conf.js +2 -1
- package/eslint.test-conf.js +1 -0
- package/examples/application/readme-screenshot.html +4 -8
- package/examples/application/video-player.html +3 -4
- package/examples/introduction/effects.html +23 -4
- package/karma.conf.js +4 -2
- package/package.json +8 -4
- package/scripts/gen-effect-samples.html +0 -3
- package/ship.config.js +80 -0
- package/src/effect/base.ts +29 -10
- package/src/effect/gaussian-blur.ts +10 -10
- package/src/effect/pixelate.ts +1 -2
- package/src/effect/shader.ts +18 -22
- package/src/effect/stack.ts +8 -4
- package/src/effect/transform.ts +13 -14
- package/src/event.ts +8 -14
- package/src/layer/audio-source.ts +16 -14
- package/src/layer/audio.ts +1 -2
- package/src/layer/base.ts +26 -7
- package/src/layer/visual.ts +11 -6
- package/src/movie.ts +70 -64
- package/src/util.ts +50 -57
- package/docs/effect.js.html +0 -1215
- package/docs/event.js.html +0 -145
- package/docs/index.html +0 -81
- package/docs/index.js.html +0 -92
- package/docs/layer.js.html +0 -888
- package/docs/module-effect-GaussianBlurComponent.html +0 -345
- package/docs/module-effect.Brightness.html +0 -339
- package/docs/module-effect.Channels.html +0 -319
- package/docs/module-effect.ChromaKey.html +0 -611
- package/docs/module-effect.Contrast.html +0 -339
- package/docs/module-effect.EllipticalMask.html +0 -200
- package/docs/module-effect.GaussianBlur.html +0 -202
- package/docs/module-effect.GaussianBlurHorizontal.html +0 -242
- package/docs/module-effect.GaussianBlurVertical.html +0 -242
- package/docs/module-effect.Pixelate.html +0 -330
- package/docs/module-effect.Shader.html +0 -1227
- package/docs/module-effect.Stack.html +0 -406
- package/docs/module-effect.Transform.Matrix.html +0 -193
- package/docs/module-effect.Transform.html +0 -1174
- package/docs/module-effect.html +0 -148
- package/docs/module-event.html +0 -473
- package/docs/module-index.html +0 -186
- package/docs/module-layer-Media.html +0 -1116
- package/docs/module-layer-MediaMixin.html +0 -164
- package/docs/module-layer.Audio.html +0 -1188
- package/docs/module-layer.Base.html +0 -629
- package/docs/module-layer.Image.html +0 -1421
- package/docs/module-layer.Text.html +0 -1731
- package/docs/module-layer.Video.html +0 -1938
- package/docs/module-layer.Visual.html +0 -1698
- package/docs/module-layer.html +0 -137
- package/docs/module-movie.html +0 -3118
- package/docs/module-util.Color.html +0 -702
- package/docs/module-util.Font.html +0 -395
- package/docs/module-util.html +0 -845
- package/docs/movie.js.html +0 -689
- package/docs/scripts/collapse.js +0 -20
- package/docs/scripts/linenumber.js +0 -25
- package/docs/scripts/nav.js +0 -12
- package/docs/scripts/polyfill.js +0 -4
- package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
- package/docs/scripts/prettify/lang-css.js +0 -2
- package/docs/scripts/prettify/prettify.js +0 -28
- package/docs/scripts/search.js +0 -83
- package/docs/styles/jsdoc.css +0 -671
- package/docs/styles/prettify.css +0 -79
- package/docs/util.js.html +0 -503
- package/spec/assets/effect/gaussian-blur-horizontal.png +0 -0
- package/spec/assets/effect/gaussian-blur-vertical.png +0 -0
- package/spec/assets/effect/grayscale.png +0 -0
- package/spec/assets/effect/original.png +0 -0
- package/spec/assets/effect/pixelate.png +0 -0
- package/spec/assets/effect/transform/multiply.png +0 -0
- package/spec/assets/effect/transform/rotate.png +0 -0
- package/spec/assets/effect/transform/scale-fraction.png +0 -0
- package/spec/assets/effect/transform/scale.png +0 -0
- package/spec/assets/effect/transform/translate-fraction.png +0 -0
- package/spec/assets/effect/transform/translate.png +0 -0
- package/spec/assets/layer/audio.wav +0 -0
- package/spec/assets/layer/image.jpg +0 -0
- package/spec/effect.spec.js +0 -421
- package/spec/event.spec.js +0 -39
- package/spec/layer.spec.js +0 -307
- package/spec/movie.spec.js +0 -346
- package/spec/util.spec.js +0 -294
package/src/util.ts
CHANGED
|
@@ -15,9 +15,9 @@ import { Movie } from './movie'
|
|
|
15
15
|
function getPropertyDescriptor (obj: unknown, name: string | number | symbol): PropertyDescriptor {
|
|
16
16
|
do {
|
|
17
17
|
const propDesc = Object.getOwnPropertyDescriptor(obj, name)
|
|
18
|
-
if (propDesc)
|
|
18
|
+
if (propDesc)
|
|
19
19
|
return propDesc
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
obj = Object.getPrototypeOf(obj)
|
|
22
22
|
} while (obj)
|
|
23
23
|
return undefined
|
|
@@ -34,12 +34,10 @@ export function applyOptions (options: object, destObj: EtroObject): void { // e
|
|
|
34
34
|
const defaultOptions = destObj.getDefaultOptions()
|
|
35
35
|
|
|
36
36
|
// Validate; make sure `keys` doesn't have any extraneous items
|
|
37
|
-
for (const option in options)
|
|
37
|
+
for (const option in options)
|
|
38
38
|
// eslint-disable-next-line no-prototype-builtins
|
|
39
|
-
if (!defaultOptions.hasOwnProperty(option))
|
|
39
|
+
if (!defaultOptions.hasOwnProperty(option))
|
|
40
40
|
throw new Error("Invalid option: '" + option + "'")
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
41
|
|
|
44
42
|
// Merge options and defaultOptions
|
|
45
43
|
options = { ...defaultOptions, ...options }
|
|
@@ -48,9 +46,8 @@ export function applyOptions (options: object, destObj: EtroObject): void { // e
|
|
|
48
46
|
for (const option in options) {
|
|
49
47
|
const propDesc = getPropertyDescriptor(destObj, option)
|
|
50
48
|
// Update the property as long as the property has not been set (unless if it has a setter)
|
|
51
|
-
if (!propDesc || propDesc.set)
|
|
49
|
+
if (!propDesc || propDesc.set)
|
|
52
50
|
destObj[option] = options[option]
|
|
53
|
-
}
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
53
|
|
|
@@ -58,15 +55,15 @@ export function applyOptions (options: object, destObj: EtroObject): void { // e
|
|
|
58
55
|
const valCache = new WeakMap()
|
|
59
56
|
function cacheValue (element: EtroObject, path: string, value: unknown) {
|
|
60
57
|
// Initiate movie cache
|
|
61
|
-
if (!valCache.has(element.movie))
|
|
58
|
+
if (!valCache.has(element.movie))
|
|
62
59
|
valCache.set(element.movie, new WeakMap())
|
|
63
|
-
|
|
60
|
+
|
|
64
61
|
const movieCache = valCache.get(element.movie)
|
|
65
62
|
|
|
66
63
|
// Iniitate element cache
|
|
67
|
-
if (!movieCache.has(element))
|
|
64
|
+
if (!movieCache.has(element))
|
|
68
65
|
movieCache.set(element, {})
|
|
69
|
-
|
|
66
|
+
|
|
70
67
|
const elementCache = movieCache.get(element)
|
|
71
68
|
|
|
72
69
|
// Cache the value
|
|
@@ -110,16 +107,16 @@ export class KeyFrame<T> {
|
|
|
110
107
|
}
|
|
111
108
|
|
|
112
109
|
evaluate (time: number): T {
|
|
113
|
-
if (this.value.length === 0)
|
|
110
|
+
if (this.value.length === 0)
|
|
114
111
|
throw new Error('Empty keyframe')
|
|
115
|
-
|
|
116
|
-
if (time === undefined)
|
|
112
|
+
|
|
113
|
+
if (time === undefined)
|
|
117
114
|
throw new Error('|time| is undefined or null')
|
|
118
|
-
|
|
115
|
+
|
|
119
116
|
const firstTime: number = this.value[0][0] as number
|
|
120
|
-
if (time < firstTime)
|
|
117
|
+
if (time < firstTime)
|
|
121
118
|
throw new Error('No keyframe point before |time|')
|
|
122
|
-
|
|
119
|
+
|
|
123
120
|
// I think reduce are slow to do per-frame (or more)?
|
|
124
121
|
for (let i = 0; i < this.value.length; i++) {
|
|
125
122
|
const startTime = this.value[i][0] as number
|
|
@@ -129,7 +126,7 @@ export class KeyFrame<T> {
|
|
|
129
126
|
if (i + 1 < this.value.length) {
|
|
130
127
|
const endTime = this.value[i + 1][0] as number
|
|
131
128
|
const endValue = this.value[i + 1][1] as T
|
|
132
|
-
if (startTime <= time && time < endTime)
|
|
129
|
+
if (startTime <= time && time < endTime)
|
|
133
130
|
// No need for endValue if it is flat interpolation
|
|
134
131
|
// TODO: support custom interpolation for 'other' types?
|
|
135
132
|
if (!(typeof startValue === 'number' || typeof endValue === 'object')) {
|
|
@@ -145,7 +142,6 @@ export class KeyFrame<T> {
|
|
|
145
142
|
percentProgress, this.interpolationKeys
|
|
146
143
|
) as unknown as T
|
|
147
144
|
}
|
|
148
|
-
}
|
|
149
145
|
} else {
|
|
150
146
|
// Repeat last value forever
|
|
151
147
|
return startValue
|
|
@@ -173,28 +169,27 @@ export type Dynamic<T> = T | KeyFrame<T> | ((element: EtroObject, time: number)
|
|
|
173
169
|
// TODO: Is this function efficient?
|
|
174
170
|
// TODO: Update doc @params to allow for keyframes
|
|
175
171
|
export function val (element: EtroObject, path: string, time: number): any { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
176
|
-
if (hasCachedValue(element, path))
|
|
172
|
+
if (hasCachedValue(element, path))
|
|
177
173
|
return getCachedValue(element, path)
|
|
178
|
-
}
|
|
179
174
|
|
|
180
175
|
// Get property of element at path
|
|
181
176
|
const pathParts = path.split('.')
|
|
182
177
|
let property = element[pathParts.shift()]
|
|
183
|
-
while (pathParts.length > 0)
|
|
178
|
+
while (pathParts.length > 0)
|
|
184
179
|
property = property[pathParts.shift()]
|
|
185
|
-
|
|
180
|
+
|
|
186
181
|
// Property filter function
|
|
187
182
|
const process = element.propertyFilters[path]
|
|
188
183
|
|
|
189
184
|
let value
|
|
190
|
-
if (property instanceof KeyFrame)
|
|
185
|
+
if (property instanceof KeyFrame)
|
|
191
186
|
value = property.evaluate(time)
|
|
192
|
-
|
|
187
|
+
else if (typeof property === 'function')
|
|
193
188
|
value = property(element, time) // TODO? add more args
|
|
194
|
-
|
|
189
|
+
else
|
|
195
190
|
// Simple value
|
|
196
191
|
value = property
|
|
197
|
-
|
|
192
|
+
|
|
198
193
|
return cacheValue(element, path, process ? process.call(element, value) : value)
|
|
199
194
|
}
|
|
200
195
|
|
|
@@ -207,18 +202,18 @@ export function val (element: EtroObject, path: string, time: number): any { //
|
|
|
207
202
|
} */
|
|
208
203
|
|
|
209
204
|
export function linearInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
|
|
210
|
-
if (typeof x1 !== typeof x2)
|
|
205
|
+
if (typeof x1 !== typeof x2)
|
|
211
206
|
throw new Error('Type mismatch')
|
|
212
|
-
|
|
213
|
-
if (typeof x1 !== 'number' && typeof x1 !== 'object')
|
|
207
|
+
|
|
208
|
+
if (typeof x1 !== 'number' && typeof x1 !== 'object')
|
|
214
209
|
// Flat interpolation (floor)
|
|
215
210
|
return x1
|
|
216
|
-
|
|
211
|
+
|
|
217
212
|
if (typeof x1 === 'object') { // to work with objects (including arrays)
|
|
218
213
|
// TODO: make this code DRY
|
|
219
|
-
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
214
|
+
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
220
215
|
throw new Error('Prototype mismatch')
|
|
221
|
-
|
|
216
|
+
|
|
222
217
|
// Preserve prototype of objects
|
|
223
218
|
const int = Object.create(Object.getPrototypeOf(x1))
|
|
224
219
|
// Take the intersection of properties
|
|
@@ -226,9 +221,9 @@ export function linearInterp (x1: number | object, x2: number | object, t: numbe
|
|
|
226
221
|
for (let i = 0; i < keys.length; i++) {
|
|
227
222
|
const key = keys[i]
|
|
228
223
|
// eslint-disable-next-line no-prototype-builtins
|
|
229
|
-
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
224
|
+
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
230
225
|
continue
|
|
231
|
-
|
|
226
|
+
|
|
232
227
|
int[key] = linearInterp(x1[key], x2[key], t)
|
|
233
228
|
}
|
|
234
229
|
return int
|
|
@@ -237,17 +232,17 @@ export function linearInterp (x1: number | object, x2: number | object, t: numbe
|
|
|
237
232
|
}
|
|
238
233
|
|
|
239
234
|
export function cosineInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
|
|
240
|
-
if (typeof x1 !== typeof x2)
|
|
235
|
+
if (typeof x1 !== typeof x2)
|
|
241
236
|
throw new Error('Type mismatch')
|
|
242
|
-
|
|
243
|
-
if (typeof x1 !== 'number' && typeof x1 !== 'object')
|
|
237
|
+
|
|
238
|
+
if (typeof x1 !== 'number' && typeof x1 !== 'object')
|
|
244
239
|
// Flat interpolation (floor)
|
|
245
240
|
return x1
|
|
246
|
-
|
|
241
|
+
|
|
247
242
|
if (typeof x1 === 'object' && typeof x2 === 'object') { // to work with objects (including arrays)
|
|
248
|
-
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
243
|
+
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
249
244
|
throw new Error('Prototype mismatch')
|
|
250
|
-
|
|
245
|
+
|
|
251
246
|
// Preserve prototype of objects
|
|
252
247
|
const int = Object.create(Object.getPrototypeOf(x1))
|
|
253
248
|
// Take the intersection of properties
|
|
@@ -255,9 +250,9 @@ export function cosineInterp (x1: number | object, x2: number | object, t: numbe
|
|
|
255
250
|
for (let i = 0; i < keys.length; i++) {
|
|
256
251
|
const key = keys[i]
|
|
257
252
|
// eslint-disable-next-line no-prototype-builtins
|
|
258
|
-
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
253
|
+
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
259
254
|
continue
|
|
260
|
-
|
|
255
|
+
|
|
261
256
|
int[key] = cosineInterp(x1[key], x2[key], t)
|
|
262
257
|
}
|
|
263
258
|
return int
|
|
@@ -408,12 +403,11 @@ export function mapPixels (
|
|
|
408
403
|
width = width || canvas.width
|
|
409
404
|
height = height || canvas.height
|
|
410
405
|
const frame = ctx.getImageData(x, y, width, height)
|
|
411
|
-
for (let i = 0, l = frame.data.length; i < l; i += 4)
|
|
406
|
+
for (let i = 0, l = frame.data.length; i < l; i += 4)
|
|
412
407
|
mapper(frame.data, i)
|
|
413
|
-
|
|
414
|
-
if (flush)
|
|
408
|
+
|
|
409
|
+
if (flush)
|
|
415
410
|
ctx.putImageData(frame, x, y)
|
|
416
|
-
}
|
|
417
411
|
}
|
|
418
412
|
|
|
419
413
|
/**
|
|
@@ -430,7 +424,8 @@ export function watchPublic (target: EtroObject): EtroObject {
|
|
|
430
424
|
// Public API property updated, emit 'modify' event.
|
|
431
425
|
publish(proxy, `${target.type}.change.modify`, { property: getPath(receiver, prop), newValue: val })
|
|
432
426
|
}
|
|
433
|
-
const
|
|
427
|
+
const canWatch = (receiver, prop) => !prop.startsWith('_') &&
|
|
428
|
+
(receiver.publicExcludes === undefined || !receiver.publicExcludes.includes(prop))
|
|
434
429
|
|
|
435
430
|
// The path to each child property (each is a unique proxy)
|
|
436
431
|
const paths = new WeakMap()
|
|
@@ -438,12 +433,11 @@ export function watchPublic (target: EtroObject): EtroObject {
|
|
|
438
433
|
const handler = {
|
|
439
434
|
set (obj, prop, val, receiver) {
|
|
440
435
|
// Recurse
|
|
441
|
-
if (typeof val === 'object' && val !== null && !paths.has(val) &&
|
|
436
|
+
if (typeof val === 'object' && val !== null && !paths.has(val) && canWatch(receiver, prop)) {
|
|
442
437
|
val = new Proxy(val, handler)
|
|
443
438
|
paths.set(val, getPath(receiver, prop))
|
|
444
439
|
}
|
|
445
440
|
|
|
446
|
-
const was = prop in obj
|
|
447
441
|
// Set property or attribute
|
|
448
442
|
// Search prototype chain for the closest setter
|
|
449
443
|
let objProto = obj
|
|
@@ -455,15 +449,14 @@ export function watchPublic (target: EtroObject): EtroObject {
|
|
|
455
449
|
break
|
|
456
450
|
}
|
|
457
451
|
}
|
|
458
|
-
if (!objProto)
|
|
452
|
+
if (!objProto)
|
|
459
453
|
// Couldn't find setter; set value on instance
|
|
460
454
|
obj[prop] = val
|
|
461
|
-
|
|
462
|
-
// Check if
|
|
463
|
-
|
|
464
|
-
if (obj !== target || (was && check(prop))) {
|
|
455
|
+
|
|
456
|
+
// Check if the property isn't blacklisted in publicExcludes.
|
|
457
|
+
if (canWatch(receiver, prop))
|
|
465
458
|
callback(prop, val, receiver)
|
|
466
|
-
|
|
459
|
+
|
|
467
460
|
return true
|
|
468
461
|
}
|
|
469
462
|
}
|