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