@checksub_team/peaks_timeline 2.3.0-alpha.0 → 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 +243 -151
- package/src/components/axis.js +24 -13
- package/src/components/line-groups.js +0 -32
- 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 +151 -1
- package/src/components/sources-layer.js +21 -114
- package/src/view.js +87 -20
|
@@ -39,12 +39,17 @@ define([
|
|
|
39
39
|
this._allowEditing = allowEditing;
|
|
40
40
|
this._sourcesGroup = {};
|
|
41
41
|
this._layer = new Konva.Layer();
|
|
42
|
-
// Separate layer for dragged elements - always renders on top of sources layer
|
|
43
|
-
this._dragLayer = new Konva.Layer();
|
|
44
42
|
this._dataRetriever = new DataRetriever(peaks);
|
|
45
43
|
this._lineGroups = new LineGroups(peaks, view, this);
|
|
46
44
|
this._lineGroups.addToLayer(this);
|
|
47
45
|
|
|
46
|
+
// Drag overlay container.
|
|
47
|
+
// Use a Group inside the main sources layer to avoid adding an extra Stage layer.
|
|
48
|
+
this._dragGroup = new Konva.Group({
|
|
49
|
+
listening: false
|
|
50
|
+
});
|
|
51
|
+
this._layer.add(this._dragGroup);
|
|
52
|
+
|
|
48
53
|
this._loadedData = {};
|
|
49
54
|
|
|
50
55
|
// Create invoker for performance optimizations
|
|
@@ -76,7 +81,7 @@ define([
|
|
|
76
81
|
|
|
77
82
|
SourcesLayer.prototype._onSourcesDelayedLineChanged = function() {
|
|
78
83
|
// Update dragged source groups when sources change line after a delay (e.g., after automatic line creation)
|
|
79
|
-
if (this.
|
|
84
|
+
if (this._draggedElements && this._draggedElements.length > 0) {
|
|
80
85
|
this._dragSourcesGroup();
|
|
81
86
|
}
|
|
82
87
|
};
|
|
@@ -107,6 +112,11 @@ define([
|
|
|
107
112
|
|
|
108
113
|
SourcesLayer.prototype.add = function(element) {
|
|
109
114
|
this._layer.add(element);
|
|
115
|
+
|
|
116
|
+
// Keep drag group on top even if other callers add elements later.
|
|
117
|
+
if (this._dragGroup) {
|
|
118
|
+
this._dragGroup.moveToTop();
|
|
119
|
+
}
|
|
110
120
|
};
|
|
111
121
|
|
|
112
122
|
/**
|
|
@@ -117,7 +127,6 @@ define([
|
|
|
117
127
|
|
|
118
128
|
SourcesLayer.prototype.addToStage = function(stage) {
|
|
119
129
|
stage.add(this._layer);
|
|
120
|
-
stage.add(this._dragLayer);
|
|
121
130
|
};
|
|
122
131
|
|
|
123
132
|
SourcesLayer.prototype.enableEditing = function(enable) {
|
|
@@ -375,10 +384,8 @@ define([
|
|
|
375
384
|
draggable: true
|
|
376
385
|
});
|
|
377
386
|
|
|
378
|
-
// Store initial source positions and create ghost previews
|
|
379
387
|
var self = this;
|
|
380
388
|
|
|
381
|
-
this._dragGhosts = [];
|
|
382
389
|
this._initialSourcePositions = {};
|
|
383
390
|
|
|
384
391
|
this._draggedElements.forEach(function(source) {
|
|
@@ -399,38 +406,22 @@ define([
|
|
|
399
406
|
// Get absolute Y position before moving (relative to line group)
|
|
400
407
|
var absoluteY = sourceGroup.getAbsoluteY();
|
|
401
408
|
|
|
402
|
-
// Move source to the drag
|
|
403
|
-
|
|
409
|
+
// Move source to the drag group so it draws above ALL other sources/segments
|
|
410
|
+
// without introducing an additional Konva.Layer on the Stage.
|
|
411
|
+
sourceGroup.moveTo(self._dragGroup);
|
|
404
412
|
// Restore the Y position (now relative to drag layer, which is at y=0)
|
|
405
413
|
sourceGroup.y(absoluteY);
|
|
406
|
-
|
|
407
|
-
// Create ghost preview
|
|
408
|
-
var ghost = self._createDragGhost(sourceGroup);
|
|
409
|
-
|
|
410
|
-
self._dragGhosts.push({
|
|
411
|
-
ghost: ghost,
|
|
412
|
-
sourceId: source.id
|
|
413
|
-
});
|
|
414
414
|
}
|
|
415
415
|
});
|
|
416
416
|
};
|
|
417
417
|
|
|
418
418
|
SourcesLayer.prototype.onSourcesGroupDragEnd = function() {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
this._dragGhosts.forEach(function(item) {
|
|
422
|
-
if (item.ghost) {
|
|
423
|
-
item.ghost.destroy();
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
this._dragGhosts = null;
|
|
427
|
-
}
|
|
419
|
+
var self = this;
|
|
420
|
+
|
|
428
421
|
this._initialSourcePositions = null;
|
|
429
422
|
this._dragOffsetX = undefined;
|
|
430
423
|
this._dragOffsetY = undefined;
|
|
431
424
|
|
|
432
|
-
var self = this;
|
|
433
|
-
|
|
434
425
|
const updatedSources = this._draggedElements.map(
|
|
435
426
|
function(source) {
|
|
436
427
|
const sourceGroup = self._sourcesGroup[source.id];
|
|
@@ -462,10 +453,9 @@ define([
|
|
|
462
453
|
SourcesLayer.prototype.onSourcesGroupDrag = function(draggedElement) {
|
|
463
454
|
var pointerPos = this._view.getPointerPosition();
|
|
464
455
|
|
|
465
|
-
this._view.updateWithAutoScroll(this._dragSourcesGroup.bind(this));
|
|
456
|
+
this._view.updateWithAutoScroll(this._dragSourcesGroup.bind(this), null, true);
|
|
466
457
|
|
|
467
458
|
// Return position that follows the mouse cursor exactly
|
|
468
|
-
// The ghost preview shows where it will actually be placed
|
|
469
459
|
var clickedSourceGroup = this._sourcesGroup[this._draggedElementId];
|
|
470
460
|
|
|
471
461
|
if (clickedSourceGroup) {
|
|
@@ -563,11 +553,8 @@ define([
|
|
|
563
553
|
mousePosY
|
|
564
554
|
);
|
|
565
555
|
|
|
566
|
-
this._updateDragGhosts();
|
|
567
|
-
|
|
568
556
|
if (shouldRedraw) {
|
|
569
557
|
this.batchDraw();
|
|
570
|
-
this._dragLayer.batchDraw();
|
|
571
558
|
}
|
|
572
559
|
};
|
|
573
560
|
|
|
@@ -582,78 +569,6 @@ define([
|
|
|
582
569
|
);
|
|
583
570
|
};
|
|
584
571
|
|
|
585
|
-
/**
|
|
586
|
-
* Creates a ghost preview element for a source being dragged.
|
|
587
|
-
* The ghost shows where the source will be placed when released.
|
|
588
|
-
*
|
|
589
|
-
* @private
|
|
590
|
-
* @param {SourceGroup} sourceGroup The source group to create a ghost for
|
|
591
|
-
* @returns {Konva.Rect} The ghost preview element
|
|
592
|
-
*/
|
|
593
|
-
SourcesLayer.prototype._createDragGhost = function(sourceGroup) {
|
|
594
|
-
var source = sourceGroup.getSource();
|
|
595
|
-
var frameOffset = this._view.getFrameOffset();
|
|
596
|
-
var x = this._view.timeToPixels(source.startTime) - frameOffset;
|
|
597
|
-
var width = this._view.timeToPixels(source.endTime - source.startTime);
|
|
598
|
-
var height = sourceGroup.getCurrentHeight();
|
|
599
|
-
var y = sourceGroup.getAbsoluteY();
|
|
600
|
-
|
|
601
|
-
var ghost = new Konva.Rect({
|
|
602
|
-
x: x,
|
|
603
|
-
y: y,
|
|
604
|
-
width: width,
|
|
605
|
-
height: height,
|
|
606
|
-
fill: source.backgroundColor,
|
|
607
|
-
opacity: 0.4,
|
|
608
|
-
stroke: source.selectedBorderColor,
|
|
609
|
-
strokeWidth: 2,
|
|
610
|
-
dash: [8, 4],
|
|
611
|
-
cornerRadius: 8,
|
|
612
|
-
listening: false
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
this._layer.add(ghost);
|
|
616
|
-
ghost.moveToBottom();
|
|
617
|
-
|
|
618
|
-
return ghost;
|
|
619
|
-
};
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Updates the positions of all drag ghost previews.
|
|
623
|
-
*
|
|
624
|
-
* @private
|
|
625
|
-
*/
|
|
626
|
-
SourcesLayer.prototype._updateDragGhosts = function() {
|
|
627
|
-
if (!this._dragGhosts) {
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
var self = this;
|
|
632
|
-
var frameOffset = this._view.getFrameOffset();
|
|
633
|
-
var lineGroupsById = this._lineGroups.getLineGroupsById();
|
|
634
|
-
|
|
635
|
-
this._dragGhosts.forEach(function(item) {
|
|
636
|
-
var sourceGroup = self._sourcesGroup[item.sourceId];
|
|
637
|
-
|
|
638
|
-
if (!sourceGroup || !item.ghost) {
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// Use current source times (updated during drag)
|
|
643
|
-
var sourceData = sourceGroup.getSource();
|
|
644
|
-
var x = self._view.timeToPixels(sourceData.startTime) - frameOffset;
|
|
645
|
-
|
|
646
|
-
item.ghost.x(x);
|
|
647
|
-
|
|
648
|
-
// Update Y position based on line
|
|
649
|
-
var lineGroup = lineGroupsById[sourceData.lineId];
|
|
650
|
-
|
|
651
|
-
if (lineGroup) {
|
|
652
|
-
item.ghost.y(lineGroup.y());
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
};
|
|
656
|
-
|
|
657
572
|
/**
|
|
658
573
|
* Updates source times during drag using initial positions.
|
|
659
574
|
*
|
|
@@ -815,17 +730,9 @@ define([
|
|
|
815
730
|
// Get Y position from line group before moving
|
|
816
731
|
var absoluteY = sourceGroup.getAbsoluteY();
|
|
817
732
|
|
|
818
|
-
// Move to drag
|
|
819
|
-
sourceGroup.moveTo(this.
|
|
733
|
+
// Move to drag group
|
|
734
|
+
sourceGroup.moveTo(this._dragGroup);
|
|
820
735
|
sourceGroup.y(absoluteY);
|
|
821
|
-
|
|
822
|
-
// Create ghost preview for this source
|
|
823
|
-
var ghost = this._createDragGhost(sourceGroup);
|
|
824
|
-
|
|
825
|
-
this._dragGhosts.push({
|
|
826
|
-
ghost: ghost,
|
|
827
|
-
sourceId: source.id
|
|
828
|
-
});
|
|
829
736
|
}
|
|
830
737
|
}
|
|
831
738
|
|
package/src/view.js
CHANGED
|
@@ -345,46 +345,72 @@ define([
|
|
|
345
345
|
* @param {Function} [updateWhileNotScrolling] Called when not scrolling.
|
|
346
346
|
*/
|
|
347
347
|
View.prototype.updateWithAutoScroll = function(updateWhileScrolling,
|
|
348
|
-
updateWhileNotScrolling
|
|
348
|
+
updateWhileNotScrolling,
|
|
349
|
+
enableVerticalAutoScroll) {
|
|
349
350
|
var self = this;
|
|
350
351
|
|
|
351
352
|
var pointer = this.getPointerPosition();
|
|
352
353
|
var pointerX = pointer ? pointer.x : null;
|
|
354
|
+
var pointerY = pointer ? pointer.y : null;
|
|
353
355
|
var viewWidth = this.getWidth();
|
|
356
|
+
var viewHeight = this.getHeight();
|
|
354
357
|
var thresholdPx = Math.round(this._peaks.options.autoScrollThreshold * viewWidth);
|
|
358
|
+
var thresholdPy = Math.round(this._peaks.options.autoScrollThreshold * viewHeight);
|
|
355
359
|
|
|
356
360
|
var MAX_AUTO_SCROLL_PX_PER_FRAME = 30;
|
|
357
361
|
var NOMINAL_FRAME_MS = 16.67; // ~60fps
|
|
358
362
|
|
|
359
|
-
function getAutoScrollVelocity(
|
|
360
|
-
if (typeof
|
|
363
|
+
function getAutoScrollVelocity(pointerValue, threshold, size) {
|
|
364
|
+
if (typeof pointerValue !== 'number' || threshold <= 0 || size <= 0) {
|
|
361
365
|
return 0;
|
|
362
366
|
}
|
|
363
367
|
|
|
364
|
-
if (
|
|
368
|
+
if (pointerValue < threshold) {
|
|
365
369
|
return Math.round(
|
|
366
|
-
-MAX_AUTO_SCROLL_PX_PER_FRAME * Math.min(1, (
|
|
370
|
+
-MAX_AUTO_SCROLL_PX_PER_FRAME * Math.min(1, (threshold - pointerValue) / threshold)
|
|
367
371
|
);
|
|
368
372
|
}
|
|
369
|
-
if (
|
|
373
|
+
if (pointerValue > size - threshold) {
|
|
370
374
|
return Math.round(
|
|
371
375
|
MAX_AUTO_SCROLL_PX_PER_FRAME
|
|
372
|
-
* Math.min(1, (
|
|
376
|
+
* Math.min(1, (pointerValue - (size - threshold)) / threshold)
|
|
373
377
|
);
|
|
374
378
|
}
|
|
375
379
|
return 0;
|
|
376
380
|
}
|
|
377
381
|
|
|
378
|
-
var
|
|
382
|
+
var velocityXPerFrame = getAutoScrollVelocity(pointerX, thresholdPx, viewWidth);
|
|
383
|
+
|
|
384
|
+
var verticalScrollingEnabled = Boolean(enableVerticalAutoScroll
|
|
385
|
+
&& this._peaks.options.enableVerticalScrolling);
|
|
386
|
+
|
|
387
|
+
var maxFrameOffsetY = 0;
|
|
388
|
+
|
|
389
|
+
if (verticalScrollingEnabled) {
|
|
390
|
+
maxFrameOffsetY = this.getFullHeight() - viewHeight;
|
|
391
|
+
if (!Number.isFinite(maxFrameOffsetY) || maxFrameOffsetY < 0) {
|
|
392
|
+
maxFrameOffsetY = 0;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
var velocityYPerFrame = verticalScrollingEnabled && maxFrameOffsetY > 0 ?
|
|
397
|
+
getAutoScrollVelocity(pointerY, thresholdPy, viewHeight) :
|
|
398
|
+
0;
|
|
379
399
|
|
|
380
400
|
// For left scroll (negative), only scroll if we can actually move left.
|
|
381
401
|
// For right scroll (positive), allow scrolling (timeline can extend).
|
|
382
|
-
var
|
|
402
|
+
var canScrollX = velocityXPerFrame > 0 || (velocityXPerFrame < 0 && self.getFrameOffset() > 0);
|
|
403
|
+
var canScrollY = verticalScrollingEnabled && maxFrameOffsetY > 0 && (
|
|
404
|
+
(velocityYPerFrame > 0 && self.getFrameOffsetY() < maxFrameOffsetY)
|
|
405
|
+
|| (velocityYPerFrame < 0 && self.getFrameOffsetY() > 0)
|
|
406
|
+
);
|
|
383
407
|
|
|
384
|
-
// Keep the current
|
|
385
|
-
this.
|
|
408
|
+
// Keep the current velocities on the instance for debugging/inspection.
|
|
409
|
+
this._autoScrollVelocityX = velocityXPerFrame;
|
|
410
|
+
this._autoScrollVelocityY = velocityYPerFrame;
|
|
386
411
|
|
|
387
|
-
if (
|
|
412
|
+
if ((velocityXPerFrame !== 0 && canScrollX)
|
|
413
|
+
|| (velocityYPerFrame !== 0 && canScrollY)) {
|
|
388
414
|
if (!this._scrollingRafId) {
|
|
389
415
|
var lastTime = performance.now();
|
|
390
416
|
|
|
@@ -393,23 +419,64 @@ define([
|
|
|
393
419
|
return;
|
|
394
420
|
}
|
|
395
421
|
|
|
422
|
+
var xVel = self._autoScrollVelocityX || 0;
|
|
423
|
+
var yVel = self._autoScrollVelocityY || 0;
|
|
424
|
+
|
|
425
|
+
var canContinueX = xVel > 0 || (xVel < 0 && self.getFrameOffset() > 0);
|
|
426
|
+
var maxY = 0;
|
|
427
|
+
var canContinueY = false;
|
|
428
|
+
|
|
429
|
+
if (verticalScrollingEnabled) {
|
|
430
|
+
maxY = self.getFullHeight() - self.getHeight();
|
|
431
|
+
if (!Number.isFinite(maxY) || maxY < 0) {
|
|
432
|
+
maxY = 0;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
canContinueY = maxY > 0 && (
|
|
436
|
+
(yVel > 0 && self.getFrameOffsetY() < maxY)
|
|
437
|
+
|| (yVel < 0 && self.getFrameOffsetY() > 0)
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if ((xVel === 0 || !canContinueX)
|
|
442
|
+
&& (yVel === 0 || !canContinueY)) {
|
|
443
|
+
self.stopAutoScroll();
|
|
444
|
+
updateWhileScrolling();
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
396
448
|
// Calculate time delta for consistent scroll speed regardless of frame rate
|
|
397
449
|
var deltaTime = currentTime - lastTime;
|
|
398
|
-
var
|
|
450
|
+
var scrollAmountX = Math.round(xVel * deltaTime / NOMINAL_FRAME_MS);
|
|
451
|
+
var scrollAmountY = Math.round(yVel * deltaTime / NOMINAL_FRAME_MS);
|
|
399
452
|
|
|
400
453
|
lastTime = currentTime;
|
|
401
454
|
|
|
402
|
-
var
|
|
455
|
+
var newOffsetX = self.getFrameOffset() + scrollAmountX;
|
|
456
|
+
var newOffsetY = self.getFrameOffsetY() + scrollAmountY;
|
|
403
457
|
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
458
|
+
if (newOffsetX < 0) {
|
|
459
|
+
newOffsetX = 0;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (verticalScrollingEnabled) {
|
|
463
|
+
if (newOffsetY < 0) {
|
|
464
|
+
newOffsetY = 0;
|
|
465
|
+
}
|
|
466
|
+
else if (newOffsetY > maxY) {
|
|
467
|
+
newOffsetY = maxY;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (!verticalScrollingEnabled) {
|
|
472
|
+
self.updateTimeline(newOffsetX);
|
|
407
473
|
}
|
|
408
474
|
else {
|
|
409
|
-
self.updateTimeline(
|
|
410
|
-
updateWhileScrolling();
|
|
411
|
-
self._scrollingRafId = requestAnimationFrame(scrollFrame);
|
|
475
|
+
self.updateTimeline(newOffsetX, newOffsetY);
|
|
412
476
|
}
|
|
477
|
+
|
|
478
|
+
updateWhileScrolling();
|
|
479
|
+
self._scrollingRafId = requestAnimationFrame(scrollFrame);
|
|
413
480
|
}
|
|
414
481
|
|
|
415
482
|
self._scrollingRafId = requestAnimationFrame(scrollFrame);
|