etro 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/CONTRIBUTING.md +23 -20
- package/README.md +3 -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 +1156 -575
- package/dist/etro-iife.js +1156 -575
- 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 +25 -3
- 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,21 @@ var etro = (function () {
|
|
|
857
1055
|
* Creates an audio layer
|
|
858
1056
|
*/
|
|
859
1057
|
function Audio(options) {
|
|
860
|
-
var _this =
|
|
861
|
-
if (
|
|
1058
|
+
var _this = this;
|
|
1059
|
+
if (typeof options.source === 'string') {
|
|
1060
|
+
var audio = document.createElement('audio');
|
|
1061
|
+
audio.src = options.source;
|
|
1062
|
+
options.source = audio;
|
|
1063
|
+
}
|
|
1064
|
+
_this = _super.call(this, options) || this;
|
|
1065
|
+
if (_this.duration === undefined) {
|
|
862
1066
|
_this.duration = (_this).source.duration - _this.sourceStartTime;
|
|
1067
|
+
}
|
|
863
1068
|
return _this;
|
|
864
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1072
|
+
*/
|
|
865
1073
|
Audio.prototype.getDefaultOptions = function () {
|
|
866
1074
|
return __assign(__assign({}, Object.getPrototypeOf(this).getDefaultOptions()), {
|
|
867
1075
|
/**
|
|
@@ -873,40 +1081,101 @@ var etro = (function () {
|
|
|
873
1081
|
return Audio;
|
|
874
1082
|
}(AudioSourceMixin(Base)));
|
|
875
1083
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
_this
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1084
|
+
var CustomArrayListener = /** @class */ (function () {
|
|
1085
|
+
function CustomArrayListener() {
|
|
1086
|
+
}
|
|
1087
|
+
return CustomArrayListener;
|
|
1088
|
+
}());
|
|
1089
|
+
/**
|
|
1090
|
+
* An array that notifies a listener when items are added or removed.
|
|
1091
|
+
*/
|
|
1092
|
+
var CustomArray = /** @class */ (function (_super) {
|
|
1093
|
+
__extends(CustomArray, _super);
|
|
1094
|
+
function CustomArray(target, listener) {
|
|
1095
|
+
var _this = _super.call(this) || this;
|
|
1096
|
+
for (var _i = 0, target_1 = target; _i < target_1.length; _i++) {
|
|
1097
|
+
var item = target_1[_i];
|
|
1098
|
+
listener.onAdd(item);
|
|
1099
|
+
}
|
|
1100
|
+
// Create proxy
|
|
1101
|
+
return new Proxy(target, {
|
|
891
1102
|
deleteProperty: function (target, property) {
|
|
892
1103
|
var value = target[property];
|
|
893
|
-
value.detach();
|
|
894
1104
|
delete target[property];
|
|
1105
|
+
listener.onRemove(value);
|
|
895
1106
|
return true;
|
|
896
1107
|
},
|
|
897
1108
|
set: function (target, property, value) {
|
|
1109
|
+
var oldValue = target[property];
|
|
1110
|
+
target[property] = value;
|
|
1111
|
+
// Check if property is a number (index)
|
|
898
1112
|
if (!isNaN(Number(property))) {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1113
|
+
if (oldValue !== undefined) {
|
|
1114
|
+
listener.onRemove(oldValue);
|
|
1115
|
+
}
|
|
1116
|
+
listener.onAdd(value);
|
|
903
1117
|
}
|
|
904
|
-
target[property] = value;
|
|
905
1118
|
return true;
|
|
906
1119
|
}
|
|
907
1120
|
});
|
|
1121
|
+
}
|
|
1122
|
+
return CustomArray;
|
|
1123
|
+
}(Array));
|
|
1124
|
+
|
|
1125
|
+
// eslint-disable-next-line no-use-before-define
|
|
1126
|
+
var VisualEffectsListener = /** @class */ (function (_super) {
|
|
1127
|
+
__extends(VisualEffectsListener, _super);
|
|
1128
|
+
// eslint-disable-next-line no-use-before-define
|
|
1129
|
+
function VisualEffectsListener(layer) {
|
|
1130
|
+
var _this = _super.call(this) || this;
|
|
1131
|
+
_this._layer = layer;
|
|
908
1132
|
return _this;
|
|
909
1133
|
}
|
|
1134
|
+
VisualEffectsListener.prototype.onAdd = function (effect) {
|
|
1135
|
+
effect.tryAttach(this._layer);
|
|
1136
|
+
};
|
|
1137
|
+
VisualEffectsListener.prototype.onRemove = function (effect) {
|
|
1138
|
+
effect.tryDetach();
|
|
1139
|
+
};
|
|
1140
|
+
return VisualEffectsListener;
|
|
1141
|
+
}(CustomArrayListener));
|
|
1142
|
+
var VisualEffects = /** @class */ (function (_super) {
|
|
1143
|
+
__extends(VisualEffects, _super);
|
|
1144
|
+
// eslint-disable-next-line no-use-before-define
|
|
1145
|
+
function VisualEffects(target, layer) {
|
|
1146
|
+
return _super.call(this, target, new VisualEffectsListener(layer)) || this;
|
|
1147
|
+
}
|
|
1148
|
+
return VisualEffects;
|
|
1149
|
+
}(CustomArray));
|
|
1150
|
+
/** Any layer that renders to a canvas */
|
|
1151
|
+
var Visual = /** @class */ (function (_super) {
|
|
1152
|
+
__extends(Visual, _super);
|
|
1153
|
+
/**
|
|
1154
|
+
* Creates a visual layer
|
|
1155
|
+
*/
|
|
1156
|
+
function Visual(options) {
|
|
1157
|
+
var _this = _super.call(this, options) || this;
|
|
1158
|
+
applyOptions(options, _this);
|
|
1159
|
+
_this.canvas = document.createElement('canvas');
|
|
1160
|
+
_this.cctx = _this.canvas.getContext('2d');
|
|
1161
|
+
_this.effects = new VisualEffects([], _this);
|
|
1162
|
+
return _this;
|
|
1163
|
+
}
|
|
1164
|
+
Visual.prototype.whenReady = function () {
|
|
1165
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1166
|
+
return __generator(this, function (_a) {
|
|
1167
|
+
switch (_a.label) {
|
|
1168
|
+
case 0: return [4 /*yield*/, _super.prototype.whenReady.call(this)];
|
|
1169
|
+
case 1:
|
|
1170
|
+
_a.sent();
|
|
1171
|
+
return [4 /*yield*/, Promise.all(this.effects.map(function (effect) { return effect.whenReady(); }))];
|
|
1172
|
+
case 2:
|
|
1173
|
+
_a.sent();
|
|
1174
|
+
return [2 /*return*/];
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
});
|
|
1178
|
+
};
|
|
910
1179
|
/**
|
|
911
1180
|
* Render visual output
|
|
912
1181
|
*/
|
|
@@ -914,8 +1183,9 @@ var etro = (function () {
|
|
|
914
1183
|
// Prevent empty canvas errors if the width or height is 0
|
|
915
1184
|
var width = val(this, 'width', this.currentTime);
|
|
916
1185
|
var height = val(this, 'height', this.currentTime);
|
|
917
|
-
if (width === 0 || height === 0)
|
|
1186
|
+
if (width === 0 || height === 0) {
|
|
918
1187
|
return;
|
|
1188
|
+
}
|
|
919
1189
|
this.beginRender();
|
|
920
1190
|
this.doRender();
|
|
921
1191
|
this.endRender();
|
|
@@ -946,20 +1216,22 @@ var etro = (function () {
|
|
|
946
1216
|
Visual.prototype.endRender = function () {
|
|
947
1217
|
var w = val(this, 'width', this.currentTime) || val(this.movie, 'width', this.movie.currentTime);
|
|
948
1218
|
var h = val(this, 'height', this.currentTime) || val(this.movie, 'height', this.movie.currentTime);
|
|
949
|
-
if (w * h > 0)
|
|
1219
|
+
if (w * h > 0) {
|
|
950
1220
|
this._applyEffects();
|
|
1221
|
+
}
|
|
951
1222
|
// else InvalidStateError for drawing zero-area image in some effects, right?
|
|
952
1223
|
};
|
|
953
1224
|
Visual.prototype._applyEffects = function () {
|
|
954
1225
|
for (var i = 0; i < this.effects.length; i++) {
|
|
955
1226
|
var effect = this.effects[i];
|
|
956
|
-
if (effect && effect.enabled)
|
|
1227
|
+
if (effect && effect.enabled) {
|
|
957
1228
|
// Pass relative time
|
|
958
1229
|
effect.apply(this, this.movie.currentTime - this.startTime);
|
|
1230
|
+
}
|
|
959
1231
|
}
|
|
960
1232
|
};
|
|
961
1233
|
/**
|
|
962
|
-
*
|
|
1234
|
+
* Convenience method for <code>effects.push()</code>
|
|
963
1235
|
* @param effect
|
|
964
1236
|
* @return the layer (for chaining)
|
|
965
1237
|
*/
|
|
@@ -967,6 +1239,18 @@ var etro = (function () {
|
|
|
967
1239
|
this.effects.push(effect);
|
|
968
1240
|
return this;
|
|
969
1241
|
};
|
|
1242
|
+
Object.defineProperty(Visual.prototype, "ready", {
|
|
1243
|
+
get: function () {
|
|
1244
|
+
// Typescript doesn't support `super.ready` when targeting es5
|
|
1245
|
+
var superReady = Object.getOwnPropertyDescriptor(Base.prototype, 'ready').get.call(this);
|
|
1246
|
+
return superReady && this.effects.every(function (effect) { return effect.ready; });
|
|
1247
|
+
},
|
|
1248
|
+
enumerable: false,
|
|
1249
|
+
configurable: true
|
|
1250
|
+
});
|
|
1251
|
+
/**
|
|
1252
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1253
|
+
*/
|
|
970
1254
|
Visual.prototype.getDefaultOptions = function () {
|
|
971
1255
|
return __assign(__assign({}, Base.prototype.getDefaultOptions()), {
|
|
972
1256
|
/**
|
|
@@ -1025,17 +1309,60 @@ var etro = (function () {
|
|
|
1025
1309
|
var MixedVisualSource = /** @class */ (function (_super) {
|
|
1026
1310
|
__extends(MixedVisualSource, _super);
|
|
1027
1311
|
function MixedVisualSource(options) {
|
|
1028
|
-
var _this =
|
|
1312
|
+
var _this = this;
|
|
1313
|
+
if (!options.source) {
|
|
1314
|
+
throw new Error('Property "source" is required in options');
|
|
1315
|
+
}
|
|
1316
|
+
_this = _super.call(this, options) || this;
|
|
1029
1317
|
applyOptions(options, _this);
|
|
1030
1318
|
return _this;
|
|
1031
1319
|
}
|
|
1320
|
+
MixedVisualSource.prototype.whenReady = function () {
|
|
1321
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1322
|
+
var _this = this;
|
|
1323
|
+
return __generator(this, function (_a) {
|
|
1324
|
+
switch (_a.label) {
|
|
1325
|
+
case 0: return [4 /*yield*/, _super.prototype.whenReady.call(this)];
|
|
1326
|
+
case 1:
|
|
1327
|
+
_a.sent();
|
|
1328
|
+
return [4 /*yield*/, new Promise(function (resolve) {
|
|
1329
|
+
if (_this.source instanceof HTMLImageElement) {
|
|
1330
|
+
// The source is an image; wait for it to load
|
|
1331
|
+
if (_this.source.complete) {
|
|
1332
|
+
resolve();
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
_this.source.addEventListener('load', function () {
|
|
1336
|
+
resolve();
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
else {
|
|
1341
|
+
// The source is a video; wait for the first frame to load
|
|
1342
|
+
if (_this.source.readyState === 4) {
|
|
1343
|
+
resolve();
|
|
1344
|
+
}
|
|
1345
|
+
else {
|
|
1346
|
+
_this.source.addEventListener('canplaythrough', function () {
|
|
1347
|
+
resolve();
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
})];
|
|
1352
|
+
case 2:
|
|
1353
|
+
_a.sent();
|
|
1354
|
+
return [2 /*return*/];
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
});
|
|
1358
|
+
};
|
|
1032
1359
|
MixedVisualSource.prototype.doRender = function () {
|
|
1033
1360
|
// Clear/fill background
|
|
1034
1361
|
_super.prototype.doRender.call(this);
|
|
1035
1362
|
/*
|
|
1036
1363
|
* Source dimensions crop the image. Dest dimensions set the size that
|
|
1037
1364
|
* the image will be rendered at *on the layer*. Note that this is
|
|
1038
|
-
* different
|
|
1365
|
+
* different from the layer dimensions (`this.width` and `this.height`).
|
|
1039
1366
|
* The main reason this distinction exists is so that an image layer can
|
|
1040
1367
|
* be rotated without being cropped (see iss #46).
|
|
1041
1368
|
*/
|
|
@@ -1043,6 +1370,21 @@ var etro = (function () {
|
|
|
1043
1370
|
// `destX` and `destY` are relative to the layer
|
|
1044
1371
|
val(this, 'destX', this.currentTime), val(this, 'destY', this.currentTime), val(this, 'destWidth', this.currentTime), val(this, 'destHeight', this.currentTime));
|
|
1045
1372
|
};
|
|
1373
|
+
Object.defineProperty(MixedVisualSource.prototype, "ready", {
|
|
1374
|
+
get: function () {
|
|
1375
|
+
// Typescript doesn't support `super.ready` when targeting es5
|
|
1376
|
+
var superReady = Object.getOwnPropertyDescriptor(superclass.prototype, 'ready').get.call(this);
|
|
1377
|
+
var sourceReady = this.source instanceof HTMLImageElement
|
|
1378
|
+
? this.source.complete
|
|
1379
|
+
: this.source.readyState === 4;
|
|
1380
|
+
return superReady && sourceReady;
|
|
1381
|
+
},
|
|
1382
|
+
enumerable: false,
|
|
1383
|
+
configurable: true
|
|
1384
|
+
});
|
|
1385
|
+
/**
|
|
1386
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1387
|
+
*/
|
|
1046
1388
|
MixedVisualSource.prototype.getDefaultOptions = function () {
|
|
1047
1389
|
return __assign(__assign({}, superclass.prototype.getDefaultOptions()), { source: undefined, sourceX: 0, sourceY: 0, sourceWidth: undefined, sourceHeight: undefined, destX: 0, destY: 0, destWidth: undefined, destHeight: undefined });
|
|
1048
1390
|
};
|
|
@@ -1091,10 +1433,19 @@ var etro = (function () {
|
|
|
1091
1433
|
return MixedVisualSource;
|
|
1092
1434
|
}
|
|
1093
1435
|
|
|
1436
|
+
/**
|
|
1437
|
+
* Layer for an HTML image element
|
|
1438
|
+
* @extends VisualSource
|
|
1439
|
+
*/
|
|
1094
1440
|
var Image = /** @class */ (function (_super) {
|
|
1095
1441
|
__extends(Image, _super);
|
|
1096
|
-
function Image() {
|
|
1097
|
-
|
|
1442
|
+
function Image(options) {
|
|
1443
|
+
if (typeof (options.source) === 'string') {
|
|
1444
|
+
var img = document.createElement('img');
|
|
1445
|
+
img.src = options.source;
|
|
1446
|
+
options.source = img;
|
|
1447
|
+
}
|
|
1448
|
+
return _super.call(this, options) || this;
|
|
1098
1449
|
}
|
|
1099
1450
|
return Image;
|
|
1100
1451
|
}(VisualSourceMixin(Visual)));
|
|
@@ -1108,9 +1459,12 @@ var etro = (function () {
|
|
|
1108
1459
|
// TODO: is textX necessary? it seems inconsistent, because you can't define
|
|
1109
1460
|
// width/height directly for a text layer
|
|
1110
1461
|
function Text(options) {
|
|
1111
|
-
var _this =
|
|
1462
|
+
var _this = this;
|
|
1463
|
+
if (!options.text) {
|
|
1464
|
+
throw new Error('Property "text" is required in TextOptions');
|
|
1465
|
+
}
|
|
1112
1466
|
// Default to no (transparent) background
|
|
1113
|
-
_super.call(this, __assign({ background: null }, options)) || this;
|
|
1467
|
+
_this = _super.call(this, __assign({ background: null }, options)) || this;
|
|
1114
1468
|
applyOptions(options, _this);
|
|
1115
1469
|
return _this;
|
|
1116
1470
|
// this._prevText = undefined;
|
|
@@ -1157,22 +1511,33 @@ var etro = (function () {
|
|
|
1157
1511
|
document.body.removeChild(s);
|
|
1158
1512
|
return metrics;
|
|
1159
1513
|
} */
|
|
1514
|
+
/**
|
|
1515
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1516
|
+
*/
|
|
1160
1517
|
Text.prototype.getDefaultOptions = function () {
|
|
1161
1518
|
return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: parseColor('#fff'), textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
|
|
1162
1519
|
};
|
|
1163
1520
|
return Text;
|
|
1164
1521
|
}(Visual));
|
|
1165
1522
|
|
|
1166
|
-
// Use mixins instead of `extend`ing two classes (which isn't supported by
|
|
1167
|
-
// JavaScript).
|
|
1168
1523
|
/**
|
|
1524
|
+
* Layer for an HTML video element
|
|
1169
1525
|
* @extends AudioSource
|
|
1170
1526
|
* @extends VisualSource
|
|
1171
1527
|
*/
|
|
1172
1528
|
var Video = /** @class */ (function (_super) {
|
|
1173
1529
|
__extends(Video, _super);
|
|
1174
|
-
function Video() {
|
|
1175
|
-
|
|
1530
|
+
function Video(options) {
|
|
1531
|
+
var _a;
|
|
1532
|
+
if (typeof (options.source) === 'string') {
|
|
1533
|
+
var video = document.createElement('video');
|
|
1534
|
+
video.src = options.source;
|
|
1535
|
+
options.source = video;
|
|
1536
|
+
}
|
|
1537
|
+
return _super.call(this, __assign(__assign({}, options), {
|
|
1538
|
+
// Set a default duration so that the super constructor doesn't throw an
|
|
1539
|
+
// error
|
|
1540
|
+
duration: (_a = options.duration) !== null && _a !== void 0 ? _a : 0 })) || this;
|
|
1176
1541
|
}
|
|
1177
1542
|
return Video;
|
|
1178
1543
|
}(AudioSourceMixin(VisualSourceMixin(Visual))));
|
|
@@ -1198,46 +1563,48 @@ var etro = (function () {
|
|
|
1198
1563
|
*/
|
|
1199
1564
|
var Base$1 = /** @class */ (function () {
|
|
1200
1565
|
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;
|
|
1566
|
+
this.enabled = true;
|
|
1567
|
+
this._occurrenceCount = 0;
|
|
1568
|
+
this._target = null;
|
|
1213
1569
|
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Wait until this effect is ready to be applied
|
|
1572
|
+
*/
|
|
1573
|
+
Base.prototype.whenReady = function () {
|
|
1574
|
+
return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
1575
|
+
return [2 /*return*/];
|
|
1576
|
+
}); });
|
|
1577
|
+
}; // eslint-disable-line @typescript-eslint/no-empty-function
|
|
1214
1578
|
/**
|
|
1215
1579
|
* Attaches this effect to `target` if not already attached.
|
|
1216
1580
|
* @ignore
|
|
1217
1581
|
*/
|
|
1218
1582
|
Base.prototype.tryAttach = function (target) {
|
|
1219
|
-
if (this._occurrenceCount === 0)
|
|
1583
|
+
if (this._occurrenceCount === 0) {
|
|
1220
1584
|
this.attach(target);
|
|
1585
|
+
}
|
|
1221
1586
|
this._occurrenceCount++;
|
|
1222
1587
|
};
|
|
1223
1588
|
Base.prototype.attach = function (movie) {
|
|
1224
1589
|
this._target = movie;
|
|
1225
1590
|
};
|
|
1226
1591
|
/**
|
|
1227
|
-
*
|
|
1592
|
+
* Detaches this effect from its target if the number of times `tryDetach`
|
|
1228
1593
|
* has been called (including this call) equals the number of times
|
|
1229
1594
|
* `tryAttach` has been called.
|
|
1230
1595
|
*
|
|
1231
1596
|
* @ignore
|
|
1232
1597
|
*/
|
|
1233
1598
|
Base.prototype.tryDetach = function () {
|
|
1234
|
-
if (this._target === null)
|
|
1599
|
+
if (this._target === null) {
|
|
1235
1600
|
throw new Error('No movie to detach from');
|
|
1601
|
+
}
|
|
1236
1602
|
this._occurrenceCount--;
|
|
1237
1603
|
// If this effect occurs in another place in the containing array, do not
|
|
1238
1604
|
// unset _target. (For calling `unshift` on the `layers` proxy)
|
|
1239
|
-
if (this._occurrenceCount === 0)
|
|
1605
|
+
if (this._occurrenceCount === 0) {
|
|
1240
1606
|
this.detach();
|
|
1607
|
+
}
|
|
1241
1608
|
};
|
|
1242
1609
|
Base.prototype.detach = function () {
|
|
1243
1610
|
this._target = null;
|
|
@@ -1262,6 +1629,14 @@ var etro = (function () {
|
|
|
1262
1629
|
enumerable: false,
|
|
1263
1630
|
configurable: true
|
|
1264
1631
|
});
|
|
1632
|
+
Object.defineProperty(Base.prototype, "ready", {
|
|
1633
|
+
/** `true` if this effect is ready to be applied */
|
|
1634
|
+
get: function () {
|
|
1635
|
+
return true;
|
|
1636
|
+
},
|
|
1637
|
+
enumerable: false,
|
|
1638
|
+
configurable: true
|
|
1639
|
+
});
|
|
1265
1640
|
Object.defineProperty(Base.prototype, "parent", {
|
|
1266
1641
|
get: function () {
|
|
1267
1642
|
return this._target;
|
|
@@ -1276,6 +1651,9 @@ var etro = (function () {
|
|
|
1276
1651
|
enumerable: false,
|
|
1277
1652
|
configurable: true
|
|
1278
1653
|
});
|
|
1654
|
+
/**
|
|
1655
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1656
|
+
*/
|
|
1279
1657
|
Base.prototype.getDefaultOptions = function () {
|
|
1280
1658
|
return {};
|
|
1281
1659
|
};
|
|
@@ -1343,16 +1721,18 @@ var etro = (function () {
|
|
|
1343
1721
|
Shader.prototype._initGl = function () {
|
|
1344
1722
|
this._canvas = document.createElement('canvas');
|
|
1345
1723
|
var gl = this._canvas.getContext('webgl');
|
|
1346
|
-
if (gl === null)
|
|
1724
|
+
if (gl === null) {
|
|
1347
1725
|
throw new Error('Unable to initialize WebGL. Your browser or machine may not support it.');
|
|
1726
|
+
}
|
|
1348
1727
|
this._gl = gl;
|
|
1349
1728
|
return gl;
|
|
1350
1729
|
};
|
|
1351
1730
|
Shader.prototype._initTextures = function (userUniforms, userTextures, sourceTextureOptions) {
|
|
1352
1731
|
var gl = this._gl;
|
|
1353
1732
|
var maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
1354
|
-
if (userTextures.length > maxTextures)
|
|
1733
|
+
if (userTextures.length > maxTextures) {
|
|
1355
1734
|
console.warn('Too many textures!');
|
|
1735
|
+
}
|
|
1356
1736
|
this._userTextures = {};
|
|
1357
1737
|
for (var name_1 in userTextures) {
|
|
1358
1738
|
var userOptions = userTextures[name_1];
|
|
@@ -1365,8 +1745,9 @@ var etro = (function () {
|
|
|
1365
1745
|
* textures, without having to define multiple properties in the effect
|
|
1366
1746
|
* object.
|
|
1367
1747
|
*/
|
|
1368
|
-
if (userUniforms[name_1])
|
|
1748
|
+
if (userUniforms[name_1]) {
|
|
1369
1749
|
throw new Error("Texture - uniform naming conflict: ".concat(name_1, "!"));
|
|
1750
|
+
}
|
|
1370
1751
|
// Add this as a "user uniform".
|
|
1371
1752
|
userUniforms[name_1] = '1i'; // texture pointer
|
|
1372
1753
|
}
|
|
@@ -1401,18 +1782,6 @@ var etro = (function () {
|
|
|
1401
1782
|
this._uniformLocations[unprefixed] = gl.getUniformLocation(this._program, prefixed);
|
|
1402
1783
|
}
|
|
1403
1784
|
};
|
|
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
1785
|
Shader.prototype.apply = function (target, reltime) {
|
|
1417
1786
|
this._checkDimensions(target);
|
|
1418
1787
|
this._refreshGl();
|
|
@@ -1499,16 +1868,22 @@ var etro = (function () {
|
|
|
1499
1868
|
i++;
|
|
1500
1869
|
}
|
|
1501
1870
|
};
|
|
1871
|
+
/**
|
|
1872
|
+
* Set the shader's uniforms.
|
|
1873
|
+
* @param target The movie or layer to apply the shader to.
|
|
1874
|
+
* @param reltime The relative time of the movie or layer.
|
|
1875
|
+
*/
|
|
1502
1876
|
Shader.prototype._prepareUniforms = function (target, reltime) {
|
|
1503
1877
|
var gl = this._gl;
|
|
1504
|
-
// Set the shader uniforms.
|
|
1505
1878
|
// Tell the shader we bound the texture to texture unit 0.
|
|
1506
1879
|
// All base (Shader class) uniforms are optional.
|
|
1507
|
-
if (this._uniformLocations.source)
|
|
1880
|
+
if (this._uniformLocations.source) {
|
|
1508
1881
|
gl.uniform1i(this._uniformLocations.source, 0);
|
|
1882
|
+
}
|
|
1509
1883
|
// All base (Shader class) uniforms are optional.
|
|
1510
|
-
if (this._uniformLocations.size)
|
|
1884
|
+
if (this._uniformLocations.size) {
|
|
1511
1885
|
gl.uniform2iv(this._uniformLocations.size, [target.canvas.width, target.canvas.height]);
|
|
1886
|
+
}
|
|
1512
1887
|
for (var unprefixed in this._userUniforms) {
|
|
1513
1888
|
var options = this._userUniforms[unprefixed];
|
|
1514
1889
|
var value = val(this, unprefixed, reltime);
|
|
@@ -1532,11 +1907,13 @@ var etro = (function () {
|
|
|
1532
1907
|
/**
|
|
1533
1908
|
* Converts a value of a standard type for javascript to a standard type for
|
|
1534
1909
|
* GLSL
|
|
1910
|
+
*
|
|
1535
1911
|
* @param value - the raw value to prepare
|
|
1536
1912
|
* @param outputType - the WebGL type of |value|; example:
|
|
1537
1913
|
* <code>1f</code> for a float
|
|
1538
1914
|
* @param reltime - current time, relative to the target
|
|
1539
|
-
* @param [options]
|
|
1915
|
+
* @param [options]
|
|
1916
|
+
* @returns the prepared value
|
|
1540
1917
|
*/
|
|
1541
1918
|
Shader.prototype._prepareValue = function (value, outputType, reltime, options) {
|
|
1542
1919
|
if (options === void 0) { options = {}; }
|
|
@@ -1559,35 +1936,40 @@ var etro = (function () {
|
|
|
1559
1936
|
var i = 0;
|
|
1560
1937
|
for (var name_4 in this._userTextures) {
|
|
1561
1938
|
var testValue = val(this, name_4, reltime);
|
|
1562
|
-
if (value === testValue)
|
|
1939
|
+
if (value === testValue) {
|
|
1563
1940
|
value = Shader.INTERNAL_TEXTURE_UNITS + i; // after the internal texture units
|
|
1941
|
+
}
|
|
1564
1942
|
i++;
|
|
1565
1943
|
}
|
|
1566
1944
|
}
|
|
1567
1945
|
if (outputType === '3fv') {
|
|
1568
1946
|
// allow 4-component vectors; TODO: why?
|
|
1569
|
-
if (Array.isArray(value) && (value.length === 3 || value.length === 4))
|
|
1947
|
+
if (Array.isArray(value) && (value.length === 3 || value.length === 4)) {
|
|
1570
1948
|
return value;
|
|
1949
|
+
}
|
|
1571
1950
|
// kind of loose so this can be changed if needed
|
|
1572
|
-
if (typeof value === 'object')
|
|
1951
|
+
if (typeof value === 'object') {
|
|
1573
1952
|
return [
|
|
1574
1953
|
value.r !== undefined ? value.r : def,
|
|
1575
1954
|
value.g !== undefined ? value.g : def,
|
|
1576
1955
|
value.b !== undefined ? value.b : def
|
|
1577
1956
|
];
|
|
1957
|
+
}
|
|
1578
1958
|
throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
|
|
1579
1959
|
}
|
|
1580
1960
|
if (outputType === '4fv') {
|
|
1581
|
-
if (Array.isArray(value) && value.length === 4)
|
|
1961
|
+
if (Array.isArray(value) && value.length === 4) {
|
|
1582
1962
|
return value;
|
|
1963
|
+
}
|
|
1583
1964
|
// kind of loose so this can be changed if needed
|
|
1584
|
-
if (typeof value === 'object')
|
|
1965
|
+
if (typeof value === 'object') {
|
|
1585
1966
|
return [
|
|
1586
1967
|
value.r !== undefined ? value.r : def,
|
|
1587
1968
|
value.g !== undefined ? value.g : def,
|
|
1588
1969
|
value.b !== undefined ? value.b : def,
|
|
1589
1970
|
value.a !== undefined ? value.a : def
|
|
1590
1971
|
];
|
|
1972
|
+
}
|
|
1591
1973
|
throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
|
|
1592
1974
|
}
|
|
1593
1975
|
return value;
|
|
@@ -1681,8 +2063,9 @@ var etro = (function () {
|
|
|
1681
2063
|
else {
|
|
1682
2064
|
// No, it's not a power of 2. Turn off mips and set
|
|
1683
2065
|
// wrapping to clamp to edge
|
|
1684
|
-
if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE)
|
|
2066
|
+
if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE) {
|
|
1685
2067
|
console.warn('Wrap mode is not CLAMP_TO_EDGE for a non-power-of-two texture. Defaulting to CLAMP_TO_EDGE');
|
|
2068
|
+
}
|
|
1686
2069
|
gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
1687
2070
|
gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
1688
2071
|
}
|
|
@@ -1734,7 +2117,6 @@ var etro = (function () {
|
|
|
1734
2117
|
Shader._IDENTITY_FRAGMENT_SOURCE = "\n precision mediump float;\n\n uniform sampler2D u_Source;\n\n varying highp vec2 v_TextureCoord;\n\n void main() {\n gl_FragColor = texture2D(u_Source, v_TextureCoord);\n }\n ";
|
|
1735
2118
|
return Shader;
|
|
1736
2119
|
}(Visual$1));
|
|
1737
|
-
// Shader.prototype.getpublicExcludes = () =>
|
|
1738
2120
|
var isPowerOf2 = function (value) { return (value && (value - 1)) === 0; };
|
|
1739
2121
|
|
|
1740
2122
|
/**
|
|
@@ -1909,6 +2291,35 @@ var etro = (function () {
|
|
|
1909
2291
|
return EllipticalMask;
|
|
1910
2292
|
}(Visual$1));
|
|
1911
2293
|
|
|
2294
|
+
var StackEffectsListener = /** @class */ (function (_super) {
|
|
2295
|
+
__extends(StackEffectsListener, _super);
|
|
2296
|
+
function StackEffectsListener(stack) {
|
|
2297
|
+
var _this = _super.call(this) || this;
|
|
2298
|
+
_this._stack = stack;
|
|
2299
|
+
return _this;
|
|
2300
|
+
}
|
|
2301
|
+
StackEffectsListener.prototype.onAdd = function (effect) {
|
|
2302
|
+
if (!this._stack.parent) {
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
effect.tryAttach(this._stack.parent);
|
|
2306
|
+
};
|
|
2307
|
+
StackEffectsListener.prototype.onRemove = function (effect) {
|
|
2308
|
+
if (!this._stack.parent) {
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
effect.tryDetach();
|
|
2312
|
+
};
|
|
2313
|
+
return StackEffectsListener;
|
|
2314
|
+
}(CustomArrayListener));
|
|
2315
|
+
var StackEffects = /** @class */ (function (_super) {
|
|
2316
|
+
__extends(StackEffects, _super);
|
|
2317
|
+
// eslint-disable-next-line no-use-before-define
|
|
2318
|
+
function StackEffects(target, stack) {
|
|
2319
|
+
return _super.call(this, target, new StackEffectsListener(stack)) || this;
|
|
2320
|
+
}
|
|
2321
|
+
return StackEffects;
|
|
2322
|
+
}(CustomArray));
|
|
1912
2323
|
/**
|
|
1913
2324
|
* A sequence of effects to apply, treated as one effect. This can be useful
|
|
1914
2325
|
* for defining reused effect sequences as one effect.
|
|
@@ -1917,48 +2328,28 @@ var etro = (function () {
|
|
|
1917
2328
|
__extends(Stack, _super);
|
|
1918
2329
|
function Stack(options) {
|
|
1919
2330
|
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
|
-
});
|
|
2331
|
+
_this.effects = new StackEffects(options.effects, _this);
|
|
1940
2332
|
options.effects.forEach(function (effect) { return _this.effects.push(effect); });
|
|
1941
2333
|
return _this;
|
|
1942
|
-
// TODO: Propogate 'change' events from children up
|
|
1943
2334
|
}
|
|
1944
2335
|
Stack.prototype.attach = function (movie) {
|
|
1945
2336
|
_super.prototype.attach.call(this, movie);
|
|
1946
2337
|
this.effects.filter(function (effect) { return !!effect; }).forEach(function (effect) {
|
|
1947
|
-
effect.
|
|
1948
|
-
effect.attach(movie);
|
|
2338
|
+
effect.tryAttach(movie);
|
|
1949
2339
|
});
|
|
1950
2340
|
};
|
|
1951
2341
|
Stack.prototype.detach = function () {
|
|
1952
2342
|
_super.prototype.detach.call(this);
|
|
1953
2343
|
this.effects.filter(function (effect) { return !!effect; }).forEach(function (effect) {
|
|
1954
|
-
effect.
|
|
2344
|
+
effect.tryDetach();
|
|
1955
2345
|
});
|
|
1956
2346
|
};
|
|
1957
2347
|
Stack.prototype.apply = function (target, reltime) {
|
|
1958
2348
|
for (var i = 0; i < this.effects.length; i++) {
|
|
1959
2349
|
var effect = this.effects[i];
|
|
1960
|
-
if (!effect)
|
|
2350
|
+
if (!effect) {
|
|
1961
2351
|
continue;
|
|
2352
|
+
}
|
|
1962
2353
|
effect.apply(target, reltime);
|
|
1963
2354
|
}
|
|
1964
2355
|
};
|
|
@@ -1977,7 +2368,7 @@ var etro = (function () {
|
|
|
1977
2368
|
* Applies a Gaussian blur
|
|
1978
2369
|
*/
|
|
1979
2370
|
// TODO: Improve performance
|
|
1980
|
-
// TODO: Make sure this is truly gaussian even though it
|
|
2371
|
+
// TODO: Make sure this is truly gaussian even though it doesn't require a
|
|
1981
2372
|
// standard deviation
|
|
1982
2373
|
var GaussianBlur = /** @class */ (function (_super) {
|
|
1983
2374
|
__extends(GaussianBlur, _super);
|
|
@@ -2022,9 +2413,10 @@ var etro = (function () {
|
|
|
2022
2413
|
}
|
|
2023
2414
|
GaussianBlurComponent.prototype.apply = function (target, reltime) {
|
|
2024
2415
|
var radiusVal = val(this, 'radius', reltime);
|
|
2025
|
-
if (radiusVal !== this._radiusCache)
|
|
2416
|
+
if (radiusVal !== this._radiusCache) {
|
|
2026
2417
|
// Regenerate gaussian distribution canvas.
|
|
2027
2418
|
this.shape = GaussianBlurComponent._render1DKernel(GaussianBlurComponent._gen1DKernel(radiusVal));
|
|
2419
|
+
}
|
|
2028
2420
|
this._radiusCache = radiusVal;
|
|
2029
2421
|
_super.prototype.apply.call(this, target, reltime);
|
|
2030
2422
|
};
|
|
@@ -2057,23 +2449,27 @@ var etro = (function () {
|
|
|
2057
2449
|
var pascal = GaussianBlurComponent._genPascalRow(2 * radius + 1);
|
|
2058
2450
|
// don't use `reduce` and `map` (overhead?)
|
|
2059
2451
|
var sum = 0;
|
|
2060
|
-
for (var i = 0; i < pascal.length; i++)
|
|
2452
|
+
for (var i = 0; i < pascal.length; i++) {
|
|
2061
2453
|
sum += pascal[i];
|
|
2062
|
-
|
|
2454
|
+
}
|
|
2455
|
+
for (var i = 0; i < pascal.length; i++) {
|
|
2063
2456
|
pascal[i] /= sum;
|
|
2457
|
+
}
|
|
2064
2458
|
return pascal;
|
|
2065
2459
|
};
|
|
2066
2460
|
GaussianBlurComponent._genPascalRow = function (index) {
|
|
2067
|
-
if (index < 0)
|
|
2461
|
+
if (index < 0) {
|
|
2068
2462
|
throw new Error("Invalid index ".concat(index));
|
|
2463
|
+
}
|
|
2069
2464
|
var currRow = [1];
|
|
2070
2465
|
for (var i = 1; i < index; i++) {
|
|
2071
2466
|
var nextRow = [];
|
|
2072
2467
|
nextRow.length = currRow.length + 1;
|
|
2073
2468
|
// edges are always 1's
|
|
2074
2469
|
nextRow[0] = nextRow[nextRow.length - 1] = 1;
|
|
2075
|
-
for (var j = 1; j < nextRow.length - 1; j++)
|
|
2470
|
+
for (var j = 1; j < nextRow.length - 1; j++) {
|
|
2076
2471
|
nextRow[j] = currRow[j - 1] + currRow[j];
|
|
2472
|
+
}
|
|
2077
2473
|
currRow = nextRow;
|
|
2078
2474
|
}
|
|
2079
2475
|
return currRow;
|
|
@@ -2144,15 +2540,14 @@ var etro = (function () {
|
|
|
2144
2540
|
pixelSize: '1i'
|
|
2145
2541
|
}
|
|
2146
2542
|
}) || this;
|
|
2147
|
-
/**
|
|
2148
|
-
*/
|
|
2149
2543
|
_this.pixelSize = options.pixelSize || 1;
|
|
2150
2544
|
return _this;
|
|
2151
2545
|
}
|
|
2152
2546
|
Pixelate.prototype.apply = function (target, reltime) {
|
|
2153
2547
|
var ps = val(this, 'pixelSize', reltime);
|
|
2154
|
-
if (ps % 1 !== 0 || ps < 0)
|
|
2548
|
+
if (ps % 1 !== 0 || ps < 0) {
|
|
2155
2549
|
throw new Error('Pixel size must be a nonnegative integer');
|
|
2550
|
+
}
|
|
2156
2551
|
_super.prototype.apply.call(this, target, reltime);
|
|
2157
2552
|
};
|
|
2158
2553
|
return Pixelate;
|
|
@@ -2181,10 +2576,12 @@ var etro = (function () {
|
|
|
2181
2576
|
return _this;
|
|
2182
2577
|
}
|
|
2183
2578
|
Transform.prototype.apply = function (target, reltime) {
|
|
2184
|
-
if (target.canvas.width !== this._tmpCanvas.width)
|
|
2579
|
+
if (target.canvas.width !== this._tmpCanvas.width) {
|
|
2185
2580
|
this._tmpCanvas.width = target.canvas.width;
|
|
2186
|
-
|
|
2581
|
+
}
|
|
2582
|
+
if (target.canvas.height !== this._tmpCanvas.height) {
|
|
2187
2583
|
this._tmpCanvas.height = target.canvas.height;
|
|
2584
|
+
}
|
|
2188
2585
|
// Use data, since that's the underlying storage
|
|
2189
2586
|
this._tmpMatrix.data = val(this, 'matrix.data', reltime);
|
|
2190
2587
|
this._tmpCtx.setTransform(this._tmpMatrix.a, this._tmpMatrix.b, this._tmpMatrix.c, this._tmpMatrix.d, this._tmpMatrix.e, this._tmpMatrix.f);
|
|
@@ -2210,8 +2607,9 @@ var etro = (function () {
|
|
|
2210
2607
|
];
|
|
2211
2608
|
}
|
|
2212
2609
|
Matrix.prototype.identity = function () {
|
|
2213
|
-
for (var i = 0; i < this.data.length; i++)
|
|
2610
|
+
for (var i = 0; i < this.data.length; i++) {
|
|
2214
2611
|
this.data[i] = Matrix.IDENTITY.data[i];
|
|
2612
|
+
}
|
|
2215
2613
|
return this;
|
|
2216
2614
|
};
|
|
2217
2615
|
/**
|
|
@@ -2220,8 +2618,9 @@ var etro = (function () {
|
|
|
2220
2618
|
* @param [val]
|
|
2221
2619
|
*/
|
|
2222
2620
|
Matrix.prototype.cell = function (x, y, val) {
|
|
2223
|
-
if (val !== undefined)
|
|
2621
|
+
if (val !== undefined) {
|
|
2224
2622
|
this.data[3 * y + x] = val;
|
|
2623
|
+
}
|
|
2225
2624
|
return this.data[3 * y + x];
|
|
2226
2625
|
};
|
|
2227
2626
|
Object.defineProperty(Matrix.prototype, "a", {
|
|
@@ -2273,16 +2672,19 @@ var etro = (function () {
|
|
|
2273
2672
|
*/
|
|
2274
2673
|
Matrix.prototype.multiply = function (other) {
|
|
2275
2674
|
// copy to temporary matrix to avoid modifying `this` while reading from it
|
|
2276
|
-
for (var x = 0; x < 3; x++)
|
|
2675
|
+
for (var x = 0; x < 3; x++) {
|
|
2277
2676
|
for (var y = 0; y < 3; y++) {
|
|
2278
2677
|
var sum = 0;
|
|
2279
|
-
for (var i = 0; i < 3; i++)
|
|
2678
|
+
for (var i = 0; i < 3; i++) {
|
|
2280
2679
|
sum += this.cell(x, i) * other.cell(i, y);
|
|
2680
|
+
}
|
|
2281
2681
|
Matrix._TMP_MATRIX.cell(x, y, sum);
|
|
2282
2682
|
}
|
|
2683
|
+
}
|
|
2283
2684
|
// copy data from TMP_MATRIX to this
|
|
2284
|
-
for (var i = 0; i < Matrix._TMP_MATRIX.data.length; i++)
|
|
2685
|
+
for (var i = 0; i < Matrix._TMP_MATRIX.data.length; i++) {
|
|
2285
2686
|
this.data[i] = Matrix._TMP_MATRIX.data[i];
|
|
2687
|
+
}
|
|
2286
2688
|
return this;
|
|
2287
2689
|
};
|
|
2288
2690
|
/**
|
|
@@ -2356,6 +2758,52 @@ var etro = (function () {
|
|
|
2356
2758
|
Visual: Visual$1
|
|
2357
2759
|
});
|
|
2358
2760
|
|
|
2761
|
+
var MovieEffectsListener = /** @class */ (function (_super) {
|
|
2762
|
+
__extends(MovieEffectsListener, _super);
|
|
2763
|
+
function MovieEffectsListener(movie) {
|
|
2764
|
+
var _this = _super.call(this) || this;
|
|
2765
|
+
_this._movie = movie;
|
|
2766
|
+
return _this;
|
|
2767
|
+
}
|
|
2768
|
+
MovieEffectsListener.prototype.onAdd = function (effect) {
|
|
2769
|
+
effect.tryAttach(this._movie);
|
|
2770
|
+
};
|
|
2771
|
+
MovieEffectsListener.prototype.onRemove = function (effect) {
|
|
2772
|
+
effect.tryDetach();
|
|
2773
|
+
};
|
|
2774
|
+
return MovieEffectsListener;
|
|
2775
|
+
}(CustomArrayListener));
|
|
2776
|
+
var MovieEffects = /** @class */ (function (_super) {
|
|
2777
|
+
__extends(MovieEffects, _super);
|
|
2778
|
+
function MovieEffects(target, movie) {
|
|
2779
|
+
return _super.call(this, target, new MovieEffectsListener(movie)) || this;
|
|
2780
|
+
}
|
|
2781
|
+
return MovieEffects;
|
|
2782
|
+
}(CustomArray));
|
|
2783
|
+
|
|
2784
|
+
var MovieLayersListener = /** @class */ (function (_super) {
|
|
2785
|
+
__extends(MovieLayersListener, _super);
|
|
2786
|
+
function MovieLayersListener(movie) {
|
|
2787
|
+
var _this = _super.call(this) || this;
|
|
2788
|
+
_this._movie = movie;
|
|
2789
|
+
return _this;
|
|
2790
|
+
}
|
|
2791
|
+
MovieLayersListener.prototype.onAdd = function (layer) {
|
|
2792
|
+
layer.tryAttach(this._movie);
|
|
2793
|
+
};
|
|
2794
|
+
MovieLayersListener.prototype.onRemove = function (layer) {
|
|
2795
|
+
layer.tryDetach();
|
|
2796
|
+
};
|
|
2797
|
+
return MovieLayersListener;
|
|
2798
|
+
}(CustomArrayListener));
|
|
2799
|
+
var MovieLayers = /** @class */ (function (_super) {
|
|
2800
|
+
__extends(MovieLayers, _super);
|
|
2801
|
+
function MovieLayers(target, movie) {
|
|
2802
|
+
return _super.call(this, target, new MovieLayersListener(movie)) || this;
|
|
2803
|
+
}
|
|
2804
|
+
return MovieLayers;
|
|
2805
|
+
}(CustomArray));
|
|
2806
|
+
|
|
2359
2807
|
/**
|
|
2360
2808
|
* @module movie
|
|
2361
2809
|
*/
|
|
@@ -2369,15 +2817,13 @@ var etro = (function () {
|
|
|
2369
2817
|
*
|
|
2370
2818
|
* Implements a pub/sub system.
|
|
2371
2819
|
*/
|
|
2372
|
-
// TODO: Make record option to make recording video output to the user while
|
|
2373
|
-
// it's recording
|
|
2374
2820
|
// TODO: rename renderingFrame -> refreshing
|
|
2375
2821
|
var Movie = /** @class */ (function () {
|
|
2376
2822
|
/**
|
|
2377
2823
|
* Creates a new movie.
|
|
2378
2824
|
*/
|
|
2379
2825
|
function Movie(options) {
|
|
2380
|
-
|
|
2826
|
+
this._recording = false;
|
|
2381
2827
|
// Set actx option manually, because it's readonly.
|
|
2382
2828
|
this.actx = options.actx ||
|
|
2383
2829
|
options.audioContext ||
|
|
@@ -2385,236 +2831,294 @@ var etro = (function () {
|
|
|
2385
2831
|
// eslint-disable-next-line new-cap
|
|
2386
2832
|
new window.webkitAudioContext();
|
|
2387
2833
|
delete options.actx;
|
|
2388
|
-
//
|
|
2389
|
-
|
|
2834
|
+
// Check if required file canvas is provided
|
|
2835
|
+
if (!options.canvas) {
|
|
2836
|
+
throw new Error('Required option "canvas" not provided to Movie');
|
|
2837
|
+
}
|
|
2390
2838
|
// Set canvas option manually, because it's readonly.
|
|
2391
|
-
this._canvas = options.canvas;
|
|
2839
|
+
this._canvas = this._visibleCanvas = options.canvas;
|
|
2392
2840
|
delete options.canvas;
|
|
2393
|
-
// Don't send updates when initializing, so use this instead of newThis:
|
|
2394
2841
|
this._cctx = this.canvas.getContext('2d'); // TODO: make private?
|
|
2842
|
+
// Set options on the movie
|
|
2395
2843
|
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
|
-
});
|
|
2844
|
+
this.effects = new MovieEffects([], this);
|
|
2845
|
+
this.layers = new MovieLayers([], this);
|
|
2468
2846
|
this._paused = true;
|
|
2469
2847
|
this._ended = false;
|
|
2470
|
-
// This
|
|
2471
|
-
//
|
|
2848
|
+
// This lock prevents multiple refresh loops at the same time (see
|
|
2849
|
+
// `render`). It's only valid while rendering.
|
|
2472
2850
|
this._renderingFrame = false;
|
|
2473
2851
|
this.currentTime = 0;
|
|
2474
|
-
//
|
|
2475
|
-
this._mediaRecorder = null;
|
|
2476
|
-
// -1 works well in inequalities
|
|
2477
|
-
// The last time `play` was called
|
|
2852
|
+
// The last time `play` was called, -1 works well in comparisons
|
|
2478
2853
|
this._lastPlayed = -1;
|
|
2479
|
-
// What
|
|
2854
|
+
// What `currentTime` was when `play` was called
|
|
2480
2855
|
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
2856
|
}
|
|
2857
|
+
Movie.prototype._whenReady = function () {
|
|
2858
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
2859
|
+
return __generator(this, function (_a) {
|
|
2860
|
+
switch (_a.label) {
|
|
2861
|
+
case 0: return [4 /*yield*/, Promise.all([
|
|
2862
|
+
Promise.all(this.layers.map(function (layer) { return layer.whenReady(); })),
|
|
2863
|
+
Promise.all(this.effects.map(function (effect) { return effect.whenReady(); }))
|
|
2864
|
+
])];
|
|
2865
|
+
case 1:
|
|
2866
|
+
_a.sent();
|
|
2867
|
+
return [2 /*return*/];
|
|
2868
|
+
}
|
|
2869
|
+
});
|
|
2870
|
+
});
|
|
2871
|
+
};
|
|
2499
2872
|
/**
|
|
2500
2873
|
* Plays the movie
|
|
2501
|
-
*
|
|
2874
|
+
*
|
|
2875
|
+
* @param [options]
|
|
2876
|
+
* @param [options.onStart] Called when the movie starts playing
|
|
2877
|
+
*
|
|
2878
|
+
* @return Fulfilled when the movie is done playing, never fails
|
|
2502
2879
|
*/
|
|
2503
|
-
Movie.prototype.play = function () {
|
|
2504
|
-
var
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2880
|
+
Movie.prototype.play = function (options) {
|
|
2881
|
+
var _a;
|
|
2882
|
+
if (options === void 0) { options = {}; }
|
|
2883
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
2884
|
+
var _this = this;
|
|
2885
|
+
return __generator(this, function (_b) {
|
|
2886
|
+
switch (_b.label) {
|
|
2887
|
+
case 0: return [4 /*yield*/, this._whenReady()];
|
|
2888
|
+
case 1:
|
|
2889
|
+
_b.sent();
|
|
2890
|
+
if (!this.paused) {
|
|
2891
|
+
throw new Error('Already playing');
|
|
2892
|
+
}
|
|
2893
|
+
this._paused = this._ended = false;
|
|
2894
|
+
this._lastPlayed = performance.now();
|
|
2895
|
+
this._lastPlayedOffset = this.currentTime;
|
|
2896
|
+
(_a = options.onStart) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
2897
|
+
// For backwards compatibility
|
|
2898
|
+
publish(this, 'movie.play', {});
|
|
2899
|
+
// Repeatedly render frames until the movie ends
|
|
2900
|
+
return [4 /*yield*/, new Promise(function (resolve) {
|
|
2901
|
+
if (!_this.renderingFrame) {
|
|
2902
|
+
// Not rendering (and not playing), so play.
|
|
2903
|
+
_this._render(true, undefined, resolve);
|
|
2904
|
+
}
|
|
2905
|
+
// Stop rendering frame if currently doing so, because playing has higher
|
|
2906
|
+
// priority. This will affect the next _render call.
|
|
2907
|
+
_this._renderingFrame = false;
|
|
2908
|
+
})];
|
|
2909
|
+
case 2:
|
|
2910
|
+
// Repeatedly render frames until the movie ends
|
|
2911
|
+
_b.sent();
|
|
2912
|
+
return [2 /*return*/];
|
|
2913
|
+
}
|
|
2914
|
+
});
|
|
2915
|
+
});
|
|
2916
|
+
};
|
|
2917
|
+
/**
|
|
2918
|
+
* Updates the rendering canvas and audio destination to the visible canvas
|
|
2919
|
+
* and the audio context destination.
|
|
2920
|
+
*/
|
|
2921
|
+
Movie.prototype._show = function () {
|
|
2922
|
+
this._canvas = this._visibleCanvas;
|
|
2923
|
+
this._cctx = this.canvas.getContext('2d');
|
|
2924
|
+
publish(this, 'audiodestinationupdate', { movie: this, destination: this.actx.destination });
|
|
2925
|
+
};
|
|
2926
|
+
/**
|
|
2927
|
+
* Streams the movie to a MediaStream
|
|
2928
|
+
*
|
|
2929
|
+
* @param options Options for the stream
|
|
2930
|
+
* @param options.frameRate The frame rate of the stream's video
|
|
2931
|
+
* @param options.duration The duration of the stream in seconds
|
|
2932
|
+
* @param options.video Whether to stream video. Defaults to true.
|
|
2933
|
+
* @param options.audio Whether to stream audio. Defaults to true.
|
|
2934
|
+
* @param options.onStart Called when the stream is started
|
|
2935
|
+
* @return Fulfilled when the stream is done, never fails
|
|
2936
|
+
*/
|
|
2937
|
+
Movie.prototype.stream = function (options) {
|
|
2938
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
2939
|
+
var tracks, visualStream, hasMediaTracks, audioDestination, audioStream;
|
|
2940
|
+
var _this = this;
|
|
2941
|
+
return __generator(this, function (_a) {
|
|
2942
|
+
switch (_a.label) {
|
|
2943
|
+
case 0:
|
|
2944
|
+
// Validate options
|
|
2945
|
+
if (!options || !options.frameRate) {
|
|
2946
|
+
throw new Error('Required option "frameRate" not provided to Movie.stream');
|
|
2947
|
+
}
|
|
2948
|
+
if (options.video === false && options.audio === false) {
|
|
2949
|
+
throw new Error('Both video and audio cannot be disabled');
|
|
2950
|
+
}
|
|
2951
|
+
if (!this.paused) {
|
|
2952
|
+
throw new Error("Cannot stream movie while it's already playing");
|
|
2953
|
+
}
|
|
2954
|
+
// Wait until all resources are loaded
|
|
2955
|
+
return [4 /*yield*/, this._whenReady()
|
|
2956
|
+
// Create a temporary canvas to stream from
|
|
2957
|
+
];
|
|
2958
|
+
case 1:
|
|
2959
|
+
// Wait until all resources are loaded
|
|
2960
|
+
_a.sent();
|
|
2961
|
+
// Create a temporary canvas to stream from
|
|
2962
|
+
this._canvas = document.createElement('canvas');
|
|
2963
|
+
this.canvas.width = this._visibleCanvas.width;
|
|
2964
|
+
this.canvas.height = this._visibleCanvas.height;
|
|
2965
|
+
this._cctx = this.canvas.getContext('2d');
|
|
2966
|
+
tracks = [];
|
|
2967
|
+
// Add video track
|
|
2968
|
+
if (options.video !== false) {
|
|
2969
|
+
visualStream = this.canvas.captureStream(options.frameRate);
|
|
2970
|
+
tracks = tracks.concat(visualStream.getTracks());
|
|
2971
|
+
}
|
|
2972
|
+
hasMediaTracks = this.layers.some(function (layer) { return layer instanceof Audio || layer instanceof Video; });
|
|
2973
|
+
// If no media tracks present, don't include an audio stream, because
|
|
2974
|
+
// Chrome doesn't record silence when an audio stream is present.
|
|
2975
|
+
if (hasMediaTracks && options.audio !== false) {
|
|
2976
|
+
audioDestination = this.actx.createMediaStreamDestination();
|
|
2977
|
+
audioStream = audioDestination.stream;
|
|
2978
|
+
tracks = tracks.concat(audioStream.getTracks());
|
|
2979
|
+
// Notify layers and any other listeners of the new audio destination
|
|
2980
|
+
publish(this, 'audiodestinationupdate', { movie: this, destination: audioDestination });
|
|
2981
|
+
}
|
|
2982
|
+
// Create the stream
|
|
2983
|
+
this._currentStream = new MediaStream(tracks);
|
|
2984
|
+
// Play the movie
|
|
2985
|
+
this._endTime = options.duration ? this.currentTime + options.duration : this.duration;
|
|
2986
|
+
return [4 /*yield*/, this.play({
|
|
2987
|
+
onStart: function () {
|
|
2988
|
+
// Call the user's onStart callback
|
|
2989
|
+
options.onStart(_this._currentStream);
|
|
2990
|
+
}
|
|
2991
|
+
})
|
|
2992
|
+
// Clear the stream after the movie is done playing
|
|
2993
|
+
];
|
|
2994
|
+
case 2:
|
|
2995
|
+
_a.sent();
|
|
2996
|
+
// Clear the stream after the movie is done playing
|
|
2997
|
+
this._currentStream.getTracks().forEach(function (track) {
|
|
2998
|
+
track.stop();
|
|
2999
|
+
});
|
|
3000
|
+
this._currentStream = null;
|
|
3001
|
+
this._show();
|
|
3002
|
+
return [2 /*return*/];
|
|
3003
|
+
}
|
|
3004
|
+
});
|
|
2518
3005
|
});
|
|
2519
3006
|
};
|
|
2520
3007
|
/**
|
|
2521
3008
|
* Plays the movie in the background and records it
|
|
2522
3009
|
*
|
|
2523
3010
|
* @param options
|
|
2524
|
-
* @param frameRate
|
|
3011
|
+
* @param [options.frameRate] - Video frame rate
|
|
2525
3012
|
* @param [options.video=true] - whether to include video in recording
|
|
2526
3013
|
* @param [options.audio=true] - whether to include audio in recording
|
|
2527
|
-
* @param [options.mediaRecorderOptions=undefined] -
|
|
3014
|
+
* @param [options.mediaRecorderOptions=undefined] - Options to pass to the
|
|
3015
|
+
* `MediaRecorder` constructor
|
|
2528
3016
|
* @param [options.type='video/webm'] - MIME type for exported video
|
|
2529
|
-
*
|
|
2530
|
-
* @return
|
|
3017
|
+
* @param [options.onStart] - Called when the recording starts
|
|
3018
|
+
* @return Resolves when done recording, rejects when media recorder errors
|
|
2531
3019
|
*/
|
|
2532
|
-
//
|
|
2533
|
-
// TODO: figure out how to do offline recording (faster than realtime).
|
|
2534
|
-
// TODO: improve recording performance to increase frame rate?
|
|
3020
|
+
// TODO: Improve recording performance to increase frame rate
|
|
2535
3021
|
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
|
-
|
|
3022
|
+
var _a;
|
|
3023
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
3024
|
+
var mimeType, stream, recordedChunks, mediaRecorderOptions;
|
|
3025
|
+
var _this = this;
|
|
3026
|
+
return __generator(this, function (_b) {
|
|
3027
|
+
switch (_b.label) {
|
|
3028
|
+
case 0:
|
|
3029
|
+
// Validate options
|
|
3030
|
+
if (options.video === false && options.audio === false) {
|
|
3031
|
+
throw new Error('Both video and audio cannot be disabled');
|
|
3032
|
+
}
|
|
3033
|
+
if (!this.paused) {
|
|
3034
|
+
throw new Error("Cannot record movie while it's already playing");
|
|
3035
|
+
}
|
|
3036
|
+
mimeType = options.type || 'video/webm';
|
|
3037
|
+
if (MediaRecorder && MediaRecorder.isTypeSupported && !MediaRecorder.isTypeSupported(mimeType)) {
|
|
3038
|
+
throw new Error('Please pass a valid MIME type for the exported video');
|
|
3039
|
+
}
|
|
3040
|
+
return [4 /*yield*/, new Promise(function (resolve) {
|
|
3041
|
+
_this.stream({
|
|
3042
|
+
frameRate: options.frameRate,
|
|
3043
|
+
duration: options.duration,
|
|
3044
|
+
video: options.video,
|
|
3045
|
+
audio: options.audio,
|
|
3046
|
+
onStart: resolve
|
|
3047
|
+
}).then(function () {
|
|
3048
|
+
// Stop the media recorder when the movie is done playing
|
|
3049
|
+
_this._recorder.requestData();
|
|
3050
|
+
_this._recorder.stop();
|
|
3051
|
+
});
|
|
3052
|
+
})
|
|
3053
|
+
// The array to store the recorded chunks
|
|
3054
|
+
];
|
|
3055
|
+
case 1:
|
|
3056
|
+
stream = _b.sent();
|
|
3057
|
+
recordedChunks = [];
|
|
3058
|
+
mediaRecorderOptions = __assign(__assign({}, (options.mediaRecorderOptions || {})), { mimeType: mimeType });
|
|
3059
|
+
this._recorder = new MediaRecorder(stream, mediaRecorderOptions);
|
|
3060
|
+
this._recorder.ondataavailable = function (event) {
|
|
3061
|
+
// if (this._paused) reject(new Error("Recording was interrupted"));
|
|
3062
|
+
if (event.data.size > 0) {
|
|
3063
|
+
recordedChunks.push(event.data);
|
|
3064
|
+
}
|
|
3065
|
+
};
|
|
3066
|
+
// Start recording
|
|
3067
|
+
this._recorder.start();
|
|
3068
|
+
this._recording = true;
|
|
3069
|
+
// Notify caller that the media recorder has started
|
|
3070
|
+
(_a = options.onStart) === null || _a === void 0 ? void 0 : _a.call(options, this._recorder);
|
|
3071
|
+
// For backwards compatibility
|
|
3072
|
+
publish(this, 'movie.record', { options: options });
|
|
3073
|
+
// Wait until the media recorder is done recording and processing
|
|
3074
|
+
return [4 /*yield*/, new Promise(function (resolve, reject) {
|
|
3075
|
+
_this._recorder.onstop = function () {
|
|
3076
|
+
resolve();
|
|
3077
|
+
};
|
|
3078
|
+
_this._recorder.onerror = reject;
|
|
3079
|
+
})
|
|
3080
|
+
// Clean up
|
|
3081
|
+
];
|
|
3082
|
+
case 2:
|
|
3083
|
+
// Wait until the media recorder is done recording and processing
|
|
3084
|
+
_b.sent();
|
|
3085
|
+
// Clean up
|
|
3086
|
+
this._paused = true;
|
|
3087
|
+
this._ended = true;
|
|
3088
|
+
this._recording = false;
|
|
3089
|
+
// Construct the exported video out of all the frame blobs.
|
|
3090
|
+
return [2 /*return*/, new Blob(recordedChunks, {
|
|
3091
|
+
type: mimeType
|
|
3092
|
+
})];
|
|
3093
|
+
}
|
|
3094
|
+
});
|
|
2597
3095
|
});
|
|
2598
3096
|
};
|
|
2599
3097
|
/**
|
|
2600
|
-
* Stops the movie
|
|
2601
|
-
* @return
|
|
3098
|
+
* Stops the movie without resetting the playback position
|
|
3099
|
+
* @return The movie
|
|
2602
3100
|
*/
|
|
2603
3101
|
Movie.prototype.pause = function () {
|
|
3102
|
+
// Update state
|
|
2604
3103
|
this._paused = true;
|
|
2605
3104
|
// Deactivate all layers
|
|
2606
|
-
for (var i = 0; i < this.layers.length; i++)
|
|
3105
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
2607
3106
|
if (Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
2608
3107
|
var layer = this.layers[i];
|
|
2609
|
-
layer.
|
|
2610
|
-
|
|
3108
|
+
if (layer.active) {
|
|
3109
|
+
layer.stop();
|
|
3110
|
+
layer.active = false;
|
|
3111
|
+
}
|
|
2611
3112
|
}
|
|
3113
|
+
}
|
|
3114
|
+
// For backwards compatibility, notify event listeners that the movie has
|
|
3115
|
+
// paused
|
|
2612
3116
|
publish(this, 'movie.pause', {});
|
|
2613
3117
|
return this;
|
|
2614
3118
|
};
|
|
2615
3119
|
/**
|
|
2616
3120
|
* Stops playback and resets the playback position
|
|
2617
|
-
* @return
|
|
3121
|
+
* @return The movie
|
|
2618
3122
|
*/
|
|
2619
3123
|
Movie.prototype.stop = function () {
|
|
2620
3124
|
this.pause();
|
|
@@ -2623,8 +3127,8 @@ var etro = (function () {
|
|
|
2623
3127
|
};
|
|
2624
3128
|
/**
|
|
2625
3129
|
* @param [timestamp=performance.now()]
|
|
2626
|
-
* @param [done=undefined] -
|
|
2627
|
-
*
|
|
3130
|
+
* @param [done=undefined] - Called when done playing or when the current
|
|
3131
|
+
* frame is loaded
|
|
2628
3132
|
*/
|
|
2629
3133
|
Movie.prototype._render = function (repeat, timestamp, done) {
|
|
2630
3134
|
var _this = this;
|
|
@@ -2632,179 +3136,207 @@ var etro = (function () {
|
|
|
2632
3136
|
if (done === void 0) { done = undefined; }
|
|
2633
3137
|
clearCachedValues(this);
|
|
2634
3138
|
if (!this.rendering) {
|
|
2635
|
-
// (
|
|
3139
|
+
// (this.paused && !this._renderingFrame) is true so it's playing or it's
|
|
2636
3140
|
// rendering a single frame.
|
|
2637
|
-
if (done)
|
|
3141
|
+
if (done) {
|
|
2638
3142
|
done();
|
|
3143
|
+
}
|
|
2639
3144
|
return;
|
|
2640
3145
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
// TODO:
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
3146
|
+
if (this.ready) {
|
|
3147
|
+
publish(this, 'movie.loadeddata', { movie: this });
|
|
3148
|
+
// If the movie is streaming or recording, resume the media recorder
|
|
3149
|
+
if (this._recording && this._recorder.state === 'paused') {
|
|
3150
|
+
this._recorder.resume();
|
|
3151
|
+
}
|
|
3152
|
+
// If the movie is streaming or recording, end at the specified duration.
|
|
3153
|
+
// Otherwise, end at the movie's duration, because play() does not
|
|
3154
|
+
// support playing a portion of the movie yet.
|
|
3155
|
+
// TODO: Is calling duration every frame bad for performance? (remember,
|
|
3156
|
+
// it's calling Array.reduce)
|
|
3157
|
+
var end = this._currentStream ? this._endTime : this.duration;
|
|
3158
|
+
this._updateCurrentTime(timestamp, end);
|
|
3159
|
+
if (this.currentTime === end) {
|
|
3160
|
+
if (this.recording) {
|
|
3161
|
+
publish(this, 'movie.recordended', { movie: this });
|
|
3162
|
+
}
|
|
3163
|
+
if (this.currentTime === this.duration) {
|
|
3164
|
+
publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
|
|
3165
|
+
}
|
|
3166
|
+
// Don't use setter, which publishes 'seek'. Instead, update the
|
|
3167
|
+
// value and publish a 'imeupdate' event.
|
|
2654
3168
|
this._currentTime = 0;
|
|
2655
3169
|
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
|
-
|
|
3170
|
+
this._lastPlayed = performance.now();
|
|
3171
|
+
this._lastPlayedOffset = 0; // this.currentTime
|
|
3172
|
+
this._renderingFrame = false;
|
|
3173
|
+
// Stop playback or recording if done (except if it's playing and repeat
|
|
3174
|
+
// is true)
|
|
3175
|
+
if (!(!this.recording && this.repeat)) {
|
|
3176
|
+
this._paused = true;
|
|
3177
|
+
this._ended = true;
|
|
3178
|
+
// Deactivate all layers
|
|
3179
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
3180
|
+
if (Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
3181
|
+
var layer = this.layers[i];
|
|
3182
|
+
// A layer that has been deleted before layers.length has been updated
|
|
3183
|
+
// (see the layers proxy in the constructor).
|
|
3184
|
+
if (!layer || !layer.active) {
|
|
3185
|
+
continue;
|
|
3186
|
+
}
|
|
3187
|
+
layer.stop();
|
|
3188
|
+
layer.active = false;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
publish(this, 'movie.pause', {});
|
|
3192
|
+
if (done) {
|
|
3193
|
+
done();
|
|
2675
3194
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
return;
|
|
3195
|
+
return;
|
|
3196
|
+
}
|
|
2679
3197
|
}
|
|
3198
|
+
// Do render
|
|
3199
|
+
this._renderBackground(timestamp);
|
|
3200
|
+
this._renderLayers();
|
|
3201
|
+
this._applyEffects();
|
|
2680
3202
|
}
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
//
|
|
2689
|
-
//
|
|
2690
|
-
//
|
|
2691
|
-
|
|
3203
|
+
else {
|
|
3204
|
+
// If we are recording, pause the media recorder until the movie is
|
|
3205
|
+
// ready.
|
|
3206
|
+
if (this.recording && this._recorder.state === 'recording') {
|
|
3207
|
+
this._recorder.pause();
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
// If the frame didn't load this instant, repeatedly frame-render until it
|
|
3211
|
+
// is loaded.
|
|
3212
|
+
// If the expression below is true, don't publish an event, just silently
|
|
3213
|
+
// stop the render loop.
|
|
3214
|
+
if (this._renderingFrame && this.ready) {
|
|
2692
3215
|
this._renderingFrame = false;
|
|
2693
|
-
if (done)
|
|
3216
|
+
if (done) {
|
|
2694
3217
|
done();
|
|
3218
|
+
}
|
|
2695
3219
|
return;
|
|
2696
3220
|
}
|
|
3221
|
+
// TODO: Is making a new arrow function every frame bad for performance?
|
|
2697
3222
|
window.requestAnimationFrame(function () {
|
|
2698
3223
|
_this._render(repeat, undefined, done);
|
|
2699
|
-
});
|
|
3224
|
+
});
|
|
2700
3225
|
};
|
|
2701
3226
|
Movie.prototype._updateCurrentTime = function (timestampMs, end) {
|
|
2702
|
-
// If we're only
|
|
2703
|
-
//
|
|
3227
|
+
// If we're only frame-rendering (current frame only), it doesn't matter if
|
|
3228
|
+
// it's paused or not.
|
|
2704
3229
|
if (!this._renderingFrame) {
|
|
2705
|
-
// if ((timestamp - this._lastUpdate) >= this._updateInterval) {
|
|
2706
3230
|
var sinceLastPlayed = (timestampMs - this._lastPlayed) / 1000;
|
|
2707
|
-
var currentTime = this._lastPlayedOffset + sinceLastPlayed;
|
|
3231
|
+
var currentTime = this._lastPlayedOffset + sinceLastPlayed;
|
|
2708
3232
|
if (this.currentTime !== currentTime) {
|
|
3233
|
+
// Update the current time (don't use setter)
|
|
2709
3234
|
this._currentTime = currentTime;
|
|
3235
|
+
// For backwards compatibility, publish a 'movie.timeupdate' event.
|
|
2710
3236
|
publish(this, 'movie.timeupdate', { movie: this });
|
|
2711
3237
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
this.currentTime = end;
|
|
3238
|
+
if (this.currentTime > end) {
|
|
3239
|
+
this._currentTime = end;
|
|
3240
|
+
}
|
|
2716
3241
|
}
|
|
2717
3242
|
};
|
|
2718
3243
|
Movie.prototype._renderBackground = function (timestamp) {
|
|
2719
3244
|
this.cctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
3245
|
+
// Evaluate background color (since it's a dynamic property)
|
|
2720
3246
|
var background = val(this, 'background', timestamp);
|
|
2721
|
-
if (background) {
|
|
3247
|
+
if (background) {
|
|
2722
3248
|
this.cctx.fillStyle = background;
|
|
2723
3249
|
this.cctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
2724
3250
|
}
|
|
2725
3251
|
};
|
|
2726
3252
|
/**
|
|
2727
|
-
* @return whether or not video frames are loaded
|
|
2728
3253
|
* @param [timestamp=performance.now()]
|
|
2729
|
-
* @private
|
|
2730
3254
|
*/
|
|
2731
3255
|
Movie.prototype._renderLayers = function () {
|
|
2732
|
-
var frameFullyLoaded = true;
|
|
2733
3256
|
for (var i = 0; i < this.layers.length; i++) {
|
|
2734
|
-
if (!Object.prototype.hasOwnProperty.call(this.layers, i))
|
|
3257
|
+
if (!Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
2735
3258
|
continue;
|
|
3259
|
+
}
|
|
2736
3260
|
var layer = this.layers[i];
|
|
2737
3261
|
// A layer that has been deleted before layers.length has been updated
|
|
2738
3262
|
// (see the layers proxy in the constructor).
|
|
2739
|
-
if (!layer)
|
|
3263
|
+
if (!layer) {
|
|
2740
3264
|
continue;
|
|
2741
|
-
|
|
3265
|
+
}
|
|
2742
3266
|
// Cancel operation if layer disabled or outside layer time interval
|
|
3267
|
+
var reltime = this.currentTime - layer.startTime;
|
|
2743
3268
|
if (!val(layer, 'enabled', reltime) ||
|
|
2744
3269
|
// TODO > or >= ?
|
|
2745
3270
|
this.currentTime < layer.startTime || this.currentTime > layer.startTime + layer.duration) {
|
|
2746
3271
|
// Layer is not active.
|
|
2747
3272
|
// If only rendering this frame, we are not "starting" the layer.
|
|
2748
3273
|
if (layer.active && !this._renderingFrame) {
|
|
2749
|
-
// TODO: make a `deactivate()` method?
|
|
2750
3274
|
layer.stop();
|
|
2751
3275
|
layer.active = false;
|
|
2752
3276
|
}
|
|
2753
3277
|
continue;
|
|
2754
3278
|
}
|
|
3279
|
+
// If we are playing (not refreshing), update the layer's progress
|
|
3280
|
+
if (!this._renderingFrame) {
|
|
3281
|
+
layer.progress(reltime);
|
|
3282
|
+
}
|
|
2755
3283
|
// If only rendering this frame, we are not "starting" the layer
|
|
2756
3284
|
if (!layer.active && val(layer, 'enabled', reltime) && !this._renderingFrame) {
|
|
2757
|
-
// TODO: make an `activate()` method?
|
|
2758
3285
|
layer.start();
|
|
2759
3286
|
layer.active = true;
|
|
2760
3287
|
}
|
|
2761
|
-
// if the layer has an input file
|
|
2762
|
-
if ('source' in layer)
|
|
2763
|
-
frameFullyLoaded = frameFullyLoaded && layer.source.readyState >= 2;
|
|
2764
3288
|
layer.render();
|
|
2765
3289
|
// if the layer has visual component
|
|
2766
3290
|
if (layer instanceof Visual) {
|
|
2767
3291
|
var canvas = layer.canvas;
|
|
2768
|
-
|
|
2769
|
-
// if the layer has an area (else InvalidStateError from canvas)
|
|
2770
|
-
if (canvas.width * canvas.height > 0)
|
|
3292
|
+
if (canvas.width * canvas.height > 0) {
|
|
2771
3293
|
this.cctx.drawImage(canvas, val(layer, 'x', reltime), val(layer, 'y', reltime), canvas.width, canvas.height);
|
|
3294
|
+
}
|
|
2772
3295
|
}
|
|
2773
3296
|
}
|
|
2774
|
-
return frameFullyLoaded;
|
|
2775
3297
|
};
|
|
2776
3298
|
Movie.prototype._applyEffects = function () {
|
|
2777
3299
|
for (var i = 0; i < this.effects.length; i++) {
|
|
2778
3300
|
var effect = this.effects[i];
|
|
2779
3301
|
// An effect that has been deleted before effects.length has been updated
|
|
2780
3302
|
// (see the effectsproxy in the constructor).
|
|
2781
|
-
if (!effect)
|
|
3303
|
+
if (!effect) {
|
|
2782
3304
|
continue;
|
|
3305
|
+
}
|
|
2783
3306
|
effect.apply(this, this.currentTime);
|
|
2784
3307
|
}
|
|
2785
3308
|
};
|
|
2786
3309
|
/**
|
|
2787
|
-
* Refreshes the screen
|
|
2788
|
-
*
|
|
3310
|
+
* Refreshes the screen
|
|
3311
|
+
*
|
|
3312
|
+
* Only use this if auto-refresh is disabled
|
|
3313
|
+
*
|
|
3314
|
+
* @return - Promise that resolves when the frame is loaded
|
|
2789
3315
|
*/
|
|
2790
3316
|
Movie.prototype.refresh = function () {
|
|
2791
3317
|
var _this = this;
|
|
3318
|
+
// Refreshing while playing can interrupt playback
|
|
3319
|
+
if (!this.paused) {
|
|
3320
|
+
throw new Error('Already playing');
|
|
3321
|
+
}
|
|
2792
3322
|
return new Promise(function (resolve) {
|
|
2793
3323
|
_this._renderingFrame = true;
|
|
2794
3324
|
_this._render(false, undefined, resolve);
|
|
2795
3325
|
});
|
|
2796
3326
|
};
|
|
2797
3327
|
/**
|
|
2798
|
-
* Convienence method
|
|
3328
|
+
* Convienence method (TODO: remove)
|
|
2799
3329
|
*/
|
|
2800
3330
|
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))
|
|
3331
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
3332
|
+
if (Object.prototype.hasOwnProperty.call(this.layers, i)) {
|
|
2803
3333
|
publish(this.layers[i], type, event);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
2804
3336
|
};
|
|
2805
3337
|
Object.defineProperty(Movie.prototype, "rendering", {
|
|
2806
3338
|
/**
|
|
2807
|
-
*
|
|
3339
|
+
* `true` if the movie is playing, recording or refreshing
|
|
2808
3340
|
*/
|
|
2809
3341
|
get: function () {
|
|
2810
3342
|
return !this.paused || this._renderingFrame;
|
|
@@ -2814,7 +3346,7 @@ var etro = (function () {
|
|
|
2814
3346
|
});
|
|
2815
3347
|
Object.defineProperty(Movie.prototype, "renderingFrame", {
|
|
2816
3348
|
/**
|
|
2817
|
-
*
|
|
3349
|
+
* `true` if the movie is refreshing the current frame
|
|
2818
3350
|
*/
|
|
2819
3351
|
get: function () {
|
|
2820
3352
|
return this._renderingFrame;
|
|
@@ -2824,17 +3356,19 @@ var etro = (function () {
|
|
|
2824
3356
|
});
|
|
2825
3357
|
Object.defineProperty(Movie.prototype, "recording", {
|
|
2826
3358
|
/**
|
|
2827
|
-
*
|
|
3359
|
+
* `true` if the movie is recording
|
|
2828
3360
|
*/
|
|
2829
3361
|
get: function () {
|
|
2830
|
-
return
|
|
3362
|
+
return this._recording;
|
|
2831
3363
|
},
|
|
2832
3364
|
enumerable: false,
|
|
2833
3365
|
configurable: true
|
|
2834
3366
|
});
|
|
2835
3367
|
Object.defineProperty(Movie.prototype, "duration", {
|
|
2836
3368
|
/**
|
|
2837
|
-
* The
|
|
3369
|
+
* The duration of the movie in seconds
|
|
3370
|
+
*
|
|
3371
|
+
* Calculated from the end time of the last layer
|
|
2838
3372
|
*/
|
|
2839
3373
|
// TODO: dirty flag?
|
|
2840
3374
|
get: function () {
|
|
@@ -2844,16 +3378,16 @@ var etro = (function () {
|
|
|
2844
3378
|
configurable: true
|
|
2845
3379
|
});
|
|
2846
3380
|
/**
|
|
2847
|
-
*
|
|
3381
|
+
* Convenience method for `layers.push()`
|
|
2848
3382
|
* @param layer
|
|
2849
|
-
* @return
|
|
3383
|
+
* @return The movie
|
|
2850
3384
|
*/
|
|
2851
3385
|
Movie.prototype.addLayer = function (layer) {
|
|
2852
3386
|
this.layers.push(layer);
|
|
2853
3387
|
return this;
|
|
2854
3388
|
};
|
|
2855
3389
|
/**
|
|
2856
|
-
*
|
|
3390
|
+
* Convenience method for `effects.push()`
|
|
2857
3391
|
* @param effect
|
|
2858
3392
|
* @return the movie
|
|
2859
3393
|
*/
|
|
@@ -2863,6 +3397,7 @@ var etro = (function () {
|
|
|
2863
3397
|
};
|
|
2864
3398
|
Object.defineProperty(Movie.prototype, "paused", {
|
|
2865
3399
|
/**
|
|
3400
|
+
* `true` if the movie is paused
|
|
2866
3401
|
*/
|
|
2867
3402
|
get: function () {
|
|
2868
3403
|
return this._paused;
|
|
@@ -2872,7 +3407,7 @@ var etro = (function () {
|
|
|
2872
3407
|
});
|
|
2873
3408
|
Object.defineProperty(Movie.prototype, "ended", {
|
|
2874
3409
|
/**
|
|
2875
|
-
*
|
|
3410
|
+
* `true` if the playback position is at the end of the movie
|
|
2876
3411
|
*/
|
|
2877
3412
|
get: function () {
|
|
2878
3413
|
return this._ended;
|
|
@@ -2880,50 +3415,89 @@ var etro = (function () {
|
|
|
2880
3415
|
enumerable: false,
|
|
2881
3416
|
configurable: true
|
|
2882
3417
|
});
|
|
3418
|
+
/**
|
|
3419
|
+
* Skips to the provided playback position, updating {@link currentTime}.
|
|
3420
|
+
*
|
|
3421
|
+
* @param time - The new playback position (in seconds)
|
|
3422
|
+
*/
|
|
3423
|
+
Movie.prototype.seek = function (time) {
|
|
3424
|
+
this._currentTime = time;
|
|
3425
|
+
// Call `seek` on every layer
|
|
3426
|
+
for (var i = 0; i < this.layers.length; i++) {
|
|
3427
|
+
var layer = this.layers[i];
|
|
3428
|
+
if (layer) {
|
|
3429
|
+
var relativeTime = time - layer.startTime;
|
|
3430
|
+
if (relativeTime >= 0 && relativeTime <= layer.duration) {
|
|
3431
|
+
layer.seek(relativeTime);
|
|
3432
|
+
}
|
|
3433
|
+
else {
|
|
3434
|
+
layer.seek(undefined);
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
// For backwards compatibility, publish a `seek` event
|
|
3439
|
+
publish(this, 'movie.seek', {});
|
|
3440
|
+
};
|
|
2883
3441
|
Object.defineProperty(Movie.prototype, "currentTime", {
|
|
2884
3442
|
/**
|
|
2885
|
-
* The current playback position
|
|
3443
|
+
* The current playback position in seconds
|
|
2886
3444
|
*/
|
|
2887
3445
|
get: function () {
|
|
2888
3446
|
return this._currentTime;
|
|
2889
3447
|
},
|
|
3448
|
+
/**
|
|
3449
|
+
* Skips to the provided playback position, updating {@link currentTime}.
|
|
3450
|
+
*
|
|
3451
|
+
* @param time - The new playback position (in seconds)
|
|
3452
|
+
*
|
|
3453
|
+
* @deprecated Use `seek` instead
|
|
3454
|
+
*/
|
|
2890
3455
|
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();
|
|
3456
|
+
this.seek(time);
|
|
2896
3457
|
},
|
|
2897
3458
|
enumerable: false,
|
|
2898
3459
|
configurable: true
|
|
2899
3460
|
});
|
|
2900
3461
|
/**
|
|
2901
|
-
*
|
|
2902
|
-
* `set currentTime`.
|
|
3462
|
+
* Skips to the provided playback position, updating {@link currentTime}.
|
|
2903
3463
|
*
|
|
2904
|
-
* @param time -
|
|
2905
|
-
* @param [refresh=true] -
|
|
2906
|
-
* @return resolves when the current frame is rendered if
|
|
2907
|
-
*
|
|
3464
|
+
* @param time - The new time (in seconds)
|
|
3465
|
+
* @param [refresh=true] - Render a single frame?
|
|
3466
|
+
* @return Promise that resolves when the current frame is rendered if
|
|
3467
|
+
* `refresh` is true; otherwise resolves immediately.
|
|
2908
3468
|
*
|
|
3469
|
+
* @deprecated Call {@link seek} and {@link refresh} separately
|
|
2909
3470
|
*/
|
|
2910
|
-
// TODO: Refresh if
|
|
3471
|
+
// TODO: Refresh only if auto-refreshing is enabled
|
|
2911
3472
|
Movie.prototype.setCurrentTime = function (time, refresh) {
|
|
2912
3473
|
var _this = this;
|
|
2913
3474
|
if (refresh === void 0) { refresh = true; }
|
|
2914
3475
|
return new Promise(function (resolve, reject) {
|
|
2915
|
-
_this.
|
|
2916
|
-
|
|
2917
|
-
if (refresh)
|
|
3476
|
+
_this.seek(time);
|
|
3477
|
+
if (refresh) {
|
|
2918
3478
|
// Pass promise callbacks to `refresh`
|
|
2919
3479
|
_this.refresh().then(resolve).catch(reject);
|
|
2920
|
-
|
|
3480
|
+
}
|
|
3481
|
+
else {
|
|
2921
3482
|
resolve();
|
|
3483
|
+
}
|
|
2922
3484
|
});
|
|
2923
3485
|
};
|
|
3486
|
+
Object.defineProperty(Movie.prototype, "ready", {
|
|
3487
|
+
/**
|
|
3488
|
+
* `true` if the movie is ready for playback
|
|
3489
|
+
*/
|
|
3490
|
+
get: function () {
|
|
3491
|
+
var layersReady = this.layers.every(function (layer) { return layer.ready; });
|
|
3492
|
+
var effectsReady = this.effects.every(function (effect) { return effect.ready; });
|
|
3493
|
+
return layersReady && effectsReady;
|
|
3494
|
+
},
|
|
3495
|
+
enumerable: false,
|
|
3496
|
+
configurable: true
|
|
3497
|
+
});
|
|
2924
3498
|
Object.defineProperty(Movie.prototype, "canvas", {
|
|
2925
3499
|
/**
|
|
2926
|
-
* The
|
|
3500
|
+
* The HTML canvas element used for rendering
|
|
2927
3501
|
*/
|
|
2928
3502
|
get: function () {
|
|
2929
3503
|
return this._canvas;
|
|
@@ -2933,7 +3507,7 @@ var etro = (function () {
|
|
|
2933
3507
|
});
|
|
2934
3508
|
Object.defineProperty(Movie.prototype, "cctx", {
|
|
2935
3509
|
/**
|
|
2936
|
-
* The
|
|
3510
|
+
* The canvas context used for rendering
|
|
2937
3511
|
*/
|
|
2938
3512
|
get: function () {
|
|
2939
3513
|
return this._cctx;
|
|
@@ -2943,7 +3517,7 @@ var etro = (function () {
|
|
|
2943
3517
|
});
|
|
2944
3518
|
Object.defineProperty(Movie.prototype, "width", {
|
|
2945
3519
|
/**
|
|
2946
|
-
* The width of the
|
|
3520
|
+
* The width of the output canvas
|
|
2947
3521
|
*/
|
|
2948
3522
|
get: function () {
|
|
2949
3523
|
return this.canvas.width;
|
|
@@ -2956,7 +3530,7 @@ var etro = (function () {
|
|
|
2956
3530
|
});
|
|
2957
3531
|
Object.defineProperty(Movie.prototype, "height", {
|
|
2958
3532
|
/**
|
|
2959
|
-
* The height of the
|
|
3533
|
+
* The height of the output canvas
|
|
2960
3534
|
*/
|
|
2961
3535
|
get: function () {
|
|
2962
3536
|
return this.canvas.height;
|
|
@@ -2968,12 +3542,18 @@ var etro = (function () {
|
|
|
2968
3542
|
configurable: true
|
|
2969
3543
|
});
|
|
2970
3544
|
Object.defineProperty(Movie.prototype, "movie", {
|
|
3545
|
+
/**
|
|
3546
|
+
* @return The movie
|
|
3547
|
+
*/
|
|
2971
3548
|
get: function () {
|
|
2972
3549
|
return this;
|
|
2973
3550
|
},
|
|
2974
3551
|
enumerable: false,
|
|
2975
3552
|
configurable: true
|
|
2976
3553
|
});
|
|
3554
|
+
/**
|
|
3555
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
3556
|
+
*/
|
|
2977
3557
|
Movie.prototype.getDefaultOptions = function () {
|
|
2978
3558
|
return {
|
|
2979
3559
|
canvas: undefined,
|
|
@@ -2985,21 +3565,23 @@ var etro = (function () {
|
|
|
2985
3565
|
/**
|
|
2986
3566
|
* @name module:movie#repeat
|
|
2987
3567
|
*/
|
|
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
|
|
3568
|
+
repeat: false
|
|
2994
3569
|
};
|
|
2995
3570
|
};
|
|
2996
3571
|
return Movie;
|
|
2997
3572
|
}());
|
|
2998
|
-
//
|
|
3573
|
+
// Id for events
|
|
2999
3574
|
Movie.prototype.type = 'movie';
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3575
|
+
Movie.prototype.propertyFilters = {};
|
|
3576
|
+
deprecate('movie.audiodestinationupdate', 'audiodestinationupdate');
|
|
3577
|
+
deprecate('movie.ended', undefined);
|
|
3578
|
+
deprecate('movie.loadeddata', undefined);
|
|
3579
|
+
deprecate('movie.pause', undefined, 'Wait for `play()`, `stream()`, or `record()` to resolve instead.');
|
|
3580
|
+
deprecate('movie.play', undefined, 'Provide an `onStart` callback to `play()`, `stream()`, or `record()` instead.');
|
|
3581
|
+
deprecate('movie.record', undefined, 'Provide an `onStart` callback to `record()` instead.');
|
|
3582
|
+
deprecate('movie.recordended', undefined, 'Wait for `record()` to resolve instead.');
|
|
3583
|
+
deprecate('movie.seek', undefined, 'Override the `seek` method on layers instead.');
|
|
3584
|
+
deprecate('movie.timeupdate', undefined, 'Override the `progress` method on layers instead.');
|
|
3003
3585
|
|
|
3004
3586
|
/*
|
|
3005
3587
|
* Typedoc can't handle default exports. To let users import default export and
|
|
@@ -3025,8 +3607,7 @@ var etro = (function () {
|
|
|
3025
3607
|
parseColor: parseColor,
|
|
3026
3608
|
Font: Font,
|
|
3027
3609
|
parseFont: parseFont,
|
|
3028
|
-
mapPixels: mapPixels
|
|
3029
|
-
watchPublic: watchPublic
|
|
3610
|
+
mapPixels: mapPixels
|
|
3030
3611
|
});
|
|
3031
3612
|
|
|
3032
3613
|
/**
|