UNPKG

@checksub_team/peaks_timeline

Version:

JavaScript UI component for displaying audio waveforms

316 lines (260 loc) 8.04 kB
/** * @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; });