@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
|
@@ -10,12 +10,14 @@ define([
|
|
|
10
10
|
'./waveform-builder',
|
|
11
11
|
'./waveform-shape',
|
|
12
12
|
'./loader',
|
|
13
|
+
'./invoker',
|
|
13
14
|
'../utils',
|
|
14
15
|
'konva'
|
|
15
16
|
], function(
|
|
16
17
|
WaveformBuilder,
|
|
17
18
|
WaveformShape,
|
|
18
19
|
Loader,
|
|
20
|
+
Invoker,
|
|
19
21
|
Utils,
|
|
20
22
|
Konva) {
|
|
21
23
|
'use strict';
|
|
@@ -24,7 +26,10 @@ define([
|
|
|
24
26
|
var SPACING_BETWEEN_PREVIEWS = 1.5;
|
|
25
27
|
var CORNER_RADIUS = 8;
|
|
26
28
|
var INDICATOR_RADIUS = 4; // px
|
|
27
|
-
var PREVIEW_CREATE_CHUNK =
|
|
29
|
+
var PREVIEW_CREATE_CHUNK = 12; // number of preview tiles to add per idle slice (increased for better throughput)
|
|
30
|
+
|
|
31
|
+
// Shared invoker for all source groups to coordinate updates
|
|
32
|
+
var sharedInvoker = new Invoker();
|
|
28
33
|
|
|
29
34
|
/**
|
|
30
35
|
* Creates a source group for the given source.
|
|
@@ -47,7 +52,6 @@ define([
|
|
|
47
52
|
|
|
48
53
|
var self = this;
|
|
49
54
|
|
|
50
|
-
this._x = this._view.timeToPixels(source.startTime);
|
|
51
55
|
this._width = this._view.timeToPixels(source.endTime - source.startTime);
|
|
52
56
|
var heights = SourceGroup.getHeights(source, peaks);
|
|
53
57
|
|
|
@@ -59,16 +63,25 @@ define([
|
|
|
59
63
|
this._selected = this._source.selected;
|
|
60
64
|
this._hovered = false;
|
|
61
65
|
this._isDragged = false;
|
|
66
|
+
this._isHandleDragging = false;
|
|
62
67
|
this._destroyed = false;
|
|
63
68
|
|
|
69
|
+
// Optional Konva element used as a drag "ghost" preview.
|
|
70
|
+
this._dragGhost = null;
|
|
71
|
+
|
|
72
|
+
// Performance: track if draw is needed
|
|
73
|
+
this._drawScheduled = false;
|
|
74
|
+
|
|
64
75
|
this._previewList = [];
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
// internal queue state for async preview creation
|
|
77
|
+
this._previewBuildQueue = new Set();
|
|
78
|
+
// Track pending idle callbacks for cleanup
|
|
79
|
+
this._pendingIdleCallbacks = new Set();
|
|
67
80
|
|
|
68
81
|
this._markersGroup = this._createMarkers();
|
|
69
82
|
|
|
70
83
|
this._group = new Konva.Group({
|
|
71
|
-
x: this.
|
|
84
|
+
x: this._view.timeToPixels(source.startTime),
|
|
72
85
|
sourceId: this._source.id,
|
|
73
86
|
draggable: this._source.draggable,
|
|
74
87
|
dragBoundFunc: function() {
|
|
@@ -135,15 +148,58 @@ define([
|
|
|
135
148
|
if (!this._source.loading) {
|
|
136
149
|
this._showButtons();
|
|
137
150
|
}
|
|
138
|
-
|
|
151
|
+
this._scheduleBatchDraw();
|
|
139
152
|
};
|
|
140
153
|
|
|
141
154
|
SourceGroup.prototype._onHoverEnd = function() {
|
|
142
155
|
this._hovered = false;
|
|
143
156
|
this._manualHover = false;
|
|
157
|
+
this._disableManualHoverTracking();
|
|
144
158
|
this._view.setHoveredElement(null);
|
|
145
159
|
this._hideButtons();
|
|
146
|
-
|
|
160
|
+
this._scheduleBatchDraw();
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
SourceGroup.prototype._enableManualHoverTracking = function() {
|
|
164
|
+
if (this._manualHoverTrackingEnabled) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!this._group || this._destroyed) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
var stage = this._group.getStage && this._group.getStage();
|
|
173
|
+
|
|
174
|
+
if (!stage) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this._manualHoverTrackingEnabled = true;
|
|
179
|
+
this._manualHoverNamespace = '.manualHover.' + this._source.id;
|
|
180
|
+
|
|
181
|
+
this._manualHoverMoveHandler = function() {
|
|
182
|
+
this._manageManualHoverStop();
|
|
183
|
+
}.bind(this);
|
|
184
|
+
|
|
185
|
+
stage.on('mousemove' + this._manualHoverNamespace, this._manualHoverMoveHandler);
|
|
186
|
+
stage.on('touchmove' + this._manualHoverNamespace, this._manualHoverMoveHandler);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
SourceGroup.prototype._disableManualHoverTracking = function() {
|
|
190
|
+
if (!this._manualHoverTrackingEnabled) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
var stage = this._group && this._group.getStage && this._group.getStage();
|
|
195
|
+
|
|
196
|
+
if (stage && this._manualHoverNamespace) {
|
|
197
|
+
stage.off(this._manualHoverNamespace);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this._manualHoverTrackingEnabled = false;
|
|
201
|
+
this._manualHoverMoveHandler = null;
|
|
202
|
+
this._manualHoverNamespace = null;
|
|
147
203
|
};
|
|
148
204
|
|
|
149
205
|
SourceGroup.prototype._onDragStart = function(element) {
|
|
@@ -182,7 +238,7 @@ define([
|
|
|
182
238
|
};
|
|
183
239
|
|
|
184
240
|
SourceGroup.prototype.isActive = function() {
|
|
185
|
-
return this._isDragged;
|
|
241
|
+
return this._isDragged || this._isHandleDragging;
|
|
186
242
|
};
|
|
187
243
|
|
|
188
244
|
SourceGroup.prototype.addToContent = function(newChild) {
|
|
@@ -202,14 +258,17 @@ define([
|
|
|
202
258
|
this._rightHandle.x(this._width - handleWidth);
|
|
203
259
|
};
|
|
204
260
|
|
|
205
|
-
SourceGroup.prototype._onSourceGroupHandleDrag = function(draggedElement,
|
|
206
|
-
const diff = this._view.pixelsToTime(dragPos.x - this._mouseDownX);
|
|
207
|
-
const timeOffsetDiff = this._view.getTimeOffset() - this._initialTimeOffset;
|
|
208
|
-
|
|
261
|
+
SourceGroup.prototype._onSourceGroupHandleDrag = function(draggedElement, leftHandle) {
|
|
209
262
|
const { start, end } = this._initialTimes;
|
|
210
263
|
|
|
211
264
|
this._view.updateWithAutoScroll(
|
|
212
265
|
function() {
|
|
266
|
+
var pointer = this._view.getPointerPosition();
|
|
267
|
+
var pointerX = pointer ? pointer.x : this._mouseDownX;
|
|
268
|
+
|
|
269
|
+
const diff = this._view.pixelsToTime(pointerX - this._mouseDownX);
|
|
270
|
+
const timeOffsetDiff = this._view.getTimeOffset() - this._initialTimeOffset;
|
|
271
|
+
|
|
213
272
|
if (this._layer.manageSourceMovements(
|
|
214
273
|
[this._source],
|
|
215
274
|
leftHandle ? start + diff + timeOffsetDiff : null,
|
|
@@ -218,7 +277,7 @@ define([
|
|
|
218
277
|
this._layer.batchDraw();
|
|
219
278
|
}
|
|
220
279
|
}.bind(this)
|
|
221
|
-
);
|
|
280
|
+
, null, false);
|
|
222
281
|
|
|
223
282
|
return {
|
|
224
283
|
x: draggedElement.absolutePosition().x,
|
|
@@ -232,9 +291,13 @@ define([
|
|
|
232
291
|
const frameOffset = this._view.timeToPixels(this._view.getTimeOffset());
|
|
233
292
|
const newTimeToPixelsScale = this._view.getTimeToPixelsScale();
|
|
234
293
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
294
|
+
// When sources are being moved (dragged as blocks), their Konva positions are
|
|
295
|
+
// controlled by the drag handler so they follow the cursor.
|
|
296
|
+
// Auto-scroll triggers frequent `updateSources()` calls; avoid overwriting
|
|
297
|
+
// the drag position here to prevent jitter.
|
|
298
|
+
if (!this._isDragged) {
|
|
299
|
+
this._group.x(startPixel - frameOffset);
|
|
300
|
+
}
|
|
238
301
|
|
|
239
302
|
const newWidth = endPixel - startPixel;
|
|
240
303
|
|
|
@@ -268,6 +331,13 @@ define([
|
|
|
268
331
|
// update unwrap
|
|
269
332
|
this.updatePreviews();
|
|
270
333
|
}
|
|
334
|
+
|
|
335
|
+
// Keep the drag ghost in sync automatically while dragging.
|
|
336
|
+
// This lets SourcesLayer avoid manually updating ghosts.
|
|
337
|
+
if (this._isDragged) {
|
|
338
|
+
this.createDragGhost();
|
|
339
|
+
this.updateDragGhost();
|
|
340
|
+
}
|
|
271
341
|
};
|
|
272
342
|
|
|
273
343
|
SourceGroup.prototype.setWrapping = function(wrap, forceCreate, notify) {
|
|
@@ -307,14 +377,20 @@ define([
|
|
|
307
377
|
start: this._source.startTime,
|
|
308
378
|
end: this._source.endTime
|
|
309
379
|
};
|
|
310
|
-
this.
|
|
380
|
+
this._isHandleDragging = true;
|
|
311
381
|
|
|
312
382
|
this._hideButtons();
|
|
313
383
|
};
|
|
314
384
|
|
|
315
385
|
SourceGroup.prototype._onHandleDragEnd = function() {
|
|
316
|
-
this.
|
|
386
|
+
this._isHandleDragging = false;
|
|
317
387
|
this._showButtons();
|
|
388
|
+
|
|
389
|
+
// When resizing via handles, drag events no longer bubble to the parent
|
|
390
|
+
// group, so we won't hit the move-drag end path that calls prepareDragEnd.
|
|
391
|
+
// Normalize handle geometry here so handles snap to the updated width.
|
|
392
|
+
this.update();
|
|
393
|
+
this.prepareDragEnd();
|
|
318
394
|
};
|
|
319
395
|
|
|
320
396
|
SourceGroup.prototype._addHandles = function(forceCreate) {
|
|
@@ -328,14 +404,23 @@ define([
|
|
|
328
404
|
height: this._unwrappedHeight,
|
|
329
405
|
visible: true,
|
|
330
406
|
draggable: this._source.resizable,
|
|
331
|
-
dragBoundFunc: function(
|
|
332
|
-
return self._onSourceGroupHandleDrag(this,
|
|
407
|
+
dragBoundFunc: function() {
|
|
408
|
+
return self._onSourceGroupHandleDrag(this, true);
|
|
333
409
|
}
|
|
334
410
|
});
|
|
335
411
|
|
|
336
|
-
|
|
412
|
+
// Prevent handle drag events from bubbling to the parent group.
|
|
413
|
+
// Otherwise the parent SourceGroup dragstart/dragend handlers run and
|
|
414
|
+
// the sources-layer creates move-drag ghosts during resize.
|
|
415
|
+
this._leftHandle.on('dragstart', function(event) {
|
|
416
|
+
event.cancelBubble = true;
|
|
417
|
+
self._onHandleDragStart(event);
|
|
418
|
+
});
|
|
337
419
|
|
|
338
|
-
this._leftHandle.on('dragend',
|
|
420
|
+
this._leftHandle.on('dragend', function(event) {
|
|
421
|
+
event.cancelBubble = true;
|
|
422
|
+
self._onHandleDragEnd(event);
|
|
423
|
+
});
|
|
339
424
|
|
|
340
425
|
if (this._source.resizable) {
|
|
341
426
|
this._leftHandle.on('mouseover', function() {
|
|
@@ -355,14 +440,23 @@ define([
|
|
|
355
440
|
height: this._unwrappedHeight,
|
|
356
441
|
visible: true,
|
|
357
442
|
draggable: this._source.resizable,
|
|
358
|
-
dragBoundFunc: function(
|
|
359
|
-
return self._onSourceGroupHandleDrag(this,
|
|
443
|
+
dragBoundFunc: function() {
|
|
444
|
+
return self._onSourceGroupHandleDrag(this, false);
|
|
360
445
|
}
|
|
361
446
|
});
|
|
362
447
|
|
|
363
|
-
|
|
448
|
+
// Prevent handle drag events from bubbling to the parent group.
|
|
449
|
+
// Otherwise the parent SourceGroup dragstart/dragend handlers run and
|
|
450
|
+
// the sources-layer creates move-drag ghosts during resize.
|
|
451
|
+
this._rightHandle.on('dragstart', function(event) {
|
|
452
|
+
event.cancelBubble = true;
|
|
453
|
+
self._onHandleDragStart(event);
|
|
454
|
+
});
|
|
364
455
|
|
|
365
|
-
this._rightHandle.on('dragend',
|
|
456
|
+
this._rightHandle.on('dragend', function(event) {
|
|
457
|
+
event.cancelBubble = true;
|
|
458
|
+
self._onHandleDragEnd(event);
|
|
459
|
+
});
|
|
366
460
|
|
|
367
461
|
if (this._source.resizable) {
|
|
368
462
|
this._rightHandle.on('mouseover', function() {
|
|
@@ -425,17 +519,20 @@ define([
|
|
|
425
519
|
)
|
|
426
520
|
)
|
|
427
521
|
);
|
|
522
|
+
|
|
523
|
+
var actualX = this._group.x() + this._view.getFrameOffset();
|
|
428
524
|
var x = Math.max(
|
|
429
525
|
0,
|
|
430
|
-
this._view.getFrameOffset() -
|
|
526
|
+
this._view.getFrameOffset() - actualX - 2 * radius
|
|
431
527
|
);
|
|
432
528
|
var width = Math.min(
|
|
433
529
|
this._width - x,
|
|
434
530
|
this._view.getWidth() + 4 * radius - Math.max(
|
|
435
531
|
0,
|
|
436
|
-
|
|
532
|
+
actualX - this._view.getFrameOffset()
|
|
437
533
|
)
|
|
438
534
|
);
|
|
535
|
+
|
|
439
536
|
var xWidth = x + width;
|
|
440
537
|
|
|
441
538
|
if (width > 0) {
|
|
@@ -678,10 +775,6 @@ define([
|
|
|
678
775
|
return this._width;
|
|
679
776
|
};
|
|
680
777
|
|
|
681
|
-
SourceGroup.prototype.getX = function() {
|
|
682
|
-
return this._x;
|
|
683
|
-
};
|
|
684
|
-
|
|
685
778
|
SourceGroup.prototype.getAbsoluteY = function() {
|
|
686
779
|
return this._group.absolutePosition().y;
|
|
687
780
|
};
|
|
@@ -700,12 +793,20 @@ define([
|
|
|
700
793
|
return this._group.y(value);
|
|
701
794
|
};
|
|
702
795
|
|
|
796
|
+
SourceGroup.prototype.absolutePosition = function(value) {
|
|
797
|
+
if (value) {
|
|
798
|
+
return this._group.absolutePosition(value);
|
|
799
|
+
}
|
|
800
|
+
return this._group.absolutePosition();
|
|
801
|
+
};
|
|
802
|
+
|
|
703
803
|
SourceGroup.prototype.getSource = function() {
|
|
704
804
|
return this._source;
|
|
705
805
|
};
|
|
706
806
|
|
|
707
807
|
SourceGroup.prototype.startHover = function() {
|
|
708
808
|
this._manualHover = true;
|
|
809
|
+
this._enableManualHoverTracking();
|
|
709
810
|
this._group.fire('mouseenter', { evt: new MouseEvent('mouseenter') }, true);
|
|
710
811
|
};
|
|
711
812
|
|
|
@@ -713,6 +814,18 @@ define([
|
|
|
713
814
|
this._group.fire('mouseleave', { evt: new MouseEvent('mouseleave') }, true);
|
|
714
815
|
};
|
|
715
816
|
|
|
817
|
+
SourceGroup.prototype.setDragging = function(isDragging) {
|
|
818
|
+
this._isDragged = isDragging;
|
|
819
|
+
|
|
820
|
+
// Ghost lifecycle is tied to dragging state.
|
|
821
|
+
if (isDragging) {
|
|
822
|
+
this.createDragGhost();
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
this.destroyDragGhost();
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
|
|
716
829
|
SourceGroup.prototype.startDrag = function() {
|
|
717
830
|
return this._group.startDrag();
|
|
718
831
|
};
|
|
@@ -725,12 +838,12 @@ define([
|
|
|
725
838
|
this._group.moveTo(group);
|
|
726
839
|
};
|
|
727
840
|
|
|
728
|
-
SourceGroup.prototype.
|
|
729
|
-
|
|
841
|
+
SourceGroup.prototype.moveToTop = function() {
|
|
842
|
+
this._group.moveToTop();
|
|
730
843
|
};
|
|
731
844
|
|
|
732
|
-
SourceGroup.prototype.
|
|
733
|
-
|
|
845
|
+
SourceGroup.prototype.isDescendantOf = function(group) {
|
|
846
|
+
return group.isAncestorOf(this._group);
|
|
734
847
|
};
|
|
735
848
|
|
|
736
849
|
SourceGroup.prototype.getParent = function() {
|
|
@@ -878,19 +991,38 @@ define([
|
|
|
878
991
|
};
|
|
879
992
|
|
|
880
993
|
SourceGroup.prototype._createAudioPreview = function(preview, redraw) {
|
|
994
|
+
var url = preview.url;
|
|
995
|
+
|
|
996
|
+
// Resample the waveform data for the current zoom level if needed
|
|
997
|
+
var scaledData = this._layer.getLoadedData(url + '-scaled');
|
|
998
|
+
var currentScale = this._view.getTimeToPixelsScale();
|
|
999
|
+
|
|
1000
|
+
if (scaledData && scaledData.scale !== currentScale) {
|
|
1001
|
+
var originalData = this._layer.getLoadedData(url);
|
|
1002
|
+
|
|
1003
|
+
if (originalData) {
|
|
1004
|
+
this._layer.setLoadedData(url + '-scaled', {
|
|
1005
|
+
data: originalData.resample({
|
|
1006
|
+
scale: originalData.sample_rate / currentScale
|
|
1007
|
+
}),
|
|
1008
|
+
scale: currentScale
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
881
1013
|
var waveform = new WaveformShape({
|
|
882
1014
|
layer: this._layer,
|
|
883
1015
|
view: this._view,
|
|
884
1016
|
source: this._source,
|
|
885
1017
|
height: preview.group.height(),
|
|
886
|
-
url:
|
|
1018
|
+
url: url
|
|
887
1019
|
});
|
|
888
1020
|
|
|
889
1021
|
preview.group.add(waveform);
|
|
890
1022
|
this._addToUnwrap(preview.group);
|
|
891
1023
|
|
|
892
1024
|
if (redraw) {
|
|
893
|
-
this.
|
|
1025
|
+
this._scheduleBatchDraw();
|
|
894
1026
|
}
|
|
895
1027
|
|
|
896
1028
|
this._previewList.push(preview);
|
|
@@ -991,6 +1123,26 @@ define([
|
|
|
991
1123
|
});
|
|
992
1124
|
};
|
|
993
1125
|
|
|
1126
|
+
/**
|
|
1127
|
+
* Schedules a batch draw using RAF to coalesce multiple draw requests.
|
|
1128
|
+
* This is more efficient than calling _batchDraw directly.
|
|
1129
|
+
*/
|
|
1130
|
+
SourceGroup.prototype._scheduleBatchDraw = function() {
|
|
1131
|
+
if (this._destroyed || this._drawScheduled) {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
this._drawScheduled = true;
|
|
1136
|
+
var self = this;
|
|
1137
|
+
|
|
1138
|
+
sharedInvoker.scheduleFrame(function() {
|
|
1139
|
+
self._drawScheduled = false;
|
|
1140
|
+
if (!self._destroyed) {
|
|
1141
|
+
self._batchDraw();
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
};
|
|
1145
|
+
|
|
994
1146
|
SourceGroup.prototype._batchDraw = function() {
|
|
995
1147
|
var layer = this._group && this._group.getLayer && this._group.getLayer();
|
|
996
1148
|
|
|
@@ -999,31 +1151,16 @@ define([
|
|
|
999
1151
|
}
|
|
1000
1152
|
};
|
|
1001
1153
|
|
|
1002
|
-
// Utility to schedule work during idle time
|
|
1154
|
+
// Utility to schedule work during idle time, with tracking for cleanup
|
|
1003
1155
|
SourceGroup.prototype._scheduleIdle = function(fn) {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
return window.requestAnimationFrame(function() {
|
|
1010
|
-
fn({
|
|
1011
|
-
timeRemaining: function() {
|
|
1012
|
-
return 0;
|
|
1013
|
-
},
|
|
1014
|
-
didTimeout: true
|
|
1015
|
-
});
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1156
|
+
var self = this;
|
|
1157
|
+
var id = Utils.scheduleIdle(function(deadline) {
|
|
1158
|
+
self._pendingIdleCallbacks.delete(id);
|
|
1159
|
+
fn(deadline);
|
|
1160
|
+
}, { timeout: 50 });
|
|
1018
1161
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
timeRemaining: function() {
|
|
1022
|
-
return 0;
|
|
1023
|
-
},
|
|
1024
|
-
didTimeout: true
|
|
1025
|
-
});
|
|
1026
|
-
}, 0);
|
|
1162
|
+
this._pendingIdleCallbacks.add(id);
|
|
1163
|
+
return id;
|
|
1027
1164
|
};
|
|
1028
1165
|
|
|
1029
1166
|
SourceGroup.prototype._ensureImagePreviewCount = function(preview, targetCount, interImageSpacing) {
|
|
@@ -1038,7 +1175,7 @@ define([
|
|
|
1038
1175
|
}
|
|
1039
1176
|
|
|
1040
1177
|
if (currentCount >= targetCount || this._previewBuildQueue.has(preview)) {
|
|
1041
|
-
this.
|
|
1178
|
+
this._scheduleBatchDraw();
|
|
1042
1179
|
return;
|
|
1043
1180
|
}
|
|
1044
1181
|
|
|
@@ -1066,7 +1203,7 @@ define([
|
|
|
1066
1203
|
added += 1;
|
|
1067
1204
|
}
|
|
1068
1205
|
|
|
1069
|
-
self.
|
|
1206
|
+
self._scheduleBatchDraw();
|
|
1070
1207
|
|
|
1071
1208
|
if (nextIndex < targetCount) {
|
|
1072
1209
|
self._scheduleIdle(buildChunk);
|
|
@@ -1111,7 +1248,7 @@ define([
|
|
|
1111
1248
|
this._ensureImagePreviewCount(preview, targetCount, interImageSpacing);
|
|
1112
1249
|
|
|
1113
1250
|
if (redraw) {
|
|
1114
|
-
this.
|
|
1251
|
+
this._scheduleBatchDraw();
|
|
1115
1252
|
}
|
|
1116
1253
|
|
|
1117
1254
|
this._previewList.push(preview);
|
|
@@ -1196,6 +1333,90 @@ define([
|
|
|
1196
1333
|
return this._height;
|
|
1197
1334
|
};
|
|
1198
1335
|
|
|
1336
|
+
/**
|
|
1337
|
+
* Creates the drag ghost preview element if it does not exist.
|
|
1338
|
+
* The ghost shows where the source will be placed when released.
|
|
1339
|
+
*/
|
|
1340
|
+
SourceGroup.prototype.createDragGhost = function() {
|
|
1341
|
+
if (this._dragGhost) {
|
|
1342
|
+
return this._dragGhost;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
var frameOffset = this._view.getFrameOffset();
|
|
1346
|
+
var x = this._view.timeToPixels(this._source.startTime) - frameOffset;
|
|
1347
|
+
var width = this._view.timeToPixels(this._source.endTime - this._source.startTime);
|
|
1348
|
+
var height = this.getCurrentHeight();
|
|
1349
|
+
var y = this.getAbsoluteY();
|
|
1350
|
+
|
|
1351
|
+
this._dragGhost = new Konva.Rect({
|
|
1352
|
+
x: x,
|
|
1353
|
+
y: y,
|
|
1354
|
+
width: width,
|
|
1355
|
+
height: height,
|
|
1356
|
+
fill: this._source.backgroundColor,
|
|
1357
|
+
opacity: 0.4,
|
|
1358
|
+
stroke: this._source.selectedBorderColor,
|
|
1359
|
+
strokeWidth: 2,
|
|
1360
|
+
dash: [8, 4],
|
|
1361
|
+
cornerRadius: 8,
|
|
1362
|
+
listening: false
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
// Add to the main sources layer (not the group) so it stays behind sources.
|
|
1366
|
+
this._layer.add(this._dragGhost);
|
|
1367
|
+
this._dragGhost.moveToBottom();
|
|
1368
|
+
|
|
1369
|
+
// Ensure initial Y snaps to the current line position.
|
|
1370
|
+
this.updateDragGhost();
|
|
1371
|
+
|
|
1372
|
+
return this._dragGhost;
|
|
1373
|
+
};
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Updates the drag ghost preview position and size.
|
|
1377
|
+
*
|
|
1378
|
+
* @param {Object} lineGroupsById Map of lineId -> Konva.Group
|
|
1379
|
+
*/
|
|
1380
|
+
SourceGroup.prototype.updateDragGhost = function(lineGroupsById) {
|
|
1381
|
+
if (!this._dragGhost) {
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// Allow callers to omit the lookup; resolve via the owning layer.
|
|
1386
|
+
if (!lineGroupsById
|
|
1387
|
+
&& this._layer
|
|
1388
|
+
&& typeof this._layer.getLineGroups === 'function') {
|
|
1389
|
+
var lineGroups = this._layer.getLineGroups();
|
|
1390
|
+
|
|
1391
|
+
if (lineGroups && typeof lineGroups.getLineGroupsById === 'function') {
|
|
1392
|
+
lineGroupsById = lineGroups.getLineGroupsById();
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
var frameOffset = this._view.getFrameOffset();
|
|
1397
|
+
var x = this._view.timeToPixels(this._source.startTime) - frameOffset;
|
|
1398
|
+
var width = this._view.timeToPixels(this._source.endTime - this._source.startTime);
|
|
1399
|
+
|
|
1400
|
+
this._dragGhost.x(x);
|
|
1401
|
+
this._dragGhost.width(width);
|
|
1402
|
+
this._dragGhost.height(this.getCurrentHeight());
|
|
1403
|
+
|
|
1404
|
+
if (lineGroupsById) {
|
|
1405
|
+
var lineGroup = lineGroupsById[this._source.lineId];
|
|
1406
|
+
|
|
1407
|
+
if (lineGroup) {
|
|
1408
|
+
this._dragGhost.y(lineGroup.y());
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
|
|
1413
|
+
SourceGroup.prototype.destroyDragGhost = function() {
|
|
1414
|
+
if (this._dragGhost) {
|
|
1415
|
+
this._dragGhost.destroy();
|
|
1416
|
+
this._dragGhost = null;
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1199
1420
|
SourceGroup.prototype.getHeights = function() {
|
|
1200
1421
|
return {
|
|
1201
1422
|
unwrapped: this._unwrappedHeight,
|
|
@@ -1684,7 +1905,7 @@ define([
|
|
|
1684
1905
|
self._source.volume = Math.max(self._source.volumeRange[0], Math.min(volume, self._source.volumeRange[1]));
|
|
1685
1906
|
self._peaks.emit('source.volumeChanged', self._source);
|
|
1686
1907
|
|
|
1687
|
-
|
|
1908
|
+
self._scheduleBatchDraw();
|
|
1688
1909
|
});
|
|
1689
1910
|
|
|
1690
1911
|
volumeSliderGroup.on('dragend', function() {
|
|
@@ -1707,6 +1928,23 @@ define([
|
|
|
1707
1928
|
};
|
|
1708
1929
|
|
|
1709
1930
|
SourceGroup.prototype.destroy = function() {
|
|
1931
|
+
this.destroyDragGhost();
|
|
1932
|
+
|
|
1933
|
+
this._disableManualHoverTracking();
|
|
1934
|
+
|
|
1935
|
+
// Cancel any pending idle callbacks to prevent memory leaks
|
|
1936
|
+
if (this._pendingIdleCallbacks) {
|
|
1937
|
+
this._pendingIdleCallbacks.forEach(function(id) {
|
|
1938
|
+
Utils.cancelIdle(id);
|
|
1939
|
+
});
|
|
1940
|
+
this._pendingIdleCallbacks.clear();
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// Clear preview build queue
|
|
1944
|
+
if (this._previewBuildQueue) {
|
|
1945
|
+
this._previewBuildQueue.clear();
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1710
1948
|
if (this._buttonsAnimation) {
|
|
1711
1949
|
this._buttonsAnimation.destroy();
|
|
1712
1950
|
this._buttonsAnimation = null;
|