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