etro 0.8.3 → 0.9.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/.github/workflows/nodejs.yml +6 -3
- package/CHANGELOG.md +37 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +10 -10
- package/README.md +32 -52
- package/dist/effect/base.d.ts +6 -6
- package/dist/effect/elliptical-mask.d.ts +4 -4
- package/dist/effect/index.d.ts +1 -0
- package/dist/effect/shader.d.ts +4 -4
- package/dist/effect/stack.d.ts +7 -7
- package/dist/effect/transform.d.ts +4 -4
- package/dist/effect/visual.d.ts +17 -0
- package/dist/etro-cjs.js +74 -6450
- package/dist/etro-iife.js +74 -6450
- package/dist/layer/audio-source.d.ts +1 -2
- package/dist/layer/image.d.ts +2 -2
- package/dist/layer/visual.d.ts +3 -3
- package/dist/movie.d.ts +14 -5
- package/dist/util.d.ts +2 -0
- package/karma.conf.js +28 -2
- package/package.json +7 -10
- package/scripts/gen-effect-samples.html +27 -18
- package/scripts/save-effect-samples.js +6 -1
- package/src/effect/base.ts +7 -7
- package/src/effect/elliptical-mask.ts +4 -4
- package/src/effect/index.ts +1 -0
- package/src/effect/shader.ts +4 -4
- package/src/effect/stack.ts +10 -10
- package/src/effect/transform.ts +4 -4
- package/src/effect/visual.ts +21 -0
- package/src/layer/audio-source.ts +8 -6
- package/src/layer/base.ts +1 -1
- package/src/layer/image.ts +3 -3
- package/src/layer/visual.ts +4 -4
- package/src/movie.ts +58 -31
- package/src/util.ts +2 -0
- package/.env +0 -2
- package/rename-file.sh +0 -18
- package/rename-versions.sh +0 -14
- package/rename.sh +0 -22
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { AudioContext, IAudioNode } from 'standardized-audio-context';
|
|
2
1
|
import { Base, BaseOptions } from './base';
|
|
3
2
|
declare type Constructor<T> = new (...args: unknown[]) => T;
|
|
4
3
|
interface AudioSource extends Base {
|
|
5
4
|
readonly source: HTMLMediaElement;
|
|
6
|
-
readonly audioNode:
|
|
5
|
+
readonly audioNode: AudioNode;
|
|
7
6
|
playbackRate: number;
|
|
8
7
|
/** The audio source node for the media */
|
|
9
8
|
sourceStartTime: number;
|
package/dist/layer/image.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare type ImageOptions =
|
|
1
|
+
import { VisualSourceOptions } from './visual-source';
|
|
2
|
+
declare type ImageOptions = VisualSourceOptions;
|
|
3
3
|
declare const Image_base: new (...args: unknown[]) => import("./visual-source").VisualSource;
|
|
4
4
|
declare class Image extends Image_base {
|
|
5
5
|
}
|
package/dist/layer/visual.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Dynamic } from '../util';
|
|
2
2
|
import { Base, BaseOptions } from './base';
|
|
3
|
-
import {
|
|
3
|
+
import { Visual as VisualEffect } from '../effect/visual';
|
|
4
4
|
interface VisualOptions extends BaseOptions {
|
|
5
5
|
x?: Dynamic<number>;
|
|
6
6
|
y?: Dynamic<number>;
|
|
@@ -33,7 +33,7 @@ declare class Visual extends Base {
|
|
|
33
33
|
* The context of {@link Visual#canvas}
|
|
34
34
|
*/
|
|
35
35
|
readonly cctx: CanvasRenderingContext2D;
|
|
36
|
-
readonly effects:
|
|
36
|
+
readonly effects: VisualEffect[];
|
|
37
37
|
private _effectsBack;
|
|
38
38
|
/**
|
|
39
39
|
* Creates a visual layer
|
|
@@ -52,7 +52,7 @@ declare class Visual extends Base {
|
|
|
52
52
|
* @param effect
|
|
53
53
|
* @return the layer (for chaining)
|
|
54
54
|
*/
|
|
55
|
-
addEffect(effect:
|
|
55
|
+
addEffect(effect: VisualEffect): Visual;
|
|
56
56
|
getDefaultOptions(): VisualOptions;
|
|
57
57
|
}
|
|
58
58
|
export { Visual, VisualOptions };
|
package/dist/movie.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module movie
|
|
3
3
|
*/
|
|
4
|
-
import { AudioContext } from 'standardized-audio-context';
|
|
5
4
|
import { Dynamic } from './util';
|
|
6
5
|
import { Base as BaseLayer } from './layer/index';
|
|
7
6
|
import { Base as BaseEffect } from './effect/index';
|
|
8
7
|
declare global {
|
|
8
|
+
interface Window {
|
|
9
|
+
webkitAudioContext: typeof AudioContext;
|
|
10
|
+
}
|
|
9
11
|
interface HTMLCanvasElement {
|
|
10
12
|
captureStream(frameRate?: number): MediaStream;
|
|
11
13
|
}
|
|
@@ -30,10 +32,19 @@ export declare class MovieOptions {
|
|
|
30
32
|
*/
|
|
31
33
|
export declare class Movie {
|
|
32
34
|
type: string;
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated Auto-refresh will be removed in the future (see issue #130).
|
|
37
|
+
*/
|
|
33
38
|
publicExcludes: string[];
|
|
34
39
|
propertyFilters: Record<string, <T>(value: T) => T>;
|
|
35
40
|
repeat: boolean;
|
|
36
|
-
/**
|
|
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
|
+
*/
|
|
37
48
|
autoRefresh: boolean;
|
|
38
49
|
/** The background color of the movie as a cSS string */
|
|
39
50
|
background: Dynamic<string>;
|
|
@@ -186,7 +197,5 @@ export declare class Movie {
|
|
|
186
197
|
get height(): number;
|
|
187
198
|
set height(height: number);
|
|
188
199
|
get movie(): Movie;
|
|
189
|
-
getDefaultOptions(): MovieOptions
|
|
190
|
-
_actx: AudioContext;
|
|
191
|
-
};
|
|
200
|
+
getDefaultOptions(): MovieOptions;
|
|
192
201
|
}
|
package/dist/util.d.ts
CHANGED
|
@@ -120,6 +120,8 @@ export declare function mapPixels(mapper: (pixels: Uint8ClampedArray, i: number)
|
|
|
120
120
|
* <p>Must be called before any watchable properties are set, and only once in
|
|
121
121
|
* the prototype chain.
|
|
122
122
|
*
|
|
123
|
+
* @deprecated Will be removed in the future (see issue #130)
|
|
124
|
+
*
|
|
123
125
|
* @param target - object to watch
|
|
124
126
|
*/
|
|
125
127
|
export declare function watchPublic(target: EtroObject): EtroObject;
|
package/karma.conf.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Karma configuration
|
|
2
2
|
// Generated on Thu Sep 19 2019 02:05:06 GMT-0400 (Eastern Daylight Time)
|
|
3
3
|
|
|
4
|
+
process.env.CHROME_BIN = require('puppeteer').executablePath()
|
|
5
|
+
|
|
4
6
|
module.exports = function (config) {
|
|
5
7
|
config.set({
|
|
6
8
|
|
|
@@ -49,7 +51,25 @@ module.exports = function (config) {
|
|
|
49
51
|
|
|
50
52
|
// start these browsers
|
|
51
53
|
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
|
52
|
-
browsers: ['
|
|
54
|
+
browsers: ['FirefoxHeadless'],
|
|
55
|
+
|
|
56
|
+
customLaunchers: {
|
|
57
|
+
'FirefoxHeadless': {
|
|
58
|
+
base: 'Firefox',
|
|
59
|
+
flags: ['-headless'],
|
|
60
|
+
prefs: {
|
|
61
|
+
'network.proxy.type': 0
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
client: {
|
|
67
|
+
captureConsole: true
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
browserConsoleLogOptions: {
|
|
71
|
+
level: 'log'
|
|
72
|
+
},
|
|
53
73
|
|
|
54
74
|
// Continuous Integration mode
|
|
55
75
|
// if true, Karma captures browsers, runs the tests and exits
|
|
@@ -57,6 +77,12 @@ module.exports = function (config) {
|
|
|
57
77
|
|
|
58
78
|
// Concurrency level
|
|
59
79
|
// how many browser should be started simultaneous
|
|
60
|
-
concurrency: Infinity
|
|
80
|
+
concurrency: Infinity,
|
|
81
|
+
|
|
82
|
+
client: {
|
|
83
|
+
jasmine: {
|
|
84
|
+
timeoutInterval: 10000
|
|
85
|
+
}
|
|
86
|
+
}
|
|
61
87
|
})
|
|
62
88
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "etro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "An extendable video-editing framework for the browser and Node",
|
|
5
5
|
"browser": "dist/etro-cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,9 +9,6 @@
|
|
|
9
9
|
"example": "examples",
|
|
10
10
|
"test": "spec"
|
|
11
11
|
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"standardized-audio-context": "^25.1.13"
|
|
14
|
-
},
|
|
15
12
|
"devDependencies": {
|
|
16
13
|
"@types/dom-mediacapture-record": "^1.0.7",
|
|
17
14
|
"@typescript-eslint/eslint-plugin": "^4.15.2",
|
|
@@ -26,30 +23,30 @@
|
|
|
26
23
|
"eslint-plugin-promise": "^4.2.1",
|
|
27
24
|
"eslint-plugin-standard": "^4.0.1",
|
|
28
25
|
"ev": "0.0.7",
|
|
29
|
-
"http-server": "^
|
|
26
|
+
"http-server": "^14.1.1",
|
|
30
27
|
"jasmine": "^3.4.0",
|
|
31
28
|
"karma": "^6.1.1",
|
|
32
|
-
"karma-chrome-launcher": "^3.1.0",
|
|
33
29
|
"karma-es6-shim": "^1.0.0",
|
|
30
|
+
"karma-firefox-launcher": "^2.1.2",
|
|
34
31
|
"karma-jasmine": "^2.0.1",
|
|
35
32
|
"karma-requirejs": "^1.1.0",
|
|
36
33
|
"karma-super-dots-reporter": "^0.2.0",
|
|
37
34
|
"keep-a-changelog": "^0.10.4",
|
|
38
35
|
"puppeteer": "^2.0.0",
|
|
39
|
-
"resemblejs": "^
|
|
36
|
+
"resemblejs": "^4.1.0",
|
|
40
37
|
"rollup": "^1.19.4",
|
|
41
38
|
"rollup-plugin-cleaner": "^1.0.0",
|
|
42
39
|
"rollup-plugin-eslint": "^7.0.0",
|
|
43
40
|
"rollup-plugin-node-resolve": "^5.2.0",
|
|
44
41
|
"rollup-plugin-typescript2": "^0.29.0",
|
|
45
42
|
"rollup-plugin-uglify-es": "^0.0.1",
|
|
46
|
-
"shipjs": "0.
|
|
47
|
-
"typedoc": "^0.
|
|
43
|
+
"shipjs": "^0.24.4",
|
|
44
|
+
"typedoc": "^0.22.11",
|
|
48
45
|
"typescript": "^4.1.3"
|
|
49
46
|
},
|
|
50
47
|
"scripts": {
|
|
51
48
|
"build": "rollup -c",
|
|
52
|
-
"doc": "rm -rf docs && npx typedoc src/etro.ts --excludePrivate --readme none
|
|
49
|
+
"doc": "rm -rf docs && npx typedoc src/etro.ts --excludePrivate --readme none",
|
|
53
50
|
"assets": "git fetch origin example-assets:example-assets && git cherry-pick example-assets && git reset --soft HEAD^ && git reset HEAD examples/assets",
|
|
54
51
|
"effects": "node scripts/save-effect-samples.js",
|
|
55
52
|
"lint": "npm run --silent lint:main && npm run --silent lint:test && npm run --silent lint:examples",
|
|
@@ -43,26 +43,35 @@
|
|
|
43
43
|
/**
|
|
44
44
|
* Save an effect sample to the disk
|
|
45
45
|
*/
|
|
46
|
-
function saveSample(original, effect, path) {
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
effect
|
|
46
|
+
async function saveSample(original, effect, path) {
|
|
47
|
+
// Create movie (needed for layer to render)
|
|
48
|
+
const movie = new etro.Movie({
|
|
49
|
+
canvas: document.createElement('canvas'),
|
|
50
|
+
autoRefresh: false
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Convert canvas to image
|
|
54
|
+
const originalImg = new Image()
|
|
55
|
+
await new Promise(resolve => {
|
|
56
|
+
originalImg.onload = resolve
|
|
57
|
+
originalImg.src = original.toDataURL()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Add an image layer with the effect to the movie
|
|
61
|
+
const layer = new etro.layer.Image({
|
|
62
|
+
startTime: 0,
|
|
63
|
+
duration: 1,
|
|
64
|
+
source: originalImg
|
|
65
|
+
})
|
|
66
|
+
layer.effects.push(effect)
|
|
67
|
+
movie.layers.push(layer)
|
|
61
68
|
|
|
62
|
-
save
|
|
69
|
+
// Render and save the layer
|
|
70
|
+
layer.render()
|
|
71
|
+
save(layer.canvas, path)
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
window.onload = () => {
|
|
74
|
+
window.onload = async () => {
|
|
66
75
|
const original = genRandomNoise(16, 16)
|
|
67
76
|
save(original, 'original.png')
|
|
68
77
|
|
|
@@ -91,7 +100,7 @@
|
|
|
91
100
|
|
|
92
101
|
for (let path in samples) {
|
|
93
102
|
const effect = samples[path]
|
|
94
|
-
saveSample(original, effect, path)
|
|
103
|
+
await saveSample(original, effect, path)
|
|
95
104
|
}
|
|
96
105
|
window.done = true
|
|
97
106
|
}
|
|
@@ -20,8 +20,13 @@ function createDirs(filePath) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
(async () => {
|
|
23
|
-
const browser = await puppeteer.launch(
|
|
23
|
+
const browser = await puppeteer.launch({
|
|
24
|
+
args: ['--autoplay-policy=no-user-gesture-required']
|
|
25
|
+
})
|
|
24
26
|
const page = await browser.newPage()
|
|
27
|
+
page.on('console', msg => {
|
|
28
|
+
console.log(`[CONSOLE] ${msg.text()}`)
|
|
29
|
+
})
|
|
25
30
|
|
|
26
31
|
await page.goto(`file://${__dirname}/gen-effect-samples.html`)
|
|
27
32
|
await page.waitForFunction(() => window.done);
|
package/src/effect/base.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { watchPublic } from '../util'
|
|
2
2
|
import { publish, subscribe } from '../event'
|
|
3
3
|
import { Movie } from '../movie'
|
|
4
|
-
import {
|
|
4
|
+
import { Base as BaseLayer } from '../layer/index'
|
|
5
5
|
import BaseObject from '../object'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* @deprecated All visual effects now inherit from `Visual` instead
|
|
9
9
|
*/
|
|
10
10
|
export class Base implements BaseObject {
|
|
11
11
|
type: string
|
|
@@ -14,7 +14,7 @@ export class Base implements BaseObject {
|
|
|
14
14
|
|
|
15
15
|
enabled: boolean
|
|
16
16
|
|
|
17
|
-
private _target: Movie |
|
|
17
|
+
private _target: Movie | BaseLayer
|
|
18
18
|
/**
|
|
19
19
|
* The number of times this effect has been attached to a target minus the
|
|
20
20
|
* number of times it's been detached. (Used for the target's array proxy with
|
|
@@ -45,14 +45,14 @@ export class Base implements BaseObject {
|
|
|
45
45
|
* Attaches this effect to `target` if not already attached.
|
|
46
46
|
* @ignore
|
|
47
47
|
*/
|
|
48
|
-
tryAttach (target: Movie |
|
|
48
|
+
tryAttach (target: Movie | BaseLayer): void {
|
|
49
49
|
if (this._occurrenceCount === 0)
|
|
50
50
|
this.attach(target)
|
|
51
51
|
|
|
52
52
|
this._occurrenceCount++
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
attach (movie: Movie |
|
|
55
|
+
attach (movie: Movie | BaseLayer): void {
|
|
56
56
|
this._target = movie
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -87,7 +87,7 @@ export class Base implements BaseObject {
|
|
|
87
87
|
* (will soon be replaced with an instance getter)
|
|
88
88
|
* @abstract
|
|
89
89
|
*/
|
|
90
|
-
apply (target: Movie |
|
|
90
|
+
apply (target: Movie | BaseLayer, reltime: number): void {} // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* The current time of the target
|
|
@@ -96,7 +96,7 @@ export class Base implements BaseObject {
|
|
|
96
96
|
return this._target ? this._target.currentTime : undefined
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
get parent (): Movie |
|
|
99
|
+
get parent (): Movie | BaseLayer {
|
|
100
100
|
return this._target
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Movie } from '../movie'
|
|
2
2
|
import { Dynamic, val } from '../util'
|
|
3
|
-
import {
|
|
4
|
-
import { Visual } from '../layer/index'
|
|
3
|
+
import { Visual } from './visual'
|
|
4
|
+
import { Visual as VisualLayer } from '../layer/index'
|
|
5
5
|
|
|
6
6
|
export class EllipticalMaskOptions {
|
|
7
7
|
x: Dynamic<number>
|
|
@@ -18,7 +18,7 @@ export class EllipticalMaskOptions {
|
|
|
18
18
|
* Preserves an ellipse of the layer and clears the rest
|
|
19
19
|
*/
|
|
20
20
|
// TODO: Parent layer mask effects will make more complex masks easier
|
|
21
|
-
export class EllipticalMask extends
|
|
21
|
+
export class EllipticalMask extends Visual {
|
|
22
22
|
x: Dynamic<number>
|
|
23
23
|
y: Dynamic<number>
|
|
24
24
|
radiusX: Dynamic<number>
|
|
@@ -46,7 +46,7 @@ export class EllipticalMask extends Base {
|
|
|
46
46
|
this._tmpCtx = this._tmpCanvas.getContext('2d')
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
apply (target: Movie |
|
|
49
|
+
apply (target: Movie | VisualLayer, reltime: number): void {
|
|
50
50
|
const ctx = target.cctx
|
|
51
51
|
const canvas = target.canvas
|
|
52
52
|
const x = val(this, 'x', reltime)
|
package/src/effect/index.ts
CHANGED
package/src/effect/shader.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Visual } from '../layer/index'
|
|
1
|
+
import { Visual as VisualLayer } from '../layer/index'
|
|
2
2
|
import { Movie } from '../movie'
|
|
3
3
|
import { val } from '../util'
|
|
4
|
-
import {
|
|
4
|
+
import { Visual } from './visual'
|
|
5
5
|
|
|
6
6
|
export interface UniformOptions {
|
|
7
7
|
type?: string
|
|
@@ -32,7 +32,7 @@ export interface ShaderOptions {
|
|
|
32
32
|
* A hardware-accelerated pixel mapping using WebGL
|
|
33
33
|
*/
|
|
34
34
|
// TODO: can `v_TextureCoord` be replaced by `gl_FragUV`?
|
|
35
|
-
export class Shader extends
|
|
35
|
+
export class Shader extends Visual {
|
|
36
36
|
/**
|
|
37
37
|
* WebGL texture units consumed by {@link Shader}
|
|
38
38
|
*/
|
|
@@ -196,7 +196,7 @@ export class Shader extends Base {
|
|
|
196
196
|
}
|
|
197
197
|
} */
|
|
198
198
|
|
|
199
|
-
apply (target: Movie |
|
|
199
|
+
apply (target: Movie | VisualLayer, reltime: number): void {
|
|
200
200
|
this._checkDimensions(target)
|
|
201
201
|
this._refreshGl()
|
|
202
202
|
|
package/src/effect/stack.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { Movie } from '../movie'
|
|
2
|
-
import {
|
|
3
|
-
import { Visual } from '../layer'
|
|
2
|
+
import { Visual } from './visual'
|
|
3
|
+
import { Visual as VisualLayer } from '../layer'
|
|
4
4
|
|
|
5
5
|
export interface StackOptions {
|
|
6
|
-
effects:
|
|
6
|
+
effects: Visual[]
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A sequence of effects to apply, treated as one effect. This can be useful
|
|
11
11
|
* for defining reused effect sequences as one effect.
|
|
12
12
|
*/
|
|
13
|
-
export class Stack extends
|
|
14
|
-
readonly effects:
|
|
13
|
+
export class Stack extends Visual {
|
|
14
|
+
readonly effects: Visual[]
|
|
15
15
|
|
|
16
|
-
private _effectsBack:
|
|
16
|
+
private _effectsBack: Visual[]
|
|
17
17
|
|
|
18
18
|
constructor (options: StackOptions) {
|
|
19
19
|
super()
|
|
@@ -21,13 +21,13 @@ export class Stack extends Base {
|
|
|
21
21
|
this._effectsBack = []
|
|
22
22
|
// TODO: Throw 'change' events in handlers
|
|
23
23
|
this.effects = new Proxy(this._effectsBack, {
|
|
24
|
-
deleteProperty: function (target:
|
|
24
|
+
deleteProperty: function (target: Visual[], property: number | string): boolean {
|
|
25
25
|
const value = target[property]
|
|
26
26
|
value.detach() // Detach effect from movie
|
|
27
27
|
delete target[property]
|
|
28
28
|
return true
|
|
29
29
|
},
|
|
30
|
-
set: function (target:
|
|
30
|
+
set: function (target: Visual[], property: number | string, value: Visual): boolean {
|
|
31
31
|
// TODO: make sure type check works
|
|
32
32
|
if (!isNaN(Number(property))) { // if property is a number (index)
|
|
33
33
|
if (target[property])
|
|
@@ -59,7 +59,7 @@ export class Stack extends Base {
|
|
|
59
59
|
})
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
apply (target: Movie |
|
|
62
|
+
apply (target: Movie | VisualLayer, reltime: number): void {
|
|
63
63
|
for (let i = 0; i < this.effects.length; i++) {
|
|
64
64
|
const effect = this.effects[i]
|
|
65
65
|
if (!effect) continue
|
|
@@ -71,7 +71,7 @@ export class Stack extends Base {
|
|
|
71
71
|
* Convenience method for chaining
|
|
72
72
|
* @param effect - the effect to append
|
|
73
73
|
*/
|
|
74
|
-
addEffect (effect:
|
|
74
|
+
addEffect (effect: Visual): Stack {
|
|
75
75
|
this.effects.push(effect)
|
|
76
76
|
return this
|
|
77
77
|
}
|
package/src/effect/transform.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Visual } from '../layer/index'
|
|
1
|
+
import { Visual as VisualLayer } from '../layer/index'
|
|
2
2
|
import { Movie } from '../movie'
|
|
3
3
|
import { val, Dynamic } from '../util'
|
|
4
|
-
import {
|
|
4
|
+
import { Visual } from './visual'
|
|
5
5
|
|
|
6
6
|
export interface TransformOptions {
|
|
7
7
|
matrix: Dynamic<Transform.Matrix> // eslint-disable-line no-use-before-define
|
|
@@ -13,7 +13,7 @@ export interface TransformOptions {
|
|
|
13
13
|
* translations, scalings and rotations) or B) input the matrix values
|
|
14
14
|
* directly, using the optional argument in the constructor.
|
|
15
15
|
*/
|
|
16
|
-
class Transform extends
|
|
16
|
+
class Transform extends Visual {
|
|
17
17
|
/** Matrix that determines how to transform the target */
|
|
18
18
|
matrix: Dynamic<Transform.Matrix>
|
|
19
19
|
|
|
@@ -35,7 +35,7 @@ class Transform extends Base {
|
|
|
35
35
|
this._tmpCtx = this._tmpCanvas.getContext('2d')
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
apply (target: Movie |
|
|
38
|
+
apply (target: Movie | VisualLayer, reltime: number): void {
|
|
39
39
|
if (target.canvas.width !== this._tmpCanvas.width)
|
|
40
40
|
this._tmpCanvas.width = target.canvas.width
|
|
41
41
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Movie } from '../movie'
|
|
2
|
+
import { Visual as VisualLayer } from '../layer/index'
|
|
3
|
+
import { Base } from './base'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Modifies the visual contents of a layer.
|
|
7
|
+
*/
|
|
8
|
+
export class Visual extends Base {
|
|
9
|
+
// subclasses must implement apply
|
|
10
|
+
/**
|
|
11
|
+
* Apply this effect to a target at the given time
|
|
12
|
+
*
|
|
13
|
+
* @param target
|
|
14
|
+
* @param reltime - the movie's current time relative to the layer
|
|
15
|
+
* (will soon be replaced with an instance getter)
|
|
16
|
+
* @abstract
|
|
17
|
+
*/
|
|
18
|
+
apply (target: Movie | VisualLayer, reltime: number): void { // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
|
19
|
+
super.apply(target, reltime)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AudioContext, IAudioNode, IAudioDestinationNode } from 'standardized-audio-context'
|
|
2
1
|
import { Movie } from '../movie'
|
|
3
2
|
import { subscribe } from '../event'
|
|
4
3
|
import { applyOptions, val } from '../util'
|
|
@@ -8,7 +7,7 @@ type Constructor<T> = new (...args: unknown[]) => T
|
|
|
8
7
|
|
|
9
8
|
interface AudioSource extends Base {
|
|
10
9
|
readonly source: HTMLMediaElement
|
|
11
|
-
readonly audioNode:
|
|
10
|
+
readonly audioNode: AudioNode
|
|
12
11
|
playbackRate: number
|
|
13
12
|
/** The audio source node for the media */
|
|
14
13
|
sourceStartTime: number
|
|
@@ -41,7 +40,7 @@ function AudioSourceMixin<OptionsSuperclass extends BaseOptions> (superclass: Co
|
|
|
41
40
|
readonly source: HTMLMediaElement
|
|
42
41
|
|
|
43
42
|
private __startTime: number
|
|
44
|
-
private _audioNode:
|
|
43
|
+
private _audioNode: AudioNode
|
|
45
44
|
private _sourceStartTime: number
|
|
46
45
|
private _unstretchedDuration: number
|
|
47
46
|
private _playbackRate: number
|
|
@@ -117,12 +116,12 @@ function AudioSourceMixin<OptionsSuperclass extends BaseOptions> (superclass: Co
|
|
|
117
116
|
// Spy on connect and disconnect to remember if it connected to
|
|
118
117
|
// actx.destination (for Movie#record).
|
|
119
118
|
const oldConnect = this._audioNode.connect.bind(this.audioNode)
|
|
120
|
-
this._audioNode.connect = <T extends
|
|
119
|
+
this._audioNode.connect = <T extends AudioDestinationNode>(destination: T, outputIndex?: number, inputIndex?: number): AudioNode => {
|
|
121
120
|
this._connectedToDestination = destination === movie.actx.destination
|
|
122
121
|
return oldConnect(destination, outputIndex, inputIndex)
|
|
123
122
|
}
|
|
124
123
|
const oldDisconnect = this._audioNode.disconnect.bind(this.audioNode)
|
|
125
|
-
this._audioNode.disconnect = <T extends
|
|
124
|
+
this._audioNode.disconnect = <T extends AudioDestinationNode>(destination?: T | number, output?: number, input?: number): AudioNode => {
|
|
126
125
|
if (this._connectedToDestination &&
|
|
127
126
|
destination === movie.actx.destination)
|
|
128
127
|
this._connectedToDestination = false
|
|
@@ -135,7 +134,10 @@ function AudioSourceMixin<OptionsSuperclass extends BaseOptions> (superclass: Co
|
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
detach () {
|
|
138
|
-
|
|
137
|
+
// Cache dest before super.detach() unsets this.movie
|
|
138
|
+
const dest = this.movie.actx.destination
|
|
139
|
+
super.detach()
|
|
140
|
+
this.audioNode.disconnect(dest)
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
start () {
|
package/src/layer/base.ts
CHANGED
|
@@ -169,7 +169,7 @@ class Base implements EtroObject {
|
|
|
169
169
|
// id for events (independent of instance, but easy to access when on prototype
|
|
170
170
|
// chain)
|
|
171
171
|
Base.prototype.type = 'layer'
|
|
172
|
-
Base.prototype.publicExcludes = []
|
|
172
|
+
Base.prototype.publicExcludes = ['active']
|
|
173
173
|
Base.prototype.propertyFilters = {}
|
|
174
174
|
|
|
175
175
|
export { Base, BaseOptions }
|
package/src/layer/image.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Visual
|
|
2
|
-
import { VisualSourceMixin } from './visual-source'
|
|
1
|
+
import { Visual } from './visual'
|
|
2
|
+
import { VisualSourceMixin, VisualSourceOptions } from './visual-source'
|
|
3
3
|
|
|
4
|
-
type ImageOptions =
|
|
4
|
+
type ImageOptions = VisualSourceOptions
|
|
5
5
|
|
|
6
6
|
class Image extends VisualSourceMixin(Visual) {}
|
|
7
7
|
|
package/src/layer/visual.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Dynamic, val, applyOptions } from '../util'
|
|
2
2
|
import { Base, BaseOptions } from './base'
|
|
3
|
-
import {
|
|
3
|
+
import { Visual as VisualEffect } from '../effect/visual'
|
|
4
4
|
|
|
5
5
|
interface VisualOptions extends BaseOptions {
|
|
6
6
|
x?: Dynamic<number>
|
|
@@ -41,9 +41,9 @@ class Visual extends Base {
|
|
|
41
41
|
readonly cctx: CanvasRenderingContext2D
|
|
42
42
|
|
|
43
43
|
// readonly because it's a proxy
|
|
44
|
-
readonly effects:
|
|
44
|
+
readonly effects: VisualEffect[]
|
|
45
45
|
|
|
46
|
-
private _effectsBack:
|
|
46
|
+
private _effectsBack: VisualEffect[]
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Creates a visual layer
|
|
@@ -142,7 +142,7 @@ class Visual extends Base {
|
|
|
142
142
|
* @param effect
|
|
143
143
|
* @return the layer (for chaining)
|
|
144
144
|
*/
|
|
145
|
-
addEffect (effect:
|
|
145
|
+
addEffect (effect: VisualEffect): Visual {
|
|
146
146
|
this.effects.push(effect); return this
|
|
147
147
|
}
|
|
148
148
|
|