@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/src/view.js CHANGED
@@ -120,8 +120,6 @@ define([
120
120
  height: self._height
121
121
  });
122
122
 
123
- self._tempGroup = new Konva.Group({ listening: false });
124
-
125
123
  self._width -= self._peaks.options.lineIndicatorWidth;
126
124
 
127
125
  self._axis = new Axis(self._peaks, self, {
@@ -134,8 +132,6 @@ define([
134
132
  self._sourcesLayer = new SourcesLayer(peaks, self, true);
135
133
  self._sourcesLayer.addToStage(self._stage);
136
134
 
137
- self._sourcesLayer.add(self._tempGroup);
138
-
139
135
  self._axis.addFrontToStage(self._stage);
140
136
 
141
137
  self._playheadLayer = new PlayheadLayer(
@@ -315,7 +311,7 @@ define([
315
311
  }
316
312
 
317
313
  View.prototype._mouseUp = function() {
318
- this.clearScrollingInterval();
314
+ this.stopAutoScroll();
319
315
  this._peaks.emit('handler.view.mouseup');
320
316
  };
321
317
 
@@ -329,68 +325,180 @@ define([
329
325
  this._isClickable = clickable;
330
326
  };
331
327
 
332
- View.prototype.getTempGroup = function() {
333
- return this._tempGroup;
334
- };
335
-
336
328
  View.prototype.getSelectedElements = function() {
337
329
  return Object.values(this._modeLayer.getSelectedElements());
338
330
  };
339
331
 
340
- View.prototype.updateWithAutoScroll = function(updateInInterval,
341
- updateOutInterval) {
332
+ /**
333
+ * Updates the view with auto-scroll behavior during drag operations.
334
+ *
335
+ * Auto-scroll activates when the pointer is within `autoScrollThreshold` of
336
+ * the left/right edges. While scrolling, we call `updateWhileScrolling` on
337
+ * every animation frame after updating the timeline offset.
338
+ *
339
+ * If auto-scroll is not active, we stop any pending auto-scroll loop and call
340
+ * `updateWhileNotScrolling` (or fall back to `updateWhileScrolling`).
341
+ *
342
+ * Uses requestAnimationFrame for smooth, consistent scrolling.
343
+ *
344
+ * @param {Function} updateWhileScrolling Called after each scroll step.
345
+ * @param {Function} [updateWhileNotScrolling] Called when not scrolling.
346
+ */
347
+ View.prototype.updateWithAutoScroll = function(updateWhileScrolling,
348
+ updateWhileNotScrolling,
349
+ enableVerticalAutoScroll) {
342
350
  var self = this;
343
- var posX = this.getPointerPosition().x;
344
- var threshold = Math.round(this._peaks.options.autoScrollThreshold * this.getWidth());
345
351
 
346
- this._limited = 0;
352
+ var pointer = this.getPointerPosition();
353
+ var pointerX = pointer ? pointer.x : null;
354
+ var pointerY = pointer ? pointer.y : null;
355
+ var viewWidth = this.getWidth();
356
+ var viewHeight = this.getHeight();
357
+ var thresholdPx = Math.round(this._peaks.options.autoScrollThreshold * viewWidth);
358
+ var thresholdPy = Math.round(this._peaks.options.autoScrollThreshold * viewHeight);
359
+
360
+ var MAX_AUTO_SCROLL_PX_PER_FRAME = 30;
361
+ var NOMINAL_FRAME_MS = 16.67; // ~60fps
347
362
 
348
- if (posX < threshold) {
349
- this._limited = Math.round(-30 * Math.min(1, (threshold - posX) / threshold));
363
+ function getAutoScrollVelocity(pointerValue, threshold, size) {
364
+ if (typeof pointerValue !== 'number' || threshold <= 0 || size <= 0) {
365
+ return 0;
366
+ }
367
+
368
+ if (pointerValue < threshold) {
369
+ return Math.round(
370
+ -MAX_AUTO_SCROLL_PX_PER_FRAME * Math.min(1, (threshold - pointerValue) / threshold)
371
+ );
372
+ }
373
+ if (pointerValue > size - threshold) {
374
+ return Math.round(
375
+ MAX_AUTO_SCROLL_PX_PER_FRAME
376
+ * Math.min(1, (pointerValue - (size - threshold)) / threshold)
377
+ );
378
+ }
379
+ return 0;
350
380
  }
351
- else if (posX > this.getWidth() - threshold) {
352
- this._limited = Math.round(
353
- 30 * Math.min(1, (posX - (this.getWidth() - threshold)) / threshold)
354
- );
381
+
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
+ }
355
394
  }
356
395
 
357
- if (this._limited && self.getFrameOffset() > 0 || this._limited > 0) {
358
- if (!this._scrollingInterval) {
359
- this._scrollingInterval = setInterval(
360
- function() {
361
- var newOffset = self.getFrameOffset() + self._limited;
396
+ var velocityYPerFrame = verticalScrollingEnabled && maxFrameOffsetY > 0 ?
397
+ getAutoScrollVelocity(pointerY, thresholdPy, viewHeight) :
398
+ 0;
399
+
400
+ // For left scroll (negative), only scroll if we can actually move left.
401
+ // For right scroll (positive), allow scrolling (timeline can extend).
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
+ );
407
+
408
+ // Keep the current velocities on the instance for debugging/inspection.
409
+ this._autoScrollVelocityX = velocityXPerFrame;
410
+ this._autoScrollVelocityY = velocityYPerFrame;
411
+
412
+ if ((velocityXPerFrame !== 0 && canScrollX)
413
+ || (velocityYPerFrame !== 0 && canScrollY)) {
414
+ if (!this._scrollingRafId) {
415
+ var lastTime = performance.now();
416
+
417
+ function scrollFrame(currentTime) {
418
+ if (!self._scrollingRafId) {
419
+ return;
420
+ }
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
+
448
+ // Calculate time delta for consistent scroll speed regardless of frame rate
449
+ var deltaTime = currentTime - lastTime;
450
+ var scrollAmountX = Math.round(xVel * deltaTime / NOMINAL_FRAME_MS);
451
+ var scrollAmountY = Math.round(yVel * deltaTime / NOMINAL_FRAME_MS);
452
+
453
+ lastTime = currentTime;
454
+
455
+ var newOffsetX = self.getFrameOffset() + scrollAmountX;
456
+ var newOffsetY = self.getFrameOffsetY() + scrollAmountY;
362
457
 
363
- if (newOffset < 0) {
364
- self.updateTimeline(0);
365
- clearInterval(self._scrollingInterval);
366
- self._scrollingInterval = null;
458
+ if (newOffsetX < 0) {
459
+ newOffsetX = 0;
460
+ }
461
+
462
+ if (verticalScrollingEnabled) {
463
+ if (newOffsetY < 0) {
464
+ newOffsetY = 0;
367
465
  }
368
- else {
369
- self.updateTimeline(self.getFrameOffset() + self._limited);
466
+ else if (newOffsetY > maxY) {
467
+ newOffsetY = maxY;
370
468
  }
469
+ }
371
470
 
372
- updateInInterval();
373
- },
374
- 10
375
- );
471
+ if (!verticalScrollingEnabled) {
472
+ self.updateTimeline(newOffsetX);
473
+ }
474
+ else {
475
+ self.updateTimeline(newOffsetX, newOffsetY);
476
+ }
477
+
478
+ updateWhileScrolling();
479
+ self._scrollingRafId = requestAnimationFrame(scrollFrame);
480
+ }
481
+
482
+ self._scrollingRafId = requestAnimationFrame(scrollFrame);
376
483
  }
377
484
  }
378
485
  else {
379
- this.clearScrollingInterval();
486
+ this.stopAutoScroll();
380
487
 
381
- if (updateOutInterval) {
382
- updateOutInterval();
488
+ if (updateWhileNotScrolling) {
489
+ updateWhileNotScrolling();
383
490
  }
384
491
  else {
385
- updateInInterval();
492
+ updateWhileScrolling();
386
493
  }
387
494
  }
388
495
  };
389
496
 
390
- View.prototype.clearScrollingInterval = function() {
391
- if (this._scrollingInterval) {
392
- clearInterval(this._scrollingInterval);
393
- this._scrollingInterval = null;
497
+ // Clear/stop any active auto-scroll loop.
498
+ View.prototype.stopAutoScroll = function() {
499
+ if (this._scrollingRafId) {
500
+ cancelAnimationFrame(this._scrollingRafId);
501
+ this._scrollingRafId = null;
394
502
  }
395
503
  };
396
504
 
@@ -663,7 +771,7 @@ define([
663
771
 
664
772
  this.updateTimeline(this._frameOffset);
665
773
 
666
- this._sourcesLayer.rescale(true);
774
+ this._sourcesLayer.rescale();
667
775
 
668
776
  // Update the playhead position after zooming.
669
777
  this._playheadLayer.updatePlayheadTime(currentTime);