@checksub_team/peaks_timeline 1.4.36 → 1.4.37
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/CHANGELOG.md +530 -0
- package/COPYING +165 -165
- package/package.json +88 -88
- package/peaks.js +492 -309
- package/src/data-retriever.js +89 -89
- package/src/default-segment-marker.js +132 -132
- package/src/invoker.js +81 -81
- package/src/keyboard-handler.js +112 -112
- package/src/line-indicator.js +377 -377
- package/src/line.js +678 -678
- package/src/lines.js +427 -427
- package/src/main.js +702 -702
- package/src/mode-layer.js +391 -391
- package/src/player.js +178 -178
- package/src/playhead-layer.js +423 -423
- package/src/segment-shape.js +354 -354
- package/src/segment.js +263 -263
- package/src/segments-group.js +765 -765
- package/src/source-group.js +987 -987
- package/src/source.js +688 -688
- package/src/sources-layer.js +592 -592
- package/src/timeline-axis.js +238 -238
- package/src/timeline-segments.js +395 -395
- package/src/timeline-sources.js +431 -431
- package/src/timeline-zoomview.js +880 -880
- package/src/utils.js +339 -339
- package/src/waveform-shape.js +216 -216
package/src/playhead-layer.js
CHANGED
|
@@ -1,423 +1,423 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file
|
|
3
|
-
*
|
|
4
|
-
* Defines the {@link PlayheadLayer} class.
|
|
5
|
-
*
|
|
6
|
-
* @module playhead-layer
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
define([
|
|
10
|
-
'./utils',
|
|
11
|
-
'konva'
|
|
12
|
-
], function(Utils, Konva) {
|
|
13
|
-
'use strict';
|
|
14
|
-
|
|
15
|
-
var HANDLE_RADIUS = 10;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Creates a Konva.Layer that displays a playhead marker.
|
|
19
|
-
*
|
|
20
|
-
* @class
|
|
21
|
-
* @alias PlayheadLayer
|
|
22
|
-
*
|
|
23
|
-
* @param {Peaks} peaks
|
|
24
|
-
* @param {WaveformOverview|WaveformZoomView} view
|
|
25
|
-
* @param {Boolean} showTime If <code>true</code> The playback time position
|
|
26
|
-
* is shown next to the playhead.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
function PlayheadLayer(peaks, view, lines, showTime) {
|
|
30
|
-
this._peaks = peaks;
|
|
31
|
-
this._view = view;
|
|
32
|
-
this._lines = lines;
|
|
33
|
-
this._playheadPixel = 0;
|
|
34
|
-
this._playheadLineAnimation = null;
|
|
35
|
-
this._playheadVisible = false;
|
|
36
|
-
this._playheadColor = peaks.options.playheadColor;
|
|
37
|
-
this._playheadTextColor = peaks.options.playheadTextColor;
|
|
38
|
-
|
|
39
|
-
this._playheadLayer = new Konva.Layer();
|
|
40
|
-
|
|
41
|
-
this._activeSegments = {};
|
|
42
|
-
this._lastActiveSegments = {};
|
|
43
|
-
|
|
44
|
-
this._createPlayhead(this._playheadColor);
|
|
45
|
-
|
|
46
|
-
if (showTime) {
|
|
47
|
-
this._createPlayheadText(this._playheadTextColor);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.fitToView();
|
|
51
|
-
|
|
52
|
-
this.zoomLevelChanged();
|
|
53
|
-
|
|
54
|
-
this._peaks.on('segments.remove_all', this._onSegmentsRemoveAll.bind(this));
|
|
55
|
-
this._peaks.on('segments.remove', this._onSegmentsRemove.bind(this));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
PlayheadLayer.prototype._onSegmentsRemoveAll = function() {
|
|
59
|
-
this._activeSegments = {};
|
|
60
|
-
this._lastActiveSegments = {};
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
PlayheadLayer.prototype._onSegmentsRemove = function(segments) {
|
|
64
|
-
if (this._activeSegments || this._lastActiveSegments) {
|
|
65
|
-
for (var id in segments) {
|
|
66
|
-
if (Utils.objectHasProperty(segments, id)) {
|
|
67
|
-
var activeSegmentId = this._activeSegments[segments[id].line] ?
|
|
68
|
-
this._activeSegments[segments[id].line].id :
|
|
69
|
-
null;
|
|
70
|
-
var lastActiveSegmentId = this._lastActiveSegments[segments[id].line] ?
|
|
71
|
-
this._lastActiveSegments[segments[id].line].id :
|
|
72
|
-
null;
|
|
73
|
-
|
|
74
|
-
if (segments[id].id === activeSegmentId) {
|
|
75
|
-
delete this._activeSegments[segments[id].line];
|
|
76
|
-
}
|
|
77
|
-
if (segments[id].id === lastActiveSegmentId) {
|
|
78
|
-
delete this._lastActiveSegments[segments[id].line];
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Adds the layer to the given {Konva.Stage}.
|
|
87
|
-
*
|
|
88
|
-
* @param {Konva.Stage} stage
|
|
89
|
-
*/
|
|
90
|
-
|
|
91
|
-
PlayheadLayer.prototype.addToStage = function(stage) {
|
|
92
|
-
stage.add(this._playheadLayer);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Decides whether to use an animation to update the playhead position.
|
|
97
|
-
*
|
|
98
|
-
* If the zoom level is such that the number of pixels per second of audio is
|
|
99
|
-
* low, we can use timeupdate events from the HTMLMediaElement to
|
|
100
|
-
* set the playhead position. Otherwise, we use an animation to update the
|
|
101
|
-
* playhead position more smoothly. The animation is CPU intensive, so we
|
|
102
|
-
* avoid using it where possible.
|
|
103
|
-
*/
|
|
104
|
-
|
|
105
|
-
PlayheadLayer.prototype.zoomLevelChanged = function() {
|
|
106
|
-
var pixelsPerSecond = this._view.timeToPixels(1.0);
|
|
107
|
-
var time;
|
|
108
|
-
|
|
109
|
-
this._useAnimation = pixelsPerSecond >= 5;
|
|
110
|
-
|
|
111
|
-
if (this._useAnimation) {
|
|
112
|
-
if (this._peaks.player.isPlaying() && !this._playheadLineAnimation) {
|
|
113
|
-
// Start the animation
|
|
114
|
-
this._start();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
if (this._playheadLineAnimation) {
|
|
119
|
-
// Stop the animation
|
|
120
|
-
time = this._peaks.player.getCurrentTime();
|
|
121
|
-
|
|
122
|
-
this.stop(time);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Resizes the playhead UI objects to fit the available space in the
|
|
129
|
-
* view.
|
|
130
|
-
*/
|
|
131
|
-
|
|
132
|
-
PlayheadLayer.prototype.fitToView = function() {
|
|
133
|
-
var height = this._view.getHeight();
|
|
134
|
-
|
|
135
|
-
this._playheadLine.points([0.5, 0, 0.5, height]);
|
|
136
|
-
|
|
137
|
-
// if (this._playheadText) {
|
|
138
|
-
// this._playheadText.y(30);
|
|
139
|
-
// }
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Creates the playhead UI objects.
|
|
144
|
-
*
|
|
145
|
-
* @private
|
|
146
|
-
* @param {String} color
|
|
147
|
-
*/
|
|
148
|
-
|
|
149
|
-
PlayheadLayer.prototype._createPlayhead = function(color) {
|
|
150
|
-
// Create with default points, the real values are set in fitToView().
|
|
151
|
-
this._playheadLine = new Konva.Line({
|
|
152
|
-
stroke: color,
|
|
153
|
-
strokeWidth: 3
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
this._playheadHandle = new Konva.RegularPolygon({
|
|
157
|
-
x: 0.5,
|
|
158
|
-
y: HANDLE_RADIUS / 2,
|
|
159
|
-
sides: 3,
|
|
160
|
-
fill: color,
|
|
161
|
-
strokeWidth: 0,
|
|
162
|
-
radius: HANDLE_RADIUS,
|
|
163
|
-
rotation: 180
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
var self = this;
|
|
167
|
-
|
|
168
|
-
this._playheadGroup = new Konva.Group({
|
|
169
|
-
x: 0,
|
|
170
|
-
y: 0,
|
|
171
|
-
draggable: true,
|
|
172
|
-
dragBoundFunc: function(pos) {
|
|
173
|
-
var time = Math.max(
|
|
174
|
-
0,
|
|
175
|
-
self._view.pixelsToTime(
|
|
176
|
-
pos.x + self._view.getFrameOffset()
|
|
177
|
-
)
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
var autoPos = self._view.updateWithAutoScroll(
|
|
181
|
-
this,
|
|
182
|
-
function() {
|
|
183
|
-
time = Math.max(
|
|
184
|
-
0,
|
|
185
|
-
self._view.pixelsToTime(
|
|
186
|
-
self._view.getPointerPosition().x + self._view.getFrameOffset()
|
|
187
|
-
)
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
self._peaks.player.seek(time);
|
|
191
|
-
},
|
|
192
|
-
function() {
|
|
193
|
-
self._peaks.player.seek(time);
|
|
194
|
-
}
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
x: autoPos.x,
|
|
199
|
-
y: autoPos.y
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
this._playheadGroup.on('dragstart', this._onPlayheadDragStart.bind(this));
|
|
205
|
-
this._playheadGroup.on('dragend', this._onPlayheadDragEnd.bind(this));
|
|
206
|
-
|
|
207
|
-
this._playheadGroup.add(this._playheadHandle);
|
|
208
|
-
this._playheadGroup.add(this._playheadLine);
|
|
209
|
-
this._playheadLayer.add(this._playheadGroup);
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
PlayheadLayer.prototype._onPlayheadDragStart = function() {
|
|
213
|
-
this._view.enableAutoScroll(false);
|
|
214
|
-
this._dragging = true;
|
|
215
|
-
this._scrollInterval = null;
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
PlayheadLayer.prototype._onPlayheadDragEnd = function() {
|
|
219
|
-
this._view.enableAutoScroll(true);
|
|
220
|
-
this._dragging = false;
|
|
221
|
-
if (this._playheadGroup._scrollingInterval) {
|
|
222
|
-
clearInterval(this._playheadGroup._scrollingInterval);
|
|
223
|
-
this._playheadGroup._scrollingInterval = null;
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
PlayheadLayer.prototype._createPlayheadText = function(color) {
|
|
228
|
-
// Create with default y, the real value is set in fitToView().
|
|
229
|
-
this._playheadText = new Konva.Text({
|
|
230
|
-
x: 13,
|
|
231
|
-
y: 3,
|
|
232
|
-
text: '00:00:00',
|
|
233
|
-
fontSize: 11,
|
|
234
|
-
fontFamily: 'sans-serif',
|
|
235
|
-
fill: color,
|
|
236
|
-
align: 'right',
|
|
237
|
-
listening: false
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
this._playheadGroup.add(this._playheadText);
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Updates the playhead position.
|
|
245
|
-
*
|
|
246
|
-
* @param {Number} time Current playhead position, in seconds.
|
|
247
|
-
*/
|
|
248
|
-
|
|
249
|
-
PlayheadLayer.prototype.updatePlayheadTime = function(time) {
|
|
250
|
-
this._syncPlayhead(time);
|
|
251
|
-
|
|
252
|
-
if (this._peaks.player.isPlaying()) {
|
|
253
|
-
this._start();
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Updates the playhead position.
|
|
259
|
-
*
|
|
260
|
-
* @private
|
|
261
|
-
* @param {Number} time Current playhead position, in seconds.
|
|
262
|
-
*/
|
|
263
|
-
|
|
264
|
-
PlayheadLayer.prototype._syncPlayhead = function(time) {
|
|
265
|
-
var pixelIndex = this._view.timeToPixels(time);
|
|
266
|
-
|
|
267
|
-
var frameOffset = this._view.timeToPixels(this._view.getTimeOffset());
|
|
268
|
-
var width = this._view.getWidth();
|
|
269
|
-
|
|
270
|
-
var isVisible = (pixelIndex + HANDLE_RADIUS >= frameOffset) &&
|
|
271
|
-
(pixelIndex - HANDLE_RADIUS < frameOffset + width);
|
|
272
|
-
|
|
273
|
-
this._playheadPixel = pixelIndex;
|
|
274
|
-
|
|
275
|
-
if (isVisible) {
|
|
276
|
-
var playheadX = this._playheadPixel - frameOffset;
|
|
277
|
-
|
|
278
|
-
if (!this._playheadVisible) {
|
|
279
|
-
this._playheadVisible = true;
|
|
280
|
-
this._playheadGroup.show();
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
var playheadPositionDiff = this._playheadGroup.x() - playheadX;
|
|
284
|
-
|
|
285
|
-
if (playheadPositionDiff) {
|
|
286
|
-
var segmentsGroup = this._lines.getSegmentsGroups();
|
|
287
|
-
|
|
288
|
-
for (var lineId in segmentsGroup) {
|
|
289
|
-
if (Utils.objectHasProperty(segmentsGroup, lineId)) {
|
|
290
|
-
var newActiveSegment = segmentsGroup[lineId].getActiveSegment(
|
|
291
|
-
this._view.pixelsToTime(playheadX + frameOffset),
|
|
292
|
-
null,
|
|
293
|
-
true
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
if (newActiveSegment !== this._activeSegments[lineId]) {
|
|
297
|
-
if (this._activeSegments[lineId]) {
|
|
298
|
-
this._peaks.emit('segments.exit', this._activeSegments[lineId]);
|
|
299
|
-
delete this._activeSegments[lineId];
|
|
300
|
-
}
|
|
301
|
-
if (newActiveSegment) {
|
|
302
|
-
this._peaks.emit('segments.enter', newActiveSegment);
|
|
303
|
-
this._activeSegments[lineId] = newActiveSegment;
|
|
304
|
-
this._lastActiveSegments[lineId] = this._activeSegments[lineId];
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
this._playheadGroup.setAttr('x', playheadX);
|
|
312
|
-
|
|
313
|
-
if (this._playheadText) {
|
|
314
|
-
var text = Utils.formatTime(time, false);
|
|
315
|
-
|
|
316
|
-
this._playheadText.setText(text);
|
|
317
|
-
|
|
318
|
-
this._peaks.emit(
|
|
319
|
-
'playhead.moved',
|
|
320
|
-
this._playheadText.getAbsolutePosition().x,
|
|
321
|
-
this._playheadText.width()
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
this._playheadLayer.draw();
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
|
-
if (this._playheadVisible) {
|
|
329
|
-
this._playheadVisible = false;
|
|
330
|
-
this._playheadGroup.hide();
|
|
331
|
-
|
|
332
|
-
this._playheadLayer.draw();
|
|
333
|
-
|
|
334
|
-
this._peaks.emit(
|
|
335
|
-
'playhead.hidden'
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Starts a playhead animation in sync with the media playback.
|
|
343
|
-
*
|
|
344
|
-
* @private
|
|
345
|
-
*/
|
|
346
|
-
|
|
347
|
-
PlayheadLayer.prototype._start = function() {
|
|
348
|
-
var self = this;
|
|
349
|
-
|
|
350
|
-
if (self._playheadLineAnimation) {
|
|
351
|
-
self._playheadLineAnimation.stop();
|
|
352
|
-
self._playheadLineAnimation = null;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (!self._useAnimation) {
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
var lastPlayheadPosition = null;
|
|
360
|
-
|
|
361
|
-
self._playheadLineAnimation = new Konva.Animation(function() {
|
|
362
|
-
var time = self._peaks.player.getCurrentTime();
|
|
363
|
-
var playheadPosition = self._view.timeToPixels(time);
|
|
364
|
-
|
|
365
|
-
if (playheadPosition !== lastPlayheadPosition) {
|
|
366
|
-
self._syncPlayhead(time);
|
|
367
|
-
lastPlayheadPosition = playheadPosition;
|
|
368
|
-
}
|
|
369
|
-
}, self._playheadLayer);
|
|
370
|
-
|
|
371
|
-
self._playheadLineAnimation.start();
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
PlayheadLayer.prototype.stop = function(time) {
|
|
375
|
-
if (this._playheadLineAnimation) {
|
|
376
|
-
this._playheadLineAnimation.stop();
|
|
377
|
-
this._playheadLineAnimation = null;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
this._syncPlayhead(time);
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Returns the position of the playhead marker, in pixels relative to the
|
|
385
|
-
* left hand side of the waveform view.
|
|
386
|
-
*
|
|
387
|
-
* @return {Number}
|
|
388
|
-
*/
|
|
389
|
-
|
|
390
|
-
PlayheadLayer.prototype.getPlayheadOffset = function() {
|
|
391
|
-
return this._playheadPixel - this._view.getFrameOffset();
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
PlayheadLayer.prototype.getPlayheadPixel = function() {
|
|
395
|
-
return this._playheadPixel;
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
PlayheadLayer.prototype.showPlayheadTime = function(show) {
|
|
399
|
-
var updated = false;
|
|
400
|
-
|
|
401
|
-
if (show) {
|
|
402
|
-
if (!this._playheadText) {
|
|
403
|
-
// Create it
|
|
404
|
-
this._createPlayheadText(this._playheadTextColor);
|
|
405
|
-
updated = true;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
if (this._playheadText) {
|
|
410
|
-
this._playheadText.remove();
|
|
411
|
-
this._playheadText.destroy();
|
|
412
|
-
this._playheadText = null;
|
|
413
|
-
updated = true;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if (updated) {
|
|
418
|
-
this._playheadLayer.draw();
|
|
419
|
-
}
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
return PlayheadLayer;
|
|
423
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
*
|
|
4
|
+
* Defines the {@link PlayheadLayer} class.
|
|
5
|
+
*
|
|
6
|
+
* @module playhead-layer
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
define([
|
|
10
|
+
'./utils',
|
|
11
|
+
'konva'
|
|
12
|
+
], function(Utils, Konva) {
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
var HANDLE_RADIUS = 10;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a Konva.Layer that displays a playhead marker.
|
|
19
|
+
*
|
|
20
|
+
* @class
|
|
21
|
+
* @alias PlayheadLayer
|
|
22
|
+
*
|
|
23
|
+
* @param {Peaks} peaks
|
|
24
|
+
* @param {WaveformOverview|WaveformZoomView} view
|
|
25
|
+
* @param {Boolean} showTime If <code>true</code> The playback time position
|
|
26
|
+
* is shown next to the playhead.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
function PlayheadLayer(peaks, view, lines, showTime) {
|
|
30
|
+
this._peaks = peaks;
|
|
31
|
+
this._view = view;
|
|
32
|
+
this._lines = lines;
|
|
33
|
+
this._playheadPixel = 0;
|
|
34
|
+
this._playheadLineAnimation = null;
|
|
35
|
+
this._playheadVisible = false;
|
|
36
|
+
this._playheadColor = peaks.options.playheadColor;
|
|
37
|
+
this._playheadTextColor = peaks.options.playheadTextColor;
|
|
38
|
+
|
|
39
|
+
this._playheadLayer = new Konva.Layer();
|
|
40
|
+
|
|
41
|
+
this._activeSegments = {};
|
|
42
|
+
this._lastActiveSegments = {};
|
|
43
|
+
|
|
44
|
+
this._createPlayhead(this._playheadColor);
|
|
45
|
+
|
|
46
|
+
if (showTime) {
|
|
47
|
+
this._createPlayheadText(this._playheadTextColor);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.fitToView();
|
|
51
|
+
|
|
52
|
+
this.zoomLevelChanged();
|
|
53
|
+
|
|
54
|
+
this._peaks.on('segments.remove_all', this._onSegmentsRemoveAll.bind(this));
|
|
55
|
+
this._peaks.on('segments.remove', this._onSegmentsRemove.bind(this));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
PlayheadLayer.prototype._onSegmentsRemoveAll = function() {
|
|
59
|
+
this._activeSegments = {};
|
|
60
|
+
this._lastActiveSegments = {};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
PlayheadLayer.prototype._onSegmentsRemove = function(segments) {
|
|
64
|
+
if (this._activeSegments || this._lastActiveSegments) {
|
|
65
|
+
for (var id in segments) {
|
|
66
|
+
if (Utils.objectHasProperty(segments, id)) {
|
|
67
|
+
var activeSegmentId = this._activeSegments[segments[id].line] ?
|
|
68
|
+
this._activeSegments[segments[id].line].id :
|
|
69
|
+
null;
|
|
70
|
+
var lastActiveSegmentId = this._lastActiveSegments[segments[id].line] ?
|
|
71
|
+
this._lastActiveSegments[segments[id].line].id :
|
|
72
|
+
null;
|
|
73
|
+
|
|
74
|
+
if (segments[id].id === activeSegmentId) {
|
|
75
|
+
delete this._activeSegments[segments[id].line];
|
|
76
|
+
}
|
|
77
|
+
if (segments[id].id === lastActiveSegmentId) {
|
|
78
|
+
delete this._lastActiveSegments[segments[id].line];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Adds the layer to the given {Konva.Stage}.
|
|
87
|
+
*
|
|
88
|
+
* @param {Konva.Stage} stage
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
PlayheadLayer.prototype.addToStage = function(stage) {
|
|
92
|
+
stage.add(this._playheadLayer);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Decides whether to use an animation to update the playhead position.
|
|
97
|
+
*
|
|
98
|
+
* If the zoom level is such that the number of pixels per second of audio is
|
|
99
|
+
* low, we can use timeupdate events from the HTMLMediaElement to
|
|
100
|
+
* set the playhead position. Otherwise, we use an animation to update the
|
|
101
|
+
* playhead position more smoothly. The animation is CPU intensive, so we
|
|
102
|
+
* avoid using it where possible.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
PlayheadLayer.prototype.zoomLevelChanged = function() {
|
|
106
|
+
var pixelsPerSecond = this._view.timeToPixels(1.0);
|
|
107
|
+
var time;
|
|
108
|
+
|
|
109
|
+
this._useAnimation = pixelsPerSecond >= 5;
|
|
110
|
+
|
|
111
|
+
if (this._useAnimation) {
|
|
112
|
+
if (this._peaks.player.isPlaying() && !this._playheadLineAnimation) {
|
|
113
|
+
// Start the animation
|
|
114
|
+
this._start();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
if (this._playheadLineAnimation) {
|
|
119
|
+
// Stop the animation
|
|
120
|
+
time = this._peaks.player.getCurrentTime();
|
|
121
|
+
|
|
122
|
+
this.stop(time);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Resizes the playhead UI objects to fit the available space in the
|
|
129
|
+
* view.
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
PlayheadLayer.prototype.fitToView = function() {
|
|
133
|
+
var height = this._view.getHeight();
|
|
134
|
+
|
|
135
|
+
this._playheadLine.points([0.5, 0, 0.5, height]);
|
|
136
|
+
|
|
137
|
+
// if (this._playheadText) {
|
|
138
|
+
// this._playheadText.y(30);
|
|
139
|
+
// }
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Creates the playhead UI objects.
|
|
144
|
+
*
|
|
145
|
+
* @private
|
|
146
|
+
* @param {String} color
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
PlayheadLayer.prototype._createPlayhead = function(color) {
|
|
150
|
+
// Create with default points, the real values are set in fitToView().
|
|
151
|
+
this._playheadLine = new Konva.Line({
|
|
152
|
+
stroke: color,
|
|
153
|
+
strokeWidth: 3
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
this._playheadHandle = new Konva.RegularPolygon({
|
|
157
|
+
x: 0.5,
|
|
158
|
+
y: HANDLE_RADIUS / 2,
|
|
159
|
+
sides: 3,
|
|
160
|
+
fill: color,
|
|
161
|
+
strokeWidth: 0,
|
|
162
|
+
radius: HANDLE_RADIUS,
|
|
163
|
+
rotation: 180
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
var self = this;
|
|
167
|
+
|
|
168
|
+
this._playheadGroup = new Konva.Group({
|
|
169
|
+
x: 0,
|
|
170
|
+
y: 0,
|
|
171
|
+
draggable: true,
|
|
172
|
+
dragBoundFunc: function(pos) {
|
|
173
|
+
var time = Math.max(
|
|
174
|
+
0,
|
|
175
|
+
self._view.pixelsToTime(
|
|
176
|
+
pos.x + self._view.getFrameOffset()
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
var autoPos = self._view.updateWithAutoScroll(
|
|
181
|
+
this,
|
|
182
|
+
function() {
|
|
183
|
+
time = Math.max(
|
|
184
|
+
0,
|
|
185
|
+
self._view.pixelsToTime(
|
|
186
|
+
self._view.getPointerPosition().x + self._view.getFrameOffset()
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
self._peaks.player.seek(time);
|
|
191
|
+
},
|
|
192
|
+
function() {
|
|
193
|
+
self._peaks.player.seek(time);
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
x: autoPos.x,
|
|
199
|
+
y: autoPos.y
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
this._playheadGroup.on('dragstart', this._onPlayheadDragStart.bind(this));
|
|
205
|
+
this._playheadGroup.on('dragend', this._onPlayheadDragEnd.bind(this));
|
|
206
|
+
|
|
207
|
+
this._playheadGroup.add(this._playheadHandle);
|
|
208
|
+
this._playheadGroup.add(this._playheadLine);
|
|
209
|
+
this._playheadLayer.add(this._playheadGroup);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
PlayheadLayer.prototype._onPlayheadDragStart = function() {
|
|
213
|
+
this._view.enableAutoScroll(false);
|
|
214
|
+
this._dragging = true;
|
|
215
|
+
this._scrollInterval = null;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
PlayheadLayer.prototype._onPlayheadDragEnd = function() {
|
|
219
|
+
this._view.enableAutoScroll(true);
|
|
220
|
+
this._dragging = false;
|
|
221
|
+
if (this._playheadGroup._scrollingInterval) {
|
|
222
|
+
clearInterval(this._playheadGroup._scrollingInterval);
|
|
223
|
+
this._playheadGroup._scrollingInterval = null;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
PlayheadLayer.prototype._createPlayheadText = function(color) {
|
|
228
|
+
// Create with default y, the real value is set in fitToView().
|
|
229
|
+
this._playheadText = new Konva.Text({
|
|
230
|
+
x: 13,
|
|
231
|
+
y: 3,
|
|
232
|
+
text: '00:00:00',
|
|
233
|
+
fontSize: 11,
|
|
234
|
+
fontFamily: 'sans-serif',
|
|
235
|
+
fill: color,
|
|
236
|
+
align: 'right',
|
|
237
|
+
listening: false
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
this._playheadGroup.add(this._playheadText);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Updates the playhead position.
|
|
245
|
+
*
|
|
246
|
+
* @param {Number} time Current playhead position, in seconds.
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
PlayheadLayer.prototype.updatePlayheadTime = function(time) {
|
|
250
|
+
this._syncPlayhead(time);
|
|
251
|
+
|
|
252
|
+
if (this._peaks.player.isPlaying()) {
|
|
253
|
+
this._start();
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Updates the playhead position.
|
|
259
|
+
*
|
|
260
|
+
* @private
|
|
261
|
+
* @param {Number} time Current playhead position, in seconds.
|
|
262
|
+
*/
|
|
263
|
+
|
|
264
|
+
PlayheadLayer.prototype._syncPlayhead = function(time) {
|
|
265
|
+
var pixelIndex = this._view.timeToPixels(time);
|
|
266
|
+
|
|
267
|
+
var frameOffset = this._view.timeToPixels(this._view.getTimeOffset());
|
|
268
|
+
var width = this._view.getWidth();
|
|
269
|
+
|
|
270
|
+
var isVisible = (pixelIndex + HANDLE_RADIUS >= frameOffset) &&
|
|
271
|
+
(pixelIndex - HANDLE_RADIUS < frameOffset + width);
|
|
272
|
+
|
|
273
|
+
this._playheadPixel = pixelIndex;
|
|
274
|
+
|
|
275
|
+
if (isVisible) {
|
|
276
|
+
var playheadX = this._playheadPixel - frameOffset;
|
|
277
|
+
|
|
278
|
+
if (!this._playheadVisible) {
|
|
279
|
+
this._playheadVisible = true;
|
|
280
|
+
this._playheadGroup.show();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
var playheadPositionDiff = this._playheadGroup.x() - playheadX;
|
|
284
|
+
|
|
285
|
+
if (playheadPositionDiff) {
|
|
286
|
+
var segmentsGroup = this._lines.getSegmentsGroups();
|
|
287
|
+
|
|
288
|
+
for (var lineId in segmentsGroup) {
|
|
289
|
+
if (Utils.objectHasProperty(segmentsGroup, lineId)) {
|
|
290
|
+
var newActiveSegment = segmentsGroup[lineId].getActiveSegment(
|
|
291
|
+
this._view.pixelsToTime(playheadX + frameOffset),
|
|
292
|
+
null,
|
|
293
|
+
true
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if (newActiveSegment !== this._activeSegments[lineId]) {
|
|
297
|
+
if (this._activeSegments[lineId]) {
|
|
298
|
+
this._peaks.emit('segments.exit', this._activeSegments[lineId]);
|
|
299
|
+
delete this._activeSegments[lineId];
|
|
300
|
+
}
|
|
301
|
+
if (newActiveSegment) {
|
|
302
|
+
this._peaks.emit('segments.enter', newActiveSegment);
|
|
303
|
+
this._activeSegments[lineId] = newActiveSegment;
|
|
304
|
+
this._lastActiveSegments[lineId] = this._activeSegments[lineId];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this._playheadGroup.setAttr('x', playheadX);
|
|
312
|
+
|
|
313
|
+
if (this._playheadText) {
|
|
314
|
+
var text = Utils.formatTime(time, false);
|
|
315
|
+
|
|
316
|
+
this._playheadText.setText(text);
|
|
317
|
+
|
|
318
|
+
this._peaks.emit(
|
|
319
|
+
'playhead.moved',
|
|
320
|
+
this._playheadText.getAbsolutePosition().x,
|
|
321
|
+
this._playheadText.width()
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this._playheadLayer.draw();
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
if (this._playheadVisible) {
|
|
329
|
+
this._playheadVisible = false;
|
|
330
|
+
this._playheadGroup.hide();
|
|
331
|
+
|
|
332
|
+
this._playheadLayer.draw();
|
|
333
|
+
|
|
334
|
+
this._peaks.emit(
|
|
335
|
+
'playhead.hidden'
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Starts a playhead animation in sync with the media playback.
|
|
343
|
+
*
|
|
344
|
+
* @private
|
|
345
|
+
*/
|
|
346
|
+
|
|
347
|
+
PlayheadLayer.prototype._start = function() {
|
|
348
|
+
var self = this;
|
|
349
|
+
|
|
350
|
+
if (self._playheadLineAnimation) {
|
|
351
|
+
self._playheadLineAnimation.stop();
|
|
352
|
+
self._playheadLineAnimation = null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!self._useAnimation) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
var lastPlayheadPosition = null;
|
|
360
|
+
|
|
361
|
+
self._playheadLineAnimation = new Konva.Animation(function() {
|
|
362
|
+
var time = self._peaks.player.getCurrentTime();
|
|
363
|
+
var playheadPosition = self._view.timeToPixels(time);
|
|
364
|
+
|
|
365
|
+
if (playheadPosition !== lastPlayheadPosition) {
|
|
366
|
+
self._syncPlayhead(time);
|
|
367
|
+
lastPlayheadPosition = playheadPosition;
|
|
368
|
+
}
|
|
369
|
+
}, self._playheadLayer);
|
|
370
|
+
|
|
371
|
+
self._playheadLineAnimation.start();
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
PlayheadLayer.prototype.stop = function(time) {
|
|
375
|
+
if (this._playheadLineAnimation) {
|
|
376
|
+
this._playheadLineAnimation.stop();
|
|
377
|
+
this._playheadLineAnimation = null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this._syncPlayhead(time);
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Returns the position of the playhead marker, in pixels relative to the
|
|
385
|
+
* left hand side of the waveform view.
|
|
386
|
+
*
|
|
387
|
+
* @return {Number}
|
|
388
|
+
*/
|
|
389
|
+
|
|
390
|
+
PlayheadLayer.prototype.getPlayheadOffset = function() {
|
|
391
|
+
return this._playheadPixel - this._view.getFrameOffset();
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
PlayheadLayer.prototype.getPlayheadPixel = function() {
|
|
395
|
+
return this._playheadPixel;
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
PlayheadLayer.prototype.showPlayheadTime = function(show) {
|
|
399
|
+
var updated = false;
|
|
400
|
+
|
|
401
|
+
if (show) {
|
|
402
|
+
if (!this._playheadText) {
|
|
403
|
+
// Create it
|
|
404
|
+
this._createPlayheadText(this._playheadTextColor);
|
|
405
|
+
updated = true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
if (this._playheadText) {
|
|
410
|
+
this._playheadText.remove();
|
|
411
|
+
this._playheadText.destroy();
|
|
412
|
+
this._playheadText = null;
|
|
413
|
+
updated = true;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (updated) {
|
|
418
|
+
this._playheadLayer.draw();
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
return PlayheadLayer;
|
|
423
|
+
});
|