etro 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/CONTRIBUTING.md +25 -34
- package/README.md +9 -17
- package/dist/custom-array.d.ts +10 -0
- package/dist/effect/base.d.ts +10 -1
- package/dist/effect/shader.d.ts +11 -1
- package/dist/effect/stack.d.ts +6 -2
- package/dist/etro-cjs.js +1182 -592
- package/dist/etro-iife.js +1182 -592
- package/dist/event.d.ts +10 -5
- package/dist/layer/audio-source.d.ts +9 -4
- package/dist/layer/audio.d.ts +15 -2
- package/dist/layer/base.d.ts +49 -3
- package/dist/layer/image.d.ts +15 -1
- package/dist/layer/text.d.ts +6 -3
- package/dist/layer/video.d.ts +13 -1
- package/dist/layer/visual-source.d.ts +18 -3
- package/dist/layer/visual.d.ts +11 -7
- package/dist/movie/effects.d.ts +6 -0
- package/dist/movie/index.d.ts +1 -0
- package/dist/movie/layers.d.ts +6 -0
- package/dist/movie/movie.d.ts +260 -0
- package/dist/object.d.ts +9 -2
- package/dist/util.d.ts +4 -10
- package/eslint.conf.js +4 -2
- package/eslint.test-conf.js +1 -2
- package/karma.conf.js +10 -14
- package/package.json +23 -22
- package/scripts/{gen-effect-samples.html → effect/gen-effect-samples.html} +24 -0
- package/scripts/{save-effect-samples.js → effect/save-effect-samples.js} +1 -1
- package/src/custom-array.ts +43 -0
- package/src/effect/base.ts +23 -22
- package/src/effect/gaussian-blur.ts +11 -6
- package/src/effect/pixelate.ts +3 -3
- package/src/effect/shader.ts +33 -27
- package/src/effect/stack.ts +43 -30
- package/src/effect/transform.ts +16 -9
- package/src/event.ts +111 -21
- package/src/layer/audio-source.ts +60 -20
- package/src/layer/audio.ts +25 -3
- package/src/layer/base.ts +79 -25
- package/src/layer/image.ts +26 -2
- package/src/layer/text.ts +11 -4
- package/src/layer/video.ts +31 -4
- package/src/layer/visual-source.ts +70 -8
- package/src/layer/visual.ts +57 -35
- package/src/movie/effects.ts +26 -0
- package/src/movie/index.ts +1 -0
- package/src/movie/layers.ts +26 -0
- package/src/movie/movie.ts +855 -0
- package/src/object.ts +9 -2
- package/src/util.ts +68 -89
- package/tsconfig.json +3 -1
- package/dist/movie.d.ts +0 -201
- package/examples/application/readme-screenshot.html +0 -85
- package/examples/application/video-player.html +0 -130
- package/examples/application/webcam.html +0 -28
- package/examples/introduction/audio.html +0 -64
- package/examples/introduction/effects.html +0 -79
- package/examples/introduction/export.html +0 -83
- package/examples/introduction/functions.html +0 -37
- package/examples/introduction/hello-world1.html +0 -37
- package/examples/introduction/hello-world2.html +0 -32
- package/examples/introduction/keyframes.html +0 -79
- package/examples/introduction/media.html +0 -63
- package/examples/introduction/text.html +0 -31
- package/private-todo.txt +0 -70
- package/src/movie.ts +0 -742
package/dist/etro-iife.js
CHANGED
|
@@ -39,11 +39,68 @@ var etro = (function () {
|
|
|
39
39
|
return t;
|
|
40
40
|
};
|
|
41
41
|
return __assign.apply(this, arguments);
|
|
42
|
-
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
45
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
46
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
47
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
48
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
49
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
50
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function __generator(thisArg, body) {
|
|
55
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
56
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
57
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
58
|
+
function step(op) {
|
|
59
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
60
|
+
while (_) try {
|
|
61
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
62
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
63
|
+
switch (op[0]) {
|
|
64
|
+
case 0: case 1: t = op; break;
|
|
65
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
66
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
67
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
68
|
+
default:
|
|
69
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
70
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
71
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
72
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
73
|
+
if (t[2]) _.ops.pop();
|
|
74
|
+
_.trys.pop(); continue;
|
|
75
|
+
}
|
|
76
|
+
op = body.call(thisArg, _);
|
|
77
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
78
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
43
81
|
|
|
44
82
|
/**
|
|
45
83
|
* @module event
|
|
46
84
|
*/
|
|
85
|
+
var DeprecatedEvent = /** @class */ (function () {
|
|
86
|
+
function DeprecatedEvent(replacement, message) {
|
|
87
|
+
if (message === void 0) { message = undefined; }
|
|
88
|
+
this.replacement = replacement;
|
|
89
|
+
this.message = message;
|
|
90
|
+
}
|
|
91
|
+
DeprecatedEvent.prototype.toString = function () {
|
|
92
|
+
var str = '';
|
|
93
|
+
if (this.replacement) {
|
|
94
|
+
str += "Use ".concat(this.replacement, " instead.");
|
|
95
|
+
}
|
|
96
|
+
if (this.message) {
|
|
97
|
+
str += " ".concat(this.message);
|
|
98
|
+
}
|
|
99
|
+
return str;
|
|
100
|
+
};
|
|
101
|
+
return DeprecatedEvent;
|
|
102
|
+
}());
|
|
103
|
+
var deprecatedEvents = {};
|
|
47
104
|
/**
|
|
48
105
|
* An event type
|
|
49
106
|
* @private
|
|
@@ -53,11 +110,14 @@ var etro = (function () {
|
|
|
53
110
|
this._parts = id.split('.');
|
|
54
111
|
}
|
|
55
112
|
TypeId.prototype.contains = function (other) {
|
|
56
|
-
if (other._parts.length > this._parts.length)
|
|
113
|
+
if (other._parts.length > this._parts.length) {
|
|
57
114
|
return false;
|
|
58
|
-
|
|
59
|
-
|
|
115
|
+
}
|
|
116
|
+
for (var i = 0; i < other._parts.length; i++) {
|
|
117
|
+
if (other._parts[i] !== this._parts[i]) {
|
|
60
118
|
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
61
121
|
return true;
|
|
62
122
|
};
|
|
63
123
|
TypeId.prototype.toString = function () {
|
|
@@ -65,23 +125,50 @@ var etro = (function () {
|
|
|
65
125
|
};
|
|
66
126
|
return TypeId;
|
|
67
127
|
}());
|
|
128
|
+
function deprecate(type, newType, message) {
|
|
129
|
+
if (message === void 0) { message = undefined; }
|
|
130
|
+
deprecatedEvents[type] = new DeprecatedEvent(newType, message);
|
|
131
|
+
}
|
|
132
|
+
function subscribeOnce(target, type, listener) {
|
|
133
|
+
var wrapped = function (event) {
|
|
134
|
+
unsubscribe(target, wrapped);
|
|
135
|
+
listener(event);
|
|
136
|
+
};
|
|
137
|
+
subscribe(target, type, wrapped);
|
|
138
|
+
}
|
|
139
|
+
function subscribeMany(target, type, listener) {
|
|
140
|
+
if (!listeners.has(target)) {
|
|
141
|
+
listeners.set(target, []);
|
|
142
|
+
}
|
|
143
|
+
listeners.get(target).push({ type: new TypeId(type), listener: listener });
|
|
144
|
+
}
|
|
68
145
|
/**
|
|
69
|
-
* Listen for an event or category of events
|
|
146
|
+
* Listen for an event or category of events.
|
|
70
147
|
*
|
|
71
|
-
* @param target -
|
|
148
|
+
* @param target - an etro object
|
|
72
149
|
* @param type - the id of the type (can contain subtypes, such as
|
|
73
150
|
* "type.subtype")
|
|
74
151
|
* @param listener
|
|
152
|
+
* @param options - options
|
|
153
|
+
* @param options.once - if true, the listener will only be called once
|
|
75
154
|
*/
|
|
76
|
-
function subscribe(target, type, listener) {
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
155
|
+
function subscribe(target, type, listener, options) {
|
|
156
|
+
if (options === void 0) { options = {}; }
|
|
157
|
+
// Check if this event is deprecated.
|
|
158
|
+
if (Object.keys(deprecatedEvents).includes(type)) {
|
|
159
|
+
console.warn("Event ".concat(type, " is deprecated. ").concat(deprecatedEvents[type]));
|
|
160
|
+
}
|
|
161
|
+
if (options.once) {
|
|
162
|
+
subscribeOnce(target, type, listener);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
subscribeMany(target, type, listener);
|
|
166
|
+
}
|
|
80
167
|
}
|
|
81
168
|
/**
|
|
82
169
|
* Remove an event listener
|
|
83
170
|
*
|
|
84
|
-
* @param target -
|
|
171
|
+
* @param target - an etro object
|
|
85
172
|
* @param type - the id of the type (can contain subtypes, such as
|
|
86
173
|
* "type.subtype")
|
|
87
174
|
* @param listener
|
|
@@ -89,33 +176,36 @@ var etro = (function () {
|
|
|
89
176
|
function unsubscribe(target, listener) {
|
|
90
177
|
// Make sure `listener` has been added with `subscribe`.
|
|
91
178
|
if (!listeners.has(target) ||
|
|
92
|
-
!listeners.get(target).map(function (pair) { return pair.listener; }).includes(listener))
|
|
179
|
+
!listeners.get(target).map(function (pair) { return pair.listener; }).includes(listener)) {
|
|
93
180
|
throw new Error('No matching event listener to remove');
|
|
181
|
+
}
|
|
94
182
|
var removed = listeners.get(target)
|
|
95
183
|
.filter(function (pair) { return pair.listener !== listener; });
|
|
96
184
|
listeners.set(target, removed);
|
|
97
185
|
}
|
|
98
186
|
/**
|
|
99
|
-
*
|
|
187
|
+
* Publish an event to all listeners without checking if it is deprecated.
|
|
100
188
|
*
|
|
101
|
-
* @param target
|
|
102
|
-
* @param type
|
|
103
|
-
*
|
|
104
|
-
* @
|
|
189
|
+
* @param target
|
|
190
|
+
* @param type
|
|
191
|
+
* @param event
|
|
192
|
+
* @returns
|
|
105
193
|
*/
|
|
106
|
-
function
|
|
194
|
+
function _publish(target, type, event) {
|
|
107
195
|
event.target = target; // could be a proxy
|
|
108
196
|
event.type = type;
|
|
109
197
|
var t = new TypeId(type);
|
|
110
|
-
if (!listeners.has(target))
|
|
198
|
+
if (!listeners.has(target)) {
|
|
111
199
|
// No event fired
|
|
112
200
|
return null;
|
|
201
|
+
}
|
|
113
202
|
// Call event listeners for this event.
|
|
114
203
|
var listenersForType = [];
|
|
115
204
|
for (var i = 0; i < listeners.get(target).length; i++) {
|
|
116
205
|
var item = listeners.get(target)[i];
|
|
117
|
-
if (t.contains(item.type))
|
|
206
|
+
if (t.contains(item.type)) {
|
|
118
207
|
listenersForType.push(item.listener);
|
|
208
|
+
}
|
|
119
209
|
}
|
|
120
210
|
for (var i = 0; i < listenersForType.length; i++) {
|
|
121
211
|
var listener = listenersForType[i];
|
|
@@ -123,9 +213,33 @@ var etro = (function () {
|
|
|
123
213
|
}
|
|
124
214
|
return event;
|
|
125
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Emits an event to all listeners
|
|
218
|
+
*
|
|
219
|
+
* @param target - an etro object
|
|
220
|
+
* @param type - the id of the type (can contain subtypes, such as
|
|
221
|
+
* "type.subtype")
|
|
222
|
+
* @param event - any additional event data
|
|
223
|
+
*/
|
|
224
|
+
function publish(target, type, event) {
|
|
225
|
+
// Check if this event is deprecated only if it can be replaced.
|
|
226
|
+
if (Object.keys(deprecatedEvents).includes(type) && deprecatedEvents[type].replacement) {
|
|
227
|
+
throw new Error("Event ".concat(type, " is deprecated. ").concat(deprecatedEvents[type]));
|
|
228
|
+
}
|
|
229
|
+
// Check for deprecated events that this event replaces.
|
|
230
|
+
for (var deprecated in deprecatedEvents) {
|
|
231
|
+
var deprecatedEvent = deprecatedEvents[deprecated];
|
|
232
|
+
if (type === deprecatedEvent.replacement) {
|
|
233
|
+
_publish(target, deprecated, __assign({}, event));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return _publish(target, type, event);
|
|
237
|
+
}
|
|
126
238
|
var listeners = new WeakMap();
|
|
127
239
|
|
|
128
240
|
var event = /*#__PURE__*/Object.freeze({
|
|
241
|
+
__proto__: null,
|
|
242
|
+
deprecate: deprecate,
|
|
129
243
|
subscribe: subscribe,
|
|
130
244
|
unsubscribe: unsubscribe,
|
|
131
245
|
publish: publish
|
|
@@ -143,8 +257,9 @@ var etro = (function () {
|
|
|
143
257
|
function getPropertyDescriptor(obj, name) {
|
|
144
258
|
do {
|
|
145
259
|
var propDesc = Object.getOwnPropertyDescriptor(obj, name);
|
|
146
|
-
if (propDesc)
|
|
260
|
+
if (propDesc) {
|
|
147
261
|
return propDesc;
|
|
262
|
+
}
|
|
148
263
|
obj = Object.getPrototypeOf(obj);
|
|
149
264
|
} while (obj);
|
|
150
265
|
return undefined;
|
|
@@ -153,36 +268,45 @@ var etro = (function () {
|
|
|
153
268
|
* Merges `options` with `defaultOptions`, and then copies the properties with
|
|
154
269
|
* the keys in `defaultOptions` from the merged object to `destObj`.
|
|
155
270
|
*
|
|
271
|
+
* @deprecated Each option should be copied individually, and the default value
|
|
272
|
+
* should be set in the constructor. See
|
|
273
|
+
* {@link https://github.com/etro-js/etro/issues/131} for more info.
|
|
274
|
+
*
|
|
156
275
|
* @return
|
|
157
276
|
*/
|
|
158
277
|
// TODO: Make methods like getDefaultOptions private
|
|
159
278
|
function applyOptions(options, destObj) {
|
|
160
279
|
var defaultOptions = destObj.getDefaultOptions();
|
|
161
280
|
// Validate; make sure `keys` doesn't have any extraneous items
|
|
162
|
-
for (var option in options)
|
|
281
|
+
for (var option in options) {
|
|
163
282
|
// eslint-disable-next-line no-prototype-builtins
|
|
164
|
-
if (!defaultOptions.hasOwnProperty(option))
|
|
283
|
+
if (!defaultOptions.hasOwnProperty(option)) {
|
|
165
284
|
throw new Error("Invalid option: '" + option + "'");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
166
287
|
// Merge options and defaultOptions
|
|
167
288
|
options = __assign(__assign({}, defaultOptions), options);
|
|
168
289
|
// Copy options
|
|
169
290
|
for (var option in options) {
|
|
170
291
|
var propDesc = getPropertyDescriptor(destObj, option);
|
|
171
292
|
// Update the property as long as the property has not been set (unless if it has a setter)
|
|
172
|
-
if (!propDesc || propDesc.set)
|
|
293
|
+
if (!propDesc || propDesc.set) {
|
|
173
294
|
destObj[option] = options[option];
|
|
295
|
+
}
|
|
174
296
|
}
|
|
175
297
|
}
|
|
176
298
|
// This must be cleared at the start of each frame
|
|
177
299
|
var valCache = new WeakMap();
|
|
178
300
|
function cacheValue(element, path, value) {
|
|
179
301
|
// Initiate movie cache
|
|
180
|
-
if (!valCache.has(element.movie))
|
|
302
|
+
if (!valCache.has(element.movie)) {
|
|
181
303
|
valCache.set(element.movie, new WeakMap());
|
|
304
|
+
}
|
|
182
305
|
var movieCache = valCache.get(element.movie);
|
|
183
|
-
//
|
|
184
|
-
if (!movieCache.has(element))
|
|
306
|
+
// Initiate element cache
|
|
307
|
+
if (!movieCache.has(element)) {
|
|
185
308
|
movieCache.set(element, {});
|
|
309
|
+
}
|
|
186
310
|
var elementCache = movieCache.get(element);
|
|
187
311
|
// Cache the value
|
|
188
312
|
elementCache[path] = value;
|
|
@@ -222,13 +346,16 @@ var etro = (function () {
|
|
|
222
346
|
return this;
|
|
223
347
|
};
|
|
224
348
|
KeyFrame.prototype.evaluate = function (time) {
|
|
225
|
-
if (this.value.length === 0)
|
|
349
|
+
if (this.value.length === 0) {
|
|
226
350
|
throw new Error('Empty keyframe');
|
|
227
|
-
|
|
351
|
+
}
|
|
352
|
+
if (time === undefined) {
|
|
228
353
|
throw new Error('|time| is undefined or null');
|
|
354
|
+
}
|
|
229
355
|
var firstTime = this.value[0][0];
|
|
230
|
-
if (time < firstTime)
|
|
356
|
+
if (time < firstTime) {
|
|
231
357
|
throw new Error('No keyframe point before |time|');
|
|
358
|
+
}
|
|
232
359
|
// I think reduce are slow to do per-frame (or more)?
|
|
233
360
|
for (var i = 0; i < this.value.length; i++) {
|
|
234
361
|
var startTime = this.value[i][0];
|
|
@@ -237,7 +364,7 @@ var etro = (function () {
|
|
|
237
364
|
if (i + 1 < this.value.length) {
|
|
238
365
|
var endTime = this.value[i + 1][0];
|
|
239
366
|
var endValue = this.value[i + 1][1];
|
|
240
|
-
if (startTime <= time && time < endTime)
|
|
367
|
+
if (startTime <= time && time < endTime) {
|
|
241
368
|
// No need for endValue if it is flat interpolation
|
|
242
369
|
// TODO: support custom interpolation for 'other' types?
|
|
243
370
|
if (!(typeof startValue === 'number' || typeof endValue === 'object')) {
|
|
@@ -253,6 +380,7 @@ var etro = (function () {
|
|
|
253
380
|
endValue, // eslint-disable-line @typescript-eslint/ban-types
|
|
254
381
|
percentProgress, this.interpolationKeys);
|
|
255
382
|
}
|
|
383
|
+
}
|
|
256
384
|
}
|
|
257
385
|
else {
|
|
258
386
|
// Repeat last value forever
|
|
@@ -278,23 +406,28 @@ var etro = (function () {
|
|
|
278
406
|
// TODO: Is this function efficient?
|
|
279
407
|
// TODO: Update doc @params to allow for keyframes
|
|
280
408
|
function val(element, path, time) {
|
|
281
|
-
if (hasCachedValue(element, path))
|
|
409
|
+
if (hasCachedValue(element, path)) {
|
|
282
410
|
return getCachedValue(element, path);
|
|
411
|
+
}
|
|
283
412
|
// Get property of element at path
|
|
284
413
|
var pathParts = path.split('.');
|
|
285
414
|
var property = element[pathParts.shift()];
|
|
286
|
-
while (pathParts.length > 0)
|
|
415
|
+
while (pathParts.length > 0) {
|
|
287
416
|
property = property[pathParts.shift()];
|
|
417
|
+
}
|
|
288
418
|
// Property filter function
|
|
289
419
|
var process = element.propertyFilters[path];
|
|
290
420
|
var value;
|
|
291
|
-
if (property instanceof KeyFrame)
|
|
421
|
+
if (property instanceof KeyFrame) {
|
|
292
422
|
value = property.evaluate(time);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
423
|
+
}
|
|
424
|
+
else if (typeof property === 'function') {
|
|
425
|
+
value = property(element, time);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
296
428
|
// Simple value
|
|
297
429
|
value = property;
|
|
430
|
+
}
|
|
298
431
|
return cacheValue(element, path, process ? process.call(element, value) : value);
|
|
299
432
|
}
|
|
300
433
|
/* export function floorInterp(x1, x2, t, objectKeys) {
|
|
@@ -305,15 +438,18 @@ var etro = (function () {
|
|
|
305
438
|
}, Object.create(Object.getPrototypeOf(x1)));
|
|
306
439
|
} */
|
|
307
440
|
function linearInterp(x1, x2, t, objectKeys) {
|
|
308
|
-
if (typeof x1 !== typeof x2)
|
|
441
|
+
if (typeof x1 !== typeof x2) {
|
|
309
442
|
throw new Error('Type mismatch');
|
|
310
|
-
|
|
443
|
+
}
|
|
444
|
+
if (typeof x1 !== 'number' && typeof x1 !== 'object') {
|
|
311
445
|
// Flat interpolation (floor)
|
|
312
446
|
return x1;
|
|
447
|
+
}
|
|
313
448
|
if (typeof x1 === 'object') { // to work with objects (including arrays)
|
|
314
449
|
// TODO: make this code DRY
|
|
315
|
-
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
450
|
+
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
|
|
316
451
|
throw new Error('Prototype mismatch');
|
|
452
|
+
}
|
|
317
453
|
// Preserve prototype of objects
|
|
318
454
|
var int = Object.create(Object.getPrototypeOf(x1));
|
|
319
455
|
// Take the intersection of properties
|
|
@@ -321,8 +457,9 @@ var etro = (function () {
|
|
|
321
457
|
for (var i = 0; i < keys.length; i++) {
|
|
322
458
|
var key = keys[i];
|
|
323
459
|
// eslint-disable-next-line no-prototype-builtins
|
|
324
|
-
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
460
|
+
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
|
|
325
461
|
continue;
|
|
462
|
+
}
|
|
326
463
|
int[key] = linearInterp(x1[key], x2[key], t);
|
|
327
464
|
}
|
|
328
465
|
return int;
|
|
@@ -330,14 +467,17 @@ var etro = (function () {
|
|
|
330
467
|
return (1 - t) * x1 + t * x2;
|
|
331
468
|
}
|
|
332
469
|
function cosineInterp(x1, x2, t, objectKeys) {
|
|
333
|
-
if (typeof x1 !== typeof x2)
|
|
470
|
+
if (typeof x1 !== typeof x2) {
|
|
334
471
|
throw new Error('Type mismatch');
|
|
335
|
-
|
|
472
|
+
}
|
|
473
|
+
if (typeof x1 !== 'number' && typeof x1 !== 'object') {
|
|
336
474
|
// Flat interpolation (floor)
|
|
337
475
|
return x1;
|
|
476
|
+
}
|
|
338
477
|
if (typeof x1 === 'object' && typeof x2 === 'object') { // to work with objects (including arrays)
|
|
339
|
-
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
|
|
478
|
+
if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
|
|
340
479
|
throw new Error('Prototype mismatch');
|
|
480
|
+
}
|
|
341
481
|
// Preserve prototype of objects
|
|
342
482
|
var int = Object.create(Object.getPrototypeOf(x1));
|
|
343
483
|
// Take the intersection of properties
|
|
@@ -345,8 +485,9 @@ var etro = (function () {
|
|
|
345
485
|
for (var i = 0; i < keys.length; i++) {
|
|
346
486
|
var key = keys[i];
|
|
347
487
|
// eslint-disable-next-line no-prototype-builtins
|
|
348
|
-
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
|
|
488
|
+
if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
|
|
349
489
|
continue;
|
|
490
|
+
}
|
|
350
491
|
int[key] = cosineInterp(x1[key], x2[key], t);
|
|
351
492
|
}
|
|
352
493
|
return int;
|
|
@@ -375,7 +516,7 @@ var etro = (function () {
|
|
|
375
516
|
* Converts to a CSS color
|
|
376
517
|
*/
|
|
377
518
|
Color.prototype.toString = function () {
|
|
378
|
-
return "rgba("
|
|
519
|
+
return "rgba(".concat(this.r, ", ").concat(this.g, ", ").concat(this.b, ", ").concat(this.a, ")");
|
|
379
520
|
};
|
|
380
521
|
return Color;
|
|
381
522
|
}());
|
|
@@ -426,17 +567,22 @@ var etro = (function () {
|
|
|
426
567
|
*/
|
|
427
568
|
Font.prototype.toString = function () {
|
|
428
569
|
var s = '';
|
|
429
|
-
if (this.style !== 'normal')
|
|
570
|
+
if (this.style !== 'normal') {
|
|
430
571
|
s += this.style + ' ';
|
|
431
|
-
|
|
572
|
+
}
|
|
573
|
+
if (this.variant !== 'normal') {
|
|
432
574
|
s += this.variant + ' ';
|
|
433
|
-
|
|
575
|
+
}
|
|
576
|
+
if (this.weight !== 'normal') {
|
|
434
577
|
s += this.weight + ' ';
|
|
435
|
-
|
|
578
|
+
}
|
|
579
|
+
if (this.stretch !== 'normal') {
|
|
436
580
|
s += this.stretch + ' ';
|
|
437
|
-
|
|
438
|
-
|
|
581
|
+
}
|
|
582
|
+
s += "".concat(this.size).concat(this.sizeUnit, " ");
|
|
583
|
+
if (this.lineHeight !== 'normal') {
|
|
439
584
|
s += this.lineHeight + ' ';
|
|
585
|
+
}
|
|
440
586
|
s += this.family;
|
|
441
587
|
return s;
|
|
442
588
|
};
|
|
@@ -451,7 +597,7 @@ var etro = (function () {
|
|
|
451
597
|
*/
|
|
452
598
|
function parseFont(str) {
|
|
453
599
|
// Assign css string to html element
|
|
454
|
-
parseFontEl.setAttribute('style', "font: "
|
|
600
|
+
parseFontEl.setAttribute('style', "font: ".concat(str));
|
|
455
601
|
var _a = parseFontEl.style, fontSize = _a.fontSize, fontFamily = _a.fontFamily, fontStyle = _a.fontStyle, fontVariant = _a.fontVariant, fontWeight = _a.fontWeight, lineHeight = _a.lineHeight;
|
|
456
602
|
parseFontEl.removeAttribute('style');
|
|
457
603
|
var size = parseFloat(fontSize);
|
|
@@ -476,61 +622,12 @@ var etro = (function () {
|
|
|
476
622
|
width = width || canvas.width;
|
|
477
623
|
height = height || canvas.height;
|
|
478
624
|
var frame = ctx.getImageData(x, y, width, height);
|
|
479
|
-
for (var i = 0, l = frame.data.length; i < l; i += 4)
|
|
625
|
+
for (var i = 0, l = frame.data.length; i < l; i += 4) {
|
|
480
626
|
mapper(frame.data, i);
|
|
481
|
-
|
|
627
|
+
}
|
|
628
|
+
if (flush) {
|
|
482
629
|
ctx.putImageData(frame, x, y);
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* <p>Emits "change" event when public properties updated, recursively.
|
|
486
|
-
* <p>Must be called before any watchable properties are set, and only once in
|
|
487
|
-
* the prototype chain.
|
|
488
|
-
*
|
|
489
|
-
* @deprecated Will be removed in the future (see issue #130)
|
|
490
|
-
*
|
|
491
|
-
* @param target - object to watch
|
|
492
|
-
*/
|
|
493
|
-
function watchPublic(target) {
|
|
494
|
-
var getPath = function (receiver, prop) {
|
|
495
|
-
return (receiver === proxy ? '' : (paths.get(receiver) + '.')) + prop;
|
|
496
|
-
};
|
|
497
|
-
var callback = function (prop, val, receiver) {
|
|
498
|
-
// Public API property updated, emit 'modify' event.
|
|
499
|
-
publish(proxy, target.type + ".change.modify", { property: getPath(receiver, prop), newValue: val });
|
|
500
|
-
};
|
|
501
|
-
var canWatch = function (receiver, prop) { return !prop.startsWith('_') &&
|
|
502
|
-
(receiver.publicExcludes === undefined || !receiver.publicExcludes.includes(prop)); };
|
|
503
|
-
// The path to each child property (each is a unique proxy)
|
|
504
|
-
var paths = new WeakMap();
|
|
505
|
-
var handler = {
|
|
506
|
-
set: function (obj, prop, val, receiver) {
|
|
507
|
-
// Recurse
|
|
508
|
-
if (typeof val === 'object' && val !== null && !paths.has(val) && canWatch(receiver, prop)) {
|
|
509
|
-
val = new Proxy(val, handler);
|
|
510
|
-
paths.set(val, getPath(receiver, prop));
|
|
511
|
-
}
|
|
512
|
-
// Set property or attribute
|
|
513
|
-
// Search prototype chain for the closest setter
|
|
514
|
-
var objProto = obj;
|
|
515
|
-
while ((objProto = Object.getPrototypeOf(objProto))) {
|
|
516
|
-
var propDesc = Object.getOwnPropertyDescriptor(objProto, prop);
|
|
517
|
-
if (propDesc && propDesc.set) {
|
|
518
|
-
// Call setter, supplying proxy as this (fixes event bugs)
|
|
519
|
-
propDesc.set.call(receiver, val);
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
if (!objProto)
|
|
524
|
-
// Couldn't find setter; set value on instance
|
|
525
|
-
obj[prop] = val;
|
|
526
|
-
// Check if the property isn't blacklisted in publicExcludes.
|
|
527
|
-
if (canWatch(receiver, prop))
|
|
528
|
-
callback(prop, val, receiver);
|
|
529
|
-
return true;
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
var proxy = new Proxy(target, handler);
|
|
533
|
-
return proxy;
|
|
630
|
+
}
|
|
534
631
|
}
|
|
535
632
|
|
|
536
633
|
/**
|
|
@@ -556,45 +653,70 @@ var etro = (function () {
|
|
|
556
653
|
*/
|
|
557
654
|
function MixedAudioSource(options) {
|
|
558
655
|
var _this = this;
|
|
656
|
+
var _a;
|
|
657
|
+
if (!options.source) {
|
|
658
|
+
throw new Error('Property "source" is required in options');
|
|
659
|
+
}
|
|
559
660
|
var onload = options.onload;
|
|
560
661
|
// Don't set as instance property
|
|
561
662
|
delete options.onload;
|
|
562
|
-
_this = _super.call(this, options)
|
|
663
|
+
_this = _super.call(this, __assign(__assign({}, options), {
|
|
664
|
+
// Set a default duration so that the super constructor doesn't throw an
|
|
665
|
+
// error
|
|
666
|
+
duration: (_a = options.duration) !== null && _a !== void 0 ? _a : 0 })) || this;
|
|
563
667
|
_this._initialized = false;
|
|
564
668
|
_this._sourceStartTime = options.sourceStartTime || 0;
|
|
565
669
|
applyOptions(options, _this);
|
|
566
670
|
var load = function () {
|
|
567
671
|
// TODO: && ?
|
|
568
|
-
if ((options.duration || (_this.source.duration - _this.sourceStartTime)) < 0)
|
|
672
|
+
if ((options.duration || (_this.source.duration - _this.sourceStartTime)) < 0) {
|
|
569
673
|
throw new Error('Invalid options.duration or options.sourceStartTime');
|
|
674
|
+
}
|
|
570
675
|
_this._unstretchedDuration = options.duration || (_this.source.duration - _this.sourceStartTime);
|
|
571
676
|
_this.duration = _this._unstretchedDuration / (_this.playbackRate);
|
|
572
677
|
// onload will use `this`, and can't bind itself because it's before
|
|
573
678
|
// super()
|
|
574
679
|
onload && onload.bind(_this)(_this.source, options);
|
|
575
680
|
};
|
|
576
|
-
if (_this.source.readyState >= 2)
|
|
681
|
+
if (_this.source.readyState >= 2) {
|
|
577
682
|
// this frame's data is available now
|
|
578
683
|
load();
|
|
579
|
-
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
580
686
|
// when this frame's data is available
|
|
581
687
|
_this.source.addEventListener('loadedmetadata', load);
|
|
688
|
+
}
|
|
582
689
|
_this.source.addEventListener('durationchange', function () {
|
|
583
690
|
_this.duration = options.duration || (_this.source.duration - _this.sourceStartTime);
|
|
584
691
|
});
|
|
585
692
|
return _this;
|
|
586
693
|
}
|
|
694
|
+
MixedAudioSource.prototype.whenReady = function () {
|
|
695
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
696
|
+
var _this = this;
|
|
697
|
+
return __generator(this, function (_a) {
|
|
698
|
+
switch (_a.label) {
|
|
699
|
+
case 0: return [4 /*yield*/, _super.prototype.whenReady.call(this)];
|
|
700
|
+
case 1:
|
|
701
|
+
_a.sent();
|
|
702
|
+
if (!(this.source.readyState < 4)) return [3 /*break*/, 3];
|
|
703
|
+
return [4 /*yield*/, new Promise(function (resolve) {
|
|
704
|
+
_this.source.addEventListener('canplaythrough', resolve);
|
|
705
|
+
})];
|
|
706
|
+
case 2:
|
|
707
|
+
_a.sent();
|
|
708
|
+
_a.label = 3;
|
|
709
|
+
case 3: return [2 /*return*/];
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
});
|
|
713
|
+
};
|
|
587
714
|
MixedAudioSource.prototype.attach = function (movie) {
|
|
588
715
|
var _this = this;
|
|
589
716
|
_super.prototype.attach.call(this, movie);
|
|
590
|
-
subscribe(movie, 'movie.seek', function () {
|
|
591
|
-
if (_this.currentTime < 0 || _this.currentTime >= _this.duration)
|
|
592
|
-
return;
|
|
593
|
-
_this.source.currentTime = _this.currentTime + _this.sourceStartTime;
|
|
594
|
-
});
|
|
595
717
|
// TODO: on unattach?
|
|
596
|
-
subscribe(movie, '
|
|
597
|
-
// Connect to new destination if
|
|
718
|
+
subscribe(movie, 'audiodestinationupdate', function (event) {
|
|
719
|
+
// Connect to new destination if immediately connected to the existing
|
|
598
720
|
// destination.
|
|
599
721
|
if (_this._connectedToDestination) {
|
|
600
722
|
_this.audioNode.disconnect(movie.actx.destination);
|
|
@@ -613,8 +735,9 @@ var etro = (function () {
|
|
|
613
735
|
var oldDisconnect = this._audioNode.disconnect.bind(this.audioNode);
|
|
614
736
|
this._audioNode.disconnect = function (destination, output, input) {
|
|
615
737
|
if (_this._connectedToDestination &&
|
|
616
|
-
destination === movie.actx.destination)
|
|
738
|
+
destination === movie.actx.destination) {
|
|
617
739
|
_this._connectedToDestination = false;
|
|
740
|
+
}
|
|
618
741
|
return oldDisconnect(destination, output, input);
|
|
619
742
|
};
|
|
620
743
|
// Connect to actx.destination by default (can be rewired by user)
|
|
@@ -630,6 +753,10 @@ var etro = (function () {
|
|
|
630
753
|
this.source.currentTime = this.currentTime + this.sourceStartTime;
|
|
631
754
|
this.source.play();
|
|
632
755
|
};
|
|
756
|
+
MixedAudioSource.prototype.seek = function (time) {
|
|
757
|
+
_super.prototype.seek.call(this, time);
|
|
758
|
+
this.source.currentTime = this.currentTime + this.sourceStartTime;
|
|
759
|
+
};
|
|
633
760
|
MixedAudioSource.prototype.render = function () {
|
|
634
761
|
_super.prototype.render.call(this);
|
|
635
762
|
// TODO: implement Issue: Create built-in audio node to support built-in
|
|
@@ -639,6 +766,7 @@ var etro = (function () {
|
|
|
639
766
|
this.source.playbackRate = val(this, 'playbackRate', this.currentTime);
|
|
640
767
|
};
|
|
641
768
|
MixedAudioSource.prototype.stop = function () {
|
|
769
|
+
_super.prototype.stop.call(this);
|
|
642
770
|
this.source.pause();
|
|
643
771
|
};
|
|
644
772
|
Object.defineProperty(MixedAudioSource.prototype, "audioNode", {
|
|
@@ -657,8 +785,9 @@ var etro = (function () {
|
|
|
657
785
|
},
|
|
658
786
|
set: function (value) {
|
|
659
787
|
this._playbackRate = value;
|
|
660
|
-
if (this._unstretchedDuration !== undefined)
|
|
788
|
+
if (this._unstretchedDuration !== undefined) {
|
|
661
789
|
this.duration = this._unstretchedDuration / value;
|
|
790
|
+
}
|
|
662
791
|
},
|
|
663
792
|
enumerable: false,
|
|
664
793
|
configurable: true
|
|
@@ -694,6 +823,18 @@ var etro = (function () {
|
|
|
694
823
|
enumerable: false,
|
|
695
824
|
configurable: true
|
|
696
825
|
});
|
|
826
|
+
Object.defineProperty(MixedAudioSource.prototype, "ready", {
|
|
827
|
+
get: function () {
|
|
828
|
+
// Typescript doesn't support `super.ready` when targeting es5
|
|
829
|
+
var superReady = Object.getOwnPropertyDescriptor(superclass.prototype, 'ready').get.call(this);
|
|
830
|
+
return superReady && this.source.readyState === 4;
|
|
831
|
+
},
|
|
832
|
+
enumerable: false,
|
|
833
|
+
configurable: true
|
|
834
|
+
});
|
|
835
|
+
/**
|
|
836
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
837
|
+
*/
|
|
697
838
|
MixedAudioSource.prototype.getDefaultOptions = function () {
|
|
698
839
|
return __assign(__assign({}, superclass.prototype.getDefaultOptions()), { source: undefined, sourceStartTime: 0, duration: undefined, muted: false, volume: 1, playbackRate: 1 });
|
|
699
840
|
};
|
|
@@ -716,56 +857,74 @@ var etro = (function () {
|
|
|
716
857
|
* movie's timeline
|
|
717
858
|
*/
|
|
718
859
|
function Base(options) {
|
|
860
|
+
if (options.duration === null || options.duration === undefined) {
|
|
861
|
+
throw new Error('Property "duration" is required in BaseOptions');
|
|
862
|
+
}
|
|
863
|
+
if (options.startTime === null || options.startTime === undefined) {
|
|
864
|
+
throw new Error('Property "startTime" is required in BaseOptions');
|
|
865
|
+
}
|
|
719
866
|
// Set startTime and duration properties manually, because they are
|
|
720
867
|
// readonly. applyOptions ignores readonly properties.
|
|
721
868
|
this._startTime = options.startTime;
|
|
722
869
|
this._duration = options.duration;
|
|
723
|
-
// Proxy that will be returned by constructor (for sending 'modified'
|
|
724
|
-
// events).
|
|
725
|
-
var newThis = watchPublic(this);
|
|
726
|
-
// Don't send updates when initializing, so use this instead of newThis
|
|
727
870
|
applyOptions(options, this);
|
|
728
871
|
// Whether this layer is currently being rendered
|
|
729
872
|
this.active = false;
|
|
730
873
|
this.enabled = true;
|
|
731
|
-
this._occurrenceCount = 0; // no
|
|
874
|
+
this._occurrenceCount = 0; // no occurrences in parent
|
|
732
875
|
this._movie = null;
|
|
733
|
-
// Propogate up to target
|
|
734
|
-
subscribe(newThis, 'layer.change', function (event) {
|
|
735
|
-
var typeOfChange = event.type.substring(event.type.lastIndexOf('.') + 1);
|
|
736
|
-
var type = "movie.change.layer." + typeOfChange;
|
|
737
|
-
publish(newThis._movie, type, __assign(__assign({}, event), { target: newThis._movie, type: type }));
|
|
738
|
-
});
|
|
739
|
-
return newThis;
|
|
740
876
|
}
|
|
877
|
+
/**
|
|
878
|
+
* Wait until this layer is ready to render
|
|
879
|
+
*/
|
|
880
|
+
Base.prototype.whenReady = function () {
|
|
881
|
+
return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
882
|
+
return [2 /*return*/];
|
|
883
|
+
}); });
|
|
884
|
+
}; // eslint-disable-line @typescript-eslint/no-empty-function
|
|
741
885
|
/**
|
|
742
886
|
* Attaches this layer to `movie` if not already attached.
|
|
743
887
|
* @ignore
|
|
744
888
|
*/
|
|
745
889
|
Base.prototype.tryAttach = function (movie) {
|
|
746
|
-
if (this._occurrenceCount === 0)
|
|
890
|
+
if (this._occurrenceCount === 0) {
|
|
747
891
|
this.attach(movie);
|
|
892
|
+
}
|
|
748
893
|
this._occurrenceCount++;
|
|
749
894
|
};
|
|
895
|
+
/**
|
|
896
|
+
* Attaches this layer to `movie`
|
|
897
|
+
*
|
|
898
|
+
* Called when the layer is added to a movie's `layers` array.
|
|
899
|
+
*
|
|
900
|
+
* @param movie The movie to attach to
|
|
901
|
+
*/
|
|
750
902
|
Base.prototype.attach = function (movie) {
|
|
751
903
|
this._movie = movie;
|
|
752
904
|
};
|
|
753
905
|
/**
|
|
754
|
-
*
|
|
906
|
+
* Detaches this layer from its movie if the number of times `tryDetach` has
|
|
755
907
|
* been called (including this call) equals the number of times `tryAttach`
|
|
756
908
|
* has been called.
|
|
757
909
|
*
|
|
758
910
|
* @ignore
|
|
759
911
|
*/
|
|
760
912
|
Base.prototype.tryDetach = function () {
|
|
761
|
-
if (this.movie === null)
|
|
913
|
+
if (this.movie === null) {
|
|
762
914
|
throw new Error('No movie to detach from');
|
|
915
|
+
}
|
|
763
916
|
this._occurrenceCount--;
|
|
764
917
|
// If this layer occurs in another place in a `layers` array, do not unset
|
|
765
918
|
// _movie. (For calling `unshift` on the `layers` proxy)
|
|
766
|
-
if (this._occurrenceCount === 0)
|
|
919
|
+
if (this._occurrenceCount === 0) {
|
|
767
920
|
this.detach();
|
|
921
|
+
}
|
|
768
922
|
};
|
|
923
|
+
/**
|
|
924
|
+
* Detaches this layer from its movie
|
|
925
|
+
*
|
|
926
|
+
* Called when the layer is removed from a movie's `layers` array.
|
|
927
|
+
*/
|
|
769
928
|
Base.prototype.detach = function () {
|
|
770
929
|
this._movie = null;
|
|
771
930
|
};
|
|
@@ -773,14 +932,40 @@ var etro = (function () {
|
|
|
773
932
|
* Called when the layer is activated
|
|
774
933
|
*/
|
|
775
934
|
Base.prototype.start = function () { }; // eslint-disable-line @typescript-eslint/no-empty-function
|
|
935
|
+
/**
|
|
936
|
+
* Update {@link currentTime} when seeking
|
|
937
|
+
*
|
|
938
|
+
* This method is called when the movie seeks to a new time at the request of
|
|
939
|
+
* the user. {@link progress} is called when the movie's `currentTime` is
|
|
940
|
+
* updated due to playback.
|
|
941
|
+
*
|
|
942
|
+
* @param time - The new time in the layer
|
|
943
|
+
*/
|
|
944
|
+
Base.prototype.seek = function (time) {
|
|
945
|
+
this._currentTime = time;
|
|
946
|
+
};
|
|
947
|
+
/**
|
|
948
|
+
* Update {@link currentTime} due to playback
|
|
949
|
+
*
|
|
950
|
+
* This method is called when the movie's `currentTime` is updated due to
|
|
951
|
+
* playback. {@link seek} is called when the movie seeks to a new time at the
|
|
952
|
+
* request of the user.
|
|
953
|
+
*
|
|
954
|
+
* @param time - The new time in the layer
|
|
955
|
+
*/
|
|
956
|
+
Base.prototype.progress = function (time) {
|
|
957
|
+
this._currentTime = time;
|
|
958
|
+
};
|
|
776
959
|
/**
|
|
777
960
|
* Called when the movie renders and the layer is active
|
|
778
961
|
*/
|
|
779
962
|
Base.prototype.render = function () { }; // eslint-disable-line @typescript-eslint/no-empty-function
|
|
780
963
|
/**
|
|
781
|
-
|
|
964
|
+
* Called when the layer is deactivated
|
|
782
965
|
*/
|
|
783
|
-
Base.prototype.stop = function () {
|
|
966
|
+
Base.prototype.stop = function () {
|
|
967
|
+
this._currentTime = undefined;
|
|
968
|
+
};
|
|
784
969
|
Object.defineProperty(Base.prototype, "parent", {
|
|
785
970
|
// TODO: is this needed?
|
|
786
971
|
get: function () {
|
|
@@ -791,6 +976,7 @@ var etro = (function () {
|
|
|
791
976
|
});
|
|
792
977
|
Object.defineProperty(Base.prototype, "startTime", {
|
|
793
978
|
/**
|
|
979
|
+
* The time in the movie at which this layer starts (in seconds)
|
|
794
980
|
*/
|
|
795
981
|
get: function () {
|
|
796
982
|
return this._startTime;
|
|
@@ -803,17 +989,17 @@ var etro = (function () {
|
|
|
803
989
|
});
|
|
804
990
|
Object.defineProperty(Base.prototype, "currentTime", {
|
|
805
991
|
/**
|
|
806
|
-
* The current time of the movie relative to this layer
|
|
992
|
+
* The current time of the movie relative to this layer (in seconds)
|
|
807
993
|
*/
|
|
808
994
|
get: function () {
|
|
809
|
-
return this.
|
|
810
|
-
: undefined;
|
|
995
|
+
return this._currentTime;
|
|
811
996
|
},
|
|
812
997
|
enumerable: false,
|
|
813
998
|
configurable: true
|
|
814
999
|
});
|
|
815
1000
|
Object.defineProperty(Base.prototype, "duration", {
|
|
816
1001
|
/**
|
|
1002
|
+
* The duration of this layer (in seconds)
|
|
817
1003
|
*/
|
|
818
1004
|
get: function () {
|
|
819
1005
|
return this._duration;
|
|
@@ -824,6 +1010,16 @@ var etro = (function () {
|
|
|
824
1010
|
enumerable: false,
|
|
825
1011
|
configurable: true
|
|
826
1012
|
});
|
|
1013
|
+
Object.defineProperty(Base.prototype, "ready", {
|
|
1014
|
+
/**
|
|
1015
|
+
* `true` if this layer is ready to be rendered, `false` otherwise
|
|
1016
|
+
*/
|
|
1017
|
+
get: function () {
|
|
1018
|
+
return true;
|
|
1019
|
+
},
|
|
1020
|
+
enumerable: false,
|
|
1021
|
+
configurable: true
|
|
1022
|
+
});
|
|
827
1023
|
Object.defineProperty(Base.prototype, "movie", {
|
|
828
1024
|
get: function () {
|
|
829
1025
|
return this._movie;
|
|
@@ -831,6 +1027,9 @@ var etro = (function () {
|
|
|
831
1027
|
enumerable: false,
|
|
832
1028
|
configurable: true
|
|
833
1029
|
});
|
|
1030
|
+
/**
|
|
1031
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1032
|
+
*/
|
|
834
1033
|
Base.prototype.getDefaultOptions = function () {
|
|
835
1034
|
return {
|
|
836
1035
|
startTime: undefined,
|
|
@@ -847,6 +1046,7 @@ var etro = (function () {
|
|
|
847
1046
|
|
|
848
1047
|
// TODO: rename to something more consistent with the naming convention of Visual and VisualSourceMixin
|
|
849
1048
|
/**
|
|
1049
|
+
* Layer for an HTML audio element
|
|
850
1050
|
* @extends AudioSource
|
|
851
1051
|
*/
|
|
852
1052
|
var Audio = /** @class */ (function (_super) {
|
|
@@ -855,11 +1055,21 @@ var etro = (function () {
|
|
|
855
1055
|
* Creates an audio layer
|
|
856
1056
|
*/
|
|
857
1057
|
function Audio(options) {
|
|
858
|
-
var _this =
|
|
859
|
-
if (
|
|
1058
|
+
var _this = this;
|
|
1059
|
+
if (typeof options.source === 'string') {
|
|
1060
|
+
var audio = document.createElement('audio');
|
|
1061
|
+
audio.src = options.source;
|
|
1062
|
+
options.source = audio;
|
|
1063
|
+
}
|
|
1064
|
+
_this = _super.call(this, options) || this;
|
|
1065
|
+
if (_this.duration === undefined) {
|
|
860
1066
|
_this.duration = (_this).source.duration - _this.sourceStartTime;
|
|
1067
|
+
}
|
|
861
1068
|
return _this;
|
|
862
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1072
|
+
*/
|
|
863
1073
|
Audio.prototype.getDefaultOptions = function () {
|
|
864
1074
|
return __assign(__assign({}, Object.getPrototypeOf(this).getDefaultOptions()), {
|
|
865
1075
|
/**
|
|
@@ -871,40 +1081,101 @@ var etro = (function () {
|
|
|
871
1081
|
return Audio;
|
|
872
1082
|
}(AudioSourceMixin(Base)));
|
|
873
1083
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
_this
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1084
|
+
var CustomArrayListener = /** @class */ (function () {
|
|
1085
|
+
function CustomArrayListener() {
|
|
1086
|
+
}
|
|
1087
|
+
return CustomArrayListener;
|
|
1088
|
+
}());
|
|
1089
|
+
/**
|
|
1090
|
+
* An array that notifies a listener when items are added or removed.
|
|
1091
|
+
*/
|
|
1092
|
+
var CustomArray = /** @class */ (function (_super) {
|
|
1093
|
+
__extends(CustomArray, _super);
|
|
1094
|
+
function CustomArray(target, listener) {
|
|
1095
|
+
var _this = _super.call(this) || this;
|
|
1096
|
+
for (var _i = 0, target_1 = target; _i < target_1.length; _i++) {
|
|
1097
|
+
var item = target_1[_i];
|
|
1098
|
+
listener.onAdd(item);
|
|
1099
|
+
}
|
|
1100
|
+
// Create proxy
|
|
1101
|
+
return new Proxy(target, {
|
|
889
1102
|
deleteProperty: function (target, property) {
|
|
890
1103
|
var value = target[property];
|
|
891
|
-
value.detach();
|
|
892
1104
|
delete target[property];
|
|
1105
|
+
listener.onRemove(value);
|
|
893
1106
|
return true;
|
|
894
1107
|
},
|
|
895
1108
|
set: function (target, property, value) {
|
|
1109
|
+
var oldValue = target[property];
|
|
1110
|
+
target[property] = value;
|
|
1111
|
+
// Check if property is a number (index)
|
|
896
1112
|
if (!isNaN(Number(property))) {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1113
|
+
if (oldValue !== undefined) {
|
|
1114
|
+
listener.onRemove(oldValue);
|
|
1115
|
+
}
|
|
1116
|
+
listener.onAdd(value);
|
|
901
1117
|
}
|
|
902
|
-
target[property] = value;
|
|
903
1118
|
return true;
|
|
904
1119
|
}
|
|
905
1120
|
});
|
|
1121
|
+
}
|
|
1122
|
+
return CustomArray;
|
|
1123
|
+
}(Array));
|
|
1124
|
+
|
|
1125
|
+
// eslint-disable-next-line no-use-before-define
|
|
1126
|
+
var VisualEffectsListener = /** @class */ (function (_super) {
|
|
1127
|
+
__extends(VisualEffectsListener, _super);
|
|
1128
|
+
// eslint-disable-next-line no-use-before-define
|
|
1129
|
+
function VisualEffectsListener(layer) {
|
|
1130
|
+
var _this = _super.call(this) || this;
|
|
1131
|
+
_this._layer = layer;
|
|
906
1132
|
return _this;
|
|
907
1133
|
}
|
|
1134
|
+
VisualEffectsListener.prototype.onAdd = function (effect) {
|
|
1135
|
+
effect.tryAttach(this._layer);
|
|
1136
|
+
};
|
|
1137
|
+
VisualEffectsListener.prototype.onRemove = function (effect) {
|
|
1138
|
+
effect.tryDetach();
|
|
1139
|
+
};
|
|
1140
|
+
return VisualEffectsListener;
|
|
1141
|
+
}(CustomArrayListener));
|
|
1142
|
+
var VisualEffects = /** @class */ (function (_super) {
|
|
1143
|
+
__extends(VisualEffects, _super);
|
|
1144
|
+
// eslint-disable-next-line no-use-before-define
|
|
1145
|
+
function VisualEffects(target, layer) {
|
|
1146
|
+
return _super.call(this, target, new VisualEffectsListener(layer)) || this;
|
|
1147
|
+
}
|
|
1148
|
+
return VisualEffects;
|
|
1149
|
+
}(CustomArray));
|
|
1150
|
+
/** Any layer that renders to a canvas */
|
|
1151
|
+
var Visual = /** @class */ (function (_super) {
|
|
1152
|
+
__extends(Visual, _super);
|
|
1153
|
+
/**
|
|
1154
|
+
* Creates a visual layer
|
|
1155
|
+
*/
|
|
1156
|
+
function Visual(options) {
|
|
1157
|
+
var _this = _super.call(this, options) || this;
|
|
1158
|
+
applyOptions(options, _this);
|
|
1159
|
+
_this.canvas = document.createElement('canvas');
|
|
1160
|
+
_this.cctx = _this.canvas.getContext('2d');
|
|
1161
|
+
_this.effects = new VisualEffects([], _this);
|
|
1162
|
+
return _this;
|
|
1163
|
+
}
|
|
1164
|
+
Visual.prototype.whenReady = function () {
|
|
1165
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1166
|
+
return __generator(this, function (_a) {
|
|
1167
|
+
switch (_a.label) {
|
|
1168
|
+
case 0: return [4 /*yield*/, _super.prototype.whenReady.call(this)];
|
|
1169
|
+
case 1:
|
|
1170
|
+
_a.sent();
|
|
1171
|
+
return [4 /*yield*/, Promise.all(this.effects.map(function (effect) { return effect.whenReady(); }))];
|
|
1172
|
+
case 2:
|
|
1173
|
+
_a.sent();
|
|
1174
|
+
return [2 /*return*/];
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
});
|
|
1178
|
+
};
|
|
908
1179
|
/**
|
|
909
1180
|
* Render visual output
|
|
910
1181
|
*/
|
|
@@ -912,8 +1183,9 @@ var etro = (function () {
|
|
|
912
1183
|
// Prevent empty canvas errors if the width or height is 0
|
|
913
1184
|
var width = val(this, 'width', this.currentTime);
|
|
914
1185
|
var height = val(this, 'height', this.currentTime);
|
|
915
|
-
if (width === 0 || height === 0)
|
|
1186
|
+
if (width === 0 || height === 0) {
|
|
916
1187
|
return;
|
|
1188
|
+
}
|
|
917
1189
|
this.beginRender();
|
|
918
1190
|
this.doRender();
|
|
919
1191
|
this.endRender();
|
|
@@ -944,20 +1216,22 @@ var etro = (function () {
|
|
|
944
1216
|
Visual.prototype.endRender = function () {
|
|
945
1217
|
var w = val(this, 'width', this.currentTime) || val(this.movie, 'width', this.movie.currentTime);
|
|
946
1218
|
var h = val(this, 'height', this.currentTime) || val(this.movie, 'height', this.movie.currentTime);
|
|
947
|
-
if (w * h > 0)
|
|
1219
|
+
if (w * h > 0) {
|
|
948
1220
|
this._applyEffects();
|
|
1221
|
+
}
|
|
949
1222
|
// else InvalidStateError for drawing zero-area image in some effects, right?
|
|
950
1223
|
};
|
|
951
1224
|
Visual.prototype._applyEffects = function () {
|
|
952
1225
|
for (var i = 0; i < this.effects.length; i++) {
|
|
953
1226
|
var effect = this.effects[i];
|
|
954
|
-
if (effect && effect.enabled)
|
|
1227
|
+
if (effect && effect.enabled) {
|
|
955
1228
|
// Pass relative time
|
|
956
1229
|
effect.apply(this, this.movie.currentTime - this.startTime);
|
|
1230
|
+
}
|
|
957
1231
|
}
|
|
958
1232
|
};
|
|
959
1233
|
/**
|
|
960
|
-
*
|
|
1234
|
+
* Convenience method for <code>effects.push()</code>
|
|
961
1235
|
* @param effect
|
|
962
1236
|
* @return the layer (for chaining)
|
|
963
1237
|
*/
|
|
@@ -965,6 +1239,18 @@ var etro = (function () {
|
|
|
965
1239
|
this.effects.push(effect);
|
|
966
1240
|
return this;
|
|
967
1241
|
};
|
|
1242
|
+
Object.defineProperty(Visual.prototype, "ready", {
|
|
1243
|
+
get: function () {
|
|
1244
|
+
// Typescript doesn't support `super.ready` when targeting es5
|
|
1245
|
+
var superReady = Object.getOwnPropertyDescriptor(Base.prototype, 'ready').get.call(this);
|
|
1246
|
+
return superReady && this.effects.every(function (effect) { return effect.ready; });
|
|
1247
|
+
},
|
|
1248
|
+
enumerable: false,
|
|
1249
|
+
configurable: true
|
|
1250
|
+
});
|
|
1251
|
+
/**
|
|
1252
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1253
|
+
*/
|
|
968
1254
|
Visual.prototype.getDefaultOptions = function () {
|
|
969
1255
|
return __assign(__assign({}, Base.prototype.getDefaultOptions()), {
|
|
970
1256
|
/**
|
|
@@ -987,13 +1273,13 @@ var etro = (function () {
|
|
|
987
1273
|
height: null,
|
|
988
1274
|
/**
|
|
989
1275
|
* @name module:layer.Visual#background
|
|
990
|
-
* @desc The
|
|
1276
|
+
* @desc The color code for the background, or <code>null</code> for
|
|
991
1277
|
* transparency
|
|
992
1278
|
*/
|
|
993
1279
|
background: null,
|
|
994
1280
|
/**
|
|
995
1281
|
* @name module:layer.Visual#border
|
|
996
|
-
* @desc The
|
|
1282
|
+
* @desc The border style, or <code>null</code> for no border
|
|
997
1283
|
*/
|
|
998
1284
|
border: null,
|
|
999
1285
|
/**
|
|
@@ -1023,17 +1309,60 @@ var etro = (function () {
|
|
|
1023
1309
|
var MixedVisualSource = /** @class */ (function (_super) {
|
|
1024
1310
|
__extends(MixedVisualSource, _super);
|
|
1025
1311
|
function MixedVisualSource(options) {
|
|
1026
|
-
var _this =
|
|
1312
|
+
var _this = this;
|
|
1313
|
+
if (!options.source) {
|
|
1314
|
+
throw new Error('Property "source" is required in options');
|
|
1315
|
+
}
|
|
1316
|
+
_this = _super.call(this, options) || this;
|
|
1027
1317
|
applyOptions(options, _this);
|
|
1028
1318
|
return _this;
|
|
1029
1319
|
}
|
|
1320
|
+
MixedVisualSource.prototype.whenReady = function () {
|
|
1321
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1322
|
+
var _this = this;
|
|
1323
|
+
return __generator(this, function (_a) {
|
|
1324
|
+
switch (_a.label) {
|
|
1325
|
+
case 0: return [4 /*yield*/, _super.prototype.whenReady.call(this)];
|
|
1326
|
+
case 1:
|
|
1327
|
+
_a.sent();
|
|
1328
|
+
return [4 /*yield*/, new Promise(function (resolve) {
|
|
1329
|
+
if (_this.source instanceof HTMLImageElement) {
|
|
1330
|
+
// The source is an image; wait for it to load
|
|
1331
|
+
if (_this.source.complete) {
|
|
1332
|
+
resolve();
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
_this.source.addEventListener('load', function () {
|
|
1336
|
+
resolve();
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
else {
|
|
1341
|
+
// The source is a video; wait for the first frame to load
|
|
1342
|
+
if (_this.source.readyState === 4) {
|
|
1343
|
+
resolve();
|
|
1344
|
+
}
|
|
1345
|
+
else {
|
|
1346
|
+
_this.source.addEventListener('canplaythrough', function () {
|
|
1347
|
+
resolve();
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
})];
|
|
1352
|
+
case 2:
|
|
1353
|
+
_a.sent();
|
|
1354
|
+
return [2 /*return*/];
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
});
|
|
1358
|
+
};
|
|
1030
1359
|
MixedVisualSource.prototype.doRender = function () {
|
|
1031
1360
|
// Clear/fill background
|
|
1032
1361
|
_super.prototype.doRender.call(this);
|
|
1033
1362
|
/*
|
|
1034
1363
|
* Source dimensions crop the image. Dest dimensions set the size that
|
|
1035
1364
|
* the image will be rendered at *on the layer*. Note that this is
|
|
1036
|
-
* different
|
|
1365
|
+
* different from the layer dimensions (`this.width` and `this.height`).
|
|
1037
1366
|
* The main reason this distinction exists is so that an image layer can
|
|
1038
1367
|
* be rotated without being cropped (see iss #46).
|
|
1039
1368
|
*/
|
|
@@ -1041,6 +1370,21 @@ var etro = (function () {
|
|
|
1041
1370
|
// `destX` and `destY` are relative to the layer
|
|
1042
1371
|
val(this, 'destX', this.currentTime), val(this, 'destY', this.currentTime), val(this, 'destWidth', this.currentTime), val(this, 'destHeight', this.currentTime));
|
|
1043
1372
|
};
|
|
1373
|
+
Object.defineProperty(MixedVisualSource.prototype, "ready", {
|
|
1374
|
+
get: function () {
|
|
1375
|
+
// Typescript doesn't support `super.ready` when targeting es5
|
|
1376
|
+
var superReady = Object.getOwnPropertyDescriptor(superclass.prototype, 'ready').get.call(this);
|
|
1377
|
+
var sourceReady = this.source instanceof HTMLImageElement
|
|
1378
|
+
? this.source.complete
|
|
1379
|
+
: this.source.readyState === 4;
|
|
1380
|
+
return superReady && sourceReady;
|
|
1381
|
+
},
|
|
1382
|
+
enumerable: false,
|
|
1383
|
+
configurable: true
|
|
1384
|
+
});
|
|
1385
|
+
/**
|
|
1386
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1387
|
+
*/
|
|
1044
1388
|
MixedVisualSource.prototype.getDefaultOptions = function () {
|
|
1045
1389
|
return __assign(__assign({}, superclass.prototype.getDefaultOptions()), { source: undefined, sourceX: 0, sourceY: 0, sourceWidth: undefined, sourceHeight: undefined, destX: 0, destY: 0, destWidth: undefined, destHeight: undefined });
|
|
1046
1390
|
};
|
|
@@ -1068,27 +1412,40 @@ var etro = (function () {
|
|
|
1068
1412
|
// instead. (TODO: fact check)
|
|
1069
1413
|
/* eslint-disable eqeqeq */
|
|
1070
1414
|
return destWidth != undefined
|
|
1071
|
-
? destWidth
|
|
1415
|
+
? destWidth
|
|
1416
|
+
: val(this, 'sourceWidth', this.currentTime);
|
|
1072
1417
|
}, destHeight: function (destHeight) {
|
|
1073
1418
|
/* eslint-disable eqeqeq */
|
|
1074
1419
|
return destHeight != undefined
|
|
1075
|
-
? destHeight
|
|
1420
|
+
? destHeight
|
|
1421
|
+
: val(this, 'sourceHeight', this.currentTime);
|
|
1076
1422
|
}, width: function (width) {
|
|
1077
1423
|
/* eslint-disable eqeqeq */
|
|
1078
1424
|
return width != undefined
|
|
1079
|
-
? width
|
|
1425
|
+
? width
|
|
1426
|
+
: val(this, 'destWidth', this.currentTime);
|
|
1080
1427
|
}, height: function (height) {
|
|
1081
1428
|
/* eslint-disable eqeqeq */
|
|
1082
1429
|
return height != undefined
|
|
1083
|
-
? height
|
|
1430
|
+
? height
|
|
1431
|
+
: val(this, 'destHeight', this.currentTime);
|
|
1084
1432
|
} });
|
|
1085
1433
|
return MixedVisualSource;
|
|
1086
1434
|
}
|
|
1087
1435
|
|
|
1436
|
+
/**
|
|
1437
|
+
* Layer for an HTML image element
|
|
1438
|
+
* @extends VisualSource
|
|
1439
|
+
*/
|
|
1088
1440
|
var Image = /** @class */ (function (_super) {
|
|
1089
1441
|
__extends(Image, _super);
|
|
1090
|
-
function Image() {
|
|
1091
|
-
|
|
1442
|
+
function Image(options) {
|
|
1443
|
+
if (typeof (options.source) === 'string') {
|
|
1444
|
+
var img = document.createElement('img');
|
|
1445
|
+
img.src = options.source;
|
|
1446
|
+
options.source = img;
|
|
1447
|
+
}
|
|
1448
|
+
return _super.call(this, options) || this;
|
|
1092
1449
|
}
|
|
1093
1450
|
return Image;
|
|
1094
1451
|
}(VisualSourceMixin(Visual)));
|
|
@@ -1102,9 +1459,12 @@ var etro = (function () {
|
|
|
1102
1459
|
// TODO: is textX necessary? it seems inconsistent, because you can't define
|
|
1103
1460
|
// width/height directly for a text layer
|
|
1104
1461
|
function Text(options) {
|
|
1105
|
-
var _this =
|
|
1462
|
+
var _this = this;
|
|
1463
|
+
if (!options.text) {
|
|
1464
|
+
throw new Error('Property "text" is required in TextOptions');
|
|
1465
|
+
}
|
|
1106
1466
|
// Default to no (transparent) background
|
|
1107
|
-
_super.call(this, __assign({ background: null }, options)) || this;
|
|
1467
|
+
_this = _super.call(this, __assign({ background: null }, options)) || this;
|
|
1108
1468
|
applyOptions(options, _this);
|
|
1109
1469
|
return _this;
|
|
1110
1470
|
// this._prevText = undefined;
|
|
@@ -1151,22 +1511,33 @@ var etro = (function () {
|
|
|
1151
1511
|
document.body.removeChild(s);
|
|
1152
1512
|
return metrics;
|
|
1153
1513
|
} */
|
|
1514
|
+
/**
|
|
1515
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1516
|
+
*/
|
|
1154
1517
|
Text.prototype.getDefaultOptions = function () {
|
|
1155
|
-
return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: '#fff', textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
|
|
1518
|
+
return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: parseColor('#fff'), textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
|
|
1156
1519
|
};
|
|
1157
1520
|
return Text;
|
|
1158
1521
|
}(Visual));
|
|
1159
1522
|
|
|
1160
|
-
// Use mixins instead of `extend`ing two classes (which isn't supported by
|
|
1161
|
-
// JavaScript).
|
|
1162
1523
|
/**
|
|
1524
|
+
* Layer for an HTML video element
|
|
1163
1525
|
* @extends AudioSource
|
|
1164
1526
|
* @extends VisualSource
|
|
1165
1527
|
*/
|
|
1166
1528
|
var Video = /** @class */ (function (_super) {
|
|
1167
1529
|
__extends(Video, _super);
|
|
1168
|
-
function Video() {
|
|
1169
|
-
|
|
1530
|
+
function Video(options) {
|
|
1531
|
+
var _a;
|
|
1532
|
+
if (typeof (options.source) === 'string') {
|
|
1533
|
+
var video = document.createElement('video');
|
|
1534
|
+
video.src = options.source;
|
|
1535
|
+
options.source = video;
|
|
1536
|
+
}
|
|
1537
|
+
return _super.call(this, __assign(__assign({}, options), {
|
|
1538
|
+
// Set a default duration so that the super constructor doesn't throw an
|
|
1539
|
+
// error
|
|
1540
|
+
duration: (_a = options.duration) !== null && _a !== void 0 ? _a : 0 })) || this;
|
|
1170
1541
|
}
|
|
1171
1542
|
return Video;
|
|
1172
1543
|
}(AudioSourceMixin(VisualSourceMixin(Visual))));
|
|
@@ -1176,6 +1547,7 @@ var etro = (function () {
|
|
|
1176
1547
|
*/
|
|
1177
1548
|
|
|
1178
1549
|
var index = /*#__PURE__*/Object.freeze({
|
|
1550
|
+
__proto__: null,
|
|
1179
1551
|
AudioSourceMixin: AudioSourceMixin,
|
|
1180
1552
|
Audio: Audio,
|
|
1181
1553
|
Base: Base,
|
|
@@ -1191,46 +1563,48 @@ var etro = (function () {
|
|
|
1191
1563
|
*/
|
|
1192
1564
|
var Base$1 = /** @class */ (function () {
|
|
1193
1565
|
function Base() {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
newThis._target = null;
|
|
1198
|
-
// Propogate up to target
|
|
1199
|
-
subscribe(newThis, 'effect.change.modify', function (event) {
|
|
1200
|
-
if (!newThis._target)
|
|
1201
|
-
return;
|
|
1202
|
-
var type = newThis._target.type + ".change.effect.modify";
|
|
1203
|
-
publish(newThis._target, type, __assign(__assign({}, event), { target: newThis._target, source: newThis, type: type }));
|
|
1204
|
-
});
|
|
1205
|
-
return newThis;
|
|
1566
|
+
this.enabled = true;
|
|
1567
|
+
this._occurrenceCount = 0;
|
|
1568
|
+
this._target = null;
|
|
1206
1569
|
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Wait until this effect is ready to be applied
|
|
1572
|
+
*/
|
|
1573
|
+
Base.prototype.whenReady = function () {
|
|
1574
|
+
return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
1575
|
+
return [2 /*return*/];
|
|
1576
|
+
}); });
|
|
1577
|
+
}; // eslint-disable-line @typescript-eslint/no-empty-function
|
|
1207
1578
|
/**
|
|
1208
1579
|
* Attaches this effect to `target` if not already attached.
|
|
1209
1580
|
* @ignore
|
|
1210
1581
|
*/
|
|
1211
1582
|
Base.prototype.tryAttach = function (target) {
|
|
1212
|
-
if (this._occurrenceCount === 0)
|
|
1583
|
+
if (this._occurrenceCount === 0) {
|
|
1213
1584
|
this.attach(target);
|
|
1585
|
+
}
|
|
1214
1586
|
this._occurrenceCount++;
|
|
1215
1587
|
};
|
|
1216
1588
|
Base.prototype.attach = function (movie) {
|
|
1217
1589
|
this._target = movie;
|
|
1218
1590
|
};
|
|
1219
1591
|
/**
|
|
1220
|
-
*
|
|
1592
|
+
* Detaches this effect from its target if the number of times `tryDetach`
|
|
1221
1593
|
* has been called (including this call) equals the number of times
|
|
1222
1594
|
* `tryAttach` has been called.
|
|
1223
1595
|
*
|
|
1224
1596
|
* @ignore
|
|
1225
1597
|
*/
|
|
1226
1598
|
Base.prototype.tryDetach = function () {
|
|
1227
|
-
if (this._target === null)
|
|
1599
|
+
if (this._target === null) {
|
|
1228
1600
|
throw new Error('No movie to detach from');
|
|
1601
|
+
}
|
|
1229
1602
|
this._occurrenceCount--;
|
|
1230
1603
|
// If this effect occurs in another place in the containing array, do not
|
|
1231
1604
|
// unset _target. (For calling `unshift` on the `layers` proxy)
|
|
1232
|
-
if (this._occurrenceCount === 0)
|
|
1605
|
+
if (this._occurrenceCount === 0) {
|
|
1233
1606
|
this.detach();
|
|
1607
|
+
}
|
|
1234
1608
|
};
|
|
1235
1609
|
Base.prototype.detach = function () {
|
|
1236
1610
|
this._target = null;
|
|
@@ -1255,6 +1629,14 @@ var etro = (function () {
|
|
|
1255
1629
|
enumerable: false,
|
|
1256
1630
|
configurable: true
|
|
1257
1631
|
});
|
|
1632
|
+
Object.defineProperty(Base.prototype, "ready", {
|
|
1633
|
+
/** `true` if this effect is ready to be applied */
|
|
1634
|
+
get: function () {
|
|
1635
|
+
return true;
|
|
1636
|
+
},
|
|
1637
|
+
enumerable: false,
|
|
1638
|
+
configurable: true
|
|
1639
|
+
});
|
|
1258
1640
|
Object.defineProperty(Base.prototype, "parent", {
|
|
1259
1641
|
get: function () {
|
|
1260
1642
|
return this._target;
|
|
@@ -1269,6 +1651,9 @@ var etro = (function () {
|
|
|
1269
1651
|
enumerable: false,
|
|
1270
1652
|
configurable: true
|
|
1271
1653
|
});
|
|
1654
|
+
/**
|
|
1655
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1656
|
+
*/
|
|
1272
1657
|
Base.prototype.getDefaultOptions = function () {
|
|
1273
1658
|
return {};
|
|
1274
1659
|
};
|
|
@@ -1336,16 +1721,18 @@ var etro = (function () {
|
|
|
1336
1721
|
Shader.prototype._initGl = function () {
|
|
1337
1722
|
this._canvas = document.createElement('canvas');
|
|
1338
1723
|
var gl = this._canvas.getContext('webgl');
|
|
1339
|
-
if (gl === null)
|
|
1724
|
+
if (gl === null) {
|
|
1340
1725
|
throw new Error('Unable to initialize WebGL. Your browser or machine may not support it.');
|
|
1726
|
+
}
|
|
1341
1727
|
this._gl = gl;
|
|
1342
1728
|
return gl;
|
|
1343
1729
|
};
|
|
1344
1730
|
Shader.prototype._initTextures = function (userUniforms, userTextures, sourceTextureOptions) {
|
|
1345
1731
|
var gl = this._gl;
|
|
1346
1732
|
var maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
1347
|
-
if (userTextures.length > maxTextures)
|
|
1733
|
+
if (userTextures.length > maxTextures) {
|
|
1348
1734
|
console.warn('Too many textures!');
|
|
1735
|
+
}
|
|
1349
1736
|
this._userTextures = {};
|
|
1350
1737
|
for (var name_1 in userTextures) {
|
|
1351
1738
|
var userOptions = userTextures[name_1];
|
|
@@ -1358,8 +1745,9 @@ var etro = (function () {
|
|
|
1358
1745
|
* textures, without having to define multiple properties in the effect
|
|
1359
1746
|
* object.
|
|
1360
1747
|
*/
|
|
1361
|
-
if (userUniforms[name_1])
|
|
1362
|
-
throw new Error("Texture - uniform naming conflict: "
|
|
1748
|
+
if (userUniforms[name_1]) {
|
|
1749
|
+
throw new Error("Texture - uniform naming conflict: ".concat(name_1, "!"));
|
|
1750
|
+
}
|
|
1363
1751
|
// Add this as a "user uniform".
|
|
1364
1752
|
userUniforms[name_1] = '1i'; // texture pointer
|
|
1365
1753
|
}
|
|
@@ -1394,18 +1782,6 @@ var etro = (function () {
|
|
|
1394
1782
|
this._uniformLocations[unprefixed] = gl.getUniformLocation(this._program, prefixed);
|
|
1395
1783
|
}
|
|
1396
1784
|
};
|
|
1397
|
-
// Not needed, right?
|
|
1398
|
-
/* watchWebGLOptions() {
|
|
1399
|
-
const pubChange = () => {
|
|
1400
|
-
this.publish("change", {});
|
|
1401
|
-
};
|
|
1402
|
-
for (let name in this._userTextures) {
|
|
1403
|
-
watch(this, name, pubChange);
|
|
1404
|
-
}
|
|
1405
|
-
for (let name in this._userUniforms) {
|
|
1406
|
-
watch(this, name, pubChange);
|
|
1407
|
-
}
|
|
1408
|
-
} */
|
|
1409
1785
|
Shader.prototype.apply = function (target, reltime) {
|
|
1410
1786
|
this._checkDimensions(target);
|
|
1411
1787
|
this._refreshGl();
|
|
@@ -1492,16 +1868,22 @@ var etro = (function () {
|
|
|
1492
1868
|
i++;
|
|
1493
1869
|
}
|
|
1494
1870
|
};
|
|
1871
|
+
/**
|
|
1872
|
+
* Set the shader's uniforms.
|
|
1873
|
+
* @param target The movie or layer to apply the shader to.
|
|
1874
|
+
* @param reltime The relative time of the movie or layer.
|
|
1875
|
+
*/
|
|
1495
1876
|
Shader.prototype._prepareUniforms = function (target, reltime) {
|
|
1496
1877
|
var gl = this._gl;
|
|
1497
|
-
// Set the shader uniforms.
|
|
1498
1878
|
// Tell the shader we bound the texture to texture unit 0.
|
|
1499
1879
|
// All base (Shader class) uniforms are optional.
|
|
1500
|
-
if (this._uniformLocations.source)
|
|
1880
|
+
if (this._uniformLocations.source) {
|
|
1501
1881
|
gl.uniform1i(this._uniformLocations.source, 0);
|
|
1882
|
+
}
|
|
1502
1883
|
// All base (Shader class) uniforms are optional.
|
|
1503
|
-
if (this._uniformLocations.size)
|
|
1884
|
+
if (this._uniformLocations.size) {
|
|
1504
1885
|
gl.uniform2iv(this._uniformLocations.size, [target.canvas.width, target.canvas.height]);
|
|
1886
|
+
}
|
|
1505
1887
|
for (var unprefixed in this._userUniforms) {
|
|
1506
1888
|
var options = this._userUniforms[unprefixed];
|
|
1507
1889
|
var value = val(this, unprefixed, reltime);
|
|
@@ -1525,11 +1907,13 @@ var etro = (function () {
|
|
|
1525
1907
|
/**
|
|
1526
1908
|
* Converts a value of a standard type for javascript to a standard type for
|
|
1527
1909
|
* GLSL
|
|
1910
|
+
*
|
|
1528
1911
|
* @param value - the raw value to prepare
|
|
1529
1912
|
* @param outputType - the WebGL type of |value|; example:
|
|
1530
1913
|
* <code>1f</code> for a float
|
|
1531
1914
|
* @param reltime - current time, relative to the target
|
|
1532
|
-
* @param [options]
|
|
1915
|
+
* @param [options]
|
|
1916
|
+
* @returns the prepared value
|
|
1533
1917
|
*/
|
|
1534
1918
|
Shader.prototype._prepareValue = function (value, outputType, reltime, options) {
|
|
1535
1919
|
if (options === void 0) { options = {}; }
|
|
@@ -1552,36 +1936,41 @@ var etro = (function () {
|
|
|
1552
1936
|
var i = 0;
|
|
1553
1937
|
for (var name_4 in this._userTextures) {
|
|
1554
1938
|
var testValue = val(this, name_4, reltime);
|
|
1555
|
-
if (value === testValue)
|
|
1939
|
+
if (value === testValue) {
|
|
1556
1940
|
value = Shader.INTERNAL_TEXTURE_UNITS + i; // after the internal texture units
|
|
1941
|
+
}
|
|
1557
1942
|
i++;
|
|
1558
1943
|
}
|
|
1559
1944
|
}
|
|
1560
1945
|
if (outputType === '3fv') {
|
|
1561
1946
|
// allow 4-component vectors; TODO: why?
|
|
1562
|
-
if (Array.isArray(value) && (value.length === 3 || value.length === 4))
|
|
1947
|
+
if (Array.isArray(value) && (value.length === 3 || value.length === 4)) {
|
|
1563
1948
|
return value;
|
|
1949
|
+
}
|
|
1564
1950
|
// kind of loose so this can be changed if needed
|
|
1565
|
-
if (typeof value === 'object')
|
|
1951
|
+
if (typeof value === 'object') {
|
|
1566
1952
|
return [
|
|
1567
1953
|
value.r !== undefined ? value.r : def,
|
|
1568
1954
|
value.g !== undefined ? value.g : def,
|
|
1569
1955
|
value.b !== undefined ? value.b : def
|
|
1570
1956
|
];
|
|
1571
|
-
|
|
1957
|
+
}
|
|
1958
|
+
throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
|
|
1572
1959
|
}
|
|
1573
1960
|
if (outputType === '4fv') {
|
|
1574
|
-
if (Array.isArray(value) && value.length === 4)
|
|
1961
|
+
if (Array.isArray(value) && value.length === 4) {
|
|
1575
1962
|
return value;
|
|
1963
|
+
}
|
|
1576
1964
|
// kind of loose so this can be changed if needed
|
|
1577
|
-
if (typeof value === 'object')
|
|
1965
|
+
if (typeof value === 'object') {
|
|
1578
1966
|
return [
|
|
1579
1967
|
value.r !== undefined ? value.r : def,
|
|
1580
1968
|
value.g !== undefined ? value.g : def,
|
|
1581
1969
|
value.b !== undefined ? value.b : def,
|
|
1582
1970
|
value.a !== undefined ? value.a : def
|
|
1583
1971
|
];
|
|
1584
|
-
|
|
1972
|
+
}
|
|
1973
|
+
throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
|
|
1585
1974
|
}
|
|
1586
1975
|
return value;
|
|
1587
1976
|
};
|
|
@@ -1674,8 +2063,9 @@ var etro = (function () {
|
|
|
1674
2063
|
else {
|
|
1675
2064
|
// No, it's not a power of 2. Turn off mips and set
|
|
1676
2065
|
// wrapping to clamp to edge
|
|
1677
|
-
if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE)
|
|
2066
|
+
if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE) {
|
|
1678
2067
|
console.warn('Wrap mode is not CLAMP_TO_EDGE for a non-power-of-two texture. Defaulting to CLAMP_TO_EDGE');
|
|
2068
|
+
}
|
|
1679
2069
|
gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
1680
2070
|
gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
1681
2071
|
}
|
|
@@ -1727,7 +2117,6 @@ var etro = (function () {
|
|
|
1727
2117
|
Shader._IDENTITY_FRAGMENT_SOURCE = "\n precision mediump float;\n\n uniform sampler2D u_Source;\n\n varying highp vec2 v_TextureCoord;\n\n void main() {\n gl_FragColor = texture2D(u_Source, v_TextureCoord);\n }\n ";
|
|
1728
2118
|
return Shader;
|
|
1729
2119
|
}(Visual$1));
|
|
1730
|
-
// Shader.prototype.getpublicExcludes = () =>
|
|
1731
2120
|
var isPowerOf2 = function (value) { return (value && (value - 1)) === 0; };
|
|
1732
2121
|
|
|
1733
2122
|
/**
|
|
@@ -1902,6 +2291,35 @@ var etro = (function () {
|
|
|
1902
2291
|
return EllipticalMask;
|
|
1903
2292
|
}(Visual$1));
|
|
1904
2293
|
|
|
2294
|
+
var StackEffectsListener = /** @class */ (function (_super) {
|
|
2295
|
+
__extends(StackEffectsListener, _super);
|
|
2296
|
+
function StackEffectsListener(stack) {
|
|
2297
|
+
var _this = _super.call(this) || this;
|
|
2298
|
+
_this._stack = stack;
|
|
2299
|
+
return _this;
|
|
2300
|
+
}
|
|
2301
|
+
StackEffectsListener.prototype.onAdd = function (effect) {
|
|
2302
|
+
if (!this._stack.parent) {
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
effect.tryAttach(this._stack.parent);
|
|
2306
|
+
};
|
|
2307
|
+
StackEffectsListener.prototype.onRemove = function (effect) {
|
|
2308
|
+
if (!this._stack.parent) {
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
effect.tryDetach();
|
|
2312
|
+
};
|
|
2313
|
+
return StackEffectsListener;
|
|
2314
|
+
}(CustomArrayListener));
|
|
2315
|
+
var StackEffects = /** @class */ (function (_super) {
|
|
2316
|
+
__extends(StackEffects, _super);
|
|
2317
|
+
// eslint-disable-next-line no-use-before-define
|
|
2318
|
+
function StackEffects(target, stack) {
|
|
2319
|
+
return _super.call(this, target, new StackEffectsListener(stack)) || this;
|
|
2320
|
+
}
|
|
2321
|
+
return StackEffects;
|
|
2322
|
+
}(CustomArray));
|
|
1905
2323
|
/**
|
|
1906
2324
|
* A sequence of effects to apply, treated as one effect. This can be useful
|
|
1907
2325
|
* for defining reused effect sequences as one effect.
|
|
@@ -1910,48 +2328,28 @@ var etro = (function () {
|
|
|
1910
2328
|
__extends(Stack, _super);
|
|
1911
2329
|
function Stack(options) {
|
|
1912
2330
|
var _this = _super.call(this) || this;
|
|
1913
|
-
_this.
|
|
1914
|
-
// TODO: Throw 'change' events in handlers
|
|
1915
|
-
_this.effects = new Proxy(_this._effectsBack, {
|
|
1916
|
-
deleteProperty: function (target, property) {
|
|
1917
|
-
var value = target[property];
|
|
1918
|
-
value.detach(); // Detach effect from movie
|
|
1919
|
-
delete target[property];
|
|
1920
|
-
return true;
|
|
1921
|
-
},
|
|
1922
|
-
set: function (target, property, value) {
|
|
1923
|
-
// TODO: make sure type check works
|
|
1924
|
-
if (!isNaN(Number(property))) { // if property is a number (index)
|
|
1925
|
-
if (target[property])
|
|
1926
|
-
target[property].detach(); // Detach old effect from movie
|
|
1927
|
-
value.attach(this._target); // Attach effect to movie
|
|
1928
|
-
}
|
|
1929
|
-
target[property] = value;
|
|
1930
|
-
return true;
|
|
1931
|
-
}
|
|
1932
|
-
});
|
|
2331
|
+
_this.effects = new StackEffects(options.effects, _this);
|
|
1933
2332
|
options.effects.forEach(function (effect) { return _this.effects.push(effect); });
|
|
1934
2333
|
return _this;
|
|
1935
|
-
// TODO: Propogate 'change' events from children up
|
|
1936
2334
|
}
|
|
1937
2335
|
Stack.prototype.attach = function (movie) {
|
|
1938
2336
|
_super.prototype.attach.call(this, movie);
|
|
1939
2337
|
this.effects.filter(function (effect) { return !!effect; }).forEach(function (effect) {
|
|
1940
|
-
effect.
|
|
1941
|
-
effect.attach(movie);
|
|
2338
|
+
effect.tryAttach(movie);
|
|
1942
2339
|
});
|
|
1943
2340
|
};
|
|
1944
2341
|
Stack.prototype.detach = function () {
|
|
1945
2342
|
_super.prototype.detach.call(this);
|
|
1946
2343
|
this.effects.filter(function (effect) { return !!effect; }).forEach(function (effect) {
|
|
1947
|
-
effect.
|
|
2344
|
+
effect.tryDetach();
|
|
1948
2345
|
});
|
|
1949
2346
|
};
|
|
1950
2347
|
Stack.prototype.apply = function (target, reltime) {
|
|
1951
2348
|
for (var i = 0; i < this.effects.length; i++) {
|
|
1952
2349
|
var effect = this.effects[i];
|
|
1953
|
-
if (!effect)
|
|
2350
|
+
if (!effect) {
|
|
1954
2351
|
continue;
|
|
2352
|
+
}
|
|
1955
2353
|
effect.apply(target, reltime);
|
|
1956
2354
|
}
|
|
1957
2355
|
};
|
|
@@ -1970,7 +2368,7 @@ var etro = (function () {
|
|
|
1970
2368
|
* Applies a Gaussian blur
|
|
1971
2369
|
*/
|
|
1972
2370
|
// TODO: Improve performance
|
|
1973
|
-
// TODO: Make sure this is truly gaussian even though it
|
|
2371
|
+
// TODO: Make sure this is truly gaussian even though it doesn't require a
|
|
1974
2372
|
// standard deviation
|
|
1975
2373
|
var GaussianBlur = /** @class */ (function (_super) {
|
|
1976
2374
|
__extends(GaussianBlur, _super);
|
|
@@ -2015,9 +2413,10 @@ var etro = (function () {
|
|
|
2015
2413
|
}
|
|
2016
2414
|
GaussianBlurComponent.prototype.apply = function (target, reltime) {
|
|
2017
2415
|
var radiusVal = val(this, 'radius', reltime);
|
|
2018
|
-
if (radiusVal !== this._radiusCache)
|
|
2416
|
+
if (radiusVal !== this._radiusCache) {
|
|
2019
2417
|
// Regenerate gaussian distribution canvas.
|
|
2020
2418
|
this.shape = GaussianBlurComponent._render1DKernel(GaussianBlurComponent._gen1DKernel(radiusVal));
|
|
2419
|
+
}
|
|
2021
2420
|
this._radiusCache = radiusVal;
|
|
2022
2421
|
_super.prototype.apply.call(this, target, reltime);
|
|
2023
2422
|
};
|
|
@@ -2050,23 +2449,27 @@ var etro = (function () {
|
|
|
2050
2449
|
var pascal = GaussianBlurComponent._genPascalRow(2 * radius + 1);
|
|
2051
2450
|
// don't use `reduce` and `map` (overhead?)
|
|
2052
2451
|
var sum = 0;
|
|
2053
|
-
for (var i = 0; i < pascal.length; i++)
|
|
2452
|
+
for (var i = 0; i < pascal.length; i++) {
|
|
2054
2453
|
sum += pascal[i];
|
|
2055
|
-
|
|
2454
|
+
}
|
|
2455
|
+
for (var i = 0; i < pascal.length; i++) {
|
|
2056
2456
|
pascal[i] /= sum;
|
|
2457
|
+
}
|
|
2057
2458
|
return pascal;
|
|
2058
2459
|
};
|
|
2059
2460
|
GaussianBlurComponent._genPascalRow = function (index) {
|
|
2060
|
-
if (index < 0)
|
|
2061
|
-
throw new Error("Invalid index "
|
|
2461
|
+
if (index < 0) {
|
|
2462
|
+
throw new Error("Invalid index ".concat(index));
|
|
2463
|
+
}
|
|
2062
2464
|
var currRow = [1];
|
|
2063
2465
|
for (var i = 1; i < index; i++) {
|
|
2064
2466
|
var nextRow = [];
|
|
2065
2467
|
nextRow.length = currRow.length + 1;
|
|
2066
2468
|
// edges are always 1's
|
|
2067
2469
|
nextRow[0] = nextRow[nextRow.length - 1] = 1;
|
|
2068
|
-
for (var j = 1; j < nextRow.length - 1; j++)
|
|
2470
|
+
for (var j = 1; j < nextRow.length - 1; j++) {
|
|
2069
2471
|
nextRow[j] = currRow[j - 1] + currRow[j];
|
|
2472
|
+
}
|
|
2070
2473
|
currRow = nextRow;
|
|
2071
2474
|
}
|
|
2072
2475
|
return currRow;
|
|
@@ -2137,15 +2540,14 @@ var etro = (function () {
|
|
|
2137
2540
|
pixelSize: '1i'
|
|
2138
2541
|
}
|
|
2139
2542
|
}) || this;
|
|
2140
|
-
/**
|
|
2141
|
-
*/
|
|
2142
2543
|
_this.pixelSize = options.pixelSize || 1;
|
|
2143
2544
|
return _this;
|
|
2144
2545
|
}
|
|
2145
2546
|
Pixelate.prototype.apply = function (target, reltime) {
|
|
2146
2547
|
var ps = val(this, 'pixelSize', reltime);
|
|
2147
|
-
if (ps % 1 !== 0 || ps < 0)
|
|
2548
|
+
if (ps % 1 !== 0 || ps < 0) {
|
|
2148
2549
|
throw new Error('Pixel size must be a nonnegative integer');
|
|
2550
|
+
}
|
|
2149
2551
|
_super.prototype.apply.call(this, target, reltime);
|
|
2150
2552
|
};
|
|
2151
2553
|
return Pixelate;
|
|
@@ -2174,10 +2576,12 @@ var etro = (function () {
|
|
|
2174
2576
|
return _this;
|
|
2175
2577
|
}
|
|
2176
2578
|
Transform.prototype.apply = function (target, reltime) {
|
|
2177
|
-
if (target.canvas.width !== this._tmpCanvas.width)
|
|
2579
|
+
if (target.canvas.width !== this._tmpCanvas.width) {
|
|
2178
2580
|
this._tmpCanvas.width = target.canvas.width;
|
|
2179
|
-
|
|
2581
|
+
}
|
|
2582
|
+
if (target.canvas.height !== this._tmpCanvas.height) {
|
|
2180
2583
|
this._tmpCanvas.height = target.canvas.height;
|
|
2584
|
+
}
|
|
2181
2585
|
// Use data, since that's the underlying storage
|
|
2182
2586
|
this._tmpMatrix.data = val(this, 'matrix.data', reltime);
|
|
2183
2587
|
this._tmpCtx.setTransform(this._tmpMatrix.a, this._tmpMatrix.b, this._tmpMatrix.c, this._tmpMatrix.d, this._tmpMatrix.e, this._tmpMatrix.f);
|
|
@@ -2203,8 +2607,9 @@ var etro = (function () {
|
|
|
2203
2607
|
];
|
|
2204
2608
|
}
|
|
2205
2609
|
Matrix.prototype.identity = function () {
|
|
2206
|
-
for (var i = 0; i < this.data.length; i++)
|
|
2610
|
+
for (var i = 0; i < this.data.length; i++) {
|
|
2207
2611
|
this.data[i] = Matrix.IDENTITY.data[i];
|
|
2612
|
+
}
|
|
2208
2613
|
return this;
|
|
2209
2614
|
};
|
|
2210
2615
|
/**
|
|
@@ -2213,8 +2618,9 @@ var etro = (function () {
|
|
|
2213
2618
|
* @param [val]
|
|
2214
2619
|
*/
|
|
2215
2620
|
Matrix.prototype.cell = function (x, y, val) {
|
|
2216
|
-
if (val !== undefined)
|
|
2621
|
+
if (val !== undefined) {
|
|
2217
2622
|
this.data[3 * y + x] = val;
|
|
2623
|
+
}
|
|
2218
2624
|
return this.data[3 * y + x];
|
|
2219
2625
|
};
|
|
2220
2626
|
Object.defineProperty(Matrix.prototype, "a", {
|
|
@@ -2266,16 +2672,19 @@ var etro = (function () {
|
|
|
2266
2672
|
*/
|
|
2267
2673
|
Matrix.prototype.multiply = function (other) {
|
|
2268
2674
|
// copy to temporary matrix to avoid modifying `this` while reading from it
|
|
2269
|
-
for (var x = 0; x < 3; x++)
|
|
2675
|
+
for (var x = 0; x < 3; x++) {
|
|
2270
2676
|
for (var y = 0; y < 3; y++) {
|
|
2271
2677
|
var sum = 0;
|
|
2272
|
-
for (var i = 0; i < 3; i++)
|
|
2678
|
+
for (var i = 0; i < 3; i++) {
|
|
2273
2679
|
sum += this.cell(x, i) * other.cell(i, y);
|
|
2680
|
+
}
|
|
2274
2681
|
Matrix._TMP_MATRIX.cell(x, y, sum);
|
|
2275
2682
|
}
|
|
2683
|
+
}
|
|
2276
2684
|
// copy data from TMP_MATRIX to this
|
|
2277
|
-
for (var i = 0; i < Matrix._TMP_MATRIX.data.length; i++)
|
|
2685
|
+
for (var i = 0; i < Matrix._TMP_MATRIX.data.length; i++) {
|
|
2278
2686
|
this.data[i] = Matrix._TMP_MATRIX.data[i];
|
|
2687
|
+
}
|
|
2279
2688
|
return this;
|
|
2280
2689
|
};
|
|
2281
2690
|
/**
|
|
@@ -2330,6 +2739,7 @@ var etro = (function () {
|
|
|
2330
2739
|
*/
|
|
2331
2740
|
|
|
2332
2741
|
var index$1 = /*#__PURE__*/Object.freeze({
|
|
2742
|
+
__proto__: null,
|
|
2333
2743
|
Base: Base$1,
|
|
2334
2744
|
Brightness: Brightness,
|
|
2335
2745
|
Channels: Channels,
|
|
@@ -2348,6 +2758,52 @@ var etro = (function () {
|
|
|
2348
2758
|
Visual: Visual$1
|
|
2349
2759
|
});
|
|
2350
2760
|
|
|
2761
|
+
var MovieEffectsListener = /** @class */ (function (_super) {
|
|
2762
|
+
__extends(MovieEffectsListener, _super);
|
|
2763
|
+
function MovieEffectsListener(movie) {
|
|
2764
|
+
var _this = _super.call(this) || this;
|
|
2765
|
+
_this._movie = movie;
|
|
2766
|
+
return _this;
|
|
2767
|
+
}
|
|
2768
|
+
MovieEffectsListener.prototype.onAdd = function (effect) {
|
|
2769
|
+
effect.tryAttach(this._movie);
|
|
2770
|
+
};
|
|
2771
|
+
MovieEffectsListener.prototype.onRemove = function (effect) {
|
|
2772
|
+
effect.tryDetach();
|
|
2773
|
+
};
|
|
2774
|
+
return MovieEffectsListener;
|
|
2775
|
+
}(CustomArrayListener));
|
|
2776
|
+
var MovieEffects = /** @class */ (function (_super) {
|
|
2777
|
+
__extends(MovieEffects, _super);
|
|
2778
|
+
function MovieEffects(target, movie) {
|
|
2779
|
+
return _super.call(this, target, new MovieEffectsListener(movie)) || this;
|
|
2780
|
+
}
|
|
2781
|
+
return MovieEffects;
|
|
2782
|
+
}(CustomArray));
|
|
2783
|
+
|
|
2784
|
+
var MovieLayersListener = /** @class */ (function (_super) {
|
|
2785
|
+
__extends(MovieLayersListener, _super);
|
|
2786
|
+
function MovieLayersListener(movie) {
|
|
2787
|
+
var _this = _super.call(this) || this;
|
|
2788
|
+
_this._movie = movie;
|
|
2789
|
+
return _this;
|
|
2790
|
+
}
|
|
2791
|
+
MovieLayersListener.prototype.onAdd = function (layer) {
|
|
2792
|
+
layer.tryAttach(this._movie);
|
|
2793
|
+
};
|
|
2794
|
+
MovieLayersListener.prototype.onRemove = function (layer) {
|
|
2795
|
+
layer.tryDetach();
|
|
2796
|
+
};
|
|
2797
|
+
return MovieLayersListener;
|
|
2798
|
+
}(CustomArrayListener));
|
|
2799
|
+
var MovieLayers = /** @class */ (function (_super) {
|
|
2800
|
+
__extends(MovieLayers, _super);
|
|
2801
|
+
function MovieLayers(target, movie) {
|
|
2802
|
+
return _super.call(this, target, new MovieLayersListener(movie)) || this;
|
|
2803
|
+
}
|
|
2804
|
+
return MovieLayers;
|
|
2805
|
+
}(CustomArray));
|
|
2806
|
+
|
|
2351
2807
|
/**
|
|
2352
2808
|
* @module movie
|
|
2353
2809
|
*/
|
|
@@ -2361,17 +2817,13 @@ var etro = (function () {
|
|
|
2361
2817
|
*
|
|
2362
2818
|
* Implements a pub/sub system.
|
|
2363
2819
|
*/
|
|
2364
|
-
// TODO: Implement event "durationchange", and more
|
|
2365
|
-
// TODO: Add width and height options
|
|
2366
|
-
// TODO: Make record option to make recording video output to the user while
|
|
2367
|
-
// it's recording
|
|
2368
2820
|
// TODO: rename renderingFrame -> refreshing
|
|
2369
2821
|
var Movie = /** @class */ (function () {
|
|
2370
2822
|
/**
|
|
2371
2823
|
* Creates a new movie.
|
|
2372
2824
|
*/
|
|
2373
2825
|
function Movie(options) {
|
|
2374
|
-
|
|
2826
|
+
this._recording = false;
|
|
2375
2827
|
// Set actx option manually, because it's readonly.
|
|
2376
2828
|
this.actx = options.actx ||
|
|
2377
2829
|
options.audioContext ||
|
|
@@ -2379,236 +2831,294 @@ var etro = (function () {
|
|
|
2379
2831
|
// eslint-disable-next-line new-cap
|
|
2380
2832
|
new window.webkitAudioContext();
|
|
2381
2833
|
delete options.actx;
|
|
2382
|
-
//
|
|
2383
|
-
|
|
2834
|
+
// Check if required file canvas is provided
|
|
2835
|
+
if (!options.canvas) {
|
|
2836
|
+
throw new Error('Required option "canvas" not provided to Movie');
|
|
2837
|
+
}
|
|
2384
2838
|
// Set canvas option manually, because it's readonly.
|
|
2385
|
-
this._canvas = options.canvas;
|
|
2839
|
+
this._canvas = this._visibleCanvas = options.canvas;
|
|
2386
2840
|
delete options.canvas;
|
|
2387
|
-
// Don't send updates when initializing, so use this instead of newThis:
|
|
2388
2841
|
this._cctx = this.canvas.getContext('2d'); // TODO: make private?
|
|
2842
|
+
// Set options on the movie
|
|
2389
2843
|
applyOptions(options, this);
|
|
2390
|
-
|
|
2391
|
-
this.
|
|
2392
|
-
this.effects = new Proxy(newThis._effectsBack, {
|
|
2393
|
-
deleteProperty: function (target, property) {
|
|
2394
|
-
// Refresh screen when effect is removed, if the movie isn't playing
|
|
2395
|
-
// already.
|
|
2396
|
-
var value = target[property];
|
|
2397
|
-
value.tryDetach();
|
|
2398
|
-
delete target[property];
|
|
2399
|
-
publish(that, 'movie.change.effect.remove', { effect: value });
|
|
2400
|
-
return true;
|
|
2401
|
-
},
|
|
2402
|
-
set: function (target, property, value) {
|
|
2403
|
-
// Check if property is an number (an index)
|
|
2404
|
-
if (!isNaN(Number(property))) {
|
|
2405
|
-
if (target[property]) {
|
|
2406
|
-
publish(that, 'movie.change.effect.remove', {
|
|
2407
|
-
effect: target[property]
|
|
2408
|
-
});
|
|
2409
|
-
target[property].tryDetach();
|
|
2410
|
-
}
|
|
2411
|
-
// Attach effect to movie
|
|
2412
|
-
value.tryAttach(that);
|
|
2413
|
-
target[property] = value;
|
|
2414
|
-
// Refresh screen when effect is set, if the movie isn't playing
|
|
2415
|
-
// already.
|
|
2416
|
-
publish(that, 'movie.change.effect.add', { effect: value });
|
|
2417
|
-
}
|
|
2418
|
-
else {
|
|
2419
|
-
target[property] = value;
|
|
2420
|
-
}
|
|
2421
|
-
return true;
|
|
2422
|
-
}
|
|
2423
|
-
});
|
|
2424
|
-
this._layersBack = [];
|
|
2425
|
-
this.layers = new Proxy(newThis._layersBack, {
|
|
2426
|
-
deleteProperty: function (target, property) {
|
|
2427
|
-
var oldDuration = this.duration;
|
|
2428
|
-
var value = target[property];
|
|
2429
|
-
value.tryDetach(that);
|
|
2430
|
-
delete target[property];
|
|
2431
|
-
var current = that.currentTime >= value.startTime && that.currentTime < value.startTime + value.duration;
|
|
2432
|
-
if (current)
|
|
2433
|
-
publish(that, 'movie.change.layer.remove', { layer: value });
|
|
2434
|
-
publish(that, 'movie.change.duration', { oldDuration: oldDuration });
|
|
2435
|
-
return true;
|
|
2436
|
-
},
|
|
2437
|
-
set: function (target, property, value) {
|
|
2438
|
-
var oldDuration = this.duration;
|
|
2439
|
-
// Check if property is an number (an index)
|
|
2440
|
-
if (!isNaN(Number(property))) {
|
|
2441
|
-
if (target[property]) {
|
|
2442
|
-
publish(that, 'movie.change.layer.remove', {
|
|
2443
|
-
layer: target[property]
|
|
2444
|
-
});
|
|
2445
|
-
target[property].tryDetach();
|
|
2446
|
-
}
|
|
2447
|
-
// Attach layer to movie
|
|
2448
|
-
value.tryAttach(that);
|
|
2449
|
-
target[property] = value;
|
|
2450
|
-
// Refresh screen when a relevant layer is added or removed
|
|
2451
|
-
var current = that.currentTime >= value.startTime && that.currentTime < value.startTime + value.duration;
|
|
2452
|
-
if (current)
|
|
2453
|
-
publish(that, 'movie.change.layer.add', { layer: value });
|
|
2454
|
-
publish(that, 'movie.change.duration', { oldDuration: oldDuration });
|
|
2455
|
-
}
|
|
2456
|
-
else {
|
|
2457
|
-
target[property] = value;
|
|
2458
|
-
}
|
|
2459
|
-
return true;
|
|
2460
|
-
}
|
|
2461
|
-
});
|
|
2844
|
+
this.effects = new MovieEffects([], this);
|
|
2845
|
+
this.layers = new MovieLayers([], this);
|
|
2462
2846
|
this._paused = true;
|
|
2463
2847
|
this._ended = false;
|
|
2464
|
-
// This
|
|
2465
|
-
//
|
|
2848
|
+
// This lock prevents multiple refresh loops at the same time (see
|
|
2849
|
+
// `render`). It's only valid while rendering.
|
|
2466
2850
|
this._renderingFrame = false;
|
|
2467
2851
|
this.currentTime = 0;
|
|
2468
|
-
//
|
|
2469
|
-
this._mediaRecorder = null;
|
|
2470
|
-
// -1 works well in inequalities
|
|
2471
|
-
// The last time `play` was called
|
|
2852
|
+
// The last time `play` was called, -1 works well in comparisons
|
|
2472
2853
|
this._lastPlayed = -1;
|
|
2473
|
-
// What
|
|
2854
|
+
// What `currentTime` was when `play` was called
|
|
2474
2855
|
this._lastPlayedOffset = -1;
|
|
2475
|
-
// newThis._updateInterval = 0.1; // time in seconds between each "timeupdate" event
|
|
2476
|
-
// newThis._lastUpdate = -1;
|
|
2477
|
-
if (newThis.autoRefresh)
|
|
2478
|
-
newThis.refresh(); // render single frame on creation
|
|
2479
|
-
// Subscribe to own event "change" (child events propogate up)
|
|
2480
|
-
subscribe(newThis, 'movie.change', function () {
|
|
2481
|
-
if (newThis.autoRefresh && !newThis.rendering)
|
|
2482
|
-
newThis.refresh();
|
|
2483
|
-
});
|
|
2484
|
-
// Subscribe to own event "ended"
|
|
2485
|
-
subscribe(newThis, 'movie.recordended', function () {
|
|
2486
|
-
if (newThis.recording) {
|
|
2487
|
-
newThis._mediaRecorder.requestData();
|
|
2488
|
-
newThis._mediaRecorder.stop();
|
|
2489
|
-
}
|
|
2490
|
-
});
|
|
2491
|
-
return newThis;
|
|
2492
2856
|
}
|
|
2857
|
+
Movie.prototype._whenReady = function () {
|
|
2858
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
2859
|
+
return __generator(this, function (_a) {
|
|
2860
|
+
switch (_a.label) {
|
|
2861
|
+
case 0: return [4 /*yield*/, Promise.all([
|
|
2862
|
+
Promise.all(this.layers.map(function (layer) { return layer.whenReady(); })),
|
|
2863
|
+
Promise.all(this.effects.map(function (effect) { return effect.whenReady(); }))
|
|
2864
|
+
])];
|
|
2865
|
+
case 1:
|
|
2866
|
+
_a.sent();
|
|
2867
|
+
return [2 /*return*/];
|
|
2868
|
+
}
|
|
2869
|
+
});
|
|
2870
|
+
});
|
|
2871
|
+
};
|
|
2493
2872
|
/**
|
|
2494
2873
|
* Plays the movie
|
|
2495
|
-
*
|
|
2874
|
+
*
|
|
2875
|
+
* @param [options]
|
|
2876
|
+
* @param [options.onStart] Called when the movie starts playing
|
|
2877
|
+
*
|
|
2878
|
+
* @return Fulfilled when the movie is done playing, never fails
|
|
2496
2879
|
*/
|
|
2497
|
-
Movie.prototype.play = function () {
|
|
2498
|
-
var
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2880
|
+
Movie.prototype.play = function (options) {
|
|
2881
|
+
var _a;
|
|
2882
|
+
if (options === void 0) { options = {}; }
|
|
2883
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
2884
|
+
var _this = this;
|
|
2885
|
+
return __generator(this, function (_b) {
|
|
2886
|
+
switch (_b.label) {
|
|
2887
|
+
case 0: return [4 /*yield*/, this._whenReady()];
|
|
2888
|
+
case 1:
|
|
2889
|
+
_b.sent();
|
|
2890
|
+
if (!this.paused) {
|
|
2891
|
+
throw new Error('Already playing');
|
|
2892
|
+
}
|
|
2893
|
+
this._paused = this._ended = false;
|
|
2894
|
+
this._lastPlayed = performance.now();
|
|
2895
|
+
this._lastPlayedOffset = this.currentTime;
|
|
2896
|
+
(_a = options.onStart) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
2897
|
+
// For backwards compatibility
|
|
2898
|
+
publish(this, 'movie.play', {});
|
|
2899
|
+
// Repeatedly render frames until the movie ends
|
|
2900
|
+
return [4 /*yield*/, new Promise(function (resolve) {
|
|
2901
|
+
if (!_this.renderingFrame) {
|
|
2902
|
+
// Not rendering (and not playing), so play.
|
|
2903
|
+
_this._render(true, undefined, resolve);
|
|
2904
|
+
}
|
|
2905
|
+
// Stop rendering frame if currently doing so, because playing has higher
|
|
2906
|
+
// priority. This will affect the next _render call.
|
|
2907
|
+
_this._renderingFrame = false;
|
|
2908
|
+
})];
|
|
2909
|
+
case 2:
|
|
2910
|
+
// Repeatedly render frames until the movie ends
|
|
2911
|
+
_b.sent();
|
|
2912
|
+
return [2 /*return*/];
|
|
2913
|
+
}
|
|
2914
|
+
});
|
|
2915
|
+
});
|
|
2916
|
+
};
|
|
2917
|
+
/**
|
|
2918
|
+
* Updates the rendering canvas and audio destination to the visible canvas
|
|
2919
|
+
* and the audio context destination.
|
|
2920
|
+
*/
|
|
2921
|
+
Movie.prototype._show = function () {
|
|
2922
|
+
this._canvas = this._visibleCanvas;
|
|
2923
|
+
this._cctx = this.canvas.getContext('2d');
|
|
2924
|
+
publish(this, 'audiodestinationupdate', { movie: this, destination: this.actx.destination });
|
|
2925
|
+
};
|
|
2926
|
+
/**
|
|
2927
|
+
* Streams the movie to a MediaStream
|
|
2928
|
+
*
|
|
2929
|
+
* @param options Options for the stream
|
|
2930
|
+
* @param options.frameRate The frame rate of the stream's video
|
|
2931
|
+
* @param options.duration The duration of the stream in seconds
|
|
2932
|
+
* @param options.video Whether to stream video. Defaults to true.
|
|
2933
|
+
* @param options.audio Whether to stream audio. Defaults to true.
|
|
2934
|
+
* @param options.onStart Called when the stream is started
|
|
2935
|
+
* @return Fulfilled when the stream is done, never fails
|
|
2936
|
+
*/
|
|
2937
|
+
Movie.prototype.stream = function (options) {
|
|
2938
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
2939
|
+
var tracks, visualStream, hasMediaTracks, audioDestination, audioStream;
|
|
2940
|
+
var _this = this;
|
|
2941
|
+
return __generator(this, function (_a) {
|
|
2942
|
+
switch (_a.label) {
|
|
2943
|
+
case 0:
|
|
2944
|
+
// Validate options
|
|
2945
|
+
if (!options || !options.frameRate) {
|
|
2946
|
+
throw new Error('Required option "frameRate" not provided to Movie.stream');
|
|
2947
|
+
}
|
|
2948
|
+
if (options.video === false && options.audio === false) {
|
|
2949
|
+
throw new Error('Both video and audio cannot be disabled');
|
|
2950
|
+
}
|
|
2951
|
+
if (!this.paused) {
|
|
2952
|
+
throw new Error("Cannot stream movie while it's already playing");
|
|
2953
|
+
}
|
|
2954
|
+
// Wait until all resources are loaded
|
|
2955
|
+
return [4 /*yield*/, this._whenReady()
|
|
2956
|
+
// Create a temporary canvas to stream from
|
|
2957
|
+
];
|
|
2958
|
+
case 1:
|
|
2959
|
+
// Wait until all resources are loaded
|
|
2960
|
+
_a.sent();
|
|
2961
|
+
// Create a temporary canvas to stream from
|
|
2962
|
+
this._canvas = document.createElement('canvas');
|
|
2963
|
+
this.canvas.width = this._visibleCanvas.width;
|
|
2964
|
+
this.canvas.height = this._visibleCanvas.height;
|
|
2965
|
+
this._cctx = this.canvas.getContext('2d');
|
|
2966
|
+
tracks = [];
|
|
2967
|
+
// Add video track
|
|
2968
|
+
if (options.video !== false) {
|
|
2969
|
+
visualStream = this.canvas.captureStream(options.frameRate);
|
|
2970
|
+
tracks = tracks.concat(visualStream.getTracks());
|
|
2971
|
+
}
|
|
2972
|
+
hasMediaTracks = this.layers.some(function (layer) { return layer instanceof Audio || layer instanceof Video; });
|
|
2973
|
+
// If no media tracks present, don't include an audio stream, because
|
|
2974
|
+
// Chrome doesn't record silence when an audio stream is present.
|
|
2975
|
+
if (hasMediaTracks && options.audio !== false) {
|
|
2976
|
+
audioDestination = this.actx.createMediaStreamDestination();
|
|
2977
|
+
audioStream = audioDestination.stream;
|
|
2978
|
+
tracks = tracks.concat(audioStream.getTracks());
|
|
2979
|
+
// Notify layers and any other listeners of the new audio destination
|
|
2980
|
+
publish(this, 'audiodestinationupdate', { movie: this, destination: audioDestination });
|
|
2981
|
+
}
|
|
2982
|
+
// Create the stream
|
|
2983
|
+
this._currentStream = new MediaStream(tracks);
|
|
2984
|
+
// Play the movie
|
|
2985
|
+
this._endTime = options.duration ? this.currentTime + options.duration : this.duration;
|
|
2986
|
+
return [4 /*yield*/, this.play({
|
|
2987
|
+
onStart: function () {
|
|
2988
|
+
// Call the user's onStart callback
|
|
2989
|
+
options.onStart(_this._currentStream);
|
|
2990
|
+
}
|
|
2991
|
+
})
|
|
2992
|
+
// Clear the stream after the movie is done playing
|
|
2993
|
+
];
|
|
2994
|
+
case 2:
|
|
2995
|
+
_a.sent();
|
|
2996
|
+
// Clear the stream after the movie is done playing
|
|
2997
|
+
this._currentStream.getTracks().forEach(function (track) {
|
|
2998
|
+
track.stop();
|
|
2999
|
+
});
|
|
3000
|
+
this._currentStream = null;
|
|
3001
|
+
this._show();
|
|
3002
|
+
return [2 /*return*/];
|
|
3003
|
+
}
|
|
3004
|
+
});
|
|
2512
3005
|
});
|
|
2513
3006
|
};
|
|
2514
3007
|
/**
|
|
2515
3008
|
* Plays the movie in the background and records it
|
|
2516
3009
|
*
|
|
2517
3010
|
* @param options
|
|
2518
|
-
* @param frameRate
|
|
3011
|
+
* @param [options.frameRate] - Video frame rate
|
|
2519
3012
|
* @param [options.video=true] - whether to include video in recording
|
|
2520
3013
|
* @param [options.audio=true] - whether to include audio in recording
|
|
2521
|
-
* @param [options.mediaRecorderOptions=undefined] -
|
|
3014
|
+
* @param [options.mediaRecorderOptions=undefined] - Options to pass to the
|
|
3015
|
+
* `MediaRecorder` constructor
|
|
2522
3016
|
* @param [options.type='video/webm'] - MIME type for exported video
|
|
2523
|
-
*
|
|
2524
|
-
* @return
|
|
3017
|
+
* @param [options.onStart] - Called when the recording starts
|
|
3018
|
+
* @return Resolves when done recording, rejects when media recorder errors
|
|
2525
3019
|
*/
|
|
2526
|
-
//
|
|
2527
|
-
// TODO: figure out how to do offline recording (faster than realtime).
|
|
2528
|
-
// TODO: improve recording performance to increase frame rate?
|
|
3020
|
+
// TODO: Improve recording performance to increase frame rate
|
|
2529
3021
|
Movie.prototype.record = function (options) {
|
|
2530
|
-
var
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
3022
|
+
var _a;
|
|
3023
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
3024
|
+
var mimeType, stream, recordedChunks, mediaRecorderOptions;
|
|
3025
|
+
var _this = this;
|
|
3026
|
+
return __generator(this, function (_b) {
|
|
3027
|
+
switch (_b.label) {
|
|
3028
|
+
case 0:
|
|
3029
|
+
// Validate options
|
|
3030
|
+
if (options.video === false && options.audio === false) {
|
|
3031
|
+
throw new Error('Both video and audio cannot be disabled');
|
|
3032
|
+
}
|
|
3033
|
+
if (!this.paused) {
|
|
3034
|
+
throw new Error("Cannot record movie while it's already playing");
|
|
3035
|
+
}
|
|
3036
|
+
mimeType = options.type || 'video/webm';
|
|
3037
|
+
if (MediaRecorder && MediaRecorder.isTypeSupported && !MediaRecorder.isTypeSupported(mimeType)) {
|
|
3038
|
+
throw new Error('Please pass a valid MIME type for the exported video');
|
|
3039
|
+
}
|
|
3040
|
+
return [4 /*yield*/, new Promise(function (resolve) {
|
|
3041
|
+
_this.stream({
|
|
3042
|
+
frameRate: options.frameRate,
|
|
3043
|
+
duration: options.duration,
|
|
3044
|
+
video: options.video,
|
|
3045
|
+
audio: options.audio,
|
|
3046
|
+
onStart: resolve
|
|
3047
|
+
}).then(function () {
|
|
3048
|
+
// Stop the media recorder when the movie is done playing
|
|
3049
|
+
_this._recorder.requestData();
|
|
3050
|
+
_this._recorder.stop();
|
|
3051
|
+
});
|
|
3052
|
+
})
|
|
3053
|
+
// The array to store the recorded chunks
|
|
3054
|
+
];
|
|
3055
|
+
case 1:
|
|
3056
|
+
stream = _b.sent();
|
|
3057
|
+
recordedChunks = [];
|
|
3058
|
+
mediaRecorderOptions = __assign(__assign({}, (options.mediaRecorderOptions || {})), { mimeType: mimeType });
|
|
3059
|
+
this._recorder = new MediaRecorder(stream, mediaRecorderOptions);
|
|
3060
|
+
this._recorder.ondataavailable = function (event) {
|
|
3061
|
+
// if (this._paused) reject(new Error("Recording was interrupted"));
|
|
3062
|
+
if (event.data.size > 0) {
|
|
3063
|
+
recordedChunks.push(event.data);
|
|
3064
|
+
}
|
|
3065
|
+
};
|
|
3066
|
+
// Start recording
|
|
3067
|
+
this._recorder.start();
|
|
3068
|
+
this._recording = true;
|
|
3069
|
+
// Notify caller that the media recorder has started
|
|
3070
|
+
(_a = options.onStart) === null || _a === void 0 ? void 0 : _a.call(options, this._recorder);
|
|
3071
|
+
// For backwards compatibility
|
|
3072
|
+
publish(this, 'movie.record', { options: options });
|
|
3073
|
+
// Wait until the media recorder is done recording and processing
|
|
3074
|
+
return [4 /*yield*/, new Promise(function (resolve, reject) {
|
|
3075
|
+
_this._recorder.onstop = function () {
|
|
3076
|
+
resolve();
|
|
3077
|
+
};
|
|
3078
|
+
_this._recorder.onerror = reject;
|
|
3079
|
+
})
|
|
3080
|
+
// Clean up
|
|
3081
|
+
];
|
|
3082
|
+
case 2:
|
|
3083
|
+
// Wait until the media recorder is done recording and processing
|
|
3084
|
+
_b.sent();
|
|
3085
|
+
// Clean up
|
|
3086
|
+
this._paused = true;
|
|
3087
|
+
this._ended = true;
|
|
3088
|
+
this._recording = false;
|
|
3089
|
+
// Construct the exported video out of all the frame blobs.
|
|
3090
|
+
return [2 /*return*/, new Blob(recordedChunks, {
|
|
3091
|
+
type: mimeType
|
|
3092
|
+
})];
|
|
3093
|
+
}
|
|
3094
|
+
});
|
|
2591
3095
|
});
|
|
2592
3096
|
};
|
|
2593
3097
|
/**
|
|
2594
|
-
* Stops the movie
|
|
2595
|
-
* @return
|
|
3098
|
+
* Stops the movie without resetting the playback position
|
|
3099
|
+
* @return The movie
|
|
2596
3100
|
*/
|
|
2597
3101
|
Movie.prototype.pause = function () {
|
|
3102
|
+
// Update state
|
|
2598
3103
|
this._paused = true;
|
|
2599
3104
|
// Deactivate all layers
|
|
2600
|
-
for (var i = 0; i < this.layers.length; i++)
|
|
3105
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
2601
3106
|
if (Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
2602
3107
|
var layer = this.layers[i];
|
|
2603
|
-
layer.
|
|
2604
|
-
|
|
3108
|
+
if (layer.active) {
|
|
3109
|
+
layer.stop();
|
|
3110
|
+
layer.active = false;
|
|
3111
|
+
}
|
|
2605
3112
|
}
|
|
3113
|
+
}
|
|
3114
|
+
// For backwards compatibility, notify event listeners that the movie has
|
|
3115
|
+
// paused
|
|
2606
3116
|
publish(this, 'movie.pause', {});
|
|
2607
3117
|
return this;
|
|
2608
3118
|
};
|
|
2609
3119
|
/**
|
|
2610
3120
|
* Stops playback and resets the playback position
|
|
2611
|
-
* @return
|
|
3121
|
+
* @return The movie
|
|
2612
3122
|
*/
|
|
2613
3123
|
Movie.prototype.stop = function () {
|
|
2614
3124
|
this.pause();
|
|
@@ -2617,8 +3127,8 @@ var etro = (function () {
|
|
|
2617
3127
|
};
|
|
2618
3128
|
/**
|
|
2619
3129
|
* @param [timestamp=performance.now()]
|
|
2620
|
-
* @param [done=undefined] -
|
|
2621
|
-
*
|
|
3130
|
+
* @param [done=undefined] - Called when done playing or when the current
|
|
3131
|
+
* frame is loaded
|
|
2622
3132
|
*/
|
|
2623
3133
|
Movie.prototype._render = function (repeat, timestamp, done) {
|
|
2624
3134
|
var _this = this;
|
|
@@ -2626,177 +3136,207 @@ var etro = (function () {
|
|
|
2626
3136
|
if (done === void 0) { done = undefined; }
|
|
2627
3137
|
clearCachedValues(this);
|
|
2628
3138
|
if (!this.rendering) {
|
|
2629
|
-
// (
|
|
3139
|
+
// (this.paused && !this._renderingFrame) is true so it's playing or it's
|
|
2630
3140
|
// rendering a single frame.
|
|
2631
|
-
if (done)
|
|
3141
|
+
if (done) {
|
|
2632
3142
|
done();
|
|
3143
|
+
}
|
|
2633
3144
|
return;
|
|
2634
3145
|
}
|
|
2635
|
-
this.
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
// TODO:
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
3146
|
+
if (this.ready) {
|
|
3147
|
+
publish(this, 'movie.loadeddata', { movie: this });
|
|
3148
|
+
// If the movie is streaming or recording, resume the media recorder
|
|
3149
|
+
if (this._recording && this._recorder.state === 'paused') {
|
|
3150
|
+
this._recorder.resume();
|
|
3151
|
+
}
|
|
3152
|
+
// If the movie is streaming or recording, end at the specified duration.
|
|
3153
|
+
// Otherwise, end at the movie's duration, because play() does not
|
|
3154
|
+
// support playing a portion of the movie yet.
|
|
3155
|
+
// TODO: Is calling duration every frame bad for performance? (remember,
|
|
3156
|
+
// it's calling Array.reduce)
|
|
3157
|
+
var end = this._currentStream ? this._endTime : this.duration;
|
|
3158
|
+
this._updateCurrentTime(timestamp, end);
|
|
3159
|
+
if (this.currentTime === end) {
|
|
3160
|
+
if (this.recording) {
|
|
3161
|
+
publish(this, 'movie.recordended', { movie: this });
|
|
3162
|
+
}
|
|
3163
|
+
if (this.currentTime === this.duration) {
|
|
3164
|
+
publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
|
|
3165
|
+
}
|
|
3166
|
+
// Don't use setter, which publishes 'seek'. Instead, update the
|
|
3167
|
+
// value and publish a 'imeupdate' event.
|
|
2648
3168
|
this._currentTime = 0;
|
|
2649
3169
|
publish(this, 'movie.timeupdate', { movie: this });
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
3170
|
+
this._lastPlayed = performance.now();
|
|
3171
|
+
this._lastPlayedOffset = 0; // this.currentTime
|
|
3172
|
+
this._renderingFrame = false;
|
|
3173
|
+
// Stop playback or recording if done (except if it's playing and repeat
|
|
3174
|
+
// is true)
|
|
3175
|
+
if (!(!this.recording && this.repeat)) {
|
|
3176
|
+
this._paused = true;
|
|
3177
|
+
this._ended = true;
|
|
3178
|
+
// Deactivate all layers
|
|
3179
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
3180
|
+
if (Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
3181
|
+
var layer = this.layers[i];
|
|
3182
|
+
// A layer that has been deleted before layers.length has been updated
|
|
3183
|
+
// (see the layers proxy in the constructor).
|
|
3184
|
+
if (!layer || !layer.active) {
|
|
3185
|
+
continue;
|
|
3186
|
+
}
|
|
3187
|
+
layer.stop();
|
|
3188
|
+
layer.active = false;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
publish(this, 'movie.pause', {});
|
|
3192
|
+
if (done) {
|
|
3193
|
+
done();
|
|
2669
3194
|
}
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
return;
|
|
3195
|
+
return;
|
|
3196
|
+
}
|
|
2673
3197
|
}
|
|
3198
|
+
// Do render
|
|
3199
|
+
this._renderBackground(timestamp);
|
|
3200
|
+
this._renderLayers();
|
|
3201
|
+
this._applyEffects();
|
|
2674
3202
|
}
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
//
|
|
2683
|
-
//
|
|
2684
|
-
//
|
|
2685
|
-
|
|
3203
|
+
else {
|
|
3204
|
+
// If we are recording, pause the media recorder until the movie is
|
|
3205
|
+
// ready.
|
|
3206
|
+
if (this.recording && this._recorder.state === 'recording') {
|
|
3207
|
+
this._recorder.pause();
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
// If the frame didn't load this instant, repeatedly frame-render until it
|
|
3211
|
+
// is loaded.
|
|
3212
|
+
// If the expression below is true, don't publish an event, just silently
|
|
3213
|
+
// stop the render loop.
|
|
3214
|
+
if (this._renderingFrame && this.ready) {
|
|
2686
3215
|
this._renderingFrame = false;
|
|
2687
|
-
if (done)
|
|
3216
|
+
if (done) {
|
|
2688
3217
|
done();
|
|
3218
|
+
}
|
|
2689
3219
|
return;
|
|
2690
3220
|
}
|
|
3221
|
+
// TODO: Is making a new arrow function every frame bad for performance?
|
|
2691
3222
|
window.requestAnimationFrame(function () {
|
|
2692
3223
|
_this._render(repeat, undefined, done);
|
|
2693
|
-
});
|
|
3224
|
+
});
|
|
2694
3225
|
};
|
|
2695
|
-
Movie.prototype._updateCurrentTime = function (
|
|
2696
|
-
// If we're only
|
|
2697
|
-
//
|
|
3226
|
+
Movie.prototype._updateCurrentTime = function (timestampMs, end) {
|
|
3227
|
+
// If we're only frame-rendering (current frame only), it doesn't matter if
|
|
3228
|
+
// it's paused or not.
|
|
2698
3229
|
if (!this._renderingFrame) {
|
|
2699
|
-
|
|
2700
|
-
var
|
|
2701
|
-
var currentTime = this._lastPlayedOffset + sinceLastPlayed; // don't use setter
|
|
3230
|
+
var sinceLastPlayed = (timestampMs - this._lastPlayed) / 1000;
|
|
3231
|
+
var currentTime = this._lastPlayedOffset + sinceLastPlayed;
|
|
2702
3232
|
if (this.currentTime !== currentTime) {
|
|
3233
|
+
// Update the current time (don't use setter)
|
|
2703
3234
|
this._currentTime = currentTime;
|
|
3235
|
+
// For backwards compatibility, publish a 'movie.timeupdate' event.
|
|
2704
3236
|
publish(this, 'movie.timeupdate', { movie: this });
|
|
2705
3237
|
}
|
|
2706
|
-
|
|
2707
|
-
|
|
3238
|
+
if (this.currentTime > end) {
|
|
3239
|
+
this._currentTime = end;
|
|
3240
|
+
}
|
|
2708
3241
|
}
|
|
2709
3242
|
};
|
|
2710
3243
|
Movie.prototype._renderBackground = function (timestamp) {
|
|
2711
3244
|
this.cctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
3245
|
+
// Evaluate background color (since it's a dynamic property)
|
|
2712
3246
|
var background = val(this, 'background', timestamp);
|
|
2713
|
-
if (background) {
|
|
3247
|
+
if (background) {
|
|
2714
3248
|
this.cctx.fillStyle = background;
|
|
2715
3249
|
this.cctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
2716
3250
|
}
|
|
2717
3251
|
};
|
|
2718
3252
|
/**
|
|
2719
|
-
* @return whether or not video frames are loaded
|
|
2720
3253
|
* @param [timestamp=performance.now()]
|
|
2721
|
-
* @private
|
|
2722
3254
|
*/
|
|
2723
3255
|
Movie.prototype._renderLayers = function () {
|
|
2724
|
-
var frameFullyLoaded = true;
|
|
2725
3256
|
for (var i = 0; i < this.layers.length; i++) {
|
|
2726
|
-
if (!Object.prototype.hasOwnProperty.call(this.layers, i))
|
|
3257
|
+
if (!Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
2727
3258
|
continue;
|
|
3259
|
+
}
|
|
2728
3260
|
var layer = this.layers[i];
|
|
2729
3261
|
// A layer that has been deleted before layers.length has been updated
|
|
2730
3262
|
// (see the layers proxy in the constructor).
|
|
2731
|
-
if (!layer)
|
|
3263
|
+
if (!layer) {
|
|
2732
3264
|
continue;
|
|
2733
|
-
|
|
3265
|
+
}
|
|
2734
3266
|
// Cancel operation if layer disabled or outside layer time interval
|
|
3267
|
+
var reltime = this.currentTime - layer.startTime;
|
|
2735
3268
|
if (!val(layer, 'enabled', reltime) ||
|
|
2736
3269
|
// TODO > or >= ?
|
|
2737
3270
|
this.currentTime < layer.startTime || this.currentTime > layer.startTime + layer.duration) {
|
|
2738
3271
|
// Layer is not active.
|
|
2739
3272
|
// If only rendering this frame, we are not "starting" the layer.
|
|
2740
3273
|
if (layer.active && !this._renderingFrame) {
|
|
2741
|
-
// TODO: make a `deactivate()` method?
|
|
2742
3274
|
layer.stop();
|
|
2743
3275
|
layer.active = false;
|
|
2744
3276
|
}
|
|
2745
3277
|
continue;
|
|
2746
3278
|
}
|
|
3279
|
+
// If we are playing (not refreshing), update the layer's progress
|
|
3280
|
+
if (!this._renderingFrame) {
|
|
3281
|
+
layer.progress(reltime);
|
|
3282
|
+
}
|
|
2747
3283
|
// If only rendering this frame, we are not "starting" the layer
|
|
2748
3284
|
if (!layer.active && val(layer, 'enabled', reltime) && !this._renderingFrame) {
|
|
2749
|
-
// TODO: make an `activate()` method?
|
|
2750
3285
|
layer.start();
|
|
2751
3286
|
layer.active = true;
|
|
2752
3287
|
}
|
|
2753
|
-
// if the layer has an input file
|
|
2754
|
-
if ('source' in layer)
|
|
2755
|
-
frameFullyLoaded = frameFullyLoaded && layer.source.readyState >= 2;
|
|
2756
3288
|
layer.render();
|
|
2757
3289
|
// if the layer has visual component
|
|
2758
3290
|
if (layer instanceof Visual) {
|
|
2759
3291
|
var canvas = layer.canvas;
|
|
2760
|
-
|
|
2761
|
-
// if the layer has an area (else InvalidStateError from canvas)
|
|
2762
|
-
if (canvas.width * canvas.height > 0)
|
|
3292
|
+
if (canvas.width * canvas.height > 0) {
|
|
2763
3293
|
this.cctx.drawImage(canvas, val(layer, 'x', reltime), val(layer, 'y', reltime), canvas.width, canvas.height);
|
|
3294
|
+
}
|
|
2764
3295
|
}
|
|
2765
3296
|
}
|
|
2766
|
-
return frameFullyLoaded;
|
|
2767
3297
|
};
|
|
2768
3298
|
Movie.prototype._applyEffects = function () {
|
|
2769
3299
|
for (var i = 0; i < this.effects.length; i++) {
|
|
2770
3300
|
var effect = this.effects[i];
|
|
2771
3301
|
// An effect that has been deleted before effects.length has been updated
|
|
2772
3302
|
// (see the effectsproxy in the constructor).
|
|
2773
|
-
if (!effect)
|
|
3303
|
+
if (!effect) {
|
|
2774
3304
|
continue;
|
|
3305
|
+
}
|
|
2775
3306
|
effect.apply(this, this.currentTime);
|
|
2776
3307
|
}
|
|
2777
3308
|
};
|
|
2778
3309
|
/**
|
|
2779
|
-
* Refreshes the screen
|
|
2780
|
-
*
|
|
3310
|
+
* Refreshes the screen
|
|
3311
|
+
*
|
|
3312
|
+
* Only use this if auto-refresh is disabled
|
|
3313
|
+
*
|
|
3314
|
+
* @return - Promise that resolves when the frame is loaded
|
|
2781
3315
|
*/
|
|
2782
3316
|
Movie.prototype.refresh = function () {
|
|
2783
3317
|
var _this = this;
|
|
3318
|
+
// Refreshing while playing can interrupt playback
|
|
3319
|
+
if (!this.paused) {
|
|
3320
|
+
throw new Error('Already playing');
|
|
3321
|
+
}
|
|
2784
3322
|
return new Promise(function (resolve) {
|
|
2785
3323
|
_this._renderingFrame = true;
|
|
2786
3324
|
_this._render(false, undefined, resolve);
|
|
2787
3325
|
});
|
|
2788
3326
|
};
|
|
2789
3327
|
/**
|
|
2790
|
-
* Convienence method
|
|
3328
|
+
* Convienence method (TODO: remove)
|
|
2791
3329
|
*/
|
|
2792
3330
|
Movie.prototype._publishToLayers = function (type, event) {
|
|
2793
|
-
for (var i = 0; i < this.layers.length; i++)
|
|
2794
|
-
if (Object.prototype.hasOwnProperty.call(this.layers, i))
|
|
3331
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
3332
|
+
if (Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
2795
3333
|
publish(this.layers[i], type, event);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
2796
3336
|
};
|
|
2797
3337
|
Object.defineProperty(Movie.prototype, "rendering", {
|
|
2798
3338
|
/**
|
|
2799
|
-
*
|
|
3339
|
+
* `true` if the movie is playing, recording or refreshing
|
|
2800
3340
|
*/
|
|
2801
3341
|
get: function () {
|
|
2802
3342
|
return !this.paused || this._renderingFrame;
|
|
@@ -2806,7 +3346,7 @@ var etro = (function () {
|
|
|
2806
3346
|
});
|
|
2807
3347
|
Object.defineProperty(Movie.prototype, "renderingFrame", {
|
|
2808
3348
|
/**
|
|
2809
|
-
*
|
|
3349
|
+
* `true` if the movie is refreshing the current frame
|
|
2810
3350
|
*/
|
|
2811
3351
|
get: function () {
|
|
2812
3352
|
return this._renderingFrame;
|
|
@@ -2816,17 +3356,19 @@ var etro = (function () {
|
|
|
2816
3356
|
});
|
|
2817
3357
|
Object.defineProperty(Movie.prototype, "recording", {
|
|
2818
3358
|
/**
|
|
2819
|
-
*
|
|
3359
|
+
* `true` if the movie is recording
|
|
2820
3360
|
*/
|
|
2821
3361
|
get: function () {
|
|
2822
|
-
return
|
|
3362
|
+
return this._recording;
|
|
2823
3363
|
},
|
|
2824
3364
|
enumerable: false,
|
|
2825
3365
|
configurable: true
|
|
2826
3366
|
});
|
|
2827
3367
|
Object.defineProperty(Movie.prototype, "duration", {
|
|
2828
3368
|
/**
|
|
2829
|
-
* The
|
|
3369
|
+
* The duration of the movie in seconds
|
|
3370
|
+
*
|
|
3371
|
+
* Calculated from the end time of the last layer
|
|
2830
3372
|
*/
|
|
2831
3373
|
// TODO: dirty flag?
|
|
2832
3374
|
get: function () {
|
|
@@ -2836,16 +3378,16 @@ var etro = (function () {
|
|
|
2836
3378
|
configurable: true
|
|
2837
3379
|
});
|
|
2838
3380
|
/**
|
|
2839
|
-
*
|
|
3381
|
+
* Convenience method for `layers.push()`
|
|
2840
3382
|
* @param layer
|
|
2841
|
-
* @return
|
|
3383
|
+
* @return The movie
|
|
2842
3384
|
*/
|
|
2843
3385
|
Movie.prototype.addLayer = function (layer) {
|
|
2844
3386
|
this.layers.push(layer);
|
|
2845
3387
|
return this;
|
|
2846
3388
|
};
|
|
2847
3389
|
/**
|
|
2848
|
-
*
|
|
3390
|
+
* Convenience method for `effects.push()`
|
|
2849
3391
|
* @param effect
|
|
2850
3392
|
* @return the movie
|
|
2851
3393
|
*/
|
|
@@ -2855,6 +3397,7 @@ var etro = (function () {
|
|
|
2855
3397
|
};
|
|
2856
3398
|
Object.defineProperty(Movie.prototype, "paused", {
|
|
2857
3399
|
/**
|
|
3400
|
+
* `true` if the movie is paused
|
|
2858
3401
|
*/
|
|
2859
3402
|
get: function () {
|
|
2860
3403
|
return this._paused;
|
|
@@ -2864,7 +3407,7 @@ var etro = (function () {
|
|
|
2864
3407
|
});
|
|
2865
3408
|
Object.defineProperty(Movie.prototype, "ended", {
|
|
2866
3409
|
/**
|
|
2867
|
-
*
|
|
3410
|
+
* `true` if the playback position is at the end of the movie
|
|
2868
3411
|
*/
|
|
2869
3412
|
get: function () {
|
|
2870
3413
|
return this._ended;
|
|
@@ -2872,50 +3415,89 @@ var etro = (function () {
|
|
|
2872
3415
|
enumerable: false,
|
|
2873
3416
|
configurable: true
|
|
2874
3417
|
});
|
|
3418
|
+
/**
|
|
3419
|
+
* Skips to the provided playback position, updating {@link currentTime}.
|
|
3420
|
+
*
|
|
3421
|
+
* @param time - The new playback position (in seconds)
|
|
3422
|
+
*/
|
|
3423
|
+
Movie.prototype.seek = function (time) {
|
|
3424
|
+
this._currentTime = time;
|
|
3425
|
+
// Call `seek` on every layer
|
|
3426
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
3427
|
+
var layer = this.layers[i];
|
|
3428
|
+
if (layer) {
|
|
3429
|
+
var relativeTime = time - layer.startTime;
|
|
3430
|
+
if (relativeTime >= 0 && relativeTime <= layer.duration) {
|
|
3431
|
+
layer.seek(relativeTime);
|
|
3432
|
+
}
|
|
3433
|
+
else {
|
|
3434
|
+
layer.seek(undefined);
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
// For backwards compatibility, publish a `seek` event
|
|
3439
|
+
publish(this, 'movie.seek', {});
|
|
3440
|
+
};
|
|
2875
3441
|
Object.defineProperty(Movie.prototype, "currentTime", {
|
|
2876
3442
|
/**
|
|
2877
|
-
* The current playback position
|
|
3443
|
+
* The current playback position in seconds
|
|
2878
3444
|
*/
|
|
2879
3445
|
get: function () {
|
|
2880
3446
|
return this._currentTime;
|
|
2881
3447
|
},
|
|
3448
|
+
/**
|
|
3449
|
+
* Skips to the provided playback position, updating {@link currentTime}.
|
|
3450
|
+
*
|
|
3451
|
+
* @param time - The new playback position (in seconds)
|
|
3452
|
+
*
|
|
3453
|
+
* @deprecated Use `seek` instead
|
|
3454
|
+
*/
|
|
2882
3455
|
set: function (time) {
|
|
2883
|
-
this.
|
|
2884
|
-
publish(this, 'movie.seek', {});
|
|
2885
|
-
// Render single frame to match new time
|
|
2886
|
-
if (this.autoRefresh)
|
|
2887
|
-
this.refresh();
|
|
3456
|
+
this.seek(time);
|
|
2888
3457
|
},
|
|
2889
3458
|
enumerable: false,
|
|
2890
3459
|
configurable: true
|
|
2891
3460
|
});
|
|
2892
3461
|
/**
|
|
2893
|
-
*
|
|
2894
|
-
* `set currentTime`.
|
|
3462
|
+
* Skips to the provided playback position, updating {@link currentTime}.
|
|
2895
3463
|
*
|
|
2896
|
-
* @param time -
|
|
2897
|
-
* @param [refresh=true] -
|
|
2898
|
-
* @return resolves when the current frame is rendered if
|
|
2899
|
-
*
|
|
3464
|
+
* @param time - The new time (in seconds)
|
|
3465
|
+
* @param [refresh=true] - Render a single frame?
|
|
3466
|
+
* @return Promise that resolves when the current frame is rendered if
|
|
3467
|
+
* `refresh` is true; otherwise resolves immediately.
|
|
2900
3468
|
*
|
|
3469
|
+
* @deprecated Call {@link seek} and {@link refresh} separately
|
|
2901
3470
|
*/
|
|
2902
|
-
// TODO: Refresh if
|
|
3471
|
+
// TODO: Refresh only if auto-refreshing is enabled
|
|
2903
3472
|
Movie.prototype.setCurrentTime = function (time, refresh) {
|
|
2904
3473
|
var _this = this;
|
|
2905
3474
|
if (refresh === void 0) { refresh = true; }
|
|
2906
3475
|
return new Promise(function (resolve, reject) {
|
|
2907
|
-
_this.
|
|
2908
|
-
|
|
2909
|
-
if (refresh)
|
|
3476
|
+
_this.seek(time);
|
|
3477
|
+
if (refresh) {
|
|
2910
3478
|
// Pass promise callbacks to `refresh`
|
|
2911
3479
|
_this.refresh().then(resolve).catch(reject);
|
|
2912
|
-
|
|
3480
|
+
}
|
|
3481
|
+
else {
|
|
2913
3482
|
resolve();
|
|
3483
|
+
}
|
|
2914
3484
|
});
|
|
2915
3485
|
};
|
|
3486
|
+
Object.defineProperty(Movie.prototype, "ready", {
|
|
3487
|
+
/**
|
|
3488
|
+
* `true` if the movie is ready for playback
|
|
3489
|
+
*/
|
|
3490
|
+
get: function () {
|
|
3491
|
+
var layersReady = this.layers.every(function (layer) { return layer.ready; });
|
|
3492
|
+
var effectsReady = this.effects.every(function (effect) { return effect.ready; });
|
|
3493
|
+
return layersReady && effectsReady;
|
|
3494
|
+
},
|
|
3495
|
+
enumerable: false,
|
|
3496
|
+
configurable: true
|
|
3497
|
+
});
|
|
2916
3498
|
Object.defineProperty(Movie.prototype, "canvas", {
|
|
2917
3499
|
/**
|
|
2918
|
-
* The
|
|
3500
|
+
* The HTML canvas element used for rendering
|
|
2919
3501
|
*/
|
|
2920
3502
|
get: function () {
|
|
2921
3503
|
return this._canvas;
|
|
@@ -2925,7 +3507,7 @@ var etro = (function () {
|
|
|
2925
3507
|
});
|
|
2926
3508
|
Object.defineProperty(Movie.prototype, "cctx", {
|
|
2927
3509
|
/**
|
|
2928
|
-
* The
|
|
3510
|
+
* The canvas context used for rendering
|
|
2929
3511
|
*/
|
|
2930
3512
|
get: function () {
|
|
2931
3513
|
return this._cctx;
|
|
@@ -2935,7 +3517,7 @@ var etro = (function () {
|
|
|
2935
3517
|
});
|
|
2936
3518
|
Object.defineProperty(Movie.prototype, "width", {
|
|
2937
3519
|
/**
|
|
2938
|
-
* The width of the
|
|
3520
|
+
* The width of the output canvas
|
|
2939
3521
|
*/
|
|
2940
3522
|
get: function () {
|
|
2941
3523
|
return this.canvas.width;
|
|
@@ -2948,7 +3530,7 @@ var etro = (function () {
|
|
|
2948
3530
|
});
|
|
2949
3531
|
Object.defineProperty(Movie.prototype, "height", {
|
|
2950
3532
|
/**
|
|
2951
|
-
* The height of the
|
|
3533
|
+
* The height of the output canvas
|
|
2952
3534
|
*/
|
|
2953
3535
|
get: function () {
|
|
2954
3536
|
return this.canvas.height;
|
|
@@ -2960,38 +3542,46 @@ var etro = (function () {
|
|
|
2960
3542
|
configurable: true
|
|
2961
3543
|
});
|
|
2962
3544
|
Object.defineProperty(Movie.prototype, "movie", {
|
|
3545
|
+
/**
|
|
3546
|
+
* @return The movie
|
|
3547
|
+
*/
|
|
2963
3548
|
get: function () {
|
|
2964
3549
|
return this;
|
|
2965
3550
|
},
|
|
2966
3551
|
enumerable: false,
|
|
2967
3552
|
configurable: true
|
|
2968
3553
|
});
|
|
3554
|
+
/**
|
|
3555
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
3556
|
+
*/
|
|
2969
3557
|
Movie.prototype.getDefaultOptions = function () {
|
|
2970
3558
|
return {
|
|
2971
3559
|
canvas: undefined,
|
|
2972
3560
|
/**
|
|
2973
3561
|
* @name module:movie#background
|
|
2974
|
-
* @desc The
|
|
3562
|
+
* @desc The color for the background, or <code>null</code> for transparency
|
|
2975
3563
|
*/
|
|
2976
|
-
background: '#000',
|
|
3564
|
+
background: parseColor('#000'),
|
|
2977
3565
|
/**
|
|
2978
3566
|
* @name module:movie#repeat
|
|
2979
3567
|
*/
|
|
2980
|
-
repeat: false
|
|
2981
|
-
/**
|
|
2982
|
-
* @name module:movie#autoRefresh
|
|
2983
|
-
* @desc Whether to refresh when changes are made that would effect the current frame
|
|
2984
|
-
*/
|
|
2985
|
-
autoRefresh: true
|
|
3568
|
+
repeat: false
|
|
2986
3569
|
};
|
|
2987
3570
|
};
|
|
2988
3571
|
return Movie;
|
|
2989
3572
|
}());
|
|
2990
|
-
//
|
|
3573
|
+
// Id for events
|
|
2991
3574
|
Movie.prototype.type = 'movie';
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
3575
|
+
Movie.prototype.propertyFilters = {};
|
|
3576
|
+
deprecate('movie.audiodestinationupdate', 'audiodestinationupdate');
|
|
3577
|
+
deprecate('movie.ended', undefined);
|
|
3578
|
+
deprecate('movie.loadeddata', undefined);
|
|
3579
|
+
deprecate('movie.pause', undefined, 'Wait for `play()`, `stream()`, or `record()` to resolve instead.');
|
|
3580
|
+
deprecate('movie.play', undefined, 'Provide an `onStart` callback to `play()`, `stream()`, or `record()` instead.');
|
|
3581
|
+
deprecate('movie.record', undefined, 'Provide an `onStart` callback to `record()` instead.');
|
|
3582
|
+
deprecate('movie.recordended', undefined, 'Wait for `record()` to resolve instead.');
|
|
3583
|
+
deprecate('movie.seek', undefined, 'Override the `seek` method on layers instead.');
|
|
3584
|
+
deprecate('movie.timeupdate', undefined, 'Override the `progress` method on layers instead.');
|
|
2995
3585
|
|
|
2996
3586
|
/*
|
|
2997
3587
|
* Typedoc can't handle default exports. To let users import default export and
|
|
@@ -3001,6 +3591,7 @@ var etro = (function () {
|
|
|
3001
3591
|
*/
|
|
3002
3592
|
|
|
3003
3593
|
var etro = /*#__PURE__*/Object.freeze({
|
|
3594
|
+
__proto__: null,
|
|
3004
3595
|
layer: index,
|
|
3005
3596
|
effect: index$1,
|
|
3006
3597
|
event: event,
|
|
@@ -3016,8 +3607,7 @@ var etro = (function () {
|
|
|
3016
3607
|
parseColor: parseColor,
|
|
3017
3608
|
Font: Font,
|
|
3018
3609
|
parseFont: parseFont,
|
|
3019
|
-
mapPixels: mapPixels
|
|
3020
|
-
watchPublic: watchPublic
|
|
3610
|
+
mapPixels: mapPixels
|
|
3021
3611
|
});
|
|
3022
3612
|
|
|
3023
3613
|
/**
|