@checksub_team/peaks_timeline 2.2.1 → 2.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/peaks.js +780 -175
- package/src/components/axis.js +24 -13
- package/src/components/invoker.js +218 -2
- package/src/components/line-group.js +6 -6
- package/src/components/line-groups.js +8 -3
- package/src/components/line-indicator.js +11 -2
- package/src/components/mode-layer.js +50 -7
- package/src/components/playhead-layer.js +13 -3
- package/src/components/source-group.js +303 -65
- package/src/components/sources-layer.js +305 -42
- package/src/components/waveform-builder.js +27 -9
- package/src/utils.js +90 -0
- package/src/view.js +152 -44
package/src/components/axis.js
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
define([
|
|
10
10
|
'../utils',
|
|
11
|
+
'./invoker',
|
|
11
12
|
'konva'
|
|
12
|
-
], function(Utils, Konva) {
|
|
13
|
+
], function(Utils, Invoker, Konva) {
|
|
13
14
|
'use strict';
|
|
14
15
|
|
|
15
16
|
var LEFT_PADDING = 4;
|
|
@@ -29,52 +30,62 @@ define([
|
|
|
29
30
|
function Axis(peaks, view, options) {
|
|
30
31
|
this._view = view;
|
|
31
32
|
|
|
33
|
+
this._invoker = new Invoker();
|
|
34
|
+
|
|
32
35
|
var self = this;
|
|
33
36
|
|
|
34
37
|
peaks.on('playhead.moved', this._onPlayheadMoved.bind(this));
|
|
35
38
|
peaks.on('playhead.hidden', this._onPlayheadHidden.bind(this));
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
this._axisGridlineColor = options.axisGridlineColor;
|
|
41
|
+
this._axisLabelColor = options.axisLabelColor;
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
this._backLayer = new Konva.Layer({
|
|
41
44
|
listening: false
|
|
42
45
|
});
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
this._frontLayer = new Konva.Layer({
|
|
45
48
|
listening: false
|
|
46
49
|
});
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
this._axisShape = new Konva.Shape({
|
|
49
52
|
sceneFunc: function(context) {
|
|
50
53
|
self.drawAxis(context, view);
|
|
51
54
|
}
|
|
52
55
|
});
|
|
53
|
-
|
|
56
|
+
this._backLayer.add(this._axisShape);
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
this._timesShape = new Konva.Shape({
|
|
56
59
|
sceneFunc: function(context) {
|
|
57
60
|
self.drawTimes(context, view);
|
|
58
61
|
}
|
|
59
62
|
});
|
|
60
|
-
|
|
63
|
+
this._frontLayer.add(this._timesShape);
|
|
64
|
+
|
|
65
|
+
// Throttled draws to prevent excessive redraws.
|
|
66
|
+
this._throttledBackDraw = this._invoker.throttleTrailing(
|
|
67
|
+
this._backLayer.batchDraw.bind(this._backLayer)
|
|
68
|
+
);
|
|
69
|
+
this._throttledFrontDraw = this._invoker.throttleTrailing(
|
|
70
|
+
this._frontLayer.batchDraw.bind(this._frontLayer)
|
|
71
|
+
);
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
Axis.prototype._onPlayheadMoved = function(playheadX, playheadWidth) {
|
|
64
75
|
this._maskStart = playheadX + this._view.getFrameOffset();
|
|
65
76
|
this._maskEnd = playheadX + this._view.getFrameOffset() + playheadWidth;
|
|
66
|
-
this.
|
|
77
|
+
this._throttledFrontDraw();
|
|
67
78
|
};
|
|
68
79
|
|
|
69
80
|
Axis.prototype._onPlayheadHidden = function() {
|
|
70
81
|
this._maskStart = null;
|
|
71
82
|
this._maskEnd = null;
|
|
72
|
-
this.
|
|
83
|
+
this._throttledFrontDraw();
|
|
73
84
|
};
|
|
74
85
|
|
|
75
86
|
Axis.prototype.batchDraw = function() {
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
87
|
+
this._throttledBackDraw();
|
|
88
|
+
this._throttledFrontDraw();
|
|
78
89
|
};
|
|
79
90
|
|
|
80
91
|
Axis.prototype.addBackToStage = function(stage) {
|
|
@@ -6,12 +6,22 @@
|
|
|
6
6
|
* @module invoker
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
define([
|
|
10
10
|
], function() {
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
+
// Use requestAnimationFrame for smoother animations when available
|
|
14
|
+
var raf = (typeof window !== 'undefined' && window.requestAnimationFrame) ||
|
|
15
|
+
function(fn) {
|
|
16
|
+
return setTimeout(fn, 16);
|
|
17
|
+
};
|
|
18
|
+
var caf = (typeof window !== 'undefined' && window.cancelAnimationFrame) ||
|
|
19
|
+
function(id) {
|
|
20
|
+
clearTimeout(id);
|
|
21
|
+
};
|
|
22
|
+
|
|
13
23
|
/**
|
|
14
|
-
* An invoker class for throttling.
|
|
24
|
+
* An invoker class for throttling and performance optimization.
|
|
15
25
|
*
|
|
16
26
|
* @class
|
|
17
27
|
* @alias Invoker
|
|
@@ -19,8 +29,14 @@
|
|
|
19
29
|
|
|
20
30
|
function Invoker() {
|
|
21
31
|
this._throttledFunc = {};
|
|
32
|
+
this._rafThrottled = {};
|
|
33
|
+
this._frameCallbacks = [];
|
|
34
|
+
this._frameScheduled = false;
|
|
22
35
|
}
|
|
23
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Throttle using setInterval - good for consistent timing
|
|
39
|
+
*/
|
|
24
40
|
Invoker.prototype.throttle = function(id, func, wait) {
|
|
25
41
|
var self = this;
|
|
26
42
|
|
|
@@ -50,6 +66,78 @@
|
|
|
50
66
|
}
|
|
51
67
|
};
|
|
52
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Throttle using requestAnimationFrame - ideal for visual updates
|
|
71
|
+
* Ensures function runs at most once per animation frame
|
|
72
|
+
*/
|
|
73
|
+
Invoker.prototype.throttleRAF = function(id, func) {
|
|
74
|
+
var self = this;
|
|
75
|
+
|
|
76
|
+
if (this._rafThrottled[id]) {
|
|
77
|
+
// Update the function but don't schedule again
|
|
78
|
+
this._rafThrottled[id].func = func;
|
|
79
|
+
this._rafThrottled[id].pending = true;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this._rafThrottled[id] = {
|
|
84
|
+
func: func,
|
|
85
|
+
pending: true,
|
|
86
|
+
rafId: null
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function frame() {
|
|
90
|
+
if (self._rafThrottled[id] && self._rafThrottled[id].pending) {
|
|
91
|
+
self._rafThrottled[id].pending = false;
|
|
92
|
+
self._rafThrottled[id].func();
|
|
93
|
+
self._rafThrottled[id].rafId = raf(frame);
|
|
94
|
+
}
|
|
95
|
+
else if (self._rafThrottled[id]) {
|
|
96
|
+
delete self._rafThrottled[id];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this._rafThrottled[id].rafId = raf(frame);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Cancel a RAF-throttled function
|
|
105
|
+
*/
|
|
106
|
+
Invoker.prototype.cancelThrottleRAF = function(id) {
|
|
107
|
+
if (this._rafThrottled[id]) {
|
|
108
|
+
if (this._rafThrottled[id].rafId) {
|
|
109
|
+
caf(this._rafThrottled[id].rafId);
|
|
110
|
+
}
|
|
111
|
+
delete this._rafThrottled[id];
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Schedule a callback for the next animation frame
|
|
117
|
+
* Multiple callbacks are batched together
|
|
118
|
+
*/
|
|
119
|
+
Invoker.prototype.scheduleFrame = function(callback) {
|
|
120
|
+
var self = this;
|
|
121
|
+
|
|
122
|
+
this._frameCallbacks.push(callback);
|
|
123
|
+
|
|
124
|
+
if (!this._frameScheduled) {
|
|
125
|
+
this._frameScheduled = true;
|
|
126
|
+
raf(function() {
|
|
127
|
+
self._frameScheduled = false;
|
|
128
|
+
var callbacks = self._frameCallbacks;
|
|
129
|
+
|
|
130
|
+
self._frameCallbacks = [];
|
|
131
|
+
for (var i = 0; i < callbacks.length; i++) {
|
|
132
|
+
callbacks[i]();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Creates a debounced function
|
|
140
|
+
*/
|
|
53
141
|
Invoker.prototype.debounce = function(func, wait, immediate) {
|
|
54
142
|
var timeout;
|
|
55
143
|
|
|
@@ -77,5 +165,133 @@
|
|
|
77
165
|
};
|
|
78
166
|
};
|
|
79
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Creates a leading-edge throttled function using RAF
|
|
170
|
+
* Executes immediately, then throttles subsequent calls
|
|
171
|
+
*/
|
|
172
|
+
Invoker.prototype.throttleLeading = function(func) {
|
|
173
|
+
var scheduled = false;
|
|
174
|
+
var savedArgs = null;
|
|
175
|
+
var savedThis = null;
|
|
176
|
+
|
|
177
|
+
return function throttled() {
|
|
178
|
+
if (scheduled) {
|
|
179
|
+
savedArgs = arguments;
|
|
180
|
+
// eslint-disable-next-line consistent-this
|
|
181
|
+
savedThis = this;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
func.apply(this, arguments);
|
|
186
|
+
scheduled = true;
|
|
187
|
+
|
|
188
|
+
raf(function() {
|
|
189
|
+
scheduled = false;
|
|
190
|
+
if (savedArgs) {
|
|
191
|
+
func.apply(savedThis, savedArgs);
|
|
192
|
+
savedArgs = null;
|
|
193
|
+
savedThis = null;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Creates a trailing-edge throttled function using RAF
|
|
201
|
+
* Collects calls and executes only the last one per frame
|
|
202
|
+
*/
|
|
203
|
+
Invoker.prototype.throttleTrailing = function(func) {
|
|
204
|
+
var rafId = null;
|
|
205
|
+
var savedArgs = null;
|
|
206
|
+
var savedThis = null;
|
|
207
|
+
|
|
208
|
+
return function throttled() {
|
|
209
|
+
savedArgs = arguments;
|
|
210
|
+
// eslint-disable-next-line consistent-this
|
|
211
|
+
savedThis = this;
|
|
212
|
+
|
|
213
|
+
if (rafId === null) {
|
|
214
|
+
rafId = raf(function() {
|
|
215
|
+
rafId = null;
|
|
216
|
+
func.apply(savedThis, savedArgs);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Batch multiple function calls into a single execution
|
|
224
|
+
* Useful for accumulating updates before rendering
|
|
225
|
+
*/
|
|
226
|
+
Invoker.prototype.createBatcher = function(processFn, options) {
|
|
227
|
+
options = options || {};
|
|
228
|
+
var maxWait = options.maxWait || 100;
|
|
229
|
+
var items = [];
|
|
230
|
+
var timeout = null;
|
|
231
|
+
var lastFlush = 0;
|
|
232
|
+
|
|
233
|
+
function flush() {
|
|
234
|
+
if (items.length === 0) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
var batch = items;
|
|
238
|
+
|
|
239
|
+
items = [];
|
|
240
|
+
timeout = null;
|
|
241
|
+
lastFlush = Date.now();
|
|
242
|
+
processFn(batch);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
add: function(item) {
|
|
247
|
+
items.push(item);
|
|
248
|
+
|
|
249
|
+
if (timeout === null) {
|
|
250
|
+
var timeSinceLastFlush = Date.now() - lastFlush;
|
|
251
|
+
|
|
252
|
+
if (timeSinceLastFlush >= maxWait) {
|
|
253
|
+
raf(flush);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
timeout = setTimeout(flush, Math.min(16, maxWait - timeSinceLastFlush));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
flush: flush,
|
|
261
|
+
size: function() {
|
|
262
|
+
return items.length;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Destroy all pending operations
|
|
269
|
+
*/
|
|
270
|
+
Invoker.prototype.destroy = function() {
|
|
271
|
+
var id;
|
|
272
|
+
|
|
273
|
+
// Clear interval-based throttles
|
|
274
|
+
for (id in this._throttledFunc) {
|
|
275
|
+
if (Object.prototype.hasOwnProperty.call(this._throttledFunc, id)) {
|
|
276
|
+
clearInterval(this._throttledFunc[id].timer);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
this._throttledFunc = {};
|
|
280
|
+
|
|
281
|
+
// Clear RAF-based throttles
|
|
282
|
+
for (id in this._rafThrottled) {
|
|
283
|
+
if (Object.prototype.hasOwnProperty.call(this._rafThrottled, id)) {
|
|
284
|
+
if (this._rafThrottled[id].rafId) {
|
|
285
|
+
caf(this._rafThrottled[id].rafId);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
this._rafThrottled = {};
|
|
290
|
+
|
|
291
|
+
// Clear frame callbacks
|
|
292
|
+
this._frameCallbacks = [];
|
|
293
|
+
this._frameScheduled = false;
|
|
294
|
+
};
|
|
295
|
+
|
|
80
296
|
return Invoker;
|
|
81
297
|
});
|
|
@@ -195,8 +195,12 @@ define([
|
|
|
195
195
|
LineGroup.prototype.addSource = function(source, sourceGroup, sourcesAround) {
|
|
196
196
|
if (sourceGroup) {
|
|
197
197
|
this._sourcesGroup[source.id] = sourceGroup;
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
// Only move to this group if not currently being dragged
|
|
199
|
+
// (during drag, source stays in drag layer for z-order)
|
|
200
|
+
if (!sourceGroup.isActive()) {
|
|
201
|
+
if (!sourceGroup.getParent() || !sourceGroup.isDescendantOf(this._group)) {
|
|
202
|
+
sourceGroup.moveTo(this._group);
|
|
203
|
+
}
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
|
|
@@ -342,10 +346,6 @@ define([
|
|
|
342
346
|
|
|
343
347
|
delete this._sourcesGroup[source.id];
|
|
344
348
|
|
|
345
|
-
if (sourceGroup) {
|
|
346
|
-
sourceGroup.hideButKeepFocus();
|
|
347
|
-
}
|
|
348
|
-
|
|
349
349
|
return sourceGroup;
|
|
350
350
|
};
|
|
351
351
|
|
|
@@ -30,6 +30,7 @@ define([
|
|
|
30
30
|
|
|
31
31
|
this._automaticallyCreatedLineId = null;
|
|
32
32
|
this._automaticLineCreationPosition = null;
|
|
33
|
+
this._automaticLineCreationMouseY = null;
|
|
33
34
|
this._automaticLineCreationTimeout = null;
|
|
34
35
|
|
|
35
36
|
this._segmentsGroups = {};
|
|
@@ -141,7 +142,6 @@ define([
|
|
|
141
142
|
|
|
142
143
|
LineGroups.prototype._onLineHeightChanged = function() {
|
|
143
144
|
this.refreshLineYs();
|
|
144
|
-
this._view.updateTimeline();
|
|
145
145
|
};
|
|
146
146
|
|
|
147
147
|
LineGroups.prototype._onSourcesWrappingChanged = function(sources) {
|
|
@@ -282,10 +282,11 @@ define([
|
|
|
282
282
|
}
|
|
283
283
|
this._automaticLineCreationTimeout = null;
|
|
284
284
|
this._automaticLineCreationPosition = null;
|
|
285
|
+
this._automaticLineCreationMouseY = null;
|
|
285
286
|
this._automaticallyCreatedLineId = null;
|
|
286
287
|
};
|
|
287
288
|
|
|
288
|
-
LineGroups.prototype.manageAutomaticLineCreation = function(newLinePosition, initialPosition, sources) {
|
|
289
|
+
LineGroups.prototype.manageAutomaticLineCreation = function(newLinePosition, initialPosition, sources, mouseY) {
|
|
289
290
|
if (!Utils.isNullOrUndefined(this._automaticallyCreatedLineId)) {
|
|
290
291
|
return;
|
|
291
292
|
}
|
|
@@ -301,6 +302,7 @@ define([
|
|
|
301
302
|
}
|
|
302
303
|
|
|
303
304
|
this._automaticLineCreationPosition = newLinePosition;
|
|
305
|
+
this._automaticLineCreationMouseY = mouseY;
|
|
304
306
|
this._automaticLineCreationTimeout = setTimeout(function() {
|
|
305
307
|
this._automaticLineCreationTimeout = null;
|
|
306
308
|
|
|
@@ -344,6 +346,9 @@ define([
|
|
|
344
346
|
|
|
345
347
|
this._automaticallyCreatedLineId = automaticallyCreatedLineGroup.getId();
|
|
346
348
|
this._moveSourcesToPositionIfPossible(sources, newLinePosition);
|
|
349
|
+
|
|
350
|
+
// Notify that sources have been moved to a new line (for ghost preview updates)
|
|
351
|
+
this._peaks.emit('sources.delayedLineChange', sources);
|
|
347
352
|
}.bind(this), this._peaks.options.automaticLineCreationDelay);
|
|
348
353
|
};
|
|
349
354
|
|
|
@@ -361,7 +366,7 @@ define([
|
|
|
361
366
|
}
|
|
362
367
|
|
|
363
368
|
if (linePos[0] !== linePos[1]) {
|
|
364
|
-
this.manageAutomaticLineCreation(linePos[0] + 1, position, sources);
|
|
369
|
+
this.manageAutomaticLineCreation(linePos[0] + 1, position, sources, mouseY);
|
|
365
370
|
}
|
|
366
371
|
else {
|
|
367
372
|
this.stopAutomaticLineCreation();
|
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
define([
|
|
10
10
|
'konva',
|
|
11
11
|
'./svgs',
|
|
12
|
+
'./invoker',
|
|
12
13
|
'../utils'
|
|
13
14
|
], function(
|
|
14
15
|
Konva,
|
|
15
16
|
SVGs,
|
|
17
|
+
Invoker,
|
|
16
18
|
Utils) {
|
|
17
19
|
'use strict';
|
|
18
20
|
|
|
@@ -31,6 +33,8 @@ define([
|
|
|
31
33
|
this._view = view;
|
|
32
34
|
this._container = container;
|
|
33
35
|
|
|
36
|
+
this._invoker = new Invoker();
|
|
37
|
+
|
|
34
38
|
this._width = this._peaks.options.lineIndicatorWidth;
|
|
35
39
|
this._height = this._view.getHeight();
|
|
36
40
|
|
|
@@ -58,6 +62,11 @@ define([
|
|
|
58
62
|
this._layer = new Konva.Layer();
|
|
59
63
|
this._stage.add(this._layer);
|
|
60
64
|
|
|
65
|
+
// Throttled draws to coalesce multiple updates per frame.
|
|
66
|
+
this._throttledBatchDraw = this._invoker.throttleTrailing(
|
|
67
|
+
this._layer.batchDraw.bind(this._layer)
|
|
68
|
+
);
|
|
69
|
+
|
|
61
70
|
this._indicators = {};
|
|
62
71
|
|
|
63
72
|
this._separatingLine = new Konva.Line({
|
|
@@ -69,7 +78,7 @@ define([
|
|
|
69
78
|
|
|
70
79
|
this._layer.add(this._separatingLine);
|
|
71
80
|
|
|
72
|
-
this.
|
|
81
|
+
this.batchDraw();
|
|
73
82
|
|
|
74
83
|
this._isDragging = false;
|
|
75
84
|
this._dragLineId = null;
|
|
@@ -423,7 +432,7 @@ define([
|
|
|
423
432
|
};
|
|
424
433
|
|
|
425
434
|
LineIndicator.prototype.batchDraw = function() {
|
|
426
|
-
this.
|
|
435
|
+
this._throttledBatchDraw();
|
|
427
436
|
};
|
|
428
437
|
|
|
429
438
|
LineIndicator.prototype._onWindowMove = function(event) {
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
define([
|
|
10
10
|
'./source-group',
|
|
11
|
+
'./invoker',
|
|
11
12
|
'../models/source',
|
|
12
13
|
'../utils',
|
|
13
14
|
'konva'
|
|
14
|
-
], function(SourceGroup, Source, Utils, Konva) {
|
|
15
|
+
], function(SourceGroup, Invoker, Source, Utils, Konva) {
|
|
15
16
|
'use strict';
|
|
16
17
|
|
|
17
18
|
var TIME_X_OFFSET = 20;
|
|
@@ -35,6 +36,8 @@ define([
|
|
|
35
36
|
this._playheadLayer = playheadLayer;
|
|
36
37
|
this._stage = stage;
|
|
37
38
|
|
|
39
|
+
this._invoker = new Invoker();
|
|
40
|
+
|
|
38
41
|
this._selectedElements = {};
|
|
39
42
|
this._currentLine = null;
|
|
40
43
|
|
|
@@ -42,6 +45,11 @@ define([
|
|
|
42
45
|
listening: this._mode !== 'default'
|
|
43
46
|
});
|
|
44
47
|
|
|
48
|
+
// Throttled draws to coalesce multiple updates per frame.
|
|
49
|
+
this._throttledBatchDraw = this._invoker.throttleTrailing(
|
|
50
|
+
this._layer.batchDraw.bind(this._layer)
|
|
51
|
+
);
|
|
52
|
+
|
|
45
53
|
this._prepareDefaultMode();
|
|
46
54
|
|
|
47
55
|
this._onMouseClickInDefaultMode = this._onMouseClickInDefaultMode.bind(this);
|
|
@@ -59,6 +67,10 @@ define([
|
|
|
59
67
|
this._peaks.on('handler.sources.destroy', this._onSourcesDestroy.bind(this));
|
|
60
68
|
}
|
|
61
69
|
|
|
70
|
+
ModeLayer.prototype.batchDraw = function() {
|
|
71
|
+
this._throttledBatchDraw();
|
|
72
|
+
};
|
|
73
|
+
|
|
62
74
|
ModeLayer.prototype._onSourcesDestroy = function(sources) {
|
|
63
75
|
const selectedElementsToDeselect = {};
|
|
64
76
|
|
|
@@ -262,7 +274,7 @@ define([
|
|
|
262
274
|
ModeLayer.prototype._onMouseEnterInCutMode = function() {
|
|
263
275
|
this._cutGroup.visible(true);
|
|
264
276
|
|
|
265
|
-
this.
|
|
277
|
+
this.batchDraw();
|
|
266
278
|
};
|
|
267
279
|
|
|
268
280
|
ModeLayer.prototype._updateCursorTime = function(position) {
|
|
@@ -363,13 +375,13 @@ define([
|
|
|
363
375
|
this._updateCursorTime(cuttingPosition);
|
|
364
376
|
this._updateCuttingLine(cuttingPosition);
|
|
365
377
|
|
|
366
|
-
this.
|
|
378
|
+
this.batchDraw();
|
|
367
379
|
};
|
|
368
380
|
|
|
369
381
|
ModeLayer.prototype._onMouseLeaveInCutMode = function() {
|
|
370
382
|
this._cutGroup.visible(false);
|
|
371
383
|
|
|
372
|
-
this.
|
|
384
|
+
this.batchDraw();
|
|
373
385
|
};
|
|
374
386
|
|
|
375
387
|
ModeLayer.prototype._onMouseClickInCutMode = function() {
|
|
@@ -414,18 +426,49 @@ define([
|
|
|
414
426
|
|
|
415
427
|
this._cuttingLine.visible(false);
|
|
416
428
|
|
|
429
|
+
// Avoid keeping a reference to a SourceGroup that will be destroyed
|
|
430
|
+
// as part of the cut update.
|
|
431
|
+
this._view.setHoveredElement(null);
|
|
432
|
+
|
|
417
433
|
this._peaks.cutSource(hoveredElement.getSource(), this._view.pixelsToTime(cuttingPixel));
|
|
418
434
|
|
|
419
|
-
|
|
435
|
+
// Cutting replaces Konva nodes under the cursor; Konva won't always emit
|
|
436
|
+
// enter/leave for the new nodes until the mouse moves. Re-sync hover now
|
|
437
|
+
// so subsequent leave events behave correctly.
|
|
438
|
+
this._syncHoveredElementFromPointer();
|
|
420
439
|
|
|
421
440
|
this._updateCursorTime(cuttingPosition);
|
|
422
441
|
this._updateCuttingLine(cuttingPosition);
|
|
423
442
|
|
|
424
|
-
this.
|
|
443
|
+
this.batchDraw();
|
|
425
444
|
}
|
|
426
445
|
}
|
|
427
446
|
};
|
|
428
447
|
|
|
448
|
+
ModeLayer.prototype._syncHoveredElementFromPointer = function() {
|
|
449
|
+
var pointerPos = this._stage.getPointerPosition();
|
|
450
|
+
|
|
451
|
+
if (!pointerPos) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
var node = this._stage.getIntersection(pointerPos);
|
|
456
|
+
|
|
457
|
+
while (node) {
|
|
458
|
+
if (node.attrs && node.attrs.sourceId) {
|
|
459
|
+
var sourceGroup = this._view.getSourceGroupById(node.attrs.sourceId);
|
|
460
|
+
|
|
461
|
+
if (sourceGroup && !sourceGroup.isHovered()) {
|
|
462
|
+
sourceGroup.startHover();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
node = node.getParent ? node.getParent() : null;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
429
472
|
ModeLayer.prototype.setMode = function(mode) {
|
|
430
473
|
if (this._mode === mode) {
|
|
431
474
|
return;
|
|
@@ -498,7 +541,7 @@ define([
|
|
|
498
541
|
}
|
|
499
542
|
|
|
500
543
|
this._mode = mode;
|
|
501
|
-
this.
|
|
544
|
+
this.batchDraw();
|
|
502
545
|
this._view.batchDrawSourcesLayer();
|
|
503
546
|
};
|
|
504
547
|
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
define([
|
|
10
10
|
'../utils',
|
|
11
|
+
'./invoker',
|
|
11
12
|
'konva'
|
|
12
|
-
], function(Utils, Konva) {
|
|
13
|
+
], function(Utils, Invoker, Konva) {
|
|
13
14
|
'use strict';
|
|
14
15
|
|
|
15
16
|
var HANDLE_RADIUS = 10;
|
|
@@ -38,6 +39,11 @@ define([
|
|
|
38
39
|
|
|
39
40
|
this._playheadLayer = new Konva.Layer();
|
|
40
41
|
|
|
42
|
+
this._invoker = new Invoker();
|
|
43
|
+
this._throttledBatchDraw = this._invoker.throttleTrailing(
|
|
44
|
+
this._playheadLayer.batchDraw.bind(this._playheadLayer)
|
|
45
|
+
);
|
|
46
|
+
|
|
41
47
|
this._activeSegments = {};
|
|
42
48
|
this._activeSources = {};
|
|
43
49
|
|
|
@@ -236,10 +242,14 @@ define([
|
|
|
236
242
|
|
|
237
243
|
this._time = time;
|
|
238
244
|
if (pixelHasChanged || timeHasChanged) {
|
|
239
|
-
this.
|
|
245
|
+
this.batchDraw();
|
|
240
246
|
}
|
|
241
247
|
};
|
|
242
248
|
|
|
249
|
+
PlayheadLayer.prototype.batchDraw = function() {
|
|
250
|
+
this._throttledBatchDraw();
|
|
251
|
+
};
|
|
252
|
+
|
|
243
253
|
/**
|
|
244
254
|
* Update cached pixel values and position of the playhead group.
|
|
245
255
|
* @private
|
|
@@ -375,7 +385,7 @@ define([
|
|
|
375
385
|
}
|
|
376
386
|
|
|
377
387
|
if (updated) {
|
|
378
|
-
this.
|
|
388
|
+
this.batchDraw();
|
|
379
389
|
}
|
|
380
390
|
};
|
|
381
391
|
|