@checksub_team/peaks_timeline 2.2.1 → 2.3.0-alpha.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/package.json +1 -1
- package/peaks.js +650 -137
- package/src/components/invoker.js +218 -2
- package/src/components/line-group.js +6 -6
- package/src/components/line-groups.js +40 -3
- package/src/components/source-group.js +152 -64
- package/src/components/sources-layer.js +397 -41
- package/src/components/waveform-builder.js +27 -9
- package/src/utils.js +90 -0
- package/src/view.js +90 -49
package/src/utils.js
CHANGED
|
@@ -338,6 +338,96 @@ define([
|
|
|
338
338
|
var BB = ((B.toString(16).length === 1) ? '0' + B.toString(16) : B.toString(16));
|
|
339
339
|
|
|
340
340
|
return '#' + RR + GG + BB;
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
// ==================== Performance Utilities ====================
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Schedules work during browser idle time.
|
|
347
|
+
* Falls back to setTimeout if requestIdleCallback is not available.
|
|
348
|
+
*
|
|
349
|
+
* @param {Function} callback Function to execute
|
|
350
|
+
* @param {Object} options Options object with timeout property
|
|
351
|
+
* @returns {Number} ID that can be used to cancel
|
|
352
|
+
*/
|
|
353
|
+
scheduleIdle: function(callback, options) {
|
|
354
|
+
options = options || { timeout: 50 };
|
|
355
|
+
|
|
356
|
+
if (typeof window !== 'undefined' && window.requestIdleCallback) {
|
|
357
|
+
return window.requestIdleCallback(callback, options);
|
|
358
|
+
}
|
|
359
|
+
return setTimeout(function() {
|
|
360
|
+
callback({
|
|
361
|
+
didTimeout: true,
|
|
362
|
+
timeRemaining: function() {
|
|
363
|
+
return 0;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}, 0);
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Cancels a scheduled idle callback.
|
|
371
|
+
*
|
|
372
|
+
* @param {Number} id The ID returned by scheduleIdle
|
|
373
|
+
*/
|
|
374
|
+
cancelIdle: function(id) {
|
|
375
|
+
if (typeof window !== 'undefined' && window.cancelIdleCallback) {
|
|
376
|
+
window.cancelIdleCallback(id);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
clearTimeout(id);
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Creates a simple LRU (Least Recently Used) cache.
|
|
385
|
+
*
|
|
386
|
+
* @param {Number} maxSize Maximum number of items to cache
|
|
387
|
+
* @returns {Object} Cache with get(), set(), has(), delete(), clear() methods
|
|
388
|
+
*/
|
|
389
|
+
createLRUCache: function(maxSize) {
|
|
390
|
+
var cache = new Map();
|
|
391
|
+
|
|
392
|
+
maxSize = maxSize || 100;
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
get: function(key) {
|
|
396
|
+
if (!cache.has(key)) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
// Move to end (most recently used)
|
|
400
|
+
var value = cache.get(key);
|
|
401
|
+
|
|
402
|
+
cache.delete(key);
|
|
403
|
+
cache.set(key, value);
|
|
404
|
+
return value;
|
|
405
|
+
},
|
|
406
|
+
set: function(key, value) {
|
|
407
|
+
if (cache.has(key)) {
|
|
408
|
+
cache.delete(key);
|
|
409
|
+
}
|
|
410
|
+
else if (cache.size >= maxSize) {
|
|
411
|
+
// Delete oldest (first) entry
|
|
412
|
+
var firstKey = cache.keys().next().value;
|
|
413
|
+
|
|
414
|
+
cache.delete(firstKey);
|
|
415
|
+
}
|
|
416
|
+
cache.set(key, value);
|
|
417
|
+
},
|
|
418
|
+
has: function(key) {
|
|
419
|
+
return cache.has(key);
|
|
420
|
+
},
|
|
421
|
+
delete: function(key) {
|
|
422
|
+
return cache.delete(key);
|
|
423
|
+
},
|
|
424
|
+
clear: function() {
|
|
425
|
+
cache.clear();
|
|
426
|
+
},
|
|
427
|
+
size: function() {
|
|
428
|
+
return cache.size;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
341
431
|
}
|
|
342
432
|
};
|
|
343
433
|
});
|
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.
|
|
314
|
+
this.stopAutoScroll();
|
|
319
315
|
this._peaks.emit('handler.view.mouseup');
|
|
320
316
|
};
|
|
321
317
|
|
|
@@ -329,68 +325,113 @@ 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
|
-
|
|
341
|
-
|
|
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) {
|
|
342
349
|
var self = this;
|
|
343
|
-
var posX = this.getPointerPosition().x;
|
|
344
|
-
var threshold = Math.round(this._peaks.options.autoScrollThreshold * this.getWidth());
|
|
345
350
|
|
|
346
|
-
|
|
351
|
+
var pointer = this.getPointerPosition();
|
|
352
|
+
var pointerX = pointer ? pointer.x : null;
|
|
353
|
+
var viewWidth = this.getWidth();
|
|
354
|
+
var thresholdPx = Math.round(this._peaks.options.autoScrollThreshold * viewWidth);
|
|
347
355
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
else if (posX > this.getWidth() - threshold) {
|
|
352
|
-
this._limited = Math.round(
|
|
353
|
-
30 * Math.min(1, (posX - (this.getWidth() - threshold)) / threshold)
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
+
var MAX_AUTO_SCROLL_PX_PER_FRAME = 30;
|
|
357
|
+
var NOMINAL_FRAME_MS = 16.67; // ~60fps
|
|
356
358
|
|
|
357
|
-
|
|
358
|
-
if (
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
clearInterval(self._scrollingInterval);
|
|
366
|
-
self._scrollingInterval = null;
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
self.updateTimeline(self.getFrameOffset() + self._limited);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
updateInInterval();
|
|
373
|
-
},
|
|
374
|
-
10
|
|
359
|
+
function getAutoScrollVelocity(pointerXValue) {
|
|
360
|
+
if (typeof pointerXValue !== 'number' || thresholdPx <= 0) {
|
|
361
|
+
return 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (pointerXValue < thresholdPx) {
|
|
365
|
+
return Math.round(
|
|
366
|
+
-MAX_AUTO_SCROLL_PX_PER_FRAME * Math.min(1, (thresholdPx - pointerXValue) / thresholdPx)
|
|
375
367
|
);
|
|
376
368
|
}
|
|
369
|
+
if (pointerXValue > viewWidth - thresholdPx) {
|
|
370
|
+
return Math.round(
|
|
371
|
+
MAX_AUTO_SCROLL_PX_PER_FRAME
|
|
372
|
+
* Math.min(1, (pointerXValue - (viewWidth - thresholdPx)) / thresholdPx)
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
return 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
var velocityPxPerFrame = getAutoScrollVelocity(pointerX);
|
|
379
|
+
|
|
380
|
+
// For left scroll (negative), only scroll if we can actually move left.
|
|
381
|
+
// For right scroll (positive), allow scrolling (timeline can extend).
|
|
382
|
+
var canScroll = velocityPxPerFrame > 0 || (velocityPxPerFrame < 0 && self.getFrameOffset() > 0);
|
|
383
|
+
|
|
384
|
+
// Keep the current velocity on the instance for debugging/inspection.
|
|
385
|
+
this._limited = velocityPxPerFrame;
|
|
386
|
+
|
|
387
|
+
if (velocityPxPerFrame !== 0 && canScroll) {
|
|
388
|
+
if (!this._scrollingRafId) {
|
|
389
|
+
var lastTime = performance.now();
|
|
390
|
+
|
|
391
|
+
function scrollFrame(currentTime) {
|
|
392
|
+
if (!self._scrollingRafId) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Calculate time delta for consistent scroll speed regardless of frame rate
|
|
397
|
+
var deltaTime = currentTime - lastTime;
|
|
398
|
+
var scrollAmount = Math.round(self._limited * deltaTime / NOMINAL_FRAME_MS);
|
|
399
|
+
|
|
400
|
+
lastTime = currentTime;
|
|
401
|
+
|
|
402
|
+
var newOffset = self.getFrameOffset() + scrollAmount;
|
|
403
|
+
|
|
404
|
+
if (newOffset < 0) {
|
|
405
|
+
self.updateTimeline(0);
|
|
406
|
+
self._scrollingRafId = null;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
self.updateTimeline(newOffset);
|
|
410
|
+
updateWhileScrolling();
|
|
411
|
+
self._scrollingRafId = requestAnimationFrame(scrollFrame);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
self._scrollingRafId = requestAnimationFrame(scrollFrame);
|
|
416
|
+
}
|
|
377
417
|
}
|
|
378
418
|
else {
|
|
379
|
-
this.
|
|
419
|
+
this.stopAutoScroll();
|
|
380
420
|
|
|
381
|
-
if (
|
|
382
|
-
|
|
421
|
+
if (updateWhileNotScrolling) {
|
|
422
|
+
updateWhileNotScrolling();
|
|
383
423
|
}
|
|
384
424
|
else {
|
|
385
|
-
|
|
425
|
+
updateWhileScrolling();
|
|
386
426
|
}
|
|
387
427
|
}
|
|
388
428
|
};
|
|
389
429
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
this.
|
|
430
|
+
// Clear/stop any active auto-scroll loop.
|
|
431
|
+
View.prototype.stopAutoScroll = function() {
|
|
432
|
+
if (this._scrollingRafId) {
|
|
433
|
+
cancelAnimationFrame(this._scrollingRafId);
|
|
434
|
+
this._scrollingRafId = null;
|
|
394
435
|
}
|
|
395
436
|
};
|
|
396
437
|
|
|
@@ -663,7 +704,7 @@ define([
|
|
|
663
704
|
|
|
664
705
|
this.updateTimeline(this._frameOffset);
|
|
665
706
|
|
|
666
|
-
this._sourcesLayer.rescale(
|
|
707
|
+
this._sourcesLayer.rescale();
|
|
667
708
|
|
|
668
709
|
// Update the playhead position after zooming.
|
|
669
710
|
this._playheadLayer.updatePlayheadTime(currentTime);
|