wavesurfer.js 7.0.0-alpha.0 → 7.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/event-emitter.d.ts +2 -0
- package/dist/wavesurfer.Regions.min.js +1 -0
- package/dist/wavesurfer.min.js +1 -0
- package/package.json +14 -6
- package/.eslintrc.json +0 -24
- package/.prettierrc +0 -8
- package/dist/base-plugin.js +0 -13
- package/dist/decoder.js +0 -43
- package/dist/event-emitter.js +0 -21
- package/dist/fetcher.js +0 -17
- package/dist/index.js +0 -166
- package/dist/player-webaudio.js +0 -31
- package/dist/player.js +0 -40
- package/dist/plugins/regions.js +0 -183
- package/dist/renderer.js +0 -193
- package/dist/timer.js +0 -17
- package/examples/audio.ogg +0 -0
- package/examples/bars.js +0 -19
- package/examples/basic.js +0 -8
- package/examples/gradient.js +0 -28
- package/examples/regions.js +0 -63
- package/examples/video.js +0 -19
- package/examples/webaudio.js +0 -14
- package/src/base-plugin.ts +0 -20
- package/src/decoder.ts +0 -41
- package/src/event-emitter.ts +0 -35
- package/src/fetcher.ts +0 -7
- package/src/index.ts +0 -252
- package/src/player-webaudio.ts +0 -34
- package/src/player.ts +0 -50
- package/src/plugins/regions.ts +0 -240
- package/src/renderer.ts +0 -250
- package/src/timer.ts +0 -27
- package/tsconfig.json +0 -105
- package/tutorial/index.html +0 -47
- package/tutorial/src/editor.js +0 -70
- package/tutorial/src/init.js +0 -66
- package/tutorial/src/url.js +0 -25
- package/tutorial/style.css +0 -211
- package/yarn-error.log +0 -1049
package/src/index.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import Fetcher from './fetcher.js'
|
|
2
|
-
import Decoder from './decoder.js'
|
|
3
|
-
import Renderer from './renderer.js'
|
|
4
|
-
import Player from './player.js'
|
|
5
|
-
import WebAudioPlayer from './player-webaudio.js'
|
|
6
|
-
import EventEmitter, { type GeneralEventTypes } from './event-emitter.js'
|
|
7
|
-
import Timer from './timer.js'
|
|
8
|
-
import BasePlugin from './base-plugin.js'
|
|
9
|
-
|
|
10
|
-
export enum PlayerType {
|
|
11
|
-
WebAudio = 'WebAudio',
|
|
12
|
-
MediaElement = 'MediaElement',
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type WaveSurferOptions = {
|
|
16
|
-
/** HTML element or CSS selector */
|
|
17
|
-
container: HTMLElement | string | null
|
|
18
|
-
/** Height of the waveform in pixels */
|
|
19
|
-
height?: number
|
|
20
|
-
/** The color of the waveform */
|
|
21
|
-
waveColor?: string
|
|
22
|
-
/** The color of the progress mask */
|
|
23
|
-
progressColor?: string
|
|
24
|
-
/** If set, the waveform will be rendered in bars like so: ▁ ▂ ▇ ▃ ▅ ▂ */
|
|
25
|
-
barWidth?: number
|
|
26
|
-
/** Spacing between bars in pixels */
|
|
27
|
-
barGap?: number
|
|
28
|
-
/** Rounded borders for bars */
|
|
29
|
-
barRadius?: number
|
|
30
|
-
/** Minimum pixels per second of audio (zoom) */
|
|
31
|
-
minPxPerSec?: number
|
|
32
|
-
/** Audio URL */
|
|
33
|
-
url?: string
|
|
34
|
-
/** Pre-computed audio data */
|
|
35
|
-
channelData?: Float32Array[]
|
|
36
|
-
/** Pre-computed duration */
|
|
37
|
-
duration?: number
|
|
38
|
-
/** Player "backend", the default is MediaElement */
|
|
39
|
-
backend?: PlayerType
|
|
40
|
-
/** Use an existing media element instead of creating one */
|
|
41
|
-
media?: HTMLMediaElement
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const defaultOptions = {
|
|
45
|
-
height: 128,
|
|
46
|
-
waveColor: '#999',
|
|
47
|
-
progressColor: '#555',
|
|
48
|
-
minPxPerSec: 0,
|
|
49
|
-
backend: 'MediaElement',
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type WaveSurferEvents = {
|
|
53
|
-
ready: { duration: number }
|
|
54
|
-
canplay: void
|
|
55
|
-
play: void
|
|
56
|
-
pause: void
|
|
57
|
-
audioprocess: { currentTime: number }
|
|
58
|
-
seek: { time: number }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export type WaveSurferPluginParams = {
|
|
62
|
-
wavesurfer: WaveSurfer
|
|
63
|
-
renderer: WaveSurfer['renderer']
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class WaveSurfer extends EventEmitter<WaveSurferEvents> {
|
|
67
|
-
private options: WaveSurferOptions & typeof defaultOptions
|
|
68
|
-
|
|
69
|
-
private fetcher: Fetcher
|
|
70
|
-
private decoder: Decoder
|
|
71
|
-
private renderer: Renderer
|
|
72
|
-
private player: Player
|
|
73
|
-
private timer: Timer
|
|
74
|
-
|
|
75
|
-
private plugins: BasePlugin<GeneralEventTypes>[] = []
|
|
76
|
-
private subscriptions: Array<() => void> = []
|
|
77
|
-
private channelData: Float32Array[] | null = null
|
|
78
|
-
private duration = 0
|
|
79
|
-
|
|
80
|
-
/** Create a new WaveSurfer instance */
|
|
81
|
-
public static create(options: WaveSurferOptions) {
|
|
82
|
-
return new WaveSurfer(options)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Create a new WaveSurfer instance */
|
|
86
|
-
constructor(options: WaveSurferOptions) {
|
|
87
|
-
super()
|
|
88
|
-
|
|
89
|
-
this.options = Object.assign({}, defaultOptions, options)
|
|
90
|
-
|
|
91
|
-
this.fetcher = new Fetcher()
|
|
92
|
-
this.decoder = new Decoder()
|
|
93
|
-
this.timer = new Timer()
|
|
94
|
-
|
|
95
|
-
this.player = new (this.options.backend === PlayerType.WebAudio ? WebAudioPlayer : Player)({
|
|
96
|
-
media: this.options.media,
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
this.renderer = new Renderer({
|
|
100
|
-
container: this.options.container,
|
|
101
|
-
height: this.options.height,
|
|
102
|
-
waveColor: this.options.waveColor,
|
|
103
|
-
progressColor: this.options.progressColor,
|
|
104
|
-
minPxPerSec: this.options.minPxPerSec,
|
|
105
|
-
barWidth: this.options.barWidth,
|
|
106
|
-
barGap: this.options.barGap,
|
|
107
|
-
barRadius: this.options.barRadius,
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
this.initPlayerEvents()
|
|
111
|
-
this.initRendererEvents()
|
|
112
|
-
this.initTimerEvents()
|
|
113
|
-
|
|
114
|
-
const url = this.options.url || this.options.media?.src
|
|
115
|
-
if (url) {
|
|
116
|
-
this.load(url, this.options.channelData, this.options.duration)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private initPlayerEvents() {
|
|
121
|
-
this.subscriptions.push(
|
|
122
|
-
this.player.on('timeupdate', () => {
|
|
123
|
-
const currentTime = this.getCurrentTime()
|
|
124
|
-
this.renderer.renderProgress(currentTime / this.duration, this.isPlaying())
|
|
125
|
-
this.emit('audioprocess', { currentTime })
|
|
126
|
-
}),
|
|
127
|
-
|
|
128
|
-
this.player.on('play', () => {
|
|
129
|
-
this.emit('play')
|
|
130
|
-
}),
|
|
131
|
-
|
|
132
|
-
this.player.on('pause', () => {
|
|
133
|
-
this.emit('pause')
|
|
134
|
-
}),
|
|
135
|
-
|
|
136
|
-
this.player.on('canplay', () => {
|
|
137
|
-
this.emit('canplay')
|
|
138
|
-
}),
|
|
139
|
-
|
|
140
|
-
this.player.on('seeking', () => {
|
|
141
|
-
this.emit('seek', { time: this.getCurrentTime() })
|
|
142
|
-
}),
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
private initRendererEvents() {
|
|
147
|
-
// Seek on click
|
|
148
|
-
this.subscriptions.push(
|
|
149
|
-
this.renderer.on('click', ({ relativeX }) => {
|
|
150
|
-
const time = this.getDuration() * relativeX
|
|
151
|
-
this.seekTo(time)
|
|
152
|
-
}),
|
|
153
|
-
)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
private initTimerEvents() {
|
|
157
|
-
// The timer fires every 16ms for a smooth progress animation
|
|
158
|
-
this.subscriptions.push(
|
|
159
|
-
this.timer.on('tick', () => {
|
|
160
|
-
if (this.isPlaying()) {
|
|
161
|
-
const currentTime = this.getCurrentTime()
|
|
162
|
-
this.renderer.renderProgress(currentTime / this.duration, true)
|
|
163
|
-
this.emit('audioprocess', { currentTime })
|
|
164
|
-
}
|
|
165
|
-
}),
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/** Unmount wavesurfer */
|
|
170
|
-
public destroy() {
|
|
171
|
-
this.subscriptions.forEach((unsubscribe) => unsubscribe())
|
|
172
|
-
this.plugins.forEach((plugin) => plugin.destroy())
|
|
173
|
-
this.timer.destroy()
|
|
174
|
-
this.player.destroy()
|
|
175
|
-
this.decoder.destroy()
|
|
176
|
-
this.renderer.destroy()
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Load an audio file by URL */
|
|
180
|
-
public async load(url: string, channelData?: Float32Array[], duration?: number) {
|
|
181
|
-
this.player.loadUrl(url)
|
|
182
|
-
|
|
183
|
-
// Fetch and decode the audio of no pre-computed audio data is provided
|
|
184
|
-
if (channelData == null || duration == null) {
|
|
185
|
-
const audio = await this.fetcher.load(url)
|
|
186
|
-
const data = await this.decoder.decode(audio)
|
|
187
|
-
channelData = data.channelData
|
|
188
|
-
duration = data.duration
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.channelData = channelData
|
|
192
|
-
this.duration = duration
|
|
193
|
-
|
|
194
|
-
this.renderer.render(this.channelData, this.duration)
|
|
195
|
-
|
|
196
|
-
this.emit('ready', { duration: this.duration })
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** Zoom in or out */
|
|
200
|
-
public zoom(minPxPerSec: number) {
|
|
201
|
-
if (this.channelData == null || this.duration == null) {
|
|
202
|
-
throw new Error('No audio loaded')
|
|
203
|
-
}
|
|
204
|
-
this.renderer.render(this.channelData, this.duration, minPxPerSec)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/** Start playing the audio */
|
|
208
|
-
public play() {
|
|
209
|
-
this.player.play()
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/** Pause the audio */
|
|
213
|
-
public pause() {
|
|
214
|
-
this.player.pause()
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** Skip to a time position in seconds */
|
|
218
|
-
public seekTo(time: number) {
|
|
219
|
-
this.player.seekTo(time)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** Check if the audio is playing */
|
|
223
|
-
public isPlaying(): boolean {
|
|
224
|
-
return this.player.isPlaying()
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** Get the duration of the audio in seconds */
|
|
228
|
-
public getDuration(): number {
|
|
229
|
-
return this.duration
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** Get the current audio position in seconds */
|
|
233
|
-
public getCurrentTime(): number {
|
|
234
|
-
return this.player.getCurrentTime()
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** Register and initialize a plugin */
|
|
238
|
-
public registerPlugin<T extends BasePlugin<GeneralEventTypes>>(
|
|
239
|
-
CustomPlugin: new (params: WaveSurferPluginParams) => T,
|
|
240
|
-
): T {
|
|
241
|
-
const plugin = new CustomPlugin({
|
|
242
|
-
wavesurfer: this,
|
|
243
|
-
renderer: this.renderer,
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
this.plugins.push(plugin)
|
|
247
|
-
|
|
248
|
-
return plugin
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export default WaveSurfer
|
package/src/player-webaudio.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import Player from './player.js'
|
|
2
|
-
|
|
3
|
-
class WebAudioPlayer extends Player {
|
|
4
|
-
audioCtx: AudioContext | null = null
|
|
5
|
-
sourceNode: MediaElementAudioSourceNode | null = null
|
|
6
|
-
|
|
7
|
-
destroy() {
|
|
8
|
-
this.sourceNode?.disconnect()
|
|
9
|
-
this.sourceNode = null
|
|
10
|
-
this.audioCtx?.close()
|
|
11
|
-
this.audioCtx = null
|
|
12
|
-
|
|
13
|
-
super.destroy()
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
loadUrl(url: string) {
|
|
17
|
-
super.loadUrl(url)
|
|
18
|
-
|
|
19
|
-
if (!this.audioCtx) {
|
|
20
|
-
this.audioCtx = new (window.AudioContext ||
|
|
21
|
-
(window as unknown as { webkitAudioContext: AudioContext }).webkitAudioContext)({
|
|
22
|
-
latencyHint: 'playback',
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (this.sourceNode) {
|
|
27
|
-
this.sourceNode.disconnect()
|
|
28
|
-
}
|
|
29
|
-
this.sourceNode = this.audioCtx.createMediaElementSource(this.media)
|
|
30
|
-
this.sourceNode.connect(this.audioCtx.destination)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export default WebAudioPlayer
|
package/src/player.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
class Player {
|
|
2
|
-
protected media: HTMLMediaElement
|
|
3
|
-
private isExternalMedia = false
|
|
4
|
-
|
|
5
|
-
constructor({ media }: { media?: HTMLMediaElement }) {
|
|
6
|
-
if (media) {
|
|
7
|
-
this.media = media
|
|
8
|
-
this.isExternalMedia = true
|
|
9
|
-
} else {
|
|
10
|
-
this.media = document.createElement('audio')
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
on(event: keyof HTMLMediaElementEventMap, callback: () => void): () => void {
|
|
15
|
-
this.media.addEventListener(event, callback)
|
|
16
|
-
return () => this.media.removeEventListener(event, callback)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
destroy() {
|
|
20
|
-
if (!this.isExternalMedia) {
|
|
21
|
-
this.media.remove()
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
loadUrl(src: string) {
|
|
26
|
-
this.media.src = src
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getCurrentTime() {
|
|
30
|
-
return this.media.currentTime
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
play() {
|
|
34
|
-
this.media.play()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
pause() {
|
|
38
|
-
this.media.pause()
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
isPlaying() {
|
|
42
|
-
return this.media.currentTime > 0 && !this.media.paused && !this.media.ended
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
seekTo(time: number) {
|
|
46
|
-
this.media.currentTime = time
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export default Player
|
package/src/plugins/regions.ts
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import BasePlugin from '../base-plugin.js'
|
|
2
|
-
import { WaveSurferPluginParams } from '../index.js'
|
|
3
|
-
|
|
4
|
-
const MIN_WIDTH = 10
|
|
5
|
-
|
|
6
|
-
type Region = {
|
|
7
|
-
startTime: number
|
|
8
|
-
endTime: number
|
|
9
|
-
title: string
|
|
10
|
-
start: number
|
|
11
|
-
end: number
|
|
12
|
-
element: HTMLElement
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
type RegionsPluginEvents = {
|
|
16
|
-
'region-created': { region: Region }
|
|
17
|
-
'region-updated': { region: Region }
|
|
18
|
-
'region-clicked': { region: Region }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
class RegionsPlugin extends BasePlugin<RegionsPluginEvents> {
|
|
22
|
-
private dragStart = NaN
|
|
23
|
-
private container: HTMLElement
|
|
24
|
-
private regions: Region[] = []
|
|
25
|
-
private createdRegion: Region | null = null
|
|
26
|
-
private modifiedRegion: Region | null = null
|
|
27
|
-
private isResizingLeft = false
|
|
28
|
-
private isMoving = false
|
|
29
|
-
|
|
30
|
-
/** Create an instance of RegionsPlugin */
|
|
31
|
-
constructor(params: WaveSurferPluginParams) {
|
|
32
|
-
super(params)
|
|
33
|
-
|
|
34
|
-
this.container = this.initContainer()
|
|
35
|
-
|
|
36
|
-
const unsubscribeReady = this.wavesurfer.on('ready', () => {
|
|
37
|
-
const wrapper = this.renderer.getContainer()
|
|
38
|
-
wrapper.appendChild(this.container)
|
|
39
|
-
unsubscribeReady()
|
|
40
|
-
})
|
|
41
|
-
this.subscriptions.push(unsubscribeReady)
|
|
42
|
-
|
|
43
|
-
const wsContainer = this.renderer.getContainer()
|
|
44
|
-
wsContainer.addEventListener('mousedown', this.handleMouseDown)
|
|
45
|
-
document.addEventListener('mousemove', this.handleMouseMove)
|
|
46
|
-
document.addEventListener('mouseup', this.handleMouseUp)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Unmounts the regions */
|
|
50
|
-
public destroy() {
|
|
51
|
-
this.renderer.getContainer().removeEventListener('mousedown', this.handleMouseDown)
|
|
52
|
-
document.removeEventListener('mousemove', this.handleMouseMove)
|
|
53
|
-
document.removeEventListener('mouseup', this.handleMouseUp, true)
|
|
54
|
-
|
|
55
|
-
this.container?.remove()
|
|
56
|
-
|
|
57
|
-
super.destroy()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private initContainer(): HTMLElement {
|
|
61
|
-
const div = document.createElement('div')
|
|
62
|
-
div.style.position = 'absolute'
|
|
63
|
-
div.style.top = '0'
|
|
64
|
-
div.style.left = '0'
|
|
65
|
-
div.style.width = '100%'
|
|
66
|
-
div.style.height = '100%'
|
|
67
|
-
div.style.zIndex = '3'
|
|
68
|
-
div.style.pointerEvents = 'none'
|
|
69
|
-
return div
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private handleMouseDown = (e: MouseEvent) => {
|
|
73
|
-
this.dragStart = e.clientX - this.container.getBoundingClientRect().left
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private handleMouseMove = (e: MouseEvent) => {
|
|
77
|
-
const dragEnd = e.clientX - this.container.getBoundingClientRect().left
|
|
78
|
-
|
|
79
|
-
if (this.modifiedRegion && this.isMoving) {
|
|
80
|
-
this.moveRegion(this.modifiedRegion, dragEnd - this.dragStart)
|
|
81
|
-
this.dragStart = dragEnd
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (this.modifiedRegion) {
|
|
86
|
-
this.updateRegion(
|
|
87
|
-
this.modifiedRegion,
|
|
88
|
-
this.isResizingLeft ? dragEnd : undefined,
|
|
89
|
-
this.isResizingLeft ? undefined : dragEnd,
|
|
90
|
-
)
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (!isNaN(this.dragStart)) {
|
|
95
|
-
const dragEnd = e.clientX - this.container.getBoundingClientRect().left
|
|
96
|
-
|
|
97
|
-
if (dragEnd - this.dragStart >= MIN_WIDTH) {
|
|
98
|
-
if (!this.createdRegion) {
|
|
99
|
-
this.renderer.getContainer().style.pointerEvents = 'none'
|
|
100
|
-
|
|
101
|
-
this.createdRegion = this.createRegion(this.dragStart, dragEnd)
|
|
102
|
-
} else {
|
|
103
|
-
this.updateRegion(this.createdRegion, this.dragStart, dragEnd)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private handleMouseUp = () => {
|
|
110
|
-
if (this.createdRegion) {
|
|
111
|
-
this.addRegion(this.createdRegion)
|
|
112
|
-
this.createdRegion = null
|
|
113
|
-
}
|
|
114
|
-
this.modifiedRegion = null
|
|
115
|
-
this.isMoving = false
|
|
116
|
-
this.dragStart = NaN
|
|
117
|
-
this.renderer.getContainer().style.pointerEvents = ''
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private createRegionElement(start: number, end: number, title = ''): HTMLElement {
|
|
121
|
-
const el = document.createElement('div')
|
|
122
|
-
el.style.position = 'absolute'
|
|
123
|
-
el.style.left = `${start}px`
|
|
124
|
-
el.style.width = `${end - start}px`
|
|
125
|
-
el.style.height = '100%'
|
|
126
|
-
el.style.backgroundColor = 'rgba(0, 0, 0, 0.1)'
|
|
127
|
-
el.style.transition = 'background-color 0.2s ease'
|
|
128
|
-
el.style.cursor = 'move'
|
|
129
|
-
el.style.pointerEvents = 'all'
|
|
130
|
-
el.title = title
|
|
131
|
-
|
|
132
|
-
const leftHandle = document.createElement('div')
|
|
133
|
-
leftHandle.style.position = 'absolute'
|
|
134
|
-
leftHandle.style.left = '0'
|
|
135
|
-
leftHandle.style.width = '6px'
|
|
136
|
-
leftHandle.style.height = '100%'
|
|
137
|
-
leftHandle.style.borderLeft = '2px solid rgba(0, 0, 0, 0.5)'
|
|
138
|
-
leftHandle.style.cursor = 'ew-resize'
|
|
139
|
-
leftHandle.style.pointerEvents = 'all'
|
|
140
|
-
el.appendChild(leftHandle)
|
|
141
|
-
|
|
142
|
-
const rightHandle = document.createElement('div')
|
|
143
|
-
rightHandle.style.position = 'absolute'
|
|
144
|
-
rightHandle.style.right = '0'
|
|
145
|
-
rightHandle.style.width = '6px'
|
|
146
|
-
rightHandle.style.height = '100%'
|
|
147
|
-
rightHandle.style.borderRight = '2px solid rgba(0, 0, 0, 0.5)'
|
|
148
|
-
rightHandle.style.cursor = 'ew-resize'
|
|
149
|
-
rightHandle.style.pointerEvents = 'all'
|
|
150
|
-
el.appendChild(rightHandle)
|
|
151
|
-
|
|
152
|
-
leftHandle.addEventListener('mousedown', (e) => {
|
|
153
|
-
e.stopPropagation()
|
|
154
|
-
this.modifiedRegion = this.regions.find((r) => r.element === el) || null
|
|
155
|
-
this.isResizingLeft = true
|
|
156
|
-
this.isMoving = false
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
rightHandle.addEventListener('mousedown', (e) => {
|
|
160
|
-
e.stopPropagation()
|
|
161
|
-
this.modifiedRegion = this.regions.find((r) => r.element === el) || null
|
|
162
|
-
this.isResizingLeft = false
|
|
163
|
-
this.isMoving = false
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
el.addEventListener('mousedown', () => {
|
|
167
|
-
this.modifiedRegion = this.regions.find((r) => r.element === el) || null
|
|
168
|
-
this.isMoving = true
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
el.addEventListener('click', () => {
|
|
172
|
-
const region = this.regions.find((r) => r.element === el)
|
|
173
|
-
if (region) {
|
|
174
|
-
this.emit('region-clicked', { region })
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
this.container.appendChild(el)
|
|
179
|
-
|
|
180
|
-
return el
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
private createRegion(start: number, end: number, title = ''): Region {
|
|
184
|
-
const duration = this.wavesurfer.getDuration()
|
|
185
|
-
const width = this.container.clientWidth
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
element: this.createRegionElement(start, end, title),
|
|
189
|
-
start,
|
|
190
|
-
end,
|
|
191
|
-
startTime: (start / width) * duration,
|
|
192
|
-
endTime: (end / width) * duration,
|
|
193
|
-
title,
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private addRegion(region: Region) {
|
|
198
|
-
this.regions.push(region)
|
|
199
|
-
|
|
200
|
-
this.emit('region-created', { region })
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private updateRegion(region: Region, start?: number, end?: number) {
|
|
204
|
-
if (start != null) {
|
|
205
|
-
region.start = start ?? region.start
|
|
206
|
-
region.element.style.left = `${region.start}px`
|
|
207
|
-
region.startTime = (start / this.container.clientWidth) * this.wavesurfer.getDuration()
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (end != null) {
|
|
211
|
-
region.end = end
|
|
212
|
-
region.element.style.width = `${region.end - region.start}px`
|
|
213
|
-
region.endTime = (end / this.container.clientWidth) * this.wavesurfer.getDuration()
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
this.emit('region-updated', { region })
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private moveRegion(region: Region, delta: number) {
|
|
220
|
-
this.updateRegion(region, region.start + delta, region.end + delta)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** Create a region at a given start and end time, with an optional title */
|
|
224
|
-
public addRegionAtTime(startTime: number, endTime: number, title = '') {
|
|
225
|
-
const duration = this.wavesurfer.getDuration()
|
|
226
|
-
const width = this.container.clientWidth
|
|
227
|
-
const start = (startTime / duration) * width
|
|
228
|
-
const end = (endTime / duration) * width
|
|
229
|
-
const region = this.createRegion(start, end, title)
|
|
230
|
-
this.addRegion(region)
|
|
231
|
-
return region
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** Set the background color of a region */
|
|
235
|
-
public setRegionColor(region: Region, color: string) {
|
|
236
|
-
region.element.style.backgroundColor = color
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export default RegionsPlugin
|