etro 0.7.0 → 0.8.2

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.
Files changed (158) hide show
  1. package/.github/workflows/nodejs.yml +4 -2
  2. package/.github/workflows/shipjs-trigger.yml +29 -0
  3. package/CHANGELOG.md +73 -12
  4. package/CODE_OF_CONDUCT.md +5 -5
  5. package/CONTRIBUTING.md +31 -77
  6. package/README.md +81 -26
  7. package/dist/effect/base.d.ts +51 -0
  8. package/dist/effect/brightness.d.ts +16 -0
  9. package/dist/effect/channels.d.ts +23 -0
  10. package/dist/effect/chroma-key.d.ts +23 -0
  11. package/dist/effect/contrast.d.ts +15 -0
  12. package/dist/effect/elliptical-mask.d.ts +31 -0
  13. package/dist/effect/gaussian-blur.d.ts +60 -0
  14. package/dist/effect/grayscale.d.ts +7 -0
  15. package/dist/effect/index.d.ts +15 -0
  16. package/dist/effect/pixelate.d.ts +18 -0
  17. package/dist/effect/shader.d.ts +99 -0
  18. package/dist/effect/stack.d.ts +23 -0
  19. package/dist/effect/transform.d.ts +73 -0
  20. package/dist/etro-cjs.js +9287 -3331
  21. package/dist/etro-iife.js +9229 -3273
  22. package/dist/etro.d.ts +7 -0
  23. package/dist/event.d.ts +35 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/layer/audio-source.d.ts +24 -0
  26. package/dist/layer/audio.d.ts +14 -0
  27. package/dist/layer/base.d.ts +82 -0
  28. package/dist/layer/image.d.ts +6 -0
  29. package/dist/layer/index.d.ts +11 -0
  30. package/dist/layer/text.d.ts +60 -0
  31. package/dist/layer/video.d.ts +11 -0
  32. package/dist/layer/visual-source.d.ts +32 -0
  33. package/dist/layer/visual.d.ts +58 -0
  34. package/dist/movie.d.ts +192 -0
  35. package/dist/object.d.ts +12 -0
  36. package/dist/util.d.ts +125 -0
  37. package/eslint.conf.js +2 -9
  38. package/eslint.example-conf.js +9 -0
  39. package/eslint.test-conf.js +1 -0
  40. package/eslint.typescript-conf.js +5 -0
  41. package/examples/application/readme-screenshot.html +16 -17
  42. package/examples/application/video-player.html +10 -11
  43. package/examples/application/webcam.html +6 -6
  44. package/examples/introduction/audio.html +30 -18
  45. package/examples/introduction/effects.html +37 -14
  46. package/examples/introduction/export.html +32 -25
  47. package/examples/introduction/functions.html +6 -4
  48. package/examples/introduction/hello-world1.html +9 -5
  49. package/examples/introduction/hello-world2.html +5 -5
  50. package/examples/introduction/keyframes.html +35 -23
  51. package/examples/introduction/media.html +26 -18
  52. package/examples/introduction/text.html +9 -5
  53. package/karma.conf.js +5 -3
  54. package/package.json +36 -14
  55. package/rollup.config.js +15 -4
  56. package/scripts/gen-effect-samples.html +26 -25
  57. package/scripts/save-effect-samples.js +14 -15
  58. package/ship.config.js +80 -0
  59. package/src/effect/base.ts +115 -0
  60. package/src/effect/brightness.ts +43 -0
  61. package/src/effect/channels.ts +50 -0
  62. package/src/effect/chroma-key.ts +82 -0
  63. package/src/effect/contrast.ts +42 -0
  64. package/src/effect/elliptical-mask.ts +75 -0
  65. package/src/effect/gaussian-blur.ts +232 -0
  66. package/src/effect/grayscale.ts +34 -0
  67. package/src/effect/index.ts +22 -0
  68. package/src/effect/pixelate.ts +58 -0
  69. package/src/effect/shader.ts +557 -0
  70. package/src/effect/stack.ts +78 -0
  71. package/src/effect/transform.ts +193 -0
  72. package/src/etro.ts +26 -0
  73. package/src/event.ts +112 -0
  74. package/src/index.ts +8 -0
  75. package/src/layer/audio-source.ts +219 -0
  76. package/src/layer/audio.ts +34 -0
  77. package/src/layer/base.ts +175 -0
  78. package/src/layer/image.ts +8 -0
  79. package/src/layer/index.ts +13 -0
  80. package/src/layer/text.ts +138 -0
  81. package/src/layer/video.ts +15 -0
  82. package/src/layer/visual-source.ts +150 -0
  83. package/src/layer/visual.ts +197 -0
  84. package/src/movie.ts +707 -0
  85. package/src/object.ts +14 -0
  86. package/src/util.ts +466 -0
  87. package/tsconfig.json +8 -0
  88. package/docs/effect.js.html +0 -1215
  89. package/docs/event.js.html +0 -145
  90. package/docs/index.html +0 -81
  91. package/docs/index.js.html +0 -92
  92. package/docs/layer.js.html +0 -888
  93. package/docs/module-effect-GaussianBlurComponent.html +0 -345
  94. package/docs/module-effect.Brightness.html +0 -339
  95. package/docs/module-effect.Channels.html +0 -319
  96. package/docs/module-effect.ChromaKey.html +0 -611
  97. package/docs/module-effect.Contrast.html +0 -339
  98. package/docs/module-effect.EllipticalMask.html +0 -200
  99. package/docs/module-effect.GaussianBlur.html +0 -202
  100. package/docs/module-effect.GaussianBlurHorizontal.html +0 -242
  101. package/docs/module-effect.GaussianBlurVertical.html +0 -242
  102. package/docs/module-effect.Pixelate.html +0 -330
  103. package/docs/module-effect.Shader.html +0 -1227
  104. package/docs/module-effect.Stack.html +0 -406
  105. package/docs/module-effect.Transform.Matrix.html +0 -193
  106. package/docs/module-effect.Transform.html +0 -1174
  107. package/docs/module-effect.html +0 -148
  108. package/docs/module-event.html +0 -473
  109. package/docs/module-index.html +0 -186
  110. package/docs/module-layer-Media.html +0 -1116
  111. package/docs/module-layer-MediaMixin.html +0 -164
  112. package/docs/module-layer.Audio.html +0 -1188
  113. package/docs/module-layer.Base.html +0 -629
  114. package/docs/module-layer.Image.html +0 -1421
  115. package/docs/module-layer.Text.html +0 -1731
  116. package/docs/module-layer.Video.html +0 -1938
  117. package/docs/module-layer.Visual.html +0 -1698
  118. package/docs/module-layer.html +0 -137
  119. package/docs/module-movie.html +0 -3118
  120. package/docs/module-util.Color.html +0 -702
  121. package/docs/module-util.Font.html +0 -395
  122. package/docs/module-util.html +0 -845
  123. package/docs/movie.js.html +0 -689
  124. package/docs/scripts/collapse.js +0 -20
  125. package/docs/scripts/linenumber.js +0 -25
  126. package/docs/scripts/nav.js +0 -12
  127. package/docs/scripts/polyfill.js +0 -4
  128. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  129. package/docs/scripts/prettify/lang-css.js +0 -2
  130. package/docs/scripts/prettify/prettify.js +0 -28
  131. package/docs/scripts/search.js +0 -83
  132. package/docs/styles/jsdoc.css +0 -671
  133. package/docs/styles/prettify.css +0 -79
  134. package/docs/util.js.html +0 -503
  135. package/screenshots/2019-08-17_0.png +0 -0
  136. package/spec/assets/effect/gaussian-blur-horizontal.png +0 -0
  137. package/spec/assets/effect/gaussian-blur-vertical.png +0 -0
  138. package/spec/assets/effect/original.png +0 -0
  139. package/spec/assets/effect/pixelate.png +0 -0
  140. package/spec/assets/effect/transform/multiply.png +0 -0
  141. package/spec/assets/effect/transform/rotate.png +0 -0
  142. package/spec/assets/effect/transform/scale-fraction.png +0 -0
  143. package/spec/assets/effect/transform/scale.png +0 -0
  144. package/spec/assets/effect/transform/translate-fraction.png +0 -0
  145. package/spec/assets/effect/transform/translate.png +0 -0
  146. package/spec/assets/layer/audio.wav +0 -0
  147. package/spec/assets/layer/image.jpg +0 -0
  148. package/spec/effect.spec.js +0 -352
  149. package/spec/event.spec.js +0 -25
  150. package/spec/layer.spec.js +0 -150
  151. package/spec/movie.spec.js +0 -162
  152. package/spec/util.spec.js +0 -285
  153. package/src/effect.js +0 -1268
  154. package/src/event.js +0 -78
  155. package/src/index.js +0 -23
  156. package/src/layer.js +0 -897
  157. package/src/movie.js +0 -637
  158. package/src/util.js +0 -505
@@ -1,352 +0,0 @@
1
- const RED = new Uint8ClampedArray([255, 0, 0, 255])
2
- const BLUE = new Uint8ClampedArray([0, 0, 255, 255])
3
-
4
- const clamp = (x, a, b) => Math.max(Math.min(x, b), a)
5
-
6
- function createPixel (colorData) {
7
- // Create 1x1 canvas and set pixel to red
8
- const ctx = document.createElement('canvas')
9
- .getContext('2d')
10
- ctx.canvas.width = ctx.canvas.height = 1
11
- const imageData = ctx.createImageData(1, 1)
12
- for (let i = 0; i < imageData.data.length; i++) {
13
- imageData.data[i] = colorData[i]
14
- }
15
- ctx.putImageData(imageData, 0, 0)
16
-
17
- return ctx
18
- }
19
-
20
- /**
21
- * Create a square canvas with random opaque noise
22
- * @param {number} size the width and height
23
- * @return {TestCanvas}
24
- *
25
- * @typedef {Object} TestCanvas
26
- * @property {CanvasRenderingContext2D} ctx
27
- * @property {ImageData} imageData
28
- */
29
- function createRandomCanvas (size) {
30
- const ctx = document.createElement('canvas')
31
- .getContext('2d')
32
- ctx.canvas.width = ctx.canvas.height = size
33
- // Create a grid of random colors
34
- const imageData = ctx.createImageData(ctx.canvas.width, ctx.canvas.height)
35
- // opaque so premultiplied alpha won't mess up the rgb comparisons
36
- const data = imageData.data.map((_, i) => i % 4 === 3 ? 255 : Math.floor(256 * Math.random()))
37
- for (let i = 0; i < data.length; i++) {
38
- imageData.data[i] = data[i]
39
- }
40
- ctx.putImageData(imageData, 0, 0)
41
-
42
- return { ctx, imageData }
43
- }
44
-
45
- function getImageData (path, targetCanvas = undefined) {
46
- return new Promise(resolve => {
47
- targetCanvas = targetCanvas || document.createElement('canvas')
48
- const img = new Image()
49
- img.onload = () => {
50
- targetCanvas.width = img.width
51
- targetCanvas.height = img.height
52
- const ctx = targetCanvas.getContext('2d')
53
- ctx.drawImage(img, 0, 0)
54
- resolve(ctx.getImageData(0, 0, img.width, img.height))
55
- }
56
- img.src = 'base/spec/assets/effect/' + path
57
- })
58
- }
59
-
60
- function copyCanvas (source) {
61
- const dest = document.createElement('canvas')
62
- dest.width = source.width
63
- dest.height = source.height
64
- dest.getContext('2d')
65
- .drawImage(source, 0, 0)
66
- return dest
67
- }
68
-
69
- function compareImageData (original, effect, path) {
70
- return new Promise(resolve => {
71
- const result = copyCanvas(original)
72
- const ctx = result.getContext('2d')
73
- effect.apply({ canvas: result, cctx: ctx, movie: new etro.Movie(dummyCanvas) }) // movie should be unique, to prevent caching!
74
- const actual = ctx.getImageData(0, 0, result.width, result.height)
75
-
76
- getImageData(path).then(expected => {
77
- expect(actual).toEqual(expected)
78
- resolve()
79
- })
80
- })
81
- }
82
-
83
- /*
84
- * Don't reload the original image for each test, just once;
85
- * However, Jasmine will exit if we don't start the tests synchronously
86
- * So, start them, and then wait for the original image to load in the
87
- * test
88
- */
89
- const whenOriginalLoaded = (() => {
90
- const original = document.createElement('canvas')
91
- const loadedCallbacks = []
92
- let loaded = false
93
- getImageData('original.png', original).then(data => {
94
- loaded = true
95
- loadedCallbacks.forEach(callback => callback(original))
96
- })
97
-
98
- function whenOriginalLoaded (callback) {
99
- if (!loaded) {
100
- loadedCallbacks.push(callback)
101
- } else {
102
- callback(original)
103
- }
104
- }
105
- return whenOriginalLoaded
106
- })()
107
-
108
- const dummyCanvas = document.createElement('canvas')
109
-
110
- /* TESTS */
111
-
112
- describe('Effects', function () {
113
- describe('Base', function () {
114
- let effect
115
-
116
- beforeEach(function () {
117
- effect = new etro.effect.Base(0, 3)
118
- })
119
-
120
- it("should be of type 'effect'", function () {
121
- expect(effect.type).toBe('effect')
122
- })
123
-
124
- it('should set _target when attached', function () {
125
- const movie = {}
126
- effect.attach(movie)
127
- expect(effect._target).toBe(movie)
128
- })
129
- })
130
-
131
- describe('Stack', function () {
132
- let effects, stack
133
-
134
- beforeEach(function () {
135
- effects = [
136
- new etro.effect.Brightness(10),
137
- new etro.effect.Contrast(1.5)
138
- ]
139
- stack = new etro.effect.Stack(effects)
140
- stack.attach(new etro.Movie(dummyCanvas))
141
- })
142
-
143
- it('should attach its children to the target when attached', function () {
144
- expect(effects.every(child => child._target === stack._target)).toBe(true)
145
- })
146
-
147
- it('should be the same as applying individual effects', function () {
148
- const original = createRandomCanvas(4).ctx.canvas
149
- const result = copyCanvas(original)
150
- const resultCtx = result.getContext('2d')
151
-
152
- effects.forEach(effect => effect.apply({
153
- canvas: result, cctx: resultCtx, movie: new etro.Movie(dummyCanvas)
154
- }))
155
- const expected = resultCtx.getImageData(0, 0, result.width, result.height)
156
-
157
- resultCtx.drawImage(original, 0, 0) // reset
158
- stack.apply({
159
- canvas: result, cctx: resultCtx, movie: new etro.Movie(dummyCanvas)
160
- })
161
- const actual = resultCtx.getImageData(0, 0, result.width, result.height)
162
- expect(actual).toEqual(expected)
163
- })
164
- })
165
-
166
- describe('Shader', function () {
167
- let effect
168
-
169
- beforeEach(function () {
170
- effect = new etro.effect.Shader()
171
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
172
- })
173
-
174
- it('should construct', function () {})
175
-
176
- it('should not change the target if no arguments are passed', function () {
177
- const { ctx, imageData: originalData } = createRandomCanvas(2)
178
- // apply effect to a fake layer containing `ctx`
179
- effect.apply({ canvas: ctx.canvas, cctx: ctx, movie: new etro.Movie(dummyCanvas) })
180
- // Verify no change
181
- const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)
182
- expect(imageData).toEqual(originalData)
183
- })
184
- })
185
-
186
- describe('Brightness', function () {
187
- it('should change the brightness', function () {
188
- const effect = new etro.effect.Brightness(5)
189
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
190
- const ctx = createPixel(RED)
191
- // Apply effect to a fake layer containing `ctx`
192
- effect.apply({ canvas: ctx.canvas, cctx: ctx, movie: new etro.Movie(dummyCanvas) })
193
- // Verify brightness changed
194
- const imageData = ctx.getImageData(0, 0, 1, 1)
195
- expect(imageData.data).toEqual(RED.map((c, i) => c % 4 === 3 ? c
196
- : clamp(c + effect.brightness, 0, 255)))
197
- })
198
- })
199
-
200
- describe('Contrast', function () {
201
- it('should change the contrast', function () {
202
- const effect = new etro.effect.Contrast(5)
203
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
204
- const ctx = createPixel(RED)
205
- // Apply effect to a fake layer containing `ctx`
206
- effect.apply({ canvas: ctx.canvas, cctx: ctx, movie: new etro.Movie(dummyCanvas) })
207
- // Verify brightness changed
208
- const imageData = ctx.getImageData(0, 0, 1, 1)
209
- expect(imageData.data).toEqual(RED.map((c, i) => c % 4 === 3 ? c
210
- : Math.round(clamp(effect.contrast * (c - 255 / 2), 0, 255))))
211
- })
212
- })
213
-
214
- describe('Channels', function () {
215
- it('should multiply each channel by a constant', function () {
216
- const effect = new etro.effect.Channels({ r: 0.5, g: 1.25, b: 2 })
217
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
218
- const ctx = createPixel(RED)
219
- // Apply effect to a fake layer containing `ctx`
220
- effect.apply({ canvas: ctx.canvas, cctx: ctx, movie: new etro.Movie(dummyCanvas) })
221
- // Verify brightness changed
222
- const imageData = ctx.getImageData(0, 0, 1, 1)
223
- expect(imageData.data).toEqual(new Uint8ClampedArray([
224
- Math.floor((effect.factors.r || 1) * RED[0]),
225
- Math.floor((effect.factors.g || 1) * RED[1]),
226
- Math.floor((effect.factors.b || 1) * RED[2]),
227
- Math.floor((effect.factors.a || 1) * RED[3])
228
- ]))
229
- })
230
- })
231
-
232
- describe('ChromaKey', function () {
233
- let effect
234
-
235
- beforeEach(function () {
236
- effect = new etro.effect.ChromaKey({ r: 250 }, 5) // will hit r=255, because threshold is 5
237
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
238
- })
239
-
240
- it('should make the target color transparent', function () {
241
- const ctx = createPixel(RED)
242
- // Apply effect to a fake layer containing `ctx`
243
- effect.apply({ canvas: ctx.canvas, cctx: ctx, movie: new etro.Movie(dummyCanvas) })
244
- // Verify brightness changed
245
- const imageData = ctx.getImageData(0, 0, 1, 1)
246
- const alpha = imageData.data[3]
247
- expect(alpha).toBe(0)
248
- })
249
-
250
- it('should not make other colors transparent', function () {
251
- const ctx = createPixel(BLUE)
252
- // Apply effect to a fake layer containing `ctx`
253
- effect.apply({ canvas: ctx.canvas, cctx: ctx, movie: new etro.Movie(dummyCanvas) })
254
- // Verify brightness changed
255
- const imageData = ctx.getImageData(0, 0, 1, 1)
256
- const alpha = imageData.data[3]
257
- expect(alpha).toBe(255)
258
- })
259
- })
260
-
261
- describe('GaussianBlurHorizontal', function () {
262
- it('should blur with 5-pixel radius', function (done) {
263
- const effect = new etro.effect.GaussianBlurHorizontal(5)
264
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
265
- const path = 'gaussian-blur-horizontal.png'
266
- whenOriginalLoaded(original =>
267
- compareImageData(original, effect, path).then(done))
268
- })
269
- })
270
-
271
- describe('GaussianBlurVertical', function () {
272
- it('should blur with 5-pixel radius', function (done) {
273
- const effect = new etro.effect.GaussianBlurVertical(5)
274
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
275
- const path = 'gaussian-blur-vertical.png'
276
- whenOriginalLoaded(original =>
277
- compareImageData(original, effect, path).then(done))
278
- })
279
- })
280
-
281
- describe('Pixelate', function () {
282
- it('should decimate to 3-pixel texels', function (done) {
283
- const effect = new etro.effect.Pixelate(3)
284
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
285
- const path = 'pixelate.png'
286
- whenOriginalLoaded(original =>
287
- compareImageData(original, effect, path).then(done))
288
- })
289
- })
290
-
291
- describe('Transform', function () {
292
- it('should translate', function (done) {
293
- const effect = new etro.effect.Transform(
294
- new etro.effect.Transform.Matrix().translate(-3, 5)
295
- )
296
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
297
- const path = 'transform/translate.png'
298
- whenOriginalLoaded(original =>
299
- compareImageData(original, effect, path).then(done))
300
- })
301
-
302
- it('should translate by non-integers', function (done) {
303
- const effect = new etro.effect.Transform(
304
- new etro.effect.Transform.Matrix().translate(0.5, 0.5)
305
- )
306
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
307
- const path = 'transform/translate-fraction.png'
308
- whenOriginalLoaded(original =>
309
- compareImageData(original, effect, path).then(done))
310
- })
311
-
312
- it('should scale', function (done) {
313
- const effect = new etro.effect.Transform(
314
- new etro.effect.Transform.Matrix().scale(2, 2)
315
- )
316
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
317
- const path = 'transform/scale.png'
318
- whenOriginalLoaded(original =>
319
- compareImageData(original, effect, path).then(done))
320
- })
321
-
322
- it('should scale by non-integers', function (done) {
323
- const effect = new etro.effect.Transform(
324
- new etro.effect.Transform.Matrix().scale(0.5, 0.5)
325
- )
326
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
327
- const path = 'transform/scale-fraction.png'
328
- whenOriginalLoaded(original =>
329
- compareImageData(original, effect, path).then(done))
330
- })
331
-
332
- it('should rotate', function (done) {
333
- const effect = new etro.effect.Transform(
334
- new etro.effect.Transform.Matrix().rotate(Math.PI / 6)
335
- )
336
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
337
- const path = 'transform/rotate.png'
338
- whenOriginalLoaded(original =>
339
- compareImageData(original, effect, path).then(done))
340
- })
341
-
342
- it('should multiply together', function (done) {
343
- const effect = new etro.effect.Transform(
344
- new etro.effect.Transform.Matrix().scale(2, 2)
345
- .multiply(new etro.effect.Transform.Matrix().translate(-3, 5)))
346
- effect._target = new etro.Movie(dummyCanvas) // so val doesn't break because it can't cache (it requires a movie)
347
- const path = 'transform/multiply.png'
348
- whenOriginalLoaded(original =>
349
- compareImageData(original, effect, path).then(done))
350
- })
351
- })
352
- })
@@ -1,25 +0,0 @@
1
- describe('Events', function () {
2
- it('should trigger subscribers', function () {
3
- const o = {}
4
-
5
- const types = ['foo.bar.test', 'foo.bar', 'foo']
6
- types.forEach(type => {
7
- etro.event.subscribe(o, type, event => {
8
- expect(event.target).toEqual(o)
9
- notified.push(type)
10
- })
11
- })
12
-
13
- let notified = []
14
- etro.event.publish(o, 'foo.bar.test', {})
15
- expect(notified).toEqual(types)
16
-
17
- notified = []
18
- etro.event.publish(o, 'foo.bar', {})
19
- expect(notified).toEqual(types.slice(1))
20
-
21
- notified = []
22
- etro.event.publish(o, 'foo', {})
23
- expect(notified).toEqual(types.slice(2))
24
- })
25
- })
@@ -1,150 +0,0 @@
1
- describe('Layers', function () {
2
- describe('Base', function () {
3
- let layer
4
-
5
- beforeEach(function () {
6
- layer = new etro.layer.Base(0, 4)
7
- })
8
-
9
- it("should be of type 'layer'", function () {
10
- expect(layer.type).toBe('layer')
11
- })
12
-
13
- it('should attach to movie', function () {
14
- const movie = {}
15
- // Simulate attach to movie
16
- layer.attach(movie)
17
- expect(layer._movie).toEqual(movie)
18
- })
19
-
20
- it('should propogate changes up', function () {
21
- // Connect to movie to publish event to
22
- const movie = {}
23
- layer.attach(movie)
24
-
25
- // Listen for event called on moive
26
- let timesFired = 0
27
- etro.event.subscribe(movie, 'movie.change.layer', () => {
28
- timesFired++
29
- })
30
- // Modify layer
31
- layer.startTime = 1
32
- expect(timesFired).toBe(1)
33
- })
34
- })
35
-
36
- describe('Visual', function () {
37
- let layer
38
-
39
- beforeEach(function () {
40
- layer = new etro.layer.Visual(0, 4, { background: 'blue' })
41
- layer.attach(
42
- { width: 400, height: 400, currentTime: 0, movie: {}, propertyFilters: {} }
43
- )
44
- layer.render(0)
45
- })
46
-
47
- it('should render the background', function () {
48
- const imageData = layer.cctx.getImageData(0, 0, 400, 400)
49
- let allBlue = true
50
- for (let i = 0; i < imageData.data.length; i += 4) {
51
- allBlue = allBlue &&
52
- imageData.data[i + 0] === 0 &&
53
- imageData.data[i + 1] === 0 &&
54
- imageData.data[i + 2] === 255 &&
55
- imageData.data[i + 3] === 255
56
- }
57
- expect(allBlue).toBe(true)
58
- })
59
- })
60
-
61
- describe('Image', function () {
62
- let layer
63
-
64
- beforeEach(function (done) {
65
- const image = new Image()
66
- image.src = '/base/spec/assets/layer/image.jpg'
67
- image.onload = () => {
68
- layer = new etro.layer.Image(0, 4, image)
69
- // Simulate attach to movie
70
- layer.attach(
71
- { width: image.width, height: image.height, currentTime: 0 }
72
- )
73
- done()
74
- }
75
- })
76
-
77
- it('should render', function () {
78
- // Render layer (actual outcome)
79
- layer.render(0)
80
- const imageData = layer.cctx.getImageData(0, 0, layer.width, layer.height)
81
-
82
- // Draw image (expected outcome)
83
- const testCanv = document.createElement('canvas')
84
- testCanv.width = layer.width
85
- testCanv.height = layer.height
86
- const testCtx = testCanv.getContext('2d')
87
- testCtx.drawImage(layer.image, 0, 0, layer.width, layer.height)
88
- const testImageData = testCtx.getImageData(0, 0, layer.width, layer.height)
89
-
90
- // Compare expected outcome with actual outcome
91
- let equal = true
92
- for (let i = 0; i < imageData.data.length; i++) {
93
- equal = equal && imageData.data[i] === testImageData.data[i]
94
- }
95
- expect(equal).toBe(true)
96
- })
97
- })
98
-
99
- describe('Media', function () {
100
- let layer
101
- // Media is an abstract mixin, so make a concrete subclass here.
102
- const CustomMedia = etro.layer.MediaMixin(etro.layer.Base)
103
- const source = new Audio()
104
-
105
- beforeAll(function (done) {
106
- source.addEventListener('canplay', done)
107
- source.src = '/base/spec/assets/layer/audio.wav'
108
- })
109
-
110
- beforeEach(function () {
111
- layer = new CustomMedia(0, source)
112
- })
113
-
114
- it('should have its duration depend on its playbackRate', function () {
115
- const oldDuration = layer.duration
116
- layer.playbackRate = 2
117
- expect(layer.duration).toBe(oldDuration / 2)
118
- })
119
- })
120
-
121
- // I suspect this doesn't work becuase of autoplay restrictions
122
- /* describe('Audio', function () {
123
- let layer
124
-
125
- beforeEach(function (done) {
126
- const audio = new Audio()
127
- audio.src = '/base/spec/assets/layer/audio.wav'
128
- // audio.muted = true // until we figure out how to allow autoplay in headless chrome
129
- audio.addEventListener('loadedmetadata', () => {
130
- layer = new etro.layer.Audio(0, audio)
131
- layer.attach(
132
- { actx: new AudioContext(), currentTime: 0 }
133
- )
134
- done()
135
- })
136
- })
137
-
138
- it('should play', function () {
139
- let timesPlayed = 0
140
- layer.media.addEventListener('play', () => {
141
- timesPlayed++
142
- })
143
- for (let i = 0; i < 3; i++) {
144
- layer.start(0) // reltime = 0s
145
- layer.stop(0) // reltime = 0s
146
- }
147
- expect(timesPlayed).toBe(3)
148
- })
149
- }) */
150
- })
@@ -1,162 +0,0 @@
1
- describe('Movie', function () {
2
- let movie, canvas
3
-
4
- beforeEach(function () {
5
- if (canvas) {
6
- document.body.removeChild(canvas)
7
- }
8
- canvas = document.createElement('canvas')
9
- document.body.appendChild(canvas)
10
- movie = new etro.Movie(canvas)
11
- movie.addLayer(new etro.layer.Visual(0, 0.5))
12
- })
13
-
14
- describe('identity ->', function () {
15
- it("should be of type 'movie'", function () {
16
- expect(movie.type).toBe('movie')
17
- })
18
- })
19
-
20
- describe('operations ->', function () {
21
- it('should not be paused after playing', function () {
22
- movie.play()
23
- expect(movie.paused).toBe(false)
24
- })
25
-
26
- it('should be paused after pausing', function () {
27
- movie.play()
28
- movie.pause()
29
- // No promise returned by `pause`, because code is async in implementation.
30
- expect(movie.paused).toBe(true)
31
- })
32
-
33
- it('should be paused after stopping', function () {
34
- movie.play()
35
- movie.stop()
36
- expect(movie.paused).toBe(true)
37
- })
38
-
39
- it('should be reset to beginning after stopping', function () {
40
- movie.play()
41
- movie.stop()
42
- expect(movie.currentTime).toBe(0)
43
- })
44
-
45
- it('should be `recording` when recording', function () {
46
- movie.record(10)
47
- expect(movie.recording).toBe(true)
48
- })
49
-
50
- it('should not be paused when recording', function () {
51
- movie.record(10)
52
- expect(movie.paused).toBe(false)
53
- })
54
-
55
- it('should return blob after recording', function (done) {
56
- movie.record(60)
57
- .then(video => {
58
- expect(video.size).toBeGreaterThan(0)
59
- done()
60
- })
61
- .catch(e => {
62
- throw e
63
- })
64
- })
65
-
66
- it('can record with custom MIME type', function (done) {
67
- movie.record(60, { type: 'video/mp4' })
68
- .then(video => {
69
- expect(video.type).toBe('video/mp4')
70
- done()
71
- })
72
- })
73
- })
74
-
75
- describe('events ->', function () {
76
- it("should fire 'movie.play' once", function () {
77
- let timesFired = 0
78
- etro.event.subscribe(movie, 'movie.play', function () {
79
- timesFired++
80
- })
81
- movie.play().then(function () {
82
- expect(timesFired).toBe(1)
83
- })
84
- })
85
-
86
- it("should fire 'movie.pause' once", function () {
87
- let timesFired = 0
88
- etro.event.subscribe(movie, 'movie.pause', function () {
89
- timesFired++
90
- })
91
- // play, pause and check if event was fired
92
- movie.play().then(function () {
93
- movie.pause()
94
- expect(timesFired).toBe(1)
95
- })
96
- })
97
-
98
- it("should fire 'movie.record' once", function () {
99
- let timesFired = 0
100
- etro.event.subscribe(movie, 'movie.record', function () {
101
- timesFired++
102
- })
103
- movie.record().then(function () {
104
- expect(timesFired).toBe(1)
105
- })
106
- })
107
-
108
- it("should fire 'movie.record' with correct options", function () {
109
- const options = {
110
- video: true, // even default values should be passed (exactly what user provides)
111
- audio: false
112
- }
113
- etro.event.subscribe(movie, 'movie.record', function (event) {
114
- expect(event.options).toEqual(options)
115
- })
116
- movie.record(options)
117
- })
118
-
119
- it("should fire 'movie.ended'", function () {
120
- let timesFired = 0
121
- etro.event.subscribe(movie, 'movie.ended', function () {
122
- timesFired++
123
- })
124
- movie.play().then(function () {
125
- expect(timesFired).toBe(1)
126
- })
127
- })
128
-
129
- it("should fire 'movie.loadeddata'", function () {
130
- /*
131
- * 'loadeddata' gets timesFired when when the frame is fully loaded
132
- */
133
-
134
- let firedOnce = false
135
- etro.event.subscribe(movie, 'movie.loadeddata', () => {
136
- firedOnce = true
137
- })
138
- movie.refresh().then(() => {
139
- expect(firedOnce).toBe(true)
140
- })
141
- })
142
-
143
- it("should fire 'movie.seek'", function () {
144
- let timesFired = 0
145
- etro.event.subscribe(movie, 'movie.seek', function () {
146
- timesFired++
147
- })
148
- movie.currentTime = movie.duration / 2
149
- expect(timesFired).toBe(1)
150
- })
151
-
152
- it("should fire 'movie.timeupdate'", function () {
153
- let firedOnce = false
154
- etro.event.subscribe(movie, 'movie.timeupdate', function () {
155
- firedOnce = true
156
- })
157
- movie.play().then(function () {
158
- expect(firedOnce).toBe(true)
159
- })
160
- })
161
- })
162
- })