From a34c3f33bdfa746fa34f879db8ab9476a6b4febb Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Sat, 9 Sep 2023 10:24:51 +0200 Subject: [PATCH] Feat: [Envelope] long touch to add a point (#3178) * Feat: [Envelope] long touch to add a point * Update the readme --- README.md | 64 ++++++------------------------------- examples/envelope.js | 5 ++- src/plugins/envelope.ts | 71 ++++++++++++++++------------------------- 3 files changed, 41 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 5914eb664..16ed631e7 100644 --- a/README.md +++ b/README.md @@ -54,57 +54,11 @@ See more [examples](https://wavesurfer-js.org/examples). ## API reference -See the documentation on wavesurfer [methods](http://wavesurfer-js.org/docs/methods), [options](http://wavesurfer-js.org/docs/options) and [events](http://wavesurfer-js.org/docs/events). - -### Wavesurfer options -- `container`: `HTMLElement | string` - Required: an HTML element or selector where the waveform will be rendered. -- `height`: `number | 'auto'` - The height of the waveform in pixels, or "auto" to fill the container height -- `waveColor`: `string | string[] | CanvasGradient` - The color of the waveform -- `progressColor`: `string | string[] | CanvasGradient` - The color of the progress mask -- `cursorColor`: `string` - The color of the playpack cursor -- `cursorWidth`: `number` - The cursor width -- `barWidth`: `number` - Render the waveform with bars like this: ▁ ▂ ▇ ▃ ▅ ▂ -- `barGap`: `number` - Spacing between bars in pixels -- `barRadius`: `number` - Rounded borders for bars -- `barHeight`: `number` - A vertical scaling factor for the waveform -- `barAlign`: `'top' | 'bottom'` - Vertical bar alignment -- `minPxPerSec`: `number` - Minimum pixels per second of audio (i.e. zoom level) -- `fillParent`: `boolean` - Stretch the waveform to fill the container, true by default -- `url`: `string` - Audio URL -- `peaks`: `Array` - Pre-computed audio data -- `duration`: `number` - Pre-computed duration -- `media`: `HTMLMediaElement` - Use an existing media element instead of creating one -- `autoplay`: `boolean` - Play the audio on load -- `interact`: `boolean` - Pass false to disable clicks on the waveform -- `hideScrollbar`: `boolean` - Hide the scrollbar -- `audioRate`: `number` - Audio rate -- `autoScroll`: `boolean` - Automatically scroll the container to keep the current position in viewport -- `autoCenter`: `boolean` - If autoScroll is enabled, keep the cursor in the center of the waveform during playback -- `sampleRate`: `number` - Decoding sample rate. Doesn't affect the playback. Defaults to 8000 -- `splitChannels`: `WaveSurferOptions[]` - Render each audio channel as a separate waveform -- `normalize`: `boolean` - Stretch the waveform to the full height -- `plugins`: `GenericPlugin[]` - The list of plugins to initialize on start -- `renderFunction`: `(peaks: Array, ctx: CanvasRenderingContext2D) => void` - Custom render function -- `fetchParams`: `RequestInit` - Options to pass to the fetch method - -### Wavesurfer events -- `load`: `[url: string]` - When audio starts loading -- `loading`: `[percentage: number]` - When audio is being loaded -- `decode`: `[duration: number]` - When the audio has been decoded -- `ready`: `[duration: number]` - When the audio is both decoded and can play -- `redraw`: `[]` - When a waveform is drawn -- `play`: `[]` - When the audio starts playing -- `pause`: `[]` - When the audio pauses -- `finish`: `[]` - When the audio finishes playing -- `timeupdate`: `[currentTime: number]` - On audio position change, fires continuously during playback -- `audioprocess`: `[currentTime: number]` - An alias of timeupdate but only when the audio is playing -- `seeking`: `[currentTime: number]` - When the user seeks to a new position -- `interaction`: `[newTime: number]` - When the user interacts with the waveform (i.g. clicks or drags on it) -- `click`: `[relativeX: number]` - When the user clicks on the waveform -- `drag`: `[relativeX: number]` - When the user drags the cursor -- `scroll`: `[visibleStartTime: number, visibleEndTime: number]` - When the waveform is scrolled (panned) -- `zoom`: `[minPxPerSec: number]` - When the zoom level changes -- `destroy`: `[]` - Just before the waveform is destroyed so you can clean up your events +See the wavesurfer.js documentation on our website: + + * [methods](http://wavesurfer-js.org/docs/methods) + * [options](http://wavesurfer-js.org/docs/options) + * [events](http://wavesurfer-js.org/docs/events) ## Plugins @@ -116,7 +70,7 @@ The "official" plugins have been completely rewritten and enhanced: * [Envelope](https://wavesurfer-js.org/examples/#envelope.js) – a graphical interface to add fade-in and -out effects and control volume * [Record](https://wavesurfer-js.org/examples/#record.js) – records audio from the microphone and renders a waveform * [Spectrogram](https://wavesurfer-js.org/examples/#spectrogram.js) – visualization of an audio frequency spectrum (written by @akreal) - * [Hover](https://wavesurfer-js.org/examples/#hover.js) – shows a vertical line and timestmap on waveform hover + * [Hover](https://wavesurfer-js.org/examples/#hover.js) – shows a vertical line and timestmap on waveform hover ## CSS styling @@ -189,8 +143,8 @@ Have a question about integrating wavesurfer.js on your website? Feel free to as --- -* **Q**: There is a mismatch between my audio and the waveform. -* **A**: If you're using a VBR (variable bit rate) mp3 file, there might be a mismatch between the audio and the waveform. This can be fixed by converting your file to CBR (constant bit rate). See [this issue](https://github.com/katspaugh/wavesurfer.js/issues/2890#issuecomment-1601067822) for details. +* **Q**: There is a mismatch between my audio and the waveform. How do I fix it? +* **A**: If you're using a VBR (variable bit rate) audio file, there might be a mismatch between the audio and the waveform. This can be fixed by converting your file to CBR (constant bit rate). See [this issue](https://github.com/katspaugh/wavesurfer.js/issues/2890#issuecomment-1601067822) for details. ## Development @@ -231,4 +185,4 @@ We appreciate your feedback and contributions! If you encounter any issues or have suggestions for improvements, please don't hesitate to post in our [forum](https://github.com/wavesurfer-js/wavesurfer.js/discussions/categories/q-a). -We hope you enjoy using wavesurfer.ts and look forward to hearing about your experiences with the library! +We hope you enjoy using wavesurfer.js and look forward to hearing about your experiences with the library! diff --git a/examples/envelope.js b/examples/envelope.js index 177ece05e..5f3265718 100644 --- a/examples/envelope.js +++ b/examples/envelope.js @@ -25,13 +25,16 @@ const wavesurfer = WaveSurfer.create({ url: '/examples/audio/audio.wav', }) +const isMobile = top.matchMedia('(max-width: 900px)').matches + // Initialize the Envelope plugin const envelope = wavesurfer.registerPlugin( EnvelopePlugin.create({ volume: 0.8, lineColor: 'rgba(255, 0, 0, 0.5)', lineWidth: 4, - dragPointSize: top.innerWidth > 900 ? 10 : 20, + dragPointSize: isMobile ? 20 : 12, + dragLine: !isMobile, dragPointFill: 'rgba(0, 255, 255, 0.8)', dragPointStroke: 'rgba(0, 0, 0, 0.5)', diff --git a/src/plugins/envelope.ts b/src/plugins/envelope.ts index 8559b1a85..434394ea6 100644 --- a/src/plugins/envelope.ts +++ b/src/plugins/envelope.ts @@ -19,18 +19,8 @@ export type EnvelopePluginOptions = { lineColor?: string dragLine?: boolean dragPointSize?: number - dragPointSizeMobile?: number dragPointFill?: string dragPointStroke?: string - - /** Deprecated. Use `points` instead. */ - fadeInStart?: number - /** Deprecated. Use `points` instead. */ - fadeInEnd?: number - /** Deprecated. Use `points` instead. */ - fadeOutStart?: number - /** Deprecated. Use `points` instead. */ - fadeOutEnd?: number } const defaultOptions = { @@ -38,7 +28,6 @@ const defaultOptions = { lineWidth: 4, lineColor: 'rgba(0, 0, 255, 0.5)', dragPointSize: 10, - dragPointSizeMobile: 30, dragPointFill: 'rgba(255, 255, 255, 0.8)', dragPointStroke: 'rgba(255, 255, 255, 0.8)', } @@ -81,7 +70,7 @@ class Polyline extends EventEmitter<{ svg.setAttribute('height', '100%') svg.setAttribute('viewBox', `0 0 ${width} ${height}`) svg.setAttribute('preserveAspectRatio', 'none') - svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;') + svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4;') svg.setAttribute('part', 'envelope') this.svg = svg @@ -123,6 +112,31 @@ class Polyline extends EventEmitter<{ const y = e.clientY - rect.top this.emit('point-create', x / rect.width, y / rect.height) }) + + // Long press on touch devices + { + let pressTimer: number + + const clearTimer = () => clearTimeout(pressTimer) + + svg.addEventListener('touchstart', (e) => { + if (e.touches.length === 1) { + pressTimer = window.setTimeout(() => { + e.preventDefault() + const rect = svg.getBoundingClientRect() + const x = e.touches[0].clientX - rect.left + const y = e.touches[0].clientY - rect.top + this.emit('point-create', x / rect.width, y / rect.height) + }, 500) + } else { + clearTimer() + } + }) + + svg.addEventListener('touchmove', clearTimer) + + svg.addEventListener('touchend', clearTimer) + } } private makeDraggable(draggable: SVGElement, onDrag: (x: number, y: number) => void) { @@ -136,7 +150,7 @@ class Polyline extends EventEmitter<{ private createCircle(x: number, y: number) { const circle = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse') - const size = Number(window.top?.innerWidth) > 900 ? this.options.dragPointSize : this.options.dragPointSizeMobile + const size = this.options.dragPointSize const radius = size / 2 circle.setAttribute('rx', radius.toString()) circle.setAttribute('ry', radius.toString()) @@ -253,6 +267,7 @@ class EnvelopePlugin extends BasePlugin