etro 0.6.0 → 0.8.1
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 +4 -2
- package/CHANGELOG.md +85 -4
- package/CODE_OF_CONDUCT.md +5 -5
- package/CONTRIBUTING.md +33 -79
- package/README.md +93 -26
- package/dist/effect/base.d.ts +51 -0
- package/dist/effect/brightness.d.ts +16 -0
- package/dist/effect/channels.d.ts +23 -0
- package/dist/effect/chroma-key.d.ts +23 -0
- package/dist/effect/contrast.d.ts +15 -0
- package/dist/effect/elliptical-mask.d.ts +31 -0
- package/dist/effect/gaussian-blur.d.ts +60 -0
- package/dist/effect/grayscale.d.ts +7 -0
- package/dist/effect/index.d.ts +15 -0
- package/dist/effect/pixelate.d.ts +18 -0
- package/dist/effect/shader.d.ts +99 -0
- package/dist/effect/stack.d.ts +23 -0
- package/dist/effect/transform.d.ts +73 -0
- package/dist/etro-cjs.js +9387 -0
- package/dist/etro-iife.js +9390 -0
- package/dist/etro.d.ts +7 -0
- package/dist/event.d.ts +35 -0
- package/dist/index.d.ts +6 -0
- package/dist/layer/audio-source.d.ts +24 -0
- package/dist/layer/audio.d.ts +14 -0
- package/dist/layer/base.d.ts +82 -0
- package/dist/layer/image.d.ts +6 -0
- package/dist/layer/index.d.ts +11 -0
- package/dist/layer/text.d.ts +60 -0
- package/dist/layer/video.d.ts +11 -0
- package/dist/layer/visual-source.d.ts +32 -0
- package/dist/layer/visual.d.ts +58 -0
- package/dist/movie.d.ts +192 -0
- package/dist/object.d.ts +12 -0
- package/dist/util.d.ts +125 -0
- package/eslint.conf.js +2 -9
- package/eslint.example-conf.js +9 -0
- package/eslint.test-conf.js +1 -0
- package/eslint.typescript-conf.js +5 -0
- package/examples/application/readme-screenshot.html +16 -17
- package/examples/application/video-player.html +10 -11
- package/examples/application/webcam.html +6 -6
- package/examples/introduction/audio.html +30 -18
- package/examples/introduction/effects.html +37 -14
- package/examples/introduction/export.html +40 -27
- package/examples/introduction/functions.html +6 -4
- package/examples/introduction/hello-world1.html +9 -5
- package/examples/introduction/hello-world2.html +5 -5
- package/examples/introduction/keyframes.html +35 -23
- package/examples/introduction/media.html +26 -18
- package/examples/introduction/text.html +9 -5
- package/karma.conf.js +6 -4
- package/package.json +34 -13
- package/rollup.config.js +19 -3
- package/scripts/gen-effect-samples.html +27 -26
- package/scripts/save-effect-samples.js +14 -15
- package/src/effect/base.ts +115 -0
- package/src/effect/brightness.ts +43 -0
- package/src/effect/channels.ts +50 -0
- package/src/effect/chroma-key.ts +82 -0
- package/src/effect/contrast.ts +42 -0
- package/src/effect/elliptical-mask.ts +75 -0
- package/src/effect/gaussian-blur.ts +232 -0
- package/src/effect/grayscale.ts +34 -0
- package/src/effect/index.ts +22 -0
- package/src/effect/pixelate.ts +58 -0
- package/src/effect/shader.ts +557 -0
- package/src/effect/stack.ts +77 -0
- package/src/effect/transform.ts +193 -0
- package/src/etro.ts +26 -0
- package/src/event.ts +112 -0
- package/src/index.ts +8 -0
- package/src/layer/audio-source.ts +219 -0
- package/src/layer/audio.ts +34 -0
- package/src/layer/base.ts +175 -0
- package/src/layer/image.ts +8 -0
- package/src/layer/index.ts +13 -0
- package/src/layer/text.ts +138 -0
- package/src/layer/video.ts +15 -0
- package/src/layer/visual-source.ts +150 -0
- package/src/layer/visual.ts +197 -0
- package/src/movie.ts +701 -0
- package/src/object.ts +14 -0
- package/src/util.ts +466 -0
- package/tsconfig.json +8 -0
- package/dist/etro.js +0 -3397
- 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/screenshots/2019-08-17_0.png +0 -0
- package/spec/assets/effect/gaussian-blur-horizontal.png +0 -0
- package/spec/assets/effect/gaussian-blur-vertical.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 -352
- package/spec/event.spec.js +0 -25
- package/spec/layer.spec.js +0 -128
- package/spec/movie.spec.js +0 -154
- package/spec/util.spec.js +0 -285
- package/src/effect.js +0 -1265
- package/src/event.js +0 -78
- package/src/index.js +0 -23
- package/src/layer.js +0 -875
- package/src/movie.js +0 -636
- package/src/util.js +0 -487
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title>Keyframes in Etro</title>
|
|
5
|
+
<script src="../../dist/etro-iife.js"></script>
|
|
5
6
|
</head>
|
|
6
7
|
<body>
|
|
7
|
-
<script
|
|
8
|
-
import etro from '../../src/index.js'
|
|
8
|
+
<script>
|
|
9
9
|
let movie
|
|
10
10
|
window.addEventListener('load', () => {
|
|
11
11
|
const canvas = document.createElement('canvas')
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
})
|
|
16
16
|
|
|
17
17
|
const initMovie = canvas => {
|
|
18
|
-
movie = new etro.Movie(canvas)
|
|
18
|
+
movie = new etro.Movie({ canvas })
|
|
19
19
|
|
|
20
20
|
canvas.width = canvas.height = 400
|
|
21
21
|
movie
|
|
@@ -23,42 +23,54 @@
|
|
|
23
23
|
// Keyframes let you make a dynamic property that interpolates.
|
|
24
24
|
// For instance, you can set a layer's opacity to decrease over time, effectively making it fade out
|
|
25
25
|
// Numbers and objects interpolate (animate smoothly)
|
|
26
|
-
.addLayer(new etro.layer.Visual(
|
|
26
|
+
.addLayer(new etro.layer.Visual({
|
|
27
|
+
startTime: 0,
|
|
28
|
+
duration: 3,
|
|
27
29
|
// omitting width or height sets the respective element to fill the screen
|
|
28
30
|
background: 'green',
|
|
29
31
|
// opacity=1 @ 0s (relative to the layer) -> opacity=0 @ 1s (relative to the layer)
|
|
30
|
-
opacity:
|
|
32
|
+
opacity: new etro.KeyFrame([0, 1], [3, 0])
|
|
31
33
|
}))
|
|
32
34
|
// Because strings don't interpolate, you need to convert colors and fonts to objects
|
|
33
35
|
// for a smooth effect (which will then be automatically `.toString()`ed when set on the canvas
|
|
34
36
|
// context).
|
|
35
|
-
.addLayer(new etro.layer.Visual(
|
|
36
|
-
|
|
37
|
+
.addLayer(new etro.layer.Visual({
|
|
38
|
+
startTime: 3,
|
|
39
|
+
duration: 3,
|
|
40
|
+
background: new etro.KeyFrame([0, etro.parseColor('red')], [3, new etro.Color(0, 0, 255)])
|
|
37
41
|
}))
|
|
38
42
|
// You can use other types in keyframes, but they will be used sequentially without interpolation
|
|
39
|
-
.addLayer(new etro.layer.Text(
|
|
43
|
+
.addLayer(new etro.layer.Text({
|
|
44
|
+
startTime: 6,
|
|
45
|
+
duration: 3,
|
|
46
|
+
text: new etro.KeyFrame([0, 'Hello ...'], [1.5, '...world'])
|
|
47
|
+
}))
|
|
40
48
|
|
|
41
49
|
// When interpolating, you can specify how the keyframes will be interpolated
|
|
42
|
-
.addLayer(new etro.layer.Visual(
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
.addLayer(new etro.layer.Visual({
|
|
51
|
+
startTime: 9,
|
|
52
|
+
duration: 3,
|
|
53
|
+
width: new etro.KeyFrame([0, movie.width, etro.linearInterp], [3, 0]), // (obviously) linear
|
|
54
|
+
height: new etro.KeyFrame([0, movie.height, etro.linearInterp], [3, 0]), // (obviously) linear
|
|
45
55
|
background: 'blue'
|
|
46
56
|
}))
|
|
47
57
|
|
|
48
58
|
// Of course, you can have more than two keyframes
|
|
49
|
-
.addLayer(new etro.layer.Text(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
.addLayer(new etro.layer.Text({
|
|
60
|
+
text: 'Etro',
|
|
61
|
+
startTime: 12,
|
|
62
|
+
duration: 6,
|
|
63
|
+
background: new etro.KeyFrame(
|
|
64
|
+
[0, etro.parseColor('#0ff')],
|
|
65
|
+
[2, etro.parseColor('#ff0')],
|
|
66
|
+
[4, etro.parseColor('#f0f')],
|
|
67
|
+
[6, etro.parseColor('#fff')]
|
|
68
|
+
),
|
|
56
69
|
// let's just add another property (fonts can be parsed into objects just like colors)
|
|
57
|
-
font:
|
|
58
|
-
0
|
|
59
|
-
6
|
|
60
|
-
|
|
61
|
-
}
|
|
70
|
+
font: new etro.KeyFrame(
|
|
71
|
+
[0, etro.parseFont('28px monospace'), etro.cosineInterp],
|
|
72
|
+
[6, etro.parseFont('36px monospace')]
|
|
73
|
+
)
|
|
62
74
|
}))
|
|
63
75
|
.play()
|
|
64
76
|
}
|
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title>Media in Etro</title>
|
|
5
|
+
<script src="../../dist/etro-iife.js"></script>
|
|
5
6
|
<style> img, video {display: none;} </style>
|
|
6
7
|
</head>
|
|
7
8
|
<body>
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
<img src="../assets/lake.jpg"/>
|
|
10
|
+
<video src="../assets/desert.mp4"></video>
|
|
11
|
+
<audio src="../assets/strings.wav"></audio>
|
|
12
|
+
<button>Start</button>
|
|
13
|
+
<script>
|
|
14
|
+
const button = document.querySelector('button')
|
|
15
|
+
button.addEventListener('click', () => {
|
|
16
|
+
button.disabled = true
|
|
13
17
|
const canvas = document.createElement('canvas')
|
|
14
18
|
canvas.width = 600
|
|
15
19
|
canvas.height = 400
|
|
@@ -19,37 +23,41 @@
|
|
|
19
23
|
})
|
|
20
24
|
|
|
21
25
|
const initMovie = canvas => {
|
|
22
|
-
movie = new etro.Movie(canvas)
|
|
26
|
+
const movie = new etro.Movie({ canvas })
|
|
23
27
|
const video = document.querySelector('video')
|
|
24
28
|
movie.width = video.videoWidth
|
|
25
29
|
movie.height = video.videoHeight
|
|
26
30
|
movie
|
|
27
|
-
.addLayer(new etro.layer.Image(
|
|
31
|
+
.addLayer(new etro.layer.Image({
|
|
32
|
+
startTime: 0,
|
|
33
|
+
duration: 3,
|
|
34
|
+
source: document.querySelector('img'),
|
|
28
35
|
// crop @ (150, 150) extending (200, 200)
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
sourceX: 100,
|
|
37
|
+
sourceY: 100,
|
|
38
|
+
sourceWidth: 400,
|
|
39
|
+
sourceHeight: 400,
|
|
31
40
|
x: 100,
|
|
32
41
|
y: 100,
|
|
33
|
-
clipWidth: 400,
|
|
34
|
-
clipHeight: 400,
|
|
35
42
|
width: 400,
|
|
36
43
|
height: 400
|
|
37
44
|
}))
|
|
38
|
-
.addLayer(new etro.layer.Video(
|
|
45
|
+
.addLayer(new etro.layer.Video({
|
|
46
|
+
source: video,
|
|
47
|
+
startTime: 3,
|
|
39
48
|
// trim video to only include 3 seconds starting 2 minutes into the video in the video
|
|
40
|
-
|
|
49
|
+
sourceStartTime: 5,
|
|
41
50
|
duration: 3
|
|
42
51
|
}))
|
|
43
|
-
.addLayer(new etro.layer.Audio(
|
|
44
|
-
|
|
52
|
+
.addLayer(new etro.layer.Audio({
|
|
53
|
+
startTime: 6,
|
|
54
|
+
source: document.querySelector('audio'),
|
|
55
|
+
sourceStartTime: 9, // start audio at 9s
|
|
45
56
|
duration: 3 // last 3s
|
|
46
57
|
// volume: 0.25 // 25% of default volume (same as setting volume attribute on audio element)
|
|
47
58
|
}))
|
|
48
59
|
.play()
|
|
49
60
|
}
|
|
50
61
|
</script>
|
|
51
|
-
<img src="../assets/sample.jpg"/>
|
|
52
|
-
<video src="../assets/sample.ogv"></video>
|
|
53
|
-
<audio src="../assets/sample.wav"></audio>
|
|
54
62
|
</body>
|
|
55
63
|
</html>
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title>Text in Etro</title>
|
|
5
|
+
<script src="../../dist/etro-iife.js"></script>
|
|
5
6
|
</head>
|
|
6
7
|
<body>
|
|
7
|
-
<script
|
|
8
|
-
import etro from '../../src/index.js'
|
|
8
|
+
<script>
|
|
9
9
|
let movie
|
|
10
10
|
window.addEventListener('load', () => {
|
|
11
11
|
const canvas = document.createElement('canvas')
|
|
@@ -17,9 +17,13 @@
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
const initMovie = canvas => {
|
|
20
|
-
movie = new etro.Movie(canvas)
|
|
21
|
-
movie.addLayer(new etro.layer.Text(
|
|
22
|
-
|
|
20
|
+
movie = new etro.Movie({ canvas })
|
|
21
|
+
movie.addLayer(new etro.layer.Text({
|
|
22
|
+
startTime: 0,
|
|
23
|
+
duration: 4,
|
|
24
|
+
text: 'Hello world',
|
|
25
|
+
font: '24px monospace',
|
|
26
|
+
color: 'blue'
|
|
23
27
|
})).play()
|
|
24
28
|
}
|
|
25
29
|
</script>
|
package/karma.conf.js
CHANGED
|
@@ -9,12 +9,14 @@ module.exports = function (config) {
|
|
|
9
9
|
|
|
10
10
|
// frameworks to use
|
|
11
11
|
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
|
12
|
-
frameworks: ['jasmine'],
|
|
12
|
+
frameworks: ['jasmine', 'requirejs', 'es6-shim'],
|
|
13
13
|
|
|
14
14
|
// list of files / patterns to load in the browser
|
|
15
15
|
files: [
|
|
16
|
-
'dist/etro.js',
|
|
17
|
-
'spec/*.spec.js',
|
|
16
|
+
'dist/etro-iife.js',
|
|
17
|
+
{ pattern: 'spec/*.spec.js', included: false },
|
|
18
|
+
{ pattern: 'node_modules/resemblejs/*.js', included: false },
|
|
19
|
+
'spec/main.js',
|
|
18
20
|
{ pattern: 'spec/assets/**/*', included: false }
|
|
19
21
|
],
|
|
20
22
|
|
|
@@ -30,7 +32,7 @@ module.exports = function (config) {
|
|
|
30
32
|
// test results reporter to use
|
|
31
33
|
// possible values: 'dots', 'progress'
|
|
32
34
|
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
|
33
|
-
reporters: ['
|
|
35
|
+
reporters: ['dots'],
|
|
34
36
|
|
|
35
37
|
// web server port
|
|
36
38
|
port: 9876,
|
package/package.json
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "etro",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"browser": "
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"description": "An extendable video-editing framework for the browser and Node",
|
|
5
|
+
"browser": "dist/etro-cjs.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
6
7
|
"directories": {
|
|
7
8
|
"doc": "docs",
|
|
8
9
|
"example": "examples",
|
|
9
10
|
"test": "spec"
|
|
10
11
|
},
|
|
11
|
-
"dependencies": {
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"standardized-audio-context": "^25.1.13"
|
|
14
|
+
},
|
|
12
15
|
"devDependencies": {
|
|
16
|
+
"@types/dom-mediacapture-record": "^1.0.7",
|
|
17
|
+
"@typescript-eslint/eslint-plugin": "^4.15.2",
|
|
18
|
+
"@typescript-eslint/parser": "^4.15.2",
|
|
13
19
|
"docdash": "^1.1.1",
|
|
20
|
+
"ecstatic": ">=4.1.3",
|
|
14
21
|
"eslint": "^6.5.1",
|
|
15
22
|
"eslint-config-standard": "^14.1.0",
|
|
16
23
|
"eslint-plugin-html": "^6.0.0",
|
|
@@ -19,28 +26,36 @@
|
|
|
19
26
|
"eslint-plugin-promise": "^4.2.1",
|
|
20
27
|
"eslint-plugin-standard": "^4.0.1",
|
|
21
28
|
"ev": "0.0.7",
|
|
22
|
-
"http-server": "^0.
|
|
29
|
+
"http-server": "^0.12.3",
|
|
23
30
|
"jasmine": "^3.4.0",
|
|
24
31
|
"jsdoc": "^3.6.3",
|
|
25
32
|
"jsdoc-export-default-interop": "^0.3.1",
|
|
26
|
-
"karma": "^
|
|
33
|
+
"karma": "^6.1.1",
|
|
27
34
|
"karma-chrome-launcher": "^3.1.0",
|
|
35
|
+
"karma-es6-shim": "^1.0.0",
|
|
28
36
|
"karma-jasmine": "^2.0.1",
|
|
37
|
+
"karma-requirejs": "^1.1.0",
|
|
38
|
+
"karma-super-dots-reporter": "^0.2.0",
|
|
29
39
|
"puppeteer": "^2.0.0",
|
|
40
|
+
"resemblejs": "^3.2.5",
|
|
30
41
|
"rollup": "^1.19.4",
|
|
42
|
+
"rollup-plugin-cleaner": "^1.0.0",
|
|
31
43
|
"rollup-plugin-eslint": "^7.0.0",
|
|
32
44
|
"rollup-plugin-node-resolve": "^5.2.0",
|
|
33
|
-
"rollup-plugin-
|
|
45
|
+
"rollup-plugin-typescript2": "^0.29.0",
|
|
46
|
+
"rollup-plugin-uglify-es": "^0.0.1",
|
|
47
|
+
"typedoc": "^0.20.34",
|
|
48
|
+
"typescript": "^4.1.3"
|
|
34
49
|
},
|
|
35
50
|
"scripts": {
|
|
36
51
|
"build": "rollup -c",
|
|
37
|
-
"doc": "rm -rf docs && npx
|
|
52
|
+
"doc": "rm -rf docs && npx typedoc src/etro.ts --excludePrivate --readme none --theme minimal",
|
|
53
|
+
"assets": "git fetch origin example-assets:example-assets && git cherry-pick example-assets && git reset --soft HEAD^ && git reset HEAD examples/assets",
|
|
38
54
|
"effects": "node scripts/save-effect-samples.js",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"lint": "npm run --silent lint:main && npm run --silent lint:test",
|
|
42
|
-
"lint:main": "eslint -c eslint.conf.js --ext .js,.html src examples",
|
|
55
|
+
"lint": "npm run --silent lint:main && npm run --silent lint:test && npm run --silent lint:examples",
|
|
56
|
+
"lint:main": "eslint -c eslint.typescript-conf.js --ext .ts src",
|
|
43
57
|
"lint:test": "eslint -c eslint.test-conf.js spec",
|
|
58
|
+
"lint:examples": "eslint -c eslint.example-conf.js --ext .html examples",
|
|
44
59
|
"start": "http-server",
|
|
45
60
|
"test": "karma start"
|
|
46
61
|
},
|
|
@@ -49,10 +64,16 @@
|
|
|
49
64
|
"url": "git+https://github.com/etro-js/etro.git"
|
|
50
65
|
},
|
|
51
66
|
"keywords": [
|
|
67
|
+
"video",
|
|
68
|
+
"audio",
|
|
69
|
+
"blob",
|
|
52
70
|
"video-editing",
|
|
53
71
|
"video-editor",
|
|
72
|
+
"video-manipulation",
|
|
54
73
|
"browser",
|
|
55
|
-
"
|
|
74
|
+
"nodejs",
|
|
75
|
+
"api-driven",
|
|
76
|
+
"etro"
|
|
56
77
|
],
|
|
57
78
|
"author": "Caleb Sacks (https://calebsacks.me)",
|
|
58
79
|
"license": "GPL-3.0",
|
package/rollup.config.js
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
// import uglify from "rollup-plugin-uglify-es";
|
|
2
|
+
import cleaner from 'rollup-plugin-cleaner'
|
|
2
3
|
import resolve from 'rollup-plugin-node-resolve'
|
|
4
|
+
import typescript from 'rollup-plugin-typescript2'
|
|
3
5
|
|
|
4
6
|
export default [
|
|
5
7
|
// iife bundle
|
|
6
8
|
{
|
|
7
|
-
input: 'src/index.
|
|
8
|
-
output: { file: 'dist/etro.js', format: 'iife', name: 'etro' },
|
|
9
|
-
plugins: [
|
|
9
|
+
input: 'src/index.ts',
|
|
10
|
+
output: { file: 'dist/etro-iife.js', format: 'iife', name: 'etro' },
|
|
11
|
+
plugins: [
|
|
12
|
+
cleaner({
|
|
13
|
+
targets: ['dist']
|
|
14
|
+
}),
|
|
15
|
+
typescript(),
|
|
16
|
+
resolve()
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
input: 'src/index.ts',
|
|
21
|
+
output: { file: 'dist/etro-cjs.js', format: 'cjs' },
|
|
22
|
+
plugins: [
|
|
23
|
+
typescript(),
|
|
24
|
+
resolve()
|
|
25
|
+
]
|
|
10
26
|
}
|
|
11
27
|
// // es6 module bundle
|
|
12
28
|
// {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<!DOCTYPE html>
|
|
3
3
|
<html>
|
|
4
4
|
<body>
|
|
5
|
-
<script src="../dist/etro.js"></script>
|
|
5
|
+
<script src="../dist/etro-iife.js"></script>
|
|
6
6
|
<script>
|
|
7
7
|
/**
|
|
8
8
|
* Prepares a canvas for saving
|
|
@@ -50,12 +50,14 @@
|
|
|
50
50
|
buffer.height = original.height
|
|
51
51
|
const ctx = buffer.getContext('2d')
|
|
52
52
|
ctx.drawImage(original, 0, 0)
|
|
53
|
-
|
|
54
|
-
// do effect
|
|
55
|
-
effect.apply({
|
|
53
|
+
const movie = {
|
|
56
54
|
canvas: buffer, cctx: ctx,
|
|
57
55
|
width: original.width, height: original.height
|
|
58
|
-
}
|
|
56
|
+
}
|
|
57
|
+
// for util.cache()
|
|
58
|
+
effect._target = { movie }
|
|
59
|
+
// Run effect
|
|
60
|
+
effect.apply(movie)
|
|
59
61
|
|
|
60
62
|
save(buffer, path)
|
|
61
63
|
}
|
|
@@ -65,34 +67,33 @@
|
|
|
65
67
|
save(original, 'original.png')
|
|
66
68
|
|
|
67
69
|
const samples = {
|
|
68
|
-
'gaussian-blur-horizontal.png': new etro.effect.GaussianBlurHorizontal(5),
|
|
69
|
-
'gaussian-blur-vertical.png': new etro.effect.GaussianBlurVertical(5),
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
'transform/multiply.png': new etro.effect.Transform(
|
|
87
|
-
new etro.effect.Transform.Matrix().scale(2, 2)
|
|
70
|
+
'gaussian-blur-horizontal.png': new etro.effect.GaussianBlurHorizontal({ radius: 5 }),
|
|
71
|
+
'gaussian-blur-vertical.png': new etro.effect.GaussianBlurVertical({ radius: 5 }),
|
|
72
|
+
'grayscale.png': new etro.effect.Grayscale(),
|
|
73
|
+
'pixelate.png': new etro.effect.Pixelate({ pixelSize: 3 }),
|
|
74
|
+
'transform/translate.png': new etro.effect.Transform({
|
|
75
|
+
matrix: new etro.effect.Transform.Matrix().translate(-3, 5)
|
|
76
|
+
}),
|
|
77
|
+
'transform/scale.png': new etro.effect.Transform({
|
|
78
|
+
matrix: new etro.effect.Transform.Matrix().scale(2, 2)
|
|
79
|
+
}),
|
|
80
|
+
'transform/scale-fraction.png': new etro.effect.Transform({
|
|
81
|
+
matrix: new etro.effect.Transform.Matrix().scale(0.5, 0.5)
|
|
82
|
+
}),
|
|
83
|
+
'transform/rotate.png': new etro.effect.Transform({
|
|
84
|
+
matrix: new etro.effect.Transform.Matrix().rotate(Math.PI / 6)
|
|
85
|
+
}),
|
|
86
|
+
'transform/multiply.png': new etro.effect.Transform({
|
|
87
|
+
matrix: new etro.effect.Transform.Matrix().scale(2, 2)
|
|
88
88
|
.multiply(new etro.effect.Transform.Matrix().translate(-3, 5))
|
|
89
|
-
)
|
|
89
|
+
})
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
for (let path in samples) {
|
|
93
93
|
const effect = samples[path]
|
|
94
94
|
saveSample(original, effect, path)
|
|
95
95
|
}
|
|
96
|
+
window.done = true
|
|
96
97
|
}
|
|
97
98
|
</script>
|
|
98
99
|
</body>
|
|
@@ -23,21 +23,20 @@ function createDirs(filePath) {
|
|
|
23
23
|
const browser = await puppeteer.launch()
|
|
24
24
|
const page = await browser.newPage()
|
|
25
25
|
|
|
26
|
-
page.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
26
|
+
await page.goto(`file://${__dirname}/gen-effect-samples.html`)
|
|
27
|
+
await page.waitForFunction(() => window.done);
|
|
28
|
+
|
|
29
|
+
const items = await page.$$eval('p', elems => elems.map(p => {
|
|
30
|
+
return { data: p.innerHTML, path: p.dataset.path }
|
|
31
|
+
}))
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
})
|
|
40
|
-
await browser.close()
|
|
33
|
+
items.forEach(item => {
|
|
34
|
+
// remove prefix and save to png
|
|
35
|
+
const buffer = Buffer.from(item.data.replace(/^data:image\/png;base64,/, ''), 'base64')
|
|
36
|
+
console.log(`writing ${item.path} ...`)
|
|
37
|
+
const path = projectDir + '/spec/assets/effect/' + item.path
|
|
38
|
+
createDirs(path)
|
|
39
|
+
fs.writeFileSync(path, buffer)
|
|
41
40
|
})
|
|
42
|
-
await
|
|
41
|
+
await browser.close()
|
|
43
42
|
})()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { watchPublic } from '../util'
|
|
2
|
+
import { publish, subscribe } from '../event'
|
|
3
|
+
import { Movie } from '../movie'
|
|
4
|
+
import { Visual } from '../layer/index'
|
|
5
|
+
import BaseObject from '../object'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Modifies the visual contents of a layer.
|
|
9
|
+
*/
|
|
10
|
+
export class Base implements BaseObject {
|
|
11
|
+
type: string
|
|
12
|
+
publicExcludes: string[]
|
|
13
|
+
propertyFilters: Record<string, <T>(value: T) => T>
|
|
14
|
+
|
|
15
|
+
enabled: boolean
|
|
16
|
+
|
|
17
|
+
private _target: Movie | Visual
|
|
18
|
+
/**
|
|
19
|
+
* The number of times this effect has been attached to a target minus the
|
|
20
|
+
* number of times it's been detached. (Used for the target's array proxy with
|
|
21
|
+
* `unshift`)
|
|
22
|
+
*/
|
|
23
|
+
private _occurrenceCount: number
|
|
24
|
+
|
|
25
|
+
constructor () {
|
|
26
|
+
const newThis = watchPublic(this) as Base // proxy that will be returned by constructor
|
|
27
|
+
|
|
28
|
+
newThis.enabled = true
|
|
29
|
+
newThis._occurrenceCount = 0
|
|
30
|
+
newThis._target = null
|
|
31
|
+
|
|
32
|
+
// Propogate up to target
|
|
33
|
+
subscribe(newThis, 'effect.change.modify', event => {
|
|
34
|
+
if (!newThis._target)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
const type = `${newThis._target.type}.change.effect.modify`
|
|
38
|
+
publish(newThis._target, type, { ...event, target: newThis._target, source: newThis, type })
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return newThis
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Attaches this effect to `target` if not already attached.
|
|
46
|
+
* @ignore
|
|
47
|
+
*/
|
|
48
|
+
tryAttach (target: Movie | Visual): void {
|
|
49
|
+
if (this._occurrenceCount === 0)
|
|
50
|
+
this.attach(target)
|
|
51
|
+
|
|
52
|
+
this._occurrenceCount++
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
attach (movie: Movie | Visual): void {
|
|
56
|
+
this._target = movie
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Dettaches this effect from its target if the number of times `tryDetach`
|
|
61
|
+
* has been called (including this call) equals the number of times
|
|
62
|
+
* `tryAttach` has been called.
|
|
63
|
+
*
|
|
64
|
+
* @ignore
|
|
65
|
+
*/
|
|
66
|
+
tryDetach (): void {
|
|
67
|
+
if (this._target === null)
|
|
68
|
+
throw new Error('No movie to detach from')
|
|
69
|
+
|
|
70
|
+
this._occurrenceCount--
|
|
71
|
+
// If this effect occurs in another place in the containing array, do not
|
|
72
|
+
// unset _target. (For calling `unshift` on the `layers` proxy)
|
|
73
|
+
if (this._occurrenceCount === 0)
|
|
74
|
+
this.detach()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
detach (): void {
|
|
78
|
+
this._target = null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// subclasses must implement apply
|
|
82
|
+
/**
|
|
83
|
+
* Apply this effect to a target at the given time
|
|
84
|
+
*
|
|
85
|
+
* @param target
|
|
86
|
+
* @param reltime - the movie's current time relative to the layer
|
|
87
|
+
* (will soon be replaced with an instance getter)
|
|
88
|
+
* @abstract
|
|
89
|
+
*/
|
|
90
|
+
apply (target: Movie | Visual, reltime: number): void {} // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The current time of the target
|
|
94
|
+
*/
|
|
95
|
+
get currentTime (): number {
|
|
96
|
+
return this._target ? this._target.currentTime : undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get parent (): Movie | Visual {
|
|
100
|
+
return this._target
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get movie (): Movie {
|
|
104
|
+
return this._target ? this._target.movie : undefined
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getDefaultOptions (): Record<string, unknown> {
|
|
108
|
+
return {}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// id for events (independent of instance, but easy to access when on prototype
|
|
112
|
+
// chain)
|
|
113
|
+
Base.prototype.type = 'effect'
|
|
114
|
+
Base.prototype.publicExcludes = []
|
|
115
|
+
Base.prototype.propertyFilters = {}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Dynamic } from '../util'
|
|
2
|
+
import { Shader } from './shader'
|
|
3
|
+
|
|
4
|
+
export interface BrightnessOptions {
|
|
5
|
+
brightness?: Dynamic<number>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Changes the brightness
|
|
10
|
+
*/
|
|
11
|
+
export class Brightness extends Shader {
|
|
12
|
+
brightness: Dynamic<number>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param [brightness=0] - the value to add to each pixel's color
|
|
16
|
+
* channels (between -255 and 255)
|
|
17
|
+
*/
|
|
18
|
+
constructor (options: BrightnessOptions = {}) {
|
|
19
|
+
super({
|
|
20
|
+
fragmentSource: `
|
|
21
|
+
precision mediump float;
|
|
22
|
+
|
|
23
|
+
uniform sampler2D u_Source;
|
|
24
|
+
uniform float u_Brightness;
|
|
25
|
+
|
|
26
|
+
varying highp vec2 v_TextureCoord;
|
|
27
|
+
|
|
28
|
+
void main() {
|
|
29
|
+
vec4 color = texture2D(u_Source, v_TextureCoord);
|
|
30
|
+
vec3 rgb = clamp(color.rgb + u_Brightness / 255.0, 0.0, 1.0);
|
|
31
|
+
gl_FragColor = vec4(rgb, color.a);
|
|
32
|
+
}
|
|
33
|
+
`,
|
|
34
|
+
uniforms: {
|
|
35
|
+
brightness: '1f'
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
/**
|
|
39
|
+
* The value to add to each pixel's color channels (between -255 and 255)
|
|
40
|
+
*/
|
|
41
|
+
this.brightness = options.brightness || 0
|
|
42
|
+
}
|
|
43
|
+
}
|