@checksub_team/peaks_timeline
Version:
JavaScript UI component for displaying audio waveforms
316 lines (260 loc) • 8.04 kB
JavaScript
/**
* @file
*
* Defines the {@link PlayheadLayer} class.
*
* @module playhead-layer
*/
define([
'./utils',
'konva'
], function(Utils, Konva) {
'use strict';
var HANDLE_RADIUS = 10;
/**
* Creates a Konva.Layer that displays a playhead marker.
*
* @class
* @alias PlayheadLayer
*
* @param {Peaks} peaks
* @param {WaveformOverview|WaveformZoomView} view
* @param {Boolean} showTime If <code>true</code> The playback time position
* is shown next to the playhead.
*/
function PlayheadLayer(peaks, view, lines, showTime) {
this._peaks = peaks;
this._view = view;
this._lines = lines;
this._playheadPixel = 0;
this._playheadVisible = false;
this._playheadColor = peaks.options.playheadColor;
this._playheadTextColor = peaks.options.playheadTextColor;
this._time = null;
this._playheadLayer = new Konva.Layer();
this._activeSegments = {};
this._createPlayhead(this._playheadColor);
if (showTime) {
this._createPlayheadText(this._playheadTextColor);
}
this.fitToView();
this._peaks.on('segments.remove_all', this._onSegmentsRemoveAll.bind(this));
this._peaks.on('segments.remove', this._onSegmentsRemove.bind(this));
}
PlayheadLayer.prototype._onSegmentsRemoveAll = function() {
this._activeSegments = {};
};
PlayheadLayer.prototype._onSegmentsRemove = function(segments) {
if (this._activeSegments) {
for (var id in segments) {
if (Utils.objectHasProperty(segments, id)) {
var activeSegmentId = this._activeSegments[segments[id].line] ?
this._activeSegments[segments[id].line].id :
null;
if (segments[id].id === activeSegmentId) {
delete this._activeSegments[segments[id].line];
}
}
}
}
};
/**
* Adds the layer to the given {Konva.Stage}.
*
* @param {Konva.Stage} stage
*/
PlayheadLayer.prototype.addToStage = function(stage) {
stage.add(this._playheadLayer);
};
PlayheadLayer.prototype.listening = function(bool) {
this._playheadLayer.listening(bool);
};
/**
* Resizes the playhead UI objects to fit the available space in the
* view.
*/
PlayheadLayer.prototype.fitToView = function() {
var height = this._view.getHeight();
this._playheadLine.points([0.5, 0, 0.5, height]);
// if (this._playheadText) {
// this._playheadText.y(30);
// }
};
/**
* Creates the playhead UI objects.
*
* @private
* @param {String} color
*/
PlayheadLayer.prototype._createPlayhead = function(color) {
// Create with default points, the real values are set in fitToView().
this._playheadLine = new Konva.Line({
stroke: color,
strokeWidth: 3
});
this._playheadHandle = new Konva.RegularPolygon({
x: 0.5,
y: HANDLE_RADIUS / 2,
sides: 3,
fill: color,
strokeWidth: 0,
radius: HANDLE_RADIUS,
rotation: 180
});
var self = this;
this._playheadGroup = new Konva.Group({
x: 0,
y: 0,
draggable: true,
dragBoundFunc: function(pos) {
var time = Math.max(
0,
self._view.pixelsToTime(
pos.x + self._view.getFrameOffset()
)
);
self._view.updateWithAutoScroll(
function() {
time = Math.max(
0,
self._view.pixelsToTime(
self._view.getPointerPosition().x + self._view.getFrameOffset()
)
);
self._onPlayheadDrag(time);
},
function() {
self._onPlayheadDrag(time);
}
);
return {
x: Math.max(pos.x, 0),
y: 0
};
}
});
this._playheadGroup.on('dragstart', this._onPlayheadDragStart.bind(this));
this._playheadGroup.on('dragend', this._onPlayheadDragEnd.bind(this));
this._playheadGroup.add(this._playheadHandle);
this._playheadGroup.add(this._playheadLine);
this._playheadLayer.add(this._playheadGroup);
};
PlayheadLayer.prototype._onPlayheadDrag = function(time) {
if (this._peaks.player._seek(time)) {
this._peaks.emit('playhead.drag', this._peaks.player.getCurrentTime());
}
};
PlayheadLayer.prototype._onPlayheadDragStart = function() {
this._view.enableAutoScroll(false);
this._dragging = true;
};
PlayheadLayer.prototype._onPlayheadDragEnd = function() {
this._view.enableAutoScroll(true);
this._dragging = false;
if (this._playheadGroup._scrollingInterval) {
clearInterval(this._playheadGroup._scrollingInterval);
this._playheadGroup._scrollingInterval = null;
}
this._peaks.emit('playhead.dragend', this._peaks.player.getCurrentTime());
};
PlayheadLayer.prototype._createPlayheadText = function(color) {
// Create with default y, the real value is set in fitToView().
this._playheadText = new Konva.Text({
x: 13,
y: 3,
text: '00:00:00',
fontSize: 11,
fontFamily: 'sans-serif',
fill: color,
align: 'right',
listening: false
});
this._playheadGroup.add(this._playheadText);
};
/**
* Updates the playhead position.
*
* @param {Number} time Current playhead position, in seconds.
*/
PlayheadLayer.prototype.updatePlayheadTime = function(time) {
this._syncPlayhead(time);
};
/**
* Updates the playhead position.
*
* @private
* @param {Number} time Current playhead position, in seconds.
*/
PlayheadLayer.prototype._syncPlayhead = function(time) {
var pixelIndex = this._view.timeToPixels(time);
var frameOffset = this._view.timeToPixels(this._view.getTimeOffset());
this._playheadPixel = pixelIndex;
var playheadX = this._playheadPixel - frameOffset;
var segmentsGroup = this._lines.getSegmentsGroups();
this._playheadGroup.setAttr('x', playheadX);
if (this._time !== time) {
for (var lineId in segmentsGroup) {
if (Utils.objectHasProperty(segmentsGroup, lineId)) {
var newActiveSegment = segmentsGroup[lineId].getActiveSegment(
this._view.pixelsToTime(playheadX + frameOffset)
);
if (newActiveSegment !== this._activeSegments[lineId]) {
if (this._activeSegments[lineId]) {
this._peaks.emit('segments.exit', this._activeSegments[lineId]);
delete this._activeSegments[lineId];
}
if (newActiveSegment) {
this._peaks.emit('segments.enter', newActiveSegment);
this._activeSegments[lineId] = newActiveSegment;
}
}
}
}
if (this._playheadText) {
var text = Utils.formatTime(time, false);
this._playheadText.setText(text);
this._peaks.emit(
'playhead.moved',
this._playheadText.getAbsolutePosition().x,
this._playheadText.width()
);
}
}
this._time = time;
this._playheadLayer.draw();
};
/**
* Returns the position of the playhead marker, in pixels relative to the
* left hand side of the waveform view.
*
* @return {Number}
*/
PlayheadLayer.prototype.getPlayheadOffset = function() {
return this._playheadPixel - this._view.getFrameOffset();
};
PlayheadLayer.prototype.getPlayheadPixel = function() {
return this._playheadPixel;
};
PlayheadLayer.prototype.showPlayheadTime = function(show) {
var updated = false;
if (show) {
if (!this._playheadText) {
// Create it
this._createPlayheadText(this._playheadTextColor);
updated = true;
}
}
else {
if (this._playheadText) {
this._playheadText.remove();
this._playheadText.destroy();
this._playheadText = null;
updated = true;
}
}
if (updated) {
this._playheadLayer.draw();
}
};
return PlayheadLayer;
});