wavesurfer.js 7.11.0 → 7.12.0
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/__tests__/memory-leaks.test.d.ts +7 -0
- package/dist/__tests__/memory-leaks.test.js +184 -0
- package/dist/__tests__/player.test.js +107 -0
- package/dist/__tests__/renderer-utils.test.d.ts +1 -0
- package/dist/__tests__/renderer-utils.test.js +373 -0
- package/dist/decoder.js +12 -4
- package/dist/draggable.d.ts +4 -0
- package/dist/draggable.js +20 -5
- package/dist/fetcher.js +14 -23
- package/dist/player.d.ts +21 -0
- package/dist/player.js +86 -0
- package/dist/plugins/envelope.cjs +1 -1
- package/dist/plugins/envelope.esm.js +1 -1
- package/dist/plugins/envelope.js +1 -1
- package/dist/plugins/envelope.min.js +1 -1
- package/dist/plugins/hover.cjs +1 -1
- package/dist/plugins/hover.d.ts +0 -3
- package/dist/plugins/hover.esm.js +1 -1
- package/dist/plugins/hover.js +1 -1
- package/dist/plugins/hover.min.js +1 -1
- package/dist/plugins/minimap.cjs +1 -1
- package/dist/plugins/minimap.esm.js +1 -1
- package/dist/plugins/minimap.js +1 -1
- package/dist/plugins/minimap.min.js +1 -1
- package/dist/plugins/record.cjs +1 -1
- package/dist/plugins/record.esm.js +1 -1
- package/dist/plugins/record.js +1 -1
- package/dist/plugins/record.min.js +1 -1
- package/dist/plugins/regions.cjs +1 -1
- package/dist/plugins/regions.esm.js +1 -1
- package/dist/plugins/regions.js +1 -1
- package/dist/plugins/regions.min.js +1 -1
- package/dist/plugins/timeline.cjs +1 -1
- package/dist/plugins/timeline.d.ts +1 -1
- package/dist/plugins/timeline.esm.js +1 -1
- package/dist/plugins/timeline.js +1 -1
- package/dist/plugins/timeline.min.js +1 -1
- package/dist/plugins/zoom.cjs +1 -1
- package/dist/plugins/zoom.d.ts +10 -1
- package/dist/plugins/zoom.esm.js +1 -1
- package/dist/plugins/zoom.js +1 -1
- package/dist/plugins/zoom.min.js +1 -1
- package/dist/reactive/__tests__/drag-stream.test.d.ts +1 -0
- package/dist/reactive/__tests__/drag-stream.test.js +199 -0
- package/dist/reactive/__tests__/event-stream-emitter.test.d.ts +1 -0
- package/dist/reactive/__tests__/event-stream-emitter.test.js +230 -0
- package/dist/reactive/__tests__/event-streams.test.d.ts +1 -0
- package/dist/reactive/__tests__/event-streams.test.js +278 -0
- package/dist/reactive/__tests__/media-event-bridge.test.d.ts +1 -0
- package/dist/reactive/__tests__/media-event-bridge.test.js +202 -0
- package/dist/reactive/__tests__/render-scheduler.test.d.ts +1 -0
- package/dist/reactive/__tests__/render-scheduler.test.js +216 -0
- package/dist/reactive/__tests__/scroll-stream.test.d.ts +1 -0
- package/dist/reactive/__tests__/scroll-stream.test.js +191 -0
- package/dist/reactive/__tests__/state-event-emitter.test.d.ts +1 -0
- package/dist/reactive/__tests__/state-event-emitter.test.js +234 -0
- package/dist/reactive/__tests__/store.test.d.ts +1 -0
- package/dist/reactive/__tests__/store.test.js +271 -0
- package/dist/reactive/drag-stream.d.ts +48 -0
- package/dist/reactive/drag-stream.js +146 -0
- package/dist/reactive/event-stream-emitter.d.ts +104 -0
- package/dist/reactive/event-stream-emitter.js +151 -0
- package/dist/reactive/event-streams.d.ts +65 -0
- package/dist/reactive/event-streams.js +146 -0
- package/dist/reactive/media-event-bridge.d.ts +51 -0
- package/dist/reactive/media-event-bridge.js +152 -0
- package/dist/reactive/render-scheduler.d.ts +43 -0
- package/dist/reactive/render-scheduler.js +75 -0
- package/dist/reactive/scroll-stream.d.ts +93 -0
- package/dist/reactive/scroll-stream.js +131 -0
- package/dist/reactive/state-event-emitter.d.ts +105 -0
- package/dist/reactive/state-event-emitter.js +213 -0
- package/dist/reactive/store.d.ts +66 -0
- package/dist/reactive/store.js +115 -0
- package/dist/renderer-utils.d.ts +126 -0
- package/dist/renderer-utils.js +252 -0
- package/dist/renderer.d.ts +4 -4
- package/dist/renderer.js +133 -197
- package/dist/state/__tests__/wavesurfer-state.test.d.ts +1 -0
- package/dist/state/__tests__/wavesurfer-state.test.js +268 -0
- package/dist/state/wavesurfer-state.d.ts +90 -0
- package/dist/state/wavesurfer-state.js +124 -0
- package/dist/types.d.ts +167 -1
- package/dist/wavesurfer.cjs +1 -1
- package/dist/wavesurfer.d.ts +18 -0
- package/dist/wavesurfer.esm.js +1 -1
- package/dist/wavesurfer.js +46 -8
- package/dist/wavesurfer.min.js +1 -1
- package/dist/webaudio.js +4 -0
- package/package.json +6 -6
package/dist/renderer.js
CHANGED
|
@@ -18,8 +18,11 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
18
18
|
}
|
|
19
19
|
return t;
|
|
20
20
|
};
|
|
21
|
-
import { makeDraggable } from './draggable.js';
|
|
22
21
|
import EventEmitter from './event-emitter.js';
|
|
22
|
+
import * as utils from './renderer-utils.js';
|
|
23
|
+
import { createDragStream } from './reactive/drag-stream.js';
|
|
24
|
+
import { createScrollStream } from './reactive/scroll-stream.js';
|
|
25
|
+
import { effect } from './reactive/store.js';
|
|
23
26
|
class Renderer extends EventEmitter {
|
|
24
27
|
constructor(options, audioElement) {
|
|
25
28
|
super();
|
|
@@ -31,7 +34,8 @@ class Renderer extends EventEmitter {
|
|
|
31
34
|
this.isDragging = false;
|
|
32
35
|
this.subscriptions = [];
|
|
33
36
|
this.unsubscribeOnScroll = [];
|
|
34
|
-
this.
|
|
37
|
+
this.dragStream = null;
|
|
38
|
+
this.scrollStream = null;
|
|
35
39
|
this.subscriptions = [];
|
|
36
40
|
this.options = options;
|
|
37
41
|
const parent = this.parentFromOptionsContainer(options.container);
|
|
@@ -63,35 +67,30 @@ class Renderer extends EventEmitter {
|
|
|
63
67
|
return parent;
|
|
64
68
|
}
|
|
65
69
|
initEvents() {
|
|
66
|
-
const getClickPosition = (e) => {
|
|
67
|
-
const rect = this.wrapper.getBoundingClientRect();
|
|
68
|
-
const x = e.clientX - rect.left;
|
|
69
|
-
const y = e.clientY - rect.top;
|
|
70
|
-
const relativeX = x / rect.width;
|
|
71
|
-
const relativeY = y / rect.height;
|
|
72
|
-
return [relativeX, relativeY];
|
|
73
|
-
};
|
|
74
70
|
// Add a click listener
|
|
75
71
|
this.wrapper.addEventListener('click', (e) => {
|
|
76
|
-
const
|
|
72
|
+
const rect = this.wrapper.getBoundingClientRect();
|
|
73
|
+
const [x, y] = utils.getRelativePointerPosition(rect, e.clientX, e.clientY);
|
|
77
74
|
this.emit('click', x, y);
|
|
78
75
|
});
|
|
79
76
|
// Add a double click listener
|
|
80
77
|
this.wrapper.addEventListener('dblclick', (e) => {
|
|
81
|
-
const
|
|
78
|
+
const rect = this.wrapper.getBoundingClientRect();
|
|
79
|
+
const [x, y] = utils.getRelativePointerPosition(rect, e.clientX, e.clientY);
|
|
82
80
|
this.emit('dblclick', x, y);
|
|
83
81
|
});
|
|
84
82
|
// Drag
|
|
85
83
|
if (this.options.dragToSeek === true || typeof this.options.dragToSeek === 'object') {
|
|
86
84
|
this.initDrag();
|
|
87
85
|
}
|
|
88
|
-
// Add a scroll listener
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
const startX
|
|
92
|
-
const
|
|
93
|
-
this.emit('scroll', startX, endX,
|
|
94
|
-
});
|
|
86
|
+
// Add a scroll listener using reactive stream
|
|
87
|
+
this.scrollStream = createScrollStream(this.scrollContainer);
|
|
88
|
+
const unsubscribeScroll = effect(() => {
|
|
89
|
+
const { startX, endX } = this.scrollStream.percentages.value;
|
|
90
|
+
const { left, right } = this.scrollStream.bounds.value;
|
|
91
|
+
this.emit('scroll', startX, endX, left, right);
|
|
92
|
+
}, [this.scrollStream.percentages, this.scrollStream.bounds]);
|
|
93
|
+
this.subscriptions.push(unsubscribeScroll);
|
|
95
94
|
// Re-render the waveform on container resize
|
|
96
95
|
if (typeof ResizeObserver === 'function') {
|
|
97
96
|
const delay = this.createDelay(100);
|
|
@@ -109,43 +108,32 @@ class Renderer extends EventEmitter {
|
|
|
109
108
|
return;
|
|
110
109
|
this.lastContainerWidth = width;
|
|
111
110
|
this.reRender();
|
|
111
|
+
this.emit('resize');
|
|
112
112
|
}
|
|
113
113
|
initDrag() {
|
|
114
114
|
// Don't initialize drag if it's already set up
|
|
115
|
-
if (this.
|
|
115
|
+
if (this.dragStream)
|
|
116
116
|
return;
|
|
117
|
-
this.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const numberOfChannels = ((_a = this.audioData) === null || _a === void 0 ? void 0 : _a.numberOfChannels) || 1;
|
|
138
|
-
if (optionsHeight == null)
|
|
139
|
-
return defaultHeight;
|
|
140
|
-
if (!isNaN(Number(optionsHeight)))
|
|
141
|
-
return Number(optionsHeight);
|
|
142
|
-
if (optionsHeight === 'auto') {
|
|
143
|
-
const height = this.parent.clientHeight || defaultHeight;
|
|
144
|
-
if (optionsSplitChannel === null || optionsSplitChannel === void 0 ? void 0 : optionsSplitChannel.every((channel) => !channel.overlay))
|
|
145
|
-
return height / numberOfChannels;
|
|
146
|
-
return height;
|
|
147
|
-
}
|
|
148
|
-
return defaultHeight;
|
|
117
|
+
this.dragStream = createDragStream(this.wrapper);
|
|
118
|
+
const unsubscribeDrag = effect(() => {
|
|
119
|
+
const drag = this.dragStream.signal.value;
|
|
120
|
+
if (!drag)
|
|
121
|
+
return;
|
|
122
|
+
const width = this.wrapper.getBoundingClientRect().width;
|
|
123
|
+
const relX = utils.clampToUnit(drag.x / width);
|
|
124
|
+
if (drag.type === 'start') {
|
|
125
|
+
this.isDragging = true;
|
|
126
|
+
this.emit('dragstart', relX);
|
|
127
|
+
}
|
|
128
|
+
else if (drag.type === 'move') {
|
|
129
|
+
this.emit('drag', relX);
|
|
130
|
+
}
|
|
131
|
+
else if (drag.type === 'end') {
|
|
132
|
+
this.isDragging = false;
|
|
133
|
+
this.emit('dragend', relX);
|
|
134
|
+
}
|
|
135
|
+
}, [this.dragStream.signal]);
|
|
136
|
+
this.subscriptions.push(unsubscribeDrag);
|
|
149
137
|
}
|
|
150
138
|
initHtml() {
|
|
151
139
|
const div = document.createElement('div');
|
|
@@ -182,6 +170,7 @@ class Renderer extends EventEmitter {
|
|
|
182
170
|
}
|
|
183
171
|
:host .canvases {
|
|
184
172
|
min-height: ${this.getHeight(this.options.height, this.options.splitChannels)}px;
|
|
173
|
+
pointer-events: none;
|
|
185
174
|
}
|
|
186
175
|
:host .canvases > div {
|
|
187
176
|
position: relative;
|
|
@@ -267,6 +256,14 @@ class Renderer extends EventEmitter {
|
|
|
267
256
|
}
|
|
268
257
|
(_a = this.unsubscribeOnScroll) === null || _a === void 0 ? void 0 : _a.forEach((unsubscribe) => unsubscribe());
|
|
269
258
|
this.unsubscribeOnScroll = [];
|
|
259
|
+
if (this.dragStream) {
|
|
260
|
+
this.dragStream.cleanup();
|
|
261
|
+
this.dragStream = null;
|
|
262
|
+
}
|
|
263
|
+
if (this.scrollStream) {
|
|
264
|
+
this.scrollStream.cleanup();
|
|
265
|
+
this.scrollStream = null;
|
|
266
|
+
}
|
|
270
267
|
}
|
|
271
268
|
createDelay(delayMs = 10) {
|
|
272
269
|
let timeout;
|
|
@@ -297,123 +294,88 @@ class Renderer extends EventEmitter {
|
|
|
297
294
|
});
|
|
298
295
|
};
|
|
299
296
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const ctx = canvasElement.getContext('2d');
|
|
310
|
-
const gradientHeight = canvasElement.height * (window.devicePixelRatio || 1);
|
|
311
|
-
const gradient = ctx.createLinearGradient(0, 0, 0, gradientHeight);
|
|
312
|
-
const colorStopPercentage = 1 / (color.length - 1);
|
|
313
|
-
color.forEach((color, index) => {
|
|
314
|
-
const offset = index * colorStopPercentage;
|
|
315
|
-
gradient.addColorStop(offset, color);
|
|
297
|
+
getHeight(optionsHeight, optionsSplitChannel) {
|
|
298
|
+
var _a;
|
|
299
|
+
const numberOfChannels = ((_a = this.audioData) === null || _a === void 0 ? void 0 : _a.numberOfChannels) || 1;
|
|
300
|
+
return utils.resolveChannelHeight({
|
|
301
|
+
optionsHeight,
|
|
302
|
+
optionsSplitChannels: optionsSplitChannel,
|
|
303
|
+
parentHeight: this.parent.clientHeight,
|
|
304
|
+
numberOfChannels,
|
|
305
|
+
defaultHeight: utils.DEFAULT_HEIGHT,
|
|
316
306
|
});
|
|
317
|
-
|
|
307
|
+
}
|
|
308
|
+
convertColorValues(color) {
|
|
309
|
+
return utils.resolveColorValue(color, this.getPixelRatio());
|
|
318
310
|
}
|
|
319
311
|
getPixelRatio() {
|
|
320
|
-
return
|
|
312
|
+
return utils.getPixelRatio(window.devicePixelRatio);
|
|
321
313
|
}
|
|
322
314
|
renderBarWaveform(channelData, options, ctx, vScale) {
|
|
323
|
-
const topChannel = channelData[0];
|
|
324
|
-
const bottomChannel = channelData[1] || channelData[0];
|
|
325
|
-
const length = topChannel.length;
|
|
326
315
|
const { width, height } = ctx.canvas;
|
|
327
|
-
const halfHeight
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
316
|
+
const { halfHeight, barWidth, barRadius, barIndexScale, barSpacing, barMinHeight } = utils.calculateBarRenderConfig({
|
|
317
|
+
width,
|
|
318
|
+
height,
|
|
319
|
+
length: (channelData[0] || []).length,
|
|
320
|
+
options,
|
|
321
|
+
pixelRatio: this.getPixelRatio(),
|
|
322
|
+
});
|
|
323
|
+
const segments = utils.calculateBarSegments({
|
|
324
|
+
channelData,
|
|
325
|
+
barIndexScale,
|
|
326
|
+
barSpacing,
|
|
327
|
+
barWidth,
|
|
328
|
+
halfHeight,
|
|
329
|
+
vScale,
|
|
330
|
+
canvasHeight: height,
|
|
331
|
+
barAlign: options.barAlign,
|
|
332
|
+
barMinHeight,
|
|
333
|
+
});
|
|
334
334
|
ctx.beginPath();
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const bottomBarHeight = Math.round(maxBottom * halfHeight * vScale);
|
|
343
|
-
const barHeight = topBarHeight + bottomBarHeight || 1;
|
|
344
|
-
// Vertical alignment
|
|
345
|
-
let y = halfHeight - topBarHeight;
|
|
346
|
-
if (options.barAlign === 'top') {
|
|
347
|
-
y = 0;
|
|
348
|
-
}
|
|
349
|
-
else if (options.barAlign === 'bottom') {
|
|
350
|
-
y = height - barHeight;
|
|
351
|
-
}
|
|
352
|
-
ctx[rectFn](prevX * (barWidth + barGap), y, barWidth, barHeight, barRadius);
|
|
353
|
-
prevX = x;
|
|
354
|
-
maxTop = 0;
|
|
355
|
-
maxBottom = 0;
|
|
335
|
+
for (const segment of segments) {
|
|
336
|
+
if (barRadius && 'roundRect' in ctx) {
|
|
337
|
+
;
|
|
338
|
+
ctx.roundRect(segment.x, segment.y, segment.width, segment.height, barRadius);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
ctx.rect(segment.x, segment.y, segment.width, segment.height);
|
|
356
342
|
}
|
|
357
|
-
const magnitudeTop = Math.abs(topChannel[i] || 0);
|
|
358
|
-
const magnitudeBottom = Math.abs(bottomChannel[i] || 0);
|
|
359
|
-
if (magnitudeTop > maxTop)
|
|
360
|
-
maxTop = magnitudeTop;
|
|
361
|
-
if (magnitudeBottom > maxBottom)
|
|
362
|
-
maxBottom = magnitudeBottom;
|
|
363
343
|
}
|
|
364
344
|
ctx.fill();
|
|
365
345
|
ctx.closePath();
|
|
366
346
|
}
|
|
367
347
|
renderLineWaveform(channelData, _options, ctx, vScale) {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
const length = channel.length;
|
|
371
|
-
const { height } = ctx.canvas;
|
|
372
|
-
const halfHeight = height / 2;
|
|
373
|
-
const hScale = ctx.canvas.width / length;
|
|
374
|
-
ctx.moveTo(0, halfHeight);
|
|
375
|
-
let prevX = 0;
|
|
376
|
-
let max = 0;
|
|
377
|
-
for (let i = 0; i <= length; i++) {
|
|
378
|
-
const x = Math.round(i * hScale);
|
|
379
|
-
if (x > prevX) {
|
|
380
|
-
const h = Math.round(max * halfHeight * vScale) || 1;
|
|
381
|
-
const y = halfHeight + h * (index === 0 ? -1 : 1);
|
|
382
|
-
ctx.lineTo(prevX, y);
|
|
383
|
-
prevX = x;
|
|
384
|
-
max = 0;
|
|
385
|
-
}
|
|
386
|
-
const value = Math.abs(channel[i] || 0);
|
|
387
|
-
if (value > max)
|
|
388
|
-
max = value;
|
|
389
|
-
}
|
|
390
|
-
ctx.lineTo(prevX, halfHeight);
|
|
391
|
-
};
|
|
348
|
+
const { width, height } = ctx.canvas;
|
|
349
|
+
const paths = utils.calculateLinePaths({ channelData, width, height, vScale });
|
|
392
350
|
ctx.beginPath();
|
|
393
|
-
|
|
394
|
-
|
|
351
|
+
for (const path of paths) {
|
|
352
|
+
if (!path.length)
|
|
353
|
+
continue;
|
|
354
|
+
ctx.moveTo(path[0].x, path[0].y);
|
|
355
|
+
for (let i = 1; i < path.length; i++) {
|
|
356
|
+
const point = path[i];
|
|
357
|
+
ctx.lineTo(point.x, point.y);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
395
360
|
ctx.fill();
|
|
396
361
|
ctx.closePath();
|
|
397
362
|
}
|
|
398
363
|
renderWaveform(channelData, options, ctx) {
|
|
399
364
|
ctx.fillStyle = this.convertColorValues(options.waveColor);
|
|
400
|
-
// Custom rendering function
|
|
401
365
|
if (options.renderFunction) {
|
|
402
366
|
options.renderFunction(channelData, ctx);
|
|
403
367
|
return;
|
|
404
368
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (options.barWidth || options.barGap || options.barAlign) {
|
|
369
|
+
const vScale = utils.calculateVerticalScale({
|
|
370
|
+
channelData,
|
|
371
|
+
barHeight: options.barHeight,
|
|
372
|
+
normalize: options.normalize,
|
|
373
|
+
maxPeak: options.maxPeak,
|
|
374
|
+
});
|
|
375
|
+
if (utils.shouldRenderBars(options)) {
|
|
413
376
|
this.renderBarWaveform(channelData, options, ctx, vScale);
|
|
414
377
|
return;
|
|
415
378
|
}
|
|
416
|
-
// Render waveform as a polyline
|
|
417
379
|
this.renderLineWaveform(channelData, options, ctx, vScale);
|
|
418
380
|
}
|
|
419
381
|
renderSingleCanvas(data, options, width, height, offset, canvasContainer, progressContainer) {
|
|
@@ -426,7 +388,13 @@ class Renderer extends EventEmitter {
|
|
|
426
388
|
canvas.style.left = `${Math.round(offset)}px`;
|
|
427
389
|
canvasContainer.appendChild(canvas);
|
|
428
390
|
const ctx = canvas.getContext('2d');
|
|
429
|
-
|
|
391
|
+
if (options.renderFunction) {
|
|
392
|
+
ctx.fillStyle = this.convertColorValues(options.waveColor);
|
|
393
|
+
options.renderFunction(data, ctx);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
this.renderWaveform(data, options, ctx);
|
|
397
|
+
}
|
|
430
398
|
// Draw a progress canvas
|
|
431
399
|
if (canvas.width > 0 && canvas.height > 0) {
|
|
432
400
|
const progressCanvas = canvas.cloneNode();
|
|
@@ -444,17 +412,8 @@ class Renderer extends EventEmitter {
|
|
|
444
412
|
const pixelRatio = this.getPixelRatio();
|
|
445
413
|
const { clientWidth } = this.scrollContainer;
|
|
446
414
|
const totalWidth = width / pixelRatio;
|
|
447
|
-
|
|
415
|
+
const singleCanvasWidth = utils.calculateSingleCanvasWidth({ clientWidth, totalWidth, options });
|
|
448
416
|
let drawnIndexes = {};
|
|
449
|
-
// Adjust width to avoid gaps between canvases when using bars
|
|
450
|
-
if (options.barWidth || options.barGap) {
|
|
451
|
-
const barWidth = options.barWidth || 0.5;
|
|
452
|
-
const barGap = options.barGap || barWidth / 2;
|
|
453
|
-
const totalBarWidth = barWidth + barGap;
|
|
454
|
-
if (singleCanvasWidth % totalBarWidth !== 0) {
|
|
455
|
-
singleCanvasWidth = Math.floor(singleCanvasWidth / totalBarWidth) * totalBarWidth;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
417
|
// Nothing to render
|
|
459
418
|
if (singleCanvasWidth === 0)
|
|
460
419
|
return;
|
|
@@ -468,24 +427,15 @@ class Renderer extends EventEmitter {
|
|
|
468
427
|
const offset = index * singleCanvasWidth;
|
|
469
428
|
let clampedWidth = Math.min(totalWidth - offset, singleCanvasWidth);
|
|
470
429
|
// Clamp the width to the bar grid to avoid empty canvases at the end
|
|
471
|
-
|
|
472
|
-
const barWidth = options.barWidth || 0.5;
|
|
473
|
-
const barGap = options.barGap || barWidth / 2;
|
|
474
|
-
const totalBarWidth = barWidth + barGap;
|
|
475
|
-
clampedWidth = Math.floor(clampedWidth / totalBarWidth) * totalBarWidth;
|
|
476
|
-
}
|
|
430
|
+
clampedWidth = utils.clampWidthToBarGrid(clampedWidth, options);
|
|
477
431
|
if (clampedWidth <= 0)
|
|
478
432
|
return;
|
|
479
|
-
const data =
|
|
480
|
-
const start = Math.floor((offset / totalWidth) * channel.length);
|
|
481
|
-
const end = Math.floor(((offset + clampedWidth) / totalWidth) * channel.length);
|
|
482
|
-
return channel.slice(start, end);
|
|
483
|
-
});
|
|
433
|
+
const data = utils.sliceChannelData({ channelData, offset, clampedWidth, totalWidth });
|
|
484
434
|
this.renderSingleCanvas(data, options, clampedWidth, height, offset, canvasContainer, progressContainer);
|
|
485
435
|
};
|
|
486
436
|
// Clear canvases to avoid too many DOM nodes
|
|
487
437
|
const clearCanvases = () => {
|
|
488
|
-
if (Object.keys(drawnIndexes).length
|
|
438
|
+
if (utils.shouldClearCanvases(Object.keys(drawnIndexes).length)) {
|
|
489
439
|
canvasContainer.innerHTML = '';
|
|
490
440
|
progressContainer.innerHTML = '';
|
|
491
441
|
drawnIndexes = {};
|
|
@@ -501,21 +451,18 @@ class Renderer extends EventEmitter {
|
|
|
501
451
|
return;
|
|
502
452
|
}
|
|
503
453
|
// Lazy rendering
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
454
|
+
const initialRange = utils.getLazyRenderRange({
|
|
455
|
+
scrollLeft: this.scrollContainer.scrollLeft,
|
|
456
|
+
totalWidth,
|
|
457
|
+
numCanvases,
|
|
458
|
+
});
|
|
459
|
+
initialRange.forEach((index) => draw(index));
|
|
510
460
|
// Subscribe to the scroll event to draw additional canvases
|
|
511
461
|
if (numCanvases > 1) {
|
|
512
462
|
const unsubscribe = this.on('scroll', () => {
|
|
513
463
|
const { scrollLeft } = this.scrollContainer;
|
|
514
|
-
const canvasIndex = Math.floor((scrollLeft / totalWidth) * numCanvases);
|
|
515
464
|
clearCanvases();
|
|
516
|
-
|
|
517
|
-
draw(canvasIndex);
|
|
518
|
-
draw(canvasIndex + 1);
|
|
465
|
+
utils.getLazyRenderRange({ scrollLeft, totalWidth, numCanvases }).forEach((index) => draw(index));
|
|
519
466
|
});
|
|
520
467
|
this.unsubscribeOnScroll.push(unsubscribe);
|
|
521
468
|
}
|
|
@@ -554,12 +501,15 @@ class Renderer extends EventEmitter {
|
|
|
554
501
|
// Determine the width of the waveform
|
|
555
502
|
const pixelRatio = this.getPixelRatio();
|
|
556
503
|
const parentWidth = this.scrollContainer.clientWidth;
|
|
557
|
-
const scrollWidth
|
|
504
|
+
const { scrollWidth, isScrollable, useParentWidth, width } = utils.calculateWaveformLayout({
|
|
505
|
+
duration: audioData.duration,
|
|
506
|
+
minPxPerSec: this.options.minPxPerSec || 0,
|
|
507
|
+
parentWidth,
|
|
508
|
+
fillParent: this.options.fillParent,
|
|
509
|
+
pixelRatio,
|
|
510
|
+
});
|
|
558
511
|
// Whether the container should scroll
|
|
559
|
-
this.isScrollable =
|
|
560
|
-
const useParentWidth = this.options.fillParent && !this.isScrollable;
|
|
561
|
-
// Width of the waveform in pixels
|
|
562
|
-
const width = (useParentWidth ? parentWidth : scrollWidth) * pixelRatio;
|
|
512
|
+
this.isScrollable = isScrollable;
|
|
563
513
|
// Set the width of the wrapper
|
|
564
514
|
this.wrapper.style.width = useParentWidth ? '100%' : `${scrollWidth}px`;
|
|
565
515
|
// Set additional styles
|
|
@@ -602,12 +552,7 @@ class Renderer extends EventEmitter {
|
|
|
602
552
|
// Adjust the scroll position so that the cursor stays in the same place
|
|
603
553
|
if (this.isScrollable && scrollWidth !== this.scrollContainer.scrollWidth) {
|
|
604
554
|
const { right: after } = this.progressWrapper.getBoundingClientRect();
|
|
605
|
-
|
|
606
|
-
// to limit compounding floating-point drift
|
|
607
|
-
// we need to round to the half px furthest from 0
|
|
608
|
-
delta *= 2;
|
|
609
|
-
delta = delta < 0 ? Math.floor(delta) : Math.ceil(delta);
|
|
610
|
-
delta /= 2;
|
|
555
|
+
const delta = utils.roundToHalfAwayFromZero(after - before);
|
|
611
556
|
this.scrollContainer.scrollLeft += delta;
|
|
612
557
|
}
|
|
613
558
|
}
|
|
@@ -638,16 +583,9 @@ class Renderer extends EventEmitter {
|
|
|
638
583
|
// Keep the cursor centered when playing
|
|
639
584
|
const center = progressWidth - scrollLeft - middle;
|
|
640
585
|
if (isPlaying && this.options.autoCenter && center > 0) {
|
|
641
|
-
this.scrollContainer.scrollLeft +=
|
|
586
|
+
this.scrollContainer.scrollLeft += center;
|
|
642
587
|
}
|
|
643
588
|
}
|
|
644
|
-
// Emit the scroll event
|
|
645
|
-
{
|
|
646
|
-
const newScroll = this.scrollContainer.scrollLeft;
|
|
647
|
-
const startX = newScroll / scrollWidth;
|
|
648
|
-
const endX = (newScroll + clientWidth) / scrollWidth;
|
|
649
|
-
this.emit('scroll', startX, endX, newScroll, newScroll + clientWidth);
|
|
650
|
-
}
|
|
651
589
|
}
|
|
652
590
|
renderProgress(progress, isPlaying) {
|
|
653
591
|
if (isNaN(progress))
|
|
@@ -690,6 +628,4 @@ class Renderer extends EventEmitter {
|
|
|
690
628
|
});
|
|
691
629
|
}
|
|
692
630
|
}
|
|
693
|
-
Renderer.MAX_CANVAS_WIDTH = 8000;
|
|
694
|
-
Renderer.MAX_NODES = 10;
|
|
695
631
|
export default Renderer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|