Skip to content

Commit

Permalink
Merge 3cea372 into 671852d
Browse files Browse the repository at this point in the history
  • Loading branch information
thijstriemstra committed Apr 30, 2019
2 parents 671852d + 3cea372 commit a281f07
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ wavesurfer.js changelog
that are currently initialised
- Update progress when seeking with HTML media controls (#1535)
- Refactor `MultiCanvas` and add `CanvasEntry` class (#1617)
- Use intersection observer to reduce canvas updates in `MultiCanvas` (#1622)
- Fix `wavesurfer.isReady`: make it a public boolean, the
broken `isReady` method is removed (#1597)
- Add support for `Blob` output type in `wavesurfer.exportImage` (#1610)
Expand Down
76 changes: 73 additions & 3 deletions src/drawer.canvasentry.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ export default class CanvasEntry {
* @type {string}
*/
this.id = getId(this.constructor.name.toLowerCase() + '_');
/**
* Whether or not the render process completed for this entry.
*
* @type {boolean}
*/
this.renderComplete = false;
/**
* Cached coordinates that can be used to redraw this entry at a later
* time.
*
* @type {object}
*/
this.cachedCoordinates = null;
/**
* Whether or not this entry is currently intersecting in the
* view-port.
*
* @type {boolean}
*/
this.intersecting = false;
/**
* Whether or not the intersection observer API is enabled.
*
* @type {boolean}
*/
this.usesIntersectionObserver = false;
}

/**
Expand Down Expand Up @@ -179,19 +205,57 @@ export default class CanvasEntry {
ctx.fillRect(x, y, width, height);
}

/**
* Draw using cached coordinates.
*/
redraw() {
// render once
if (!this.renderComplete && this.cachedCoordinates) {
this.drawLines(
this.cachedCoordinates.peaks,
this.cachedCoordinates.absmax,
this.cachedCoordinates.halfH,
this.cachedCoordinates.offsetY,
this.cachedCoordinates.start,
this.cachedCoordinates.end
);
}
}

/**
* Render the actual wave and progress lines
*
* @param {number[]} peaks Array with peaks data
* @param {number} absmax Maximum peak value (absolute)
* @param {number} halfH Half the height of the waveform
* @param {number} offsetY Offset to the top
* @param {number} start The x-offset of the beginning of the area that
* should be rendered
* @param {number} start The x-offset of the beginning of the area
* that should be rendered
* @param {number} end The x-offset of the end of the area that
* should be rendered
*/
drawLines(peaks, absmax, halfH, offsetY, start, end) {
if (this.usesIntersectionObserver) {
// do not draw unconditionally, only draw when this entry is
// visible in the wrapper's viewport. save coordinates until
// that happens so it can be redrawn later.
if (this.cachedCoordinates === null) {
this.cachedCoordinates = {
peaks: peaks,
absmax: absmax,
halfH: halfH,
offsetY: offsetY,
start: start,
end: end
};
}
if (!this.intersecting) {
// not visible yet
return;
}
}

// wave
this.drawLineToContext(
this.waveCtx,
peaks,
Expand All @@ -201,7 +265,7 @@ export default class CanvasEntry {
start,
end
);

// progress
if (this.hasProgressCanvas) {
this.drawLineToContext(
this.progressCtx,
Expand All @@ -213,6 +277,8 @@ export default class CanvasEntry {
end
);
}

this.renderComplete = true;
}

/**
Expand Down Expand Up @@ -292,6 +358,10 @@ export default class CanvasEntry {

this.progressCtx = null;
this.progress = null;

this.renderComplete = false;
this.intersecting = false;
this.cachedCoordinates = null;
}

/**
Expand Down
72 changes: 71 additions & 1 deletion src/drawer.multicanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,37 @@ export default class MultiCanvas extends Drawer {
* @type {number}
*/
this.overlap = 2 * Math.ceil(params.pixelRatio / 2);

/**
* @private
* @type {IntersectionObserver}
*/
this.intersectionObserver = null;

/**
* @private
* @type {boolean}
*/
this.supportsIntersectionObserver = 'IntersectionObserver' in window;
}

/**
* Initialize the drawer
*/
init() {
if (this.supportsIntersectionObserver) {
// observe canvas entry intersections
const intersectionOptions = {
root: this.wrapper,
rootMargin: '0px',
threshold: 0.001
};
this.intersectionObserver = new IntersectionObserver(
entries => this.onIntersectionChange(entries),
intersectionOptions
);
}

this.createWrapper();
this.createElements();
}
Expand Down Expand Up @@ -142,6 +167,10 @@ export default class MultiCanvas extends Drawer {
let canvasWidth = this.maxCanvasWidth + this.overlap;
const lastCanvas = this.canvases.length - 1;
this.canvases.forEach((entry, i) => {
// reset entry
entry.renderComplete = false;
entry.cachedCoordinates = null;

if (i == lastCanvas) {
canvasWidth = this.width - this.maxCanvasWidth * lastCanvas;
}
Expand All @@ -157,10 +186,11 @@ export default class MultiCanvas extends Drawer {
* @private
*/
addCanvas() {
const leftOffset = this.maxCanvasElementWidth * this.canvases.length;
const entry = new this.EntryClass();
entry.hasProgressCanvas = this.hasProgressCanvas;
entry.halfPixel = this.halfPixel;
const leftOffset = this.maxCanvasElementWidth * this.canvases.length;
entry.usesIntersectionObserver = this.supportsIntersectionObserver;

// wave
entry.initWave(
Expand Down Expand Up @@ -192,6 +222,11 @@ export default class MultiCanvas extends Drawer {
);
}

if (this.intersectionObserver) {
// start observing intersections
this.intersectionObserver.observe(entry.wave);
}

this.canvases.push(entry);
}

Expand All @@ -203,6 +238,11 @@ export default class MultiCanvas extends Drawer {
removeCanvas() {
let lastEntry = this.canvases[this.canvases.length - 1];

if (this.intersectionObserver) {
// stop observing
this.intersectionObserver.unobserve(lastEntry.wave);
}

// wave
lastEntry.wave.parentElement.removeChild(lastEntry.wave);

Expand Down Expand Up @@ -482,6 +522,36 @@ export default class MultiCanvas extends Drawer {
entry.setFillStyles(this.params.waveColor, this.params.progressColor);
}

/**
* Called when an entry intersection goes above or below threshold.
*
* @param {array} entries
* @private
*/
onIntersectionChange(entries) {
entries.forEach(entry => {
let canvasEntry = this.getCanvasEntryByElement(entry.target);
canvasEntry.intersecting = entry.isIntersecting;

// entry is visible in view-port
if (entry.isIntersecting === true) {
// render content
canvasEntry.redraw();
}
});
}

/**
* Find `CanvasEntry` by HTML element.
*
* @private
* @param {HTMLCanvasElement} element
* @return {CanvasEntry} The canvas entry associated with `element`.
*/
getCanvasEntryByElement(element) {
return this.canvases.find(entry => entry.wave == element);
}

/**
* Return image data of the multi-canvas
*
Expand Down
2 changes: 1 addition & 1 deletion src/util/get-id.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Get a random prefixed ID
*
* @param (string) prefix Prefix to use. Default is `'wavesurfer_'`.
* @param {String} prefix Prefix to use. Default is `'wavesurfer_'`.
* @returns {String} Random prefixed ID
* @example
* console.log(getId()); // logs 'wavesurfer_b5pors4ru6g'
Expand Down

0 comments on commit a281f07

Please sign in to comment.