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