UNPKG

@checksub_team/peaks_timeline

Version:

JavaScript UI component for displaying audio waveforms

461 lines (369 loc) 13.7 kB
/** * @file * * Defines the {@link LineIndicator} class. * * @module line-indicator */ define([ 'konva', './utils', './svgs' ], function( Konva, Utils, SVGs) { 'use strict'; /** * Creates a Konva.Stage that displays a representation of each line. * * @class * @alias LineIndicator * * @param {Peaks} peaks * @param {WaveformOverview|WaveformZoomView} view */ function LineIndicator(peaks, view, container) { this._peaks = peaks; this._view = view; this._container = container; this._width = this._peaks.options.lineIndicatorWidth; this._height = this._view.getHeight(); this._sizes = { font: this._peaks.options.lineIndicatorFontSize, icon: { default: this._peaks.options.lineIndicatorDefaultIconSize, volume: this._peaks.options.lineIndicatorVolumeIconSize, noVolume: this._peaks.options.lineIndicatorNoVolumeIconSize, visibility: this._peaks.options.lineIndicatorVisibilityIconSize, noVisibility: this._peaks.options.lineIndicatorNoVisibilityIconSize } }; this._yPadding = 30; this._defaultPadding = 5; this._types = ['default'].concat(Object.keys(SVGs)); this._stage = new Konva.Stage({ container: container, width: this._width, height: this._height }); this._layer = new Konva.Layer(); this._stage.add(this._layer); this._indicators = {}; this._separatingLine = new Konva.Line({ points: [this._width, 0, this._width, this._height], stroke: 'gray', strokeWidth: 1, listening: false }); this._layer.add(this._separatingLine); this._layer.draw(); if (this._peaks.options.enableLineIndicatorContextMenu) { this._createContextMenu(); } this._peaks.on('lineIndicator.setType', this._onSetType.bind(this)); this._peaks.on('lineIndicator.setText', this._onSetText.bind(this)); } LineIndicator.prototype.fitToView = function() { this._height = this._view.getHeight(); this._stage.height(this._height); this._separatingLine.points([this._width, 0, this._width, this._height]); }; LineIndicator.prototype._onSetType = function(lineId, type) { this.removeIndicator(lineId, true); type = this._types.includes(type) ? type : 'default'; var indicator = this._createIndicator( this._indicators[lineId].line, type, this._indicators[lineId].text ); this._layer.add(indicator); this._indicators[lineId].indicator = indicator; this._indicators[lineId].type = type; this.draw(); }; LineIndicator.prototype._onSetText = function(lineId, text) { this.removeIndicator(lineId, true); var indicator = this._createIndicator( this._indicators[lineId].line, this._indicators[lineId].type, text ); this._layer.add(indicator); this._indicators[lineId].indicator = indicator; this._indicators[lineId].text = text; this.draw(); }; LineIndicator.prototype._showMenu = function(menu) { menu.style.display = 'block'; var containerRect = this._stage.container().getBoundingClientRect(); menu.style.top = containerRect.top + this._stage.getPointerPosition().y - menu.offsetHeight + 'px'; menu.style.left = containerRect.left + this._stage.getPointerPosition().x + 6 + 'px'; }; LineIndicator.prototype._createIndicator = function(line, type, text) { var indicator = new Konva.Group(); var indicatorHeight = 0; if (text) { var textNode = new Konva.Text({ text: text, fontSize: this._sizes.font, fontFamily: this._peaks.options.lineIndicatorFont, fill: this._peaks.options.lineIndicatorTextColor, align: 'center', width: this._width, listening: false }); textNode.setAttr('defaultColor', this._peaks.options.lineIndicatorTextColor); textNode.setAttr('selectedColor', this._peaks.options.lineIndicatorSelectedTextColor); indicator.add(textNode); indicatorHeight += this._sizes.font; } type = this._types.includes(type) ? type : 'default'; var iconNode; if (type === 'default') { var padding = text ? this._defaultPadding : 0; iconNode = new Konva.Circle({ x: this._width / 2, y: indicatorHeight + this._sizes.icon.default / 2 + padding, radius: this._sizes.icon.default / 2, fill: this._peaks.options.lineIndicatorIconColor, strokeWidth: 0, lineId: line.getId(), listening: false }); indicatorHeight += padding; } else { iconNode = new Konva.Path({ x: (this._width - this._sizes.icon[type]) / 2, y: indicatorHeight, data: SVGs[type].path, fill: this._peaks.options.lineIndicatorIconColor, scale: { x: (this._sizes.icon[type]) / SVGs[type].width, y: (this._sizes.icon[type]) / SVGs[type].height }, lineId: line.getId(), listening: false }); } iconNode.setAttr('defaultColor', this._peaks.options.lineIndicatorIconColor); iconNode.setAttr('selectedColor', this._peaks.options.lineIndicatorSelectedIconColor); indicator.add(iconNode); indicatorHeight += this._sizes.icon[type]; indicator.setAttr('trueHeight', indicatorHeight); var backgroundRect = new Konva.Rect({ x: 0, y: 0, width: this._width, height: indicatorHeight, fill: 'transparent', listening: true }); indicator.add(backgroundRect); backgroundRect.moveToTop(); indicator.y(line.getY() + (line.lineHeight() - indicatorHeight) / 2); var self = this; indicator.on('mouseenter', function() { indicator.getChildren().forEach(function(child) { child.fill(child.getAttr('selectedColor')); }); self.draw(); self._stage.container().style.cursor = 'pointer'; }); indicator.on('mouseout', function() { indicator.getChildren().forEach(function(child) { child.fill(child.getAttr('defaultColor')); }); self.draw(); self._stage.container().style.cursor = 'default'; }); indicator.on('click', function(e) { self._peaks.emit('lineIndicator.click', self._indicators[line.getId()], e.evt); }); return indicator; }; LineIndicator.prototype.addIndicator = function(line) { if (!this._indicators[line.getId()]) { var indicator = this._createIndicator(line); this._layer.add(indicator); this._indicators[line.getId()] = { indicator: indicator, line: line, type: 'default' }; } }; LineIndicator.prototype.removeIndicator = function(lineId, keepInList) { if (this._indicators[lineId]) { if (this._indicators[lineId].indicator) { this._indicators[lineId].indicator.destroy(); } if (!keepInList) { delete this._indicators[lineId]; } else { this._indicators[lineId].indicator = null; } } }; LineIndicator.prototype.updateIndicator = function(lineId) { if (this._indicators[lineId]) { var y = this._indicators[lineId].line.getY(); var isVisible = y + this._indicators[lineId].line.lineHeight() + this._yPadding > 0 && y - this._yPadding < this._height; var isLineNotEmpty = !this._indicators[lineId].line.isEmpty(); if (isLineNotEmpty && isVisible) { if (!this._indicators[lineId].indicator) { this._indicators[lineId].indicator = this._createIndicator( this._indicators[lineId].line, this._indicators[lineId].type, this._indicators[lineId].text ); this._layer.add(this._indicators[lineId].indicator); } else { this._indicators[lineId].indicator.y( y + this._indicators[lineId].line.lineHeight() / 2 - this._indicators[lineId].indicator.getAttr('trueHeight') / 2 ); } } else { this.removeIndicator(lineId, true); } } }; LineIndicator.prototype.updateIndicators = function() { for (var lineId in this._indicators) { if (Utils.objectHasProperty(this._indicators, lineId)) { this.updateIndicator(lineId); } } }; LineIndicator.prototype.draw = function() { this._layer.draw(); }; LineIndicator.prototype._createContextMenu = function() { var menu = document.createElement('div'); var addLine = document.createElement('button'); var addLineAbove = document.createElement('button'); var addLineBelow = document.createElement('button'); var deleteLine = document.createElement('button'); var currentIndicator = null; var self = this; menu.style.display = 'none'; menu.style.position = 'absolute'; menu.style.backgroundColor = 'white'; menu.style.boxShadow = '0 0 5px grey'; menu.style.borderRadius = '3px'; menu.style.zIndex = 2; addLine.style.display = 'none'; addLine.style.border = 'none'; addLine.style.margin = '0'; addLine.style.width = '100%'; addLine.style.backgroundColor = 'white'; addLine.style.padding = '10px'; addLine.textContent = 'Add first line'; addLine.onmouseover = function() { this.style.backgroundColor = 'lightgray'; }; addLine.onmouseout = function() { this.style.backgroundColor = 'white'; }; addLineAbove.style.display = 'none'; addLineAbove.style.border = 'none'; addLineAbove.style.margin = '0'; addLineAbove.style.width = '100%'; addLineAbove.style.backgroundColor = 'white'; addLineAbove.style.padding = '10px'; addLineAbove.textContent = 'Add line above'; addLineAbove.onmouseover = function() { this.style.backgroundColor = 'lightgray'; }; addLineAbove.onmouseout = function() { this.style.backgroundColor = 'white'; }; addLineBelow.style.display = 'none'; addLineBelow.style.border = 'none'; addLineBelow.style.margin = '0'; addLineBelow.style.width = '100%'; addLineBelow.style.backgroundColor = 'white'; addLineBelow.style.padding = '10px'; addLineBelow.textContent = 'Add line below'; addLineBelow.onmouseover = function() { this.style.backgroundColor = 'lightgray'; }; addLineBelow.onmouseout = function() { this.style.backgroundColor = 'white'; }; deleteLine.style.display = 'none'; deleteLine.style.border = 'none'; deleteLine.style.margin = '0'; deleteLine.style.width = '100%'; deleteLine.style.backgroundColor = 'white'; deleteLine.style.padding = '10px'; deleteLine.textContent = 'Delete line'; deleteLine.onmouseover = function() { this.style.backgroundColor = 'lightgray'; }; deleteLine.onmouseout = function() { this.style.backgroundColor = 'white'; }; menu.appendChild(addLine); menu.appendChild(addLineAbove); menu.appendChild(addLineBelow); menu.appendChild(deleteLine); this._container.appendChild(menu); addLine.addEventListener('click', function() { self._peaks.emit('line.add', 0); self.draw(); }); addLineAbove.addEventListener('click', function() { var line = self._indicators[currentIndicator.getAttr('lineId')].line; self._peaks.emit('line.add', Number(line.getPosition())); self.draw(); }); addLineBelow.addEventListener('click', function() { var line = self._indicators[currentIndicator.getAttr('lineId')].line; self._peaks.emit('line.add', Number(line.getPosition()) + 1); self.draw(); }); deleteLine.addEventListener('click', function() { var line = self._indicators[currentIndicator.getAttr('lineId')].line; self._peaks.emit('line.remove', Number(line.getPosition())); self.draw(); }); window.addEventListener('click', function() { // hide menu menu.style.display = 'none'; }); this._stage.on('contextmenu', function(e) { // prevent default behavior e.evt.preventDefault(); if (e.target === self._stage && Object.keys(self._indicators).length === 0) { // if we are on empty place of the stage // we will show the possibility to add a first line addLine.style.display = 'block'; addLineAbove.style.display = 'none'; addLineBelow.style.display = 'none'; deleteLine.style.display = 'none'; // show menu self._showMenu(menu); } else if (e.target !== self._stage) { currentIndicator = e.target; addLine.style.display = 'none'; addLineAbove.style.display = 'block'; addLineBelow.style.display = 'block'; deleteLine.style.display = 'block'; deleteLine.disabled = !self._indicators[currentIndicator.getAttr('lineId')].line.isEmpty(); // show menu self._showMenu(menu); } }); }; return LineIndicator; });