123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /**
- * @typedef {Object} MinimapPluginParams
- * @desc Extends the `WavesurferParams` wavesurfer was initialised with
- * @property {?string|HTMLElement} container CSS selector or HTML element where
- * the ELAN information should be renderer. By default it is simply appended
- * after the waveform.
- * @property {?boolean} deferInit Set to true to manually call
- * `initPlugin('minimap')`
- */
- /**
- * Renders a smaller version waveform as a minimap of the main waveform.
- *
- * @implements {PluginClass}
- * @extends {Observer}
- * @example
- * // es6
- * import MinimapPlugin from 'wavesurfer.minimap.js';
- *
- * // commonjs
- * var MinimapPlugin = require('wavesurfer.minimap.js');
- *
- * // if you are using <script> tags
- * var MinimapPlugin = window.WaveSurfer.minimap;
- *
- * // ... initialising wavesurfer with the plugin
- * var wavesurfer = WaveSurfer.create({
- * // wavesurfer options ...
- * plugins: [
- * MinimapPlugin.create({
- * // plugin options ...
- * })
- * ]
- * });
- */
- export default class MinimapPlugin {
- /**
- * Minimap plugin definition factory
- *
- * This function must be used to create a plugin definition which can be
- * used by wavesurfer to correctly instantiate the plugin.
- *
- * @param {MinimapPluginParams} params parameters use to initialise the plugin
- * @return {PluginDefinition} an object representing the plugin
- */
- static create(params) {
- return {
- name: 'minimap',
- deferInit: params && params.deferInit ? params.deferInit : false,
- params: params,
- staticProps: {
- initMinimap(customConfig) {
- console.warn('Deprecated initMinimap!');
- params = customConfig;
- this.initPlugins('minimap');
- }
- },
- instance: MinimapPlugin
- };
- }
- constructor(params, ws) {
- this.params = ws.util.extend(
- {}, ws.params, {
- showRegions: false,
- showOverview: false,
- overviewBorderColor: 'green',
- overviewBorderSize: 2,
- // the container should be different
- container: false,
- height: Math.max(Math.round(ws.params.height / 4), 20)
- }, params, {
- scrollParent: false,
- fillParent: true
- }
- );
- // if container is a selector, get the element
- if (typeof params.container === 'string') {
- const el = document.querySelector(params.container);
- if (!el) {
- console.warn(`Wavesurfer minimap container ${params.container} was not found! The minimap will be automatically appended below the waveform.`);
- }
- this.params.container = el;
- }
- // if no container is specified add a new element and insert it
- if (!params.container) {
- this.params.container = ws.util.style(document.createElement('minimap'), {
- display: 'block'
- });
- }
- this.drawer = new (ws.Drawer)(this.params.container, this.params);
- this.wavesurfer = ws;
- this.util = ws.util;
- /**
- * Minimap needs to register to ready and waveform-ready events to
- * work with MediaElement, the time when ready is called is different
- * (peaks can not be got)
- *
- * @type {string}
- * @see https://github.com/katspaugh/wavesurfer.js/issues/736
- */
- this.renderEvent = ws.params.backend === 'MediaElement' ? 'waveform-ready' : 'ready';
- this.overviewRegion = null;
- this.drawer.createWrapper();
- this.createElements();
- let isInitialised = false;
- // ws ready event listener
- this._onShouldRender = () => {
- // only bind the events in the first run
- if (!isInitialised) {
- this.bindWavesurferEvents();
- this.bindMinimapEvents();
- isInitialised = true;
- }
- // if there is no such element, append it to the container (below
- // the waveform)
- if (!document.body.contains(this.params.container)) {
- ws.container.insertBefore(this.params.container, null);
- }
- if (this.wavesurfer.regions && this.params.showRegions) {
- this.regions();
- }
- this.render();
- };
- this._onAudioprocess = currentTime => {
- this.drawer.progress(this.wavesurfer.backend.getPlayedPercents());
- };
- // ws seek event listener
- this._onSeek = () => this.drawer.progress(ws.backend.getPlayedPercents());
- // event listeners for the overview region
- this._onScroll = e => {
- if (!this.draggingOverview) {
- this.moveOverviewRegion(e.target.scrollLeft / this.ratio);
- }
- };
- this._onMouseover = e => {
- if (this.draggingOverview) {
- this.draggingOverview = false;
- }
- };
- let prevWidth = 0;
- this._onResize = ws.util.debounce(() => {
- if (prevWidth != this.drawer.wrapper.clientWidth) {
- prevWidth = this.drawer.wrapper.clientWidth;
- this.render();
- this.drawer.progress(this.wavesurfer.backend.getPlayedPercents());
- }
- });
- }
- init() {
- if (this.wavesurfer.isReady) {
- this._onShouldRender();
- }
- this.wavesurfer.on(this.renderEvent, this._onShouldRender);
- }
- destroy() {
- window.removeEventListener('resize', this._onResize, true);
- window.removeEventListener('orientationchange', this._onResize, true);
- this.wavesurfer.drawer.wrapper.removeEventListener('mouseover', this._onMouseover);
- this.wavesurfer.un(this.renderEvent, this._onShouldRender);
- this.wavesurfer.un('seek', this._onSeek);
- this.wavesurfer.un('scroll', this._onScroll);
- this.wavesurfer.un('audioprocess', this._onAudioprocess);
- this.drawer.destroy();
- this.overviewRegion = null;
- this.unAll();
- }
- regions() {
- this.regions = {};
- this.wavesurfer.on('region-created', region => {
- this.regions[region.id] = region;
- this.renderRegions();
- });
- this.wavesurfer.on('region-updated', region => {
- this.regions[region.id] = region;
- this.renderRegions();
- });
- this.wavesurfer.on('region-removed', region => {
- delete this.regions[region.id];
- this.renderRegions();
- });
- }
- renderRegions() {
- const regionElements = this.drawer.wrapper.querySelectorAll('region');
- let i;
- for (i = 0; i < regionElements.length; ++i) {
- this.drawer.wrapper.removeChild(regionElements[i]);
- }
- Object.keys(this.regions).forEach(id => {
- const region = this.regions[id];
- const width = (this.drawer.width * ((region.end - region.start) / this.wavesurfer.getDuration()));
- const left = (this.drawer.width * (region.start / this.wavesurfer.getDuration()));
- const regionElement = this.util.style(document.createElement('region'), {
- height: 'inherit',
- backgroundColor: region.color,
- width: width + 'px',
- left: left + 'px',
- display: 'block',
- position: 'absolute'
- });
- regionElement.classList.add(id);
- this.drawer.wrapper.appendChild(regionElement);
- });
- }
- createElements() {
- this.drawer.createElements();
- if (this.params.showOverview) {
- this.overviewRegion = this.util.style(document.createElement('overview'), {
- height: (this.drawer.wrapper.offsetHeight - (this.params.overviewBorderSize * 2)) + 'px',
- width: '0px',
- display: 'block',
- position: 'absolute',
- cursor: 'move',
- border: this.params.overviewBorderSize + 'px solid ' + this.params.overviewBorderColor,
- zIndex: 2,
- opacity: this.params.overviewOpacity
- });
- this.drawer.wrapper.appendChild(this.overviewRegion);
- }
- }
- bindWavesurferEvents() {
- window.addEventListener('resize', this._onResize, true);
- window.addEventListener('orientationchange', this._onResize, true);
- this.wavesurfer.on('audioprocess', this._onAudioprocess);
- this.wavesurfer.on('seek', this._onSeek);
- if (this.params.showOverview) {
- this.wavesurfer.on('scroll', this._onScroll);
- this.wavesurfer.drawer.wrapper.addEventListener('mouseover', this._onMouseover);
- }
- }
- bindMinimapEvents() {
- const positionMouseDown = {
- clientX: 0,
- clientY: 0
- };
- let relativePositionX = 0;
- let seek = true;
- // the following event listeners will be destroyed by using
- // this.unAll() and nullifying the DOM node references after
- // removing them
- this.on('click', (e, position) => {
- if (seek) {
- this.progress(position);
- this.wavesurfer.seekAndCenter(position);
- } else {
- seek = true;
- }
- });
- if (this.params.showOverview) {
- this.overviewRegion.addEventListener('mousedown', event => {
- this.draggingOverview = true;
- relativePositionX = event.layerX;
- positionMouseDown.clientX = event.clientX;
- positionMouseDown.clientY = event.clientY;
- });
- this.drawer.wrapper.addEventListener('mousemove', event => {
- if (this.draggingOverview) {
- this.moveOverviewRegion(event.clientX - this.drawer.container.getBoundingClientRect().left - relativePositionX);
- }
- });
- this.drawer.wrapper.addEventListener('mouseup', event => {
- if (positionMouseDown.clientX - event.clientX === 0 && positionMouseDown.clientX - event.clientX === 0) {
- seek = true;
- this.draggingOverview = false;
- } else if (this.draggingOverview) {
- seek = false;
- this.draggingOverview = false;
- }
- });
- }
- }
- render() {
- const len = this.drawer.getWidth();
- const peaks = this.wavesurfer.backend.getPeaks(len, 0, len);
- this.drawer.drawPeaks(peaks, len, 0, len);
- this.drawer.progress(this.wavesurfer.backend.getPlayedPercents());
- if (this.params.showOverview) {
- //get proportional width of overview region considering the respective
- //width of the drawers
- this.ratio = this.wavesurfer.drawer.width / this.drawer.width;
- this.waveShowedWidth = this.wavesurfer.drawer.width / this.ratio;
- this.waveWidth = this.wavesurfer.drawer.width;
- this.overviewWidth = (this.drawer.width / this.ratio);
- this.overviewPosition = 0;
- this.moveOverviewRegion(this.wavesurfer.drawer.wrapper.scrollLeft / this.ratio);
- this.overviewRegion.style.width = (this.overviewWidth - (this.params.overviewBorderSize * 2)) + 'px';
- }
- }
- moveOverviewRegion(pixels) {
- if (pixels < 0) {
- this.overviewPosition = 0;
- } else if (pixels + this.overviewWidth < this.drawer.width) {
- this.overviewPosition = pixels;
- } else {
- this.overviewPosition = (this.drawer.width - this.overviewWidth);
- }
- this.overviewRegion.style.left = this.overviewPosition + 'px';
- if (this.draggingOverview) {
- this.wavesurfer.drawer.wrapper.scrollLeft = this.overviewPosition * this.ratio;
- }
- }
- }
|