@checksub_team/peaks_timeline 2.3.0-alpha.0 → 2.3.0-alpha.2

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.
@@ -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
- self._axisGridlineColor = options.axisGridlineColor;
38
- self._axisLabelColor = options.axisLabelColor;
40
+ this._axisGridlineColor = options.axisGridlineColor;
41
+ this._axisLabelColor = options.axisLabelColor;
39
42
 
40
- self._backLayer = new Konva.Layer({
43
+ this._backLayer = new Konva.Layer({
41
44
  listening: false
42
45
  });
43
46
 
44
- self._frontLayer = new Konva.Layer({
47
+ this._frontLayer = new Konva.Layer({
45
48
  listening: false
46
49
  });
47
50
 
48
- self._axisShape = new Konva.Shape({
51
+ this._axisShape = new Konva.Shape({
49
52
  sceneFunc: function(context) {
50
53
  self.drawAxis(context, view);
51
54
  }
52
55
  });
53
- self._backLayer.add(self._axisShape);
56
+ this._backLayer.add(this._axisShape);
54
57
 
55
- self._timesShape = new Konva.Shape({
58
+ this._timesShape = new Konva.Shape({
56
59
  sceneFunc: function(context) {
57
60
  self.drawTimes(context, view);
58
61
  }
59
62
  });
60
- self._frontLayer.add(self._timesShape);
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._frontLayer.batchDraw();
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._frontLayer.batchDraw();
83
+ this._throttledFrontDraw();
73
84
  };
74
85
 
75
86
  Axis.prototype.batchDraw = function() {
76
- this._backLayer.batchDraw();
77
- this._frontLayer.batchDraw();
87
+ this._throttledBackDraw();
88
+ this._throttledFrontDraw();
78
89
  };
79
90
 
80
91
  Axis.prototype.addBackToStage = function(stage) {
@@ -197,7 +197,7 @@ define([
197
197
  this._sourcesGroup[source.id] = sourceGroup;
198
198
  // Only move to this group if not currently being dragged
199
199
  // (during drag, source stays in drag layer for z-order)
200
- if (!sourceGroup.isActive()) {
200
+ if (!sourceGroup.isDragged()) {
201
201
  if (!sourceGroup.getParent() || !sourceGroup.isDescendantOf(this._group)) {
202
202
  sourceGroup.moveTo(this._group);
203
203
  }
@@ -347,43 +347,11 @@ define([
347
347
  this._automaticallyCreatedLineId = automaticallyCreatedLineGroup.getId();
348
348
  this._moveSourcesToPositionIfPossible(sources, newLinePosition);
349
349
 
350
- // After creating and moving to the new line, nudge the vertical scroll so
351
- // the pointer is closer to the center of that line.
352
- this._nudgeFrameOffsetYToLineCenter(newLinePosition, this._automaticLineCreationMouseY);
353
-
354
350
  // Notify that sources have been moved to a new line (for ghost preview updates)
355
351
  this._peaks.emit('sources.delayedLineChange', sources);
356
352
  }.bind(this), this._peaks.options.automaticLineCreationDelay);
357
353
  };
358
354
 
359
- LineGroups.prototype._nudgeFrameOffsetYToLineCenter = function(linePosition, mouseY) {
360
- if (!this._peaks.options.enableVerticalScrolling) {
361
- return;
362
- }
363
-
364
- if (Utils.isNullOrUndefined(mouseY)) {
365
- return;
366
- }
367
-
368
- const lineGroup = this._lineGroupsByPosition[linePosition];
369
-
370
- if (!lineGroup) {
371
- return;
372
- }
373
-
374
- const targetCenterY = lineGroup.y() + (lineGroup.lineHeight() / 2);
375
- const deltaY = targetCenterY - mouseY;
376
-
377
- if (deltaY === 0) {
378
- return;
379
- }
380
-
381
- const maxOffsetY = Math.max(0, this._view.getFullHeight() - this._view.getHeight());
382
- const nextOffsetY = Utils.clamp(this._view.getFrameOffsetY() + deltaY, 0, maxOffsetY);
383
-
384
- this._view.updateTimeline(null, nextOffsetY);
385
- };
386
-
387
355
  LineGroups.prototype.manageVerticalPosition = function(sources, startTime, endTime, mouseX, mouseY) {
388
356
  if (Utils.isNullOrUndefined(mouseX)) {
389
357
  return;
@@ -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._layer.batchDraw();
81
+ this.batchDraw();
73
82
 
74
83
  this._isDragging = false;
75
84
  this._dragLineId = null;
@@ -89,7 +98,7 @@ define([
89
98
  this.refreshIndicators();
90
99
  };
91
100
 
92
- LineIndicator.prototype._createIndicator = function(lineGroup, type, text, subText) {
101
+ LineIndicator.prototype._createIndicator = function(lineGroup, type, text, subText, defaultStyle = {}) {
93
102
  const indicator = new Konva.Group();
94
103
  let indicatorHeight = 0;
95
104
  var self = this;
@@ -97,13 +106,13 @@ define([
97
106
  var textNode, iconNode, subTextNode;
98
107
 
99
108
  if (text) {
100
- [textGroup, textNode] = this._createIndicatorText(text);
109
+ [textGroup, textNode] = this._createIndicatorText(text, 'line-indicator-text', defaultStyle);
101
110
 
102
111
  indicator.add(textGroup);
103
112
  indicatorHeight += textGroup.getAttr('trueHeight') + this._topPadding;
104
113
  }
105
114
 
106
- [iconGroup, iconNode] = this._createIndicatorIcon(type);
115
+ [iconGroup, iconNode] = this._createIndicatorIcon(type, 'line-indicator-icon', defaultStyle);
107
116
 
108
117
  iconGroup.y(indicatorHeight);
109
118
  indicator.add(iconGroup);
@@ -112,7 +121,7 @@ define([
112
121
  if (subText) {
113
122
  indicatorHeight += this._bottomPadding;
114
123
 
115
- [subTextGroup, subTextNode] = this._createIndicatorText(subText);
124
+ [subTextGroup, subTextNode] = this._createIndicatorText(subText, 'line-indicator-subText', defaultStyle);
116
125
 
117
126
  subTextGroup.y(indicatorHeight);
118
127
  indicator.add(subTextGroup);
@@ -208,17 +217,19 @@ define([
208
217
  return indicator;
209
218
  };
210
219
 
211
- LineIndicator.prototype._createIndicatorText = function(text) {
220
+ LineIndicator.prototype._createIndicatorText = function(text, role, defaultStyle = {}) {
221
+ defaultStyle = defaultStyle[role] || {};
212
222
  const textGroup = new Konva.Group();
213
223
 
214
224
  const textNode = new Konva.Text({
215
225
  text: text,
216
226
  fontSize: this._sizes.font,
217
227
  fontFamily: this._peaks.options.lineIndicatorFont,
218
- fill: this._peaks.options.lineIndicatorTextColor,
228
+ fill: defaultStyle.fill || this._peaks.options.lineIndicatorTextColor,
219
229
  align: 'center',
220
230
  width: this._width,
221
- listening: false
231
+ listening: false,
232
+ name: role
222
233
  });
223
234
 
224
235
  textNode.setAttr('defaultColor', this._peaks.options.lineIndicatorTextColor);
@@ -242,7 +253,8 @@ define([
242
253
  return [textGroup, textNode];
243
254
  };
244
255
 
245
- LineIndicator.prototype._createIndicatorIcon = function(type) {
256
+ LineIndicator.prototype._createIndicatorIcon = function(type, role, defaultStyle = {}) {
257
+ defaultStyle = defaultStyle[role] || {};
246
258
  type = this._types.includes(type) ? type : 'default';
247
259
 
248
260
  const iconGroup = new Konva.Group();
@@ -255,9 +267,10 @@ define([
255
267
  x: this._width / 2,
256
268
  y: this._sizes.icon.default / 2,
257
269
  radius: this._sizes.icon.default / 2,
258
- fill: this._peaks.options.lineIndicatorIconColor,
270
+ fill: defaultStyle.fill || this._peaks.options.lineIndicatorIconColor,
259
271
  strokeWidth: 0,
260
- listening: false
272
+ listening: false,
273
+ name: role
261
274
  });
262
275
  }
263
276
  else {
@@ -265,12 +278,13 @@ define([
265
278
  x: (this._width - this._sizes.icon[type]) / 2,
266
279
  y: 0,
267
280
  data: SVGs[type].path,
268
- fill: this._peaks.options.lineIndicatorIconColor,
281
+ fill: defaultStyle.fill || this._peaks.options.lineIndicatorIconColor,
269
282
  scale: {
270
283
  x: (this._sizes.icon[type]) / SVGs[type].width,
271
284
  y: (this._sizes.icon[type]) / SVGs[type].height
272
285
  },
273
- listening: false
286
+ listening: false,
287
+ name: role
274
288
  });
275
289
  }
276
290
 
@@ -326,17 +340,20 @@ define([
326
340
  return;
327
341
  }
328
342
 
329
- if (indicatorData.type === line.indicatorType && indicatorData.text === line.indicatorText) {
343
+ if (indicatorData.type === line.indicatorType
344
+ && indicatorData.text === line.indicatorText
345
+ && indicatorData.subText === line.indicatorSubText) {
330
346
  return;
331
347
  }
332
348
 
333
- this.removeIndicator(line.id, true);
349
+ const styleData = this.removeIndicator(line.id, true);
334
350
 
335
351
  var indicator = this._createIndicator(
336
352
  indicatorData.lineGroup,
337
353
  line.indicatorType,
338
354
  line.indicatorText,
339
- line.indicatorSubText
355
+ line.indicatorSubText,
356
+ styleData
340
357
  );
341
358
 
342
359
  this._layer.add(indicator);
@@ -349,18 +366,51 @@ define([
349
366
  this.batchDraw();
350
367
  };
351
368
 
369
+ LineIndicator.prototype._getStyleData = function(konvaItem) {
370
+ if (!konvaItem) {
371
+ return {};
372
+ }
373
+
374
+ var styleData = {};
375
+ const name = konvaItem.name();
376
+
377
+ if (name) {
378
+ styleData[name] = {
379
+ fill: konvaItem.fill()
380
+ };
381
+ }
382
+
383
+ if (typeof konvaItem.getChildren === 'function') {
384
+ const children = konvaItem.getChildren();
385
+
386
+ if (children && children.length > 0) {
387
+ children.forEach(function(child) {
388
+ styleData = Object.assign(styleData, this._getStyleData(child));
389
+ }.bind(this));
390
+ }
391
+ }
392
+
393
+ return styleData;
394
+ };
395
+
352
396
  LineIndicator.prototype.removeIndicator = function(lineId, keepInList) {
397
+ var styleData = {};
398
+
353
399
  if (this._indicators[lineId]) {
354
- if (this._indicators[lineId].indicator) {
355
- this._indicators[lineId].indicator.destroy();
356
- }
400
+ const indicator = this._indicators[lineId].indicator;
401
+
357
402
  if (!keepInList) {
358
403
  delete this._indicators[lineId];
359
404
  }
360
405
  else {
361
406
  this._indicators[lineId].indicator = null;
407
+ styleData = this._getStyleData(indicator);
408
+ }
409
+ if (indicator) {
410
+ indicator.destroy();
362
411
  }
363
412
  }
413
+ return styleData;
364
414
  };
365
415
 
366
416
  LineIndicator.prototype.refreshIndicator = function(lineId) {
@@ -423,7 +473,7 @@ define([
423
473
  };
424
474
 
425
475
  LineIndicator.prototype.batchDraw = function() {
426
- this._layer.batchDraw();
476
+ this._throttledBatchDraw();
427
477
  };
428
478
 
429
479
  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._layer.batchDraw();
277
+ this.batchDraw();
266
278
  };
267
279
 
268
280
  ModeLayer.prototype._updateCursorTime = function(position) {
@@ -343,6 +355,15 @@ define([
343
355
  ModeLayer.prototype._onMouseMoveInCutMode = function() {
344
356
  var mousePosition = this._stage.getPointerPosition();
345
357
 
358
+ if (!mousePosition) {
359
+ return;
360
+ }
361
+
362
+ // In cut mode, cutting can replace Konva nodes under the pointer.
363
+ // Konva doesn't always emit enter/leave reliably across those updates,
364
+ // so keep the hovered element in sync as the mouse moves.
365
+ this._syncHoveredElementFromPointer();
366
+
346
367
  mousePosition.x = this._view.timeToPixels(this._view.pixelsToTime(mousePosition.x));
347
368
 
348
369
  var cuttingPosition = mousePosition;
@@ -363,13 +384,13 @@ define([
363
384
  this._updateCursorTime(cuttingPosition);
364
385
  this._updateCuttingLine(cuttingPosition);
365
386
 
366
- this._layer.batchDraw();
387
+ this.batchDraw();
367
388
  };
368
389
 
369
390
  ModeLayer.prototype._onMouseLeaveInCutMode = function() {
370
391
  this._cutGroup.visible(false);
371
392
 
372
- this._layer.batchDraw();
393
+ this.batchDraw();
373
394
  };
374
395
 
375
396
  ModeLayer.prototype._onMouseClickInCutMode = function() {
@@ -414,16 +435,55 @@ define([
414
435
 
415
436
  this._cuttingLine.visible(false);
416
437
 
438
+ // Avoid keeping a reference to a SourceGroup that will be destroyed
439
+ // as part of the cut update.
440
+ this._view.setHoveredElement(null);
441
+
417
442
  this._peaks.cutSource(hoveredElement.getSource(), this._view.pixelsToTime(cuttingPixel));
418
443
 
419
- this._view.setHoveredElement(null);
444
+ // Cutting replaces Konva nodes under the cursor; Konva won't always emit
445
+ // enter/leave for the new nodes until the mouse moves. Re-sync hover now
446
+ // so subsequent leave events behave correctly.
447
+ this._syncHoveredElementFromPointer();
420
448
 
421
449
  this._updateCursorTime(cuttingPosition);
422
450
  this._updateCuttingLine(cuttingPosition);
423
451
 
424
- this._layer.batchDraw();
452
+ this.batchDraw();
453
+ }
454
+ }
455
+ };
456
+
457
+ ModeLayer.prototype._syncHoveredElementFromPointer = function() {
458
+ var pointerPos = this._stage.getPointerPosition();
459
+
460
+ if (!pointerPos) {
461
+ return;
462
+ }
463
+
464
+ var node = this._stage.getIntersection(pointerPos);
465
+
466
+ while (node) {
467
+ if (node.attrs && node.attrs.sourceId) {
468
+ var sourceGroup = this._view.getSourceGroupById(node.attrs.sourceId);
469
+
470
+ if (sourceGroup) {
471
+ // Ensure the view state matches what is actually under the pointer.
472
+ this._view.setHoveredElement(sourceGroup);
473
+
474
+ if (!sourceGroup.isHovered()) {
475
+ sourceGroup.startHover();
476
+ }
477
+ }
478
+
479
+ return;
425
480
  }
481
+
482
+ node = node.getParent ? node.getParent() : null;
426
483
  }
484
+
485
+ // Pointer isn't over a source; clear any stale hovered element.
486
+ this._view.setHoveredElement(null);
427
487
  };
428
488
 
429
489
  ModeLayer.prototype.setMode = function(mode) {
@@ -498,7 +558,7 @@ define([
498
558
  }
499
559
 
500
560
  this._mode = mode;
501
- this._layer.batchDraw();
561
+ this.batchDraw();
502
562
  this._view.batchDrawSourcesLayer();
503
563
  };
504
564
 
@@ -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._playheadLayer.batchDraw();
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._playheadLayer.batchDraw();
388
+ this.batchDraw();
379
389
  }
380
390
  };
381
391