Split out Preview component
- Self-host the wrapper div. - Make component accept getPreview as an async function prop. - Explicitly dispose ResizeObserver on unmount. - Move to a new file.
This commit is contained in:
85
ui/index.js
85
ui/index.js
@@ -1,5 +1,6 @@
|
|||||||
import { h, render, Component, createRef, Fragment } from 'preact';
|
import { h, render, Component, Fragment } from 'preact';
|
||||||
import { scheduleOp, start } from './ops.js';
|
import { scheduleOp, start } from './ops.js';
|
||||||
|
import { Preview } from './preview.js';
|
||||||
import { Widget } from './widget.js';
|
import { Widget } from './widget.js';
|
||||||
|
|
||||||
/** @typedef {import('../libapi.mjs').Context} Context */
|
/** @typedef {import('../libapi.mjs').Context} Context */
|
||||||
@@ -159,7 +160,14 @@ class App extends Component {
|
|||||||
return h(
|
return h(
|
||||||
'div',
|
'div',
|
||||||
{ class: 'pure-g' },
|
{ class: 'pure-g' },
|
||||||
h('div', { class: 'pure-u-2-3' }, h(PreviewCanvas, null)),
|
h(
|
||||||
|
'div',
|
||||||
|
{ class: 'pure-u-2-3' },
|
||||||
|
h(Preview, {
|
||||||
|
getPreview: () =>
|
||||||
|
scheduleOp(context => context.capturePreviewAsBlob())
|
||||||
|
})
|
||||||
|
),
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{ id: 'config', class: 'pure-u-1-3' },
|
{ id: 'config', class: 'pure-u-1-3' },
|
||||||
@@ -176,76 +184,3 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(h(App, null), document.body);
|
render(h(App, null), document.body);
|
||||||
|
|
||||||
class PreviewCanvas extends Component {
|
|
||||||
canvasHolderRef = createRef();
|
|
||||||
canvasRef = createRef();
|
|
||||||
|
|
||||||
render(props) {
|
|
||||||
return h(
|
|
||||||
'div',
|
|
||||||
{ class: 'center-parent', ref: this.canvasHolderRef },
|
|
||||||
h('canvas', { class: 'center', ref: this.canvasRef })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async componentDidMount() {
|
|
||||||
let canvas = /** @type {HTMLCanvasElement} */ (this.canvasRef.current);
|
|
||||||
let canvasHolder = this.canvasHolderRef.current;
|
|
||||||
|
|
||||||
let canvasCtx = canvas.getContext('bitmaprenderer');
|
|
||||||
|
|
||||||
let ratio = 0;
|
|
||||||
|
|
||||||
let throttled = 0;
|
|
||||||
function updateCanvasSize() {
|
|
||||||
if (throttled) {
|
|
||||||
cancelAnimationFrame(throttled);
|
|
||||||
}
|
|
||||||
throttled = requestAnimationFrame(() => {
|
|
||||||
throttled = 0;
|
|
||||||
|
|
||||||
let width = canvasHolder.offsetWidth - 10;
|
|
||||||
let height = canvasHolder.offsetHeight;
|
|
||||||
|
|
||||||
if (height * ratio > width) {
|
|
||||||
height = width / ratio;
|
|
||||||
} else {
|
|
||||||
width = height * ratio;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(canvas, { width, height });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
new ResizeObserver(updateCanvasSize).observe(canvasHolder);
|
|
||||||
|
|
||||||
// I have no idea why, but if we connect too soon, it just hangs...
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
let blob = await scheduleOp(context => context.capturePreviewAsBlob());
|
|
||||||
|
|
||||||
// If ratio is known; decode resized image right away - it's a bit faster.
|
|
||||||
// If it isn't known, retrieve entire image to calculate ratio from its dimensions.
|
|
||||||
let img = await createImageBitmap(
|
|
||||||
blob,
|
|
||||||
ratio
|
|
||||||
? {
|
|
||||||
resizeWidth: canvas.width,
|
|
||||||
resizeHeight: canvas.height
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
);
|
|
||||||
if (!ratio) {
|
|
||||||
ratio = img.width / img.height;
|
|
||||||
updateCanvasSize();
|
|
||||||
}
|
|
||||||
canvasCtx.transferFromImageBitmap(img);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
}
|
|
||||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
83
ui/preview.js
Normal file
83
ui/preview.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { h, Component, createRef } from 'preact';
|
||||||
|
|
||||||
|
/** @extends Component<{ getPreview: () => Promise<Blob> }, null> */
|
||||||
|
export class Preview extends Component {
|
||||||
|
canvasHolderRef = createRef();
|
||||||
|
canvasRef = createRef();
|
||||||
|
/** @type {ResizeObserver} */
|
||||||
|
resizeObserver;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
{ class: 'center-parent', ref: this.canvasHolderRef },
|
||||||
|
h('canvas', { class: 'center', ref: this.canvasRef })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
let canvas = /** @type {HTMLCanvasElement} */ (this.canvasRef.current);
|
||||||
|
let canvasHolder = this.canvasHolderRef.current;
|
||||||
|
|
||||||
|
let canvasCtx = canvas.getContext('bitmaprenderer');
|
||||||
|
|
||||||
|
let ratio = 0;
|
||||||
|
|
||||||
|
let throttled = 0;
|
||||||
|
function updateCanvasSize() {
|
||||||
|
if (throttled) {
|
||||||
|
cancelAnimationFrame(throttled);
|
||||||
|
}
|
||||||
|
throttled = requestAnimationFrame(() => {
|
||||||
|
throttled = 0;
|
||||||
|
|
||||||
|
let width = canvasHolder.offsetWidth - 10;
|
||||||
|
let height = canvasHolder.offsetHeight;
|
||||||
|
|
||||||
|
if (height * ratio > width) {
|
||||||
|
height = width / ratio;
|
||||||
|
} else {
|
||||||
|
width = height * ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(canvas, { width, height });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(this.resizeObserver = new ResizeObserver(updateCanvasSize)).observe(
|
||||||
|
canvasHolder
|
||||||
|
);
|
||||||
|
|
||||||
|
// I have no idea why, but if we connect too soon, it just hangs...
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
while (this.canvasRef.current) {
|
||||||
|
try {
|
||||||
|
let blob = await this.props.getPreview();
|
||||||
|
|
||||||
|
// If ratio is known; decode resized image right away - it's a bit faster.
|
||||||
|
// If it isn't known, retrieve entire image to calculate ratio from its dimensions.
|
||||||
|
let img = await createImageBitmap(
|
||||||
|
blob,
|
||||||
|
ratio
|
||||||
|
? {
|
||||||
|
resizeWidth: canvas.width,
|
||||||
|
resizeHeight: canvas.height
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
);
|
||||||
|
if (!ratio) {
|
||||||
|
ratio = img.width / img.height;
|
||||||
|
updateCanvasSize();
|
||||||
|
}
|
||||||
|
canvasCtx.transferFromImageBitmap(img);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user