wavesurfer.js 7.0.0-alpha.21 → 7.0.0-alpha.23

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.
@@ -18,13 +18,22 @@ class EnvelopePlugin extends BasePlugin {
18
18
  volume = 1;
19
19
  isFadingIn = false;
20
20
  isFadingOut = false;
21
- constructor(params, options) {
22
- super(params, options);
21
+ constructor(options) {
22
+ super(options);
23
23
  this.options = Object.assign({}, defaultOptions, options);
24
24
  this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
25
25
  this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
26
26
  this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
27
27
  this.volume = this.options.volume ?? 1;
28
+ }
29
+ static create(options) {
30
+ return new EnvelopePlugin(options);
31
+ }
32
+ init(params) {
33
+ super.init(params);
34
+ if (!this.wavesurfer) {
35
+ throw Error('WaveSurfer is not initialized');
36
+ }
28
37
  this.subscriptions.push(this.wavesurfer.once('decode', ({ duration }) => {
29
38
  this.options.startTime = this.options.startTime || 0;
30
39
  this.options.endTime = this.options.endTime || duration;
@@ -48,10 +57,10 @@ class EnvelopePlugin extends BasePlugin {
48
57
  draggable.addEventListener('mousedown', (e) => {
49
58
  let x = e.clientX;
50
59
  let y = e.clientY;
51
- const wasInteractive = this.wavesurfer.options.interactive;
60
+ const wasInteractive = this.wavesurfer?.options.interact || true;
52
61
  let delay;
53
62
  // Make the wavesurfer ignore clicks when we're dragging
54
- this.wavesurfer.toggleInteractive(false);
63
+ this.wavesurfer?.toggleInteractive(false);
55
64
  const move = (e) => {
56
65
  const dx = e.clientX - x;
57
66
  const dy = e.clientY - y;
@@ -66,7 +75,7 @@ class EnvelopePlugin extends BasePlugin {
66
75
  if (delay)
67
76
  clearTimeout(delay);
68
77
  delay = setTimeout(() => {
69
- this.wavesurfer.toggleInteractive(wasInteractive);
78
+ this.wavesurfer?.toggleInteractive(wasInteractive);
70
79
  }, 100);
71
80
  };
72
81
  document.addEventListener('mousemove', move);
@@ -76,7 +85,7 @@ class EnvelopePlugin extends BasePlugin {
76
85
  });
77
86
  }
78
87
  renderPolyline() {
79
- if (!this.svg)
88
+ if (!this.svg || !this.wrapper || !this.wavesurfer)
80
89
  return;
81
90
  const polyline = this.svg.querySelector('polyline');
82
91
  const points = polyline.points;
@@ -99,6 +108,8 @@ class EnvelopePlugin extends BasePlugin {
99
108
  }
100
109
  }
101
110
  initSvg() {
111
+ if (!this.wrapper || !this.wavesurfer)
112
+ return;
102
113
  const width = this.wrapper.clientWidth;
103
114
  const height = this.wrapper.clientHeight;
104
115
  const duration = this.wavesurfer.getDuration();
@@ -180,7 +191,7 @@ class EnvelopePlugin extends BasePlugin {
180
191
  this.emit('fade-out-change', { time: newTime });
181
192
  }
182
193
  // Also allow dragging points vertically
183
- if (dy > 1) {
194
+ if (dy > 1 || dy < -1) {
184
195
  onDragY(dy);
185
196
  }
186
197
  else {
@@ -201,7 +212,7 @@ class EnvelopePlugin extends BasePlugin {
201
212
  super.destroy();
202
213
  }
203
214
  initWebAudio() {
204
- const audio = this.wavesurfer.getMediaElement();
215
+ const audio = this.wavesurfer?.getMediaElement();
205
216
  if (!audio)
206
217
  return null;
207
218
  this.volume = this.options.volume ?? audio.volume;
@@ -233,12 +244,12 @@ class EnvelopePlugin extends BasePlugin {
233
244
  this.gainNode.gain.value = volume;
234
245
  }
235
246
  initFadeEffects() {
236
- if (!this.audioContext)
247
+ if (!this.audioContext || !this.wavesurfer)
237
248
  return;
238
249
  const unsub = this.wavesurfer.on('timeupdate', ({ currentTime }) => {
239
250
  if (!this.audioContext || !this.gainNode)
240
251
  return;
241
- if (!this.wavesurfer.isPlaying())
252
+ if (!this.wavesurfer?.isPlaying())
242
253
  return;
243
254
  if (this.audioContext.state === 'suspended') {
244
255
  this.audioContext.resume();
@@ -281,7 +292,6 @@ class EnvelopePlugin extends BasePlugin {
281
292
  }
282
293
  setStartTime(time) {
283
294
  this.options.startTime = time;
284
- console.log(time);
285
295
  this.renderPolyline();
286
296
  }
287
297
  setEndTime(time) {
@@ -1,5 +1,5 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import { type WaveSurferPluginParams, type WaveSurferOptions } from '../index.js';
2
+ import { type WaveSurferPluginParams, type WaveSurferOptions } from '../wavesurfer.js';
3
3
  export type MinimapPluginOptions = {
4
4
  overlayColor?: string;
5
5
  } & WaveSurferOptions;
@@ -10,16 +10,18 @@ declare const defaultOptions: {
10
10
  export type MinimapPluginEvents = {
11
11
  ready: void;
12
12
  };
13
- declare class TimelinePlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions> {
13
+ declare class MinimapPlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions> {
14
14
  protected options: MinimapPluginOptions & typeof defaultOptions;
15
15
  private minimapWrapper;
16
16
  private miniWavesurfer;
17
17
  private overlay;
18
- constructor(params: WaveSurferPluginParams, options: MinimapPluginOptions);
18
+ constructor(options: MinimapPluginOptions);
19
+ static create(options: MinimapPluginOptions): MinimapPlugin;
20
+ init(params: WaveSurferPluginParams): void;
19
21
  private initMinimapWrapper;
20
22
  private initOverlay;
21
23
  private initMinimap;
22
24
  /** Unmount */
23
25
  destroy(): void;
24
26
  }
25
- export default TimelinePlugin;
27
+ export default MinimapPlugin;
@@ -1,19 +1,29 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import WaveSurfer from '../index.js';
2
+ import WaveSurfer from '../wavesurfer.js';
3
3
  const defaultOptions = {
4
4
  height: 50,
5
5
  overlayColor: 'rgba(100, 100, 100, 0.1)',
6
6
  };
7
- class TimelinePlugin extends BasePlugin {
7
+ class MinimapPlugin extends BasePlugin {
8
8
  options;
9
9
  minimapWrapper;
10
10
  miniWavesurfer = null;
11
11
  overlay;
12
- constructor(params, options) {
13
- super(params, options);
12
+ constructor(options) {
13
+ super(options);
14
14
  this.options = Object.assign({}, defaultOptions, options);
15
15
  this.minimapWrapper = this.initMinimapWrapper();
16
16
  this.overlay = this.initOverlay();
17
+ }
18
+ static create(options) {
19
+ return new MinimapPlugin(options);
20
+ }
21
+ init(params) {
22
+ super.init(params);
23
+ if (!this.wavesurfer) {
24
+ throw Error('WaveSurfer is not initialized');
25
+ }
26
+ this.container?.insertAdjacentElement('afterend', this.minimapWrapper);
17
27
  this.subscriptions.push(this.wavesurfer.on('decode', () => {
18
28
  this.initMinimap();
19
29
  }));
@@ -21,7 +31,6 @@ class TimelinePlugin extends BasePlugin {
21
31
  initMinimapWrapper() {
22
32
  const div = document.createElement('div');
23
33
  div.style.position = 'relative';
24
- this.container.insertAdjacentElement('afterend', div);
25
34
  return div;
26
35
  }
27
36
  initOverlay() {
@@ -32,6 +41,8 @@ class TimelinePlugin extends BasePlugin {
32
41
  return div;
33
42
  }
34
43
  initMinimap() {
44
+ if (!this.wavesurfer || !this.wrapper)
45
+ return;
35
46
  const data = this.wavesurfer.getDecodedData();
36
47
  const media = this.wavesurfer.getMediaElement();
37
48
  if (!data || !media)
@@ -49,7 +60,7 @@ class TimelinePlugin extends BasePlugin {
49
60
  const overlayWidth = Math.round((this.minimapWrapper.clientWidth / this.wrapper.clientWidth) * 100);
50
61
  this.overlay.style.width = `${overlayWidth}%`;
51
62
  this.wavesurfer.on('timeupdate', ({ currentTime }) => {
52
- const offset = Math.max(0, Math.min((currentTime / this.wavesurfer.getDuration()) * 100 - overlayWidth / 2, 100 - overlayWidth)).toFixed(2);
63
+ const offset = Math.max(0, Math.min((currentTime / data.duration) * 100 - overlayWidth / 2, 100 - overlayWidth)).toFixed(2);
53
64
  this.overlay.style.left = `${offset}%`;
54
65
  });
55
66
  }
@@ -60,4 +71,4 @@ class TimelinePlugin extends BasePlugin {
60
71
  super.destroy();
61
72
  }
62
73
  }
63
- export default TimelinePlugin;
74
+ export default MinimapPlugin;
@@ -1,4 +1,4 @@
1
- import { type WaveSurferOptions } from '../index.js';
1
+ import { type WaveSurferOptions } from '../wavesurfer.js';
2
2
  import { type EnvelopePluginOptions } from './envelope.js';
3
3
  import EventEmitter from '../event-emitter.js';
4
4
  export type TrackId = string | number;
@@ -95,7 +95,6 @@ declare class MultiTrack extends EventEmitter<MultitrackEvents> {
95
95
  private updatePosition;
96
96
  private setIsDragging;
97
97
  private onDrag;
98
- private onMove;
99
98
  private findCurrentTracks;
100
99
  private startSync;
101
100
  play(): void;
@@ -1,4 +1,4 @@
1
- import WaveSurfer from '../index.js';
1
+ import WaveSurfer from '../wavesurfer.js';
2
2
  import RegionsPlugin from './regions.js';
3
3
  import TimelinePlugin from './timeline.js';
4
4
  import EnvelopePlugin from './envelope.js';
@@ -39,6 +39,11 @@ class MultiTrack extends EventEmitter {
39
39
  const drag = initDragging(container, (delta) => this.onDrag(index, delta), options.rightButtonDrag);
40
40
  this.wavesurfers[index].once('destroy', () => drag?.destroy());
41
41
  });
42
+ this.rendering.addClickHandler((position) => {
43
+ if (this.isDragging)
44
+ return;
45
+ this.seekTo(position * this.maxDuration);
46
+ });
42
47
  this.emit('canplay');
43
48
  });
44
49
  }
@@ -48,11 +53,6 @@ class MultiTrack extends EventEmitter {
48
53
  return Math.max(max, track.startPosition + durations[index]);
49
54
  }, 0);
50
55
  this.rendering.setMainWidth(durations, this.maxDuration);
51
- this.rendering.addClickHandler((position) => {
52
- if (this.isDragging)
53
- return;
54
- this.seekTo(position * this.maxDuration);
55
- });
56
56
  }
57
57
  initAudio(track) {
58
58
  const audio = new Audio(track.url);
@@ -77,14 +77,14 @@ class MultiTrack extends EventEmitter {
77
77
  peaks: track.peaks,
78
78
  cursorColor: 'transparent',
79
79
  cursorWidth: 0,
80
- interactive: false,
80
+ interact: false,
81
81
  });
82
82
  // Regions and markers
83
- const wsRegions = ws.registerPlugin(RegionsPlugin, {
83
+ const wsRegions = ws.registerPlugin(RegionsPlugin.create({
84
84
  draggable: false,
85
85
  resizable: true,
86
86
  dragSelection: false,
87
- });
87
+ }));
88
88
  this.subscriptions.push(ws.once('decode', () => {
89
89
  // Start and end cues
90
90
  if (track.startCue != null || track.endCue != null) {
@@ -133,14 +133,14 @@ class MultiTrack extends EventEmitter {
133
133
  }
134
134
  }));
135
135
  // Envelope
136
- const envelope = ws.registerPlugin(EnvelopePlugin, {
136
+ const envelope = ws.registerPlugin(EnvelopePlugin.create({
137
137
  ...this.options.envelopeOptions,
138
138
  startTime: track.startCue,
139
139
  endTime: track.endCue,
140
140
  fadeInEnd: track.fadeInEnd,
141
141
  fadeOutStart: track.fadeOutStart,
142
142
  volume: track.volume,
143
- });
143
+ }));
144
144
  this.subscriptions.push(envelope.on('volume-change', ({ volume }) => {
145
145
  this.setIsDragging();
146
146
  this.emit('volume-change', { id: track.id, volume });
@@ -171,16 +171,18 @@ class MultiTrack extends EventEmitter {
171
171
  initTimeline() {
172
172
  if (this.timeline)
173
173
  this.timeline.destroy();
174
- this.timeline = this.wavesurfers[0].registerPlugin(TimelinePlugin, {
174
+ this.timeline = this.wavesurfers[0].registerPlugin(TimelinePlugin.create({
175
175
  duration: this.maxDuration,
176
176
  container: this.rendering.containers[0].parentElement,
177
- });
177
+ }));
178
178
  }
179
179
  updatePosition(time, autoCenter = false) {
180
180
  const precisionSeconds = 0.3;
181
- this.currentTime = time;
182
- this.rendering.updateCursor(time / this.maxDuration, autoCenter);
183
181
  const isPaused = !this.isPlaying();
182
+ if (time !== this.currentTime) {
183
+ this.currentTime = time;
184
+ this.rendering.updateCursor(time / this.maxDuration, autoCenter);
185
+ }
184
186
  // Update the current time of each audio
185
187
  this.tracks.forEach((track, index) => {
186
188
  const audio = this.audios[index];
@@ -212,19 +214,17 @@ class MultiTrack extends EventEmitter {
212
214
  }
213
215
  onDrag(index, delta) {
214
216
  this.setIsDragging();
215
- const newTime = this.tracks[index].startPosition + delta * this.maxDuration;
216
- this.onMove(index, newTime);
217
- }
218
- onMove(index, newStartPosition) {
219
217
  const track = this.tracks[index];
220
218
  if (!track.draggable)
221
219
  return;
220
+ const newStartPosition = track.startPosition + delta * this.maxDuration;
222
221
  const mainIndex = this.tracks.findIndex((item) => item.url && !item.draggable);
223
222
  const mainTrack = this.tracks[mainIndex];
224
223
  const minStart = (mainTrack ? mainTrack.startPosition : 0) - this.durations[index];
225
224
  const maxStart = mainTrack ? mainTrack.startPosition + this.durations[mainIndex] : this.maxDuration;
226
225
  if (newStartPosition >= minStart && newStartPosition <= maxStart) {
227
226
  track.startPosition = newStartPosition;
227
+ this.initDurations(this.durations);
228
228
  this.rendering.setContainerOffsets();
229
229
  this.updatePosition(this.currentTime);
230
230
  this.emit('start-position-change', { id: track.id, startPosition: newStartPosition });
@@ -1,5 +1,5 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import type { WaveSurferPluginParams } from '../index.js';
2
+ import type { WaveSurferPluginParams } from '../wavesurfer.js';
3
3
  export type RegionsPluginOptions = {
4
4
  dragSelection?: boolean;
5
5
  draggable?: boolean;
@@ -32,8 +32,11 @@ declare class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPlugi
32
32
  private modifiedRegion;
33
33
  private isResizingLeft;
34
34
  private isMoving;
35
+ private wasInteractive;
35
36
  /** Create an instance of RegionsPlugin */
36
- constructor(params: WaveSurferPluginParams, options: RegionsPluginOptions);
37
+ constructor(options?: RegionsPluginOptions);
38
+ static create(options?: RegionsPluginOptions): RegionsPlugin;
39
+ init(params: WaveSurferPluginParams): void;
37
40
  /** Unmount */
38
41
  destroy(): void;
39
42
  private initRegionsContainer;
@@ -23,13 +23,23 @@ class RegionsPlugin extends BasePlugin {
23
23
  modifiedRegion = null;
24
24
  isResizingLeft = false;
25
25
  isMoving = false;
26
+ wasInteractive = true;
26
27
  /** Create an instance of RegionsPlugin */
27
- constructor(params, options) {
28
- super(params, options);
28
+ constructor(options) {
29
+ super(options || {});
29
30
  this.options = Object.assign({}, defaultOptions, options);
30
31
  this.regionsContainer = this.initRegionsContainer();
32
+ }
33
+ static create(options) {
34
+ return new RegionsPlugin(options);
35
+ }
36
+ init(params) {
37
+ super.init(params);
38
+ if (!this.wavesurfer || !this.wrapper) {
39
+ throw Error('WaveSurfer is not initialized');
40
+ }
31
41
  this.subscriptions.push(this.wavesurfer.once('decode', () => {
32
- this.wrapper.appendChild(this.regionsContainer);
42
+ this.wrapper?.appendChild(this.regionsContainer);
33
43
  }));
34
44
  this.wrapper.addEventListener('mousedown', this.handleMouseDown);
35
45
  document.addEventListener('mousemove', this.handleMouseMove);
@@ -37,7 +47,7 @@ class RegionsPlugin extends BasePlugin {
37
47
  }
38
48
  /** Unmount */
39
49
  destroy() {
40
- this.wrapper.removeEventListener('mousedown', this.handleMouseDown);
50
+ this.wrapper?.removeEventListener('mousedown', this.handleMouseDown);
41
51
  document.removeEventListener('mousemove', this.handleMouseMove);
42
52
  document.removeEventListener('mouseup', this.handleMouseUp, true);
43
53
  this.regionsContainer.remove();
@@ -56,10 +66,14 @@ class RegionsPlugin extends BasePlugin {
56
66
  }
57
67
  handleMouseDown = (e) => {
58
68
  if (this.options.draggable || this.options.resizable || this.options.dragSelection) {
69
+ if (!this.wrapper)
70
+ return;
59
71
  this.dragStart = e.clientX - this.wrapper.getBoundingClientRect().left;
60
72
  }
61
73
  };
62
74
  handleMouseMove = (e) => {
75
+ if (!this.wrapper)
76
+ return;
63
77
  const box = this.wrapper.getBoundingClientRect();
64
78
  const { width } = box;
65
79
  const dragEnd = e.clientX - box.left;
@@ -76,7 +90,8 @@ class RegionsPlugin extends BasePlugin {
76
90
  const dragEnd = e.clientX - this.regionsContainer.getBoundingClientRect().left;
77
91
  if (dragEnd - this.dragStart >= MIN_WIDTH) {
78
92
  if (!this.createdRegion) {
79
- this.wrapper.style.pointerEvents = 'none';
93
+ this.wasInteractive = this.wavesurfer?.options.interact || true;
94
+ this.wavesurfer?.toggleInteractive(false);
80
95
  this.createdRegion = this.createRegion(this.dragStart / width, dragEnd / width);
81
96
  }
82
97
  else {
@@ -93,7 +108,7 @@ class RegionsPlugin extends BasePlugin {
93
108
  this.modifiedRegion = null;
94
109
  this.isMoving = false;
95
110
  this.dragStart = NaN;
96
- this.wrapper.style.pointerEvents = '';
111
+ this.wavesurfer?.toggleInteractive(this.wasInteractive);
97
112
  };
98
113
  createRegionElement(start, end, title = '') {
99
114
  const noWidth = start === end;
@@ -181,7 +196,7 @@ class RegionsPlugin extends BasePlugin {
181
196
  return div;
182
197
  }
183
198
  createRegion(start, end, title = '') {
184
- const duration = this.wavesurfer.getDuration();
199
+ const duration = this.wavesurfer?.getDuration() || 0;
185
200
  return {
186
201
  element: this.createRegionElement(start, end, title),
187
202
  start,
@@ -196,16 +211,17 @@ class RegionsPlugin extends BasePlugin {
196
211
  this.emit('region-created', { region });
197
212
  }
198
213
  updateRegion(region, start, end) {
214
+ const duration = this.wavesurfer?.getDuration() || 0;
199
215
  if (start != null) {
200
216
  region.start = start;
201
217
  region.element.style.left = `${region.start * 100}%`;
202
218
  region.element.style.width = `${(region.end - region.start) * 100}%`;
203
- region.startTime = start * this.wavesurfer.getDuration();
219
+ region.startTime = start * duration;
204
220
  }
205
221
  if (end != null) {
206
222
  region.end = end;
207
223
  region.element.style.width = `${(region.end - region.start) * 100}%`;
208
- region.endTime = end * this.wavesurfer.getDuration();
224
+ region.endTime = end * duration;
209
225
  }
210
226
  this.emit('region-updated', { region });
211
227
  }
@@ -214,7 +230,7 @@ class RegionsPlugin extends BasePlugin {
214
230
  }
215
231
  /** Create a region at a given start and end time, with an optional title */
216
232
  add(startTime, endTime, title = '', color = '') {
217
- const duration = this.wavesurfer.getDuration();
233
+ const duration = this.wavesurfer?.getDuration() || 0;
218
234
  const start = startTime / duration;
219
235
  const end = endTime / duration;
220
236
  const region = this.createRegion(start, end, title);
@@ -1,5 +1,5 @@
1
1
  import BasePlugin from '../base-plugin.js';
2
- import type { WaveSurferPluginParams } from '../index.js';
2
+ import type { WaveSurferPluginParams } from '../wavesurfer.js';
3
3
  export type TimelinePluginOptions = {
4
4
  /** The height of the timeline in pixels, defaults to 20 */
5
5
  height?: number;
@@ -23,7 +23,9 @@ export type TimelinePluginEvents = {
23
23
  declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
24
24
  private timelineWrapper;
25
25
  protected options: TimelinePluginOptions & typeof defaultOptions;
26
- constructor(params: WaveSurferPluginParams, options: TimelinePluginOptions);
26
+ constructor(options: TimelinePluginOptions);
27
+ static create(options: TimelinePluginOptions): TimelinePlugin;
28
+ init(params: WaveSurferPluginParams): void;
27
29
  /** Unmount */
28
30
  destroy(): void;
29
31
  private initTimelineWrapper;
@@ -5,12 +5,21 @@ const defaultOptions = {
5
5
  class TimelinePlugin extends BasePlugin {
6
6
  timelineWrapper;
7
7
  options;
8
- constructor(params, options) {
9
- super(params, options);
8
+ constructor(options) {
9
+ super(options);
10
10
  this.options = Object.assign({}, defaultOptions, options);
11
- this.container = this.options.container ?? this.wrapper;
12
11
  this.timelineWrapper = this.initTimelineWrapper();
13
- this.container.appendChild(this.timelineWrapper);
12
+ }
13
+ static create(options) {
14
+ return new TimelinePlugin(options);
15
+ }
16
+ init(params) {
17
+ super.init(params);
18
+ if (!this.wavesurfer || !this.wrapper) {
19
+ throw Error('WaveSurfer is not initialized');
20
+ }
21
+ const container = this.options.container ?? this.wrapper;
22
+ container.appendChild(this.timelineWrapper);
14
23
  if (this.options.duration) {
15
24
  this.initTimeline(this.options.duration);
16
25
  }
@@ -11,7 +11,7 @@ type RendererOptions = {
11
11
  barWidth?: number;
12
12
  barGap?: number;
13
13
  barRadius?: number;
14
- noScrollbar?: boolean;
14
+ hideScrollbar?: boolean;
15
15
  };
16
16
  type RendererEvents = {
17
17
  click: {
@@ -27,6 +27,8 @@ declare class Renderer extends EventEmitter<RendererEvents> {
27
27
  private progressWrapper;
28
28
  private timeout;
29
29
  private isScrolling;
30
+ private audioData;
31
+ private resizeObserver;
30
32
  constructor(options: RendererOptions);
31
33
  getContainer(): HTMLElement;
32
34
  getWrapper(): HTMLElement;
package/dist/renderer.js CHANGED
@@ -8,6 +8,8 @@ class Renderer extends EventEmitter {
8
8
  progressWrapper;
9
9
  timeout = null;
10
10
  isScrolling = false;
11
+ audioData = null;
12
+ resizeObserver = null;
11
13
  constructor(options) {
12
14
  super();
13
15
  this.options = options;
@@ -33,10 +35,10 @@ class Renderer extends EventEmitter {
33
35
  overflow-y: hidden;
34
36
  width: 100%;
35
37
  position: relative;
36
- ${this.options.noScrollbar ? 'scrollbar-color: transparent;' : ''}
38
+ ${this.options.hideScrollbar ? 'scrollbar-color: transparent;' : ''}
37
39
  }
38
40
  :host ::-webkit-scrollbar {
39
- display: ${this.options.noScrollbar ? 'none' : 'auto'};
41
+ display: ${this.options.hideScrollbar ? 'none' : 'auto'};
40
42
  }
41
43
  :host .wrapper {
42
44
  position: relative;
@@ -89,6 +91,11 @@ class Renderer extends EventEmitter {
89
91
  const relativeX = x / rect.width;
90
92
  this.emit('click', { relativeX });
91
93
  });
94
+ // Re-render the waveform on container resize
95
+ this.resizeObserver = new ResizeObserver(() => {
96
+ this.delay(() => this.audioData && this.zoom(this.audioData, this.options.minPxPerSec), 100);
97
+ });
98
+ this.resizeObserver.observe(this.scrollContainer);
92
99
  }
93
100
  getContainer() {
94
101
  return this.scrollContainer;
@@ -98,6 +105,7 @@ class Renderer extends EventEmitter {
98
105
  }
99
106
  destroy() {
100
107
  this.container.remove();
108
+ this.resizeObserver?.disconnect();
101
109
  }
102
110
  delay(fn, delayMs = 10) {
103
111
  if (this.timeout) {
@@ -178,11 +186,12 @@ class Renderer extends EventEmitter {
178
186
  // Clear the canvas
179
187
  this.canvasWrapper.innerHTML = '';
180
188
  this.progressWrapper.innerHTML = '';
181
- // Draw the currently visible part of the waveform
189
+ // Determine the currently visible part of the waveform
182
190
  const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
183
191
  const scale = len / scrollWidth;
184
192
  const start = Math.floor(scrollLeft * scale);
185
193
  const end = Math.ceil(Math.min(scrollWidth, scrollLeft + clientWidth) * scale);
194
+ // Draw the visible portion of the waveform
186
195
  draw(start, end);
187
196
  // Draw the rest of the waveform with a timeout for better performance
188
197
  const step = end - start;
@@ -198,30 +207,34 @@ class Renderer extends EventEmitter {
198
207
  }
199
208
  }
200
209
  render(audioData) {
201
- // Determine the width of the canvas
210
+ // Determine the width of the waveform
202
211
  const pixelRatio = window.devicePixelRatio || 1;
203
212
  const parentWidth = this.options.fillParent ? this.scrollContainer.clientWidth * pixelRatio : 0;
204
- const scrollWidth = audioData.duration * this.options.minPxPerSec;
205
- this.isScrolling = scrollWidth > parentWidth;
206
- const width = Math.max(1, this.isScrolling ? scrollWidth : parentWidth);
213
+ const scrollWidth = Math.max(1, audioData.duration * this.options.minPxPerSec);
214
+ const width = scrollWidth > parentWidth ? scrollWidth : parentWidth;
207
215
  const { height } = this.options;
208
- // Remember the current cursor position
209
- const oldCursorPosition = this.progressWrapper.clientWidth;
216
+ // Set the width of the container
217
+ this.isScrolling = width > parentWidth;
210
218
  this.scrollContainer.style.overflowX = this.isScrolling ? 'auto' : 'hidden';
211
219
  this.wrapper.style.width = `${Math.floor(width / pixelRatio)}px`;
212
- // Adjust the scroll position so that the cursor stays in the same place
213
- const newCursortPosition = this.progressWrapper.clientWidth;
214
- this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
215
- // First two channels are used
220
+ // Only the first two channels are used
216
221
  const channelData = [audioData.getChannelData(0)];
217
222
  if (audioData.numberOfChannels > 1) {
218
223
  channelData.push(audioData.getChannelData(1));
219
224
  }
225
+ // Render the waveform
220
226
  this.renderPeaks(channelData, width, height, pixelRatio);
227
+ this.audioData = audioData;
221
228
  }
222
229
  zoom(audioData, minPxPerSec) {
230
+ // Remember the current cursor position
231
+ const oldCursorPosition = this.progressWrapper.clientWidth;
232
+ // Set the new zoom level and re-render the waveform
223
233
  this.options.minPxPerSec = minPxPerSec;
224
234
  this.render(audioData);
235
+ // Adjust the scroll position so that the cursor stays in the same place
236
+ const newCursortPosition = this.progressWrapper.clientWidth;
237
+ this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
225
238
  }
226
239
  renderProgress(progress, autoCenter = false) {
227
240
  this.progressWrapper.style.width = `${progress * 100}%`;