Zwei UI -Leinwände mit TransferControltoOffscreen () < /h3>
Die Leinwände sind: die aktuellen (sichtbaren) und die anhängigen (unsichtbaren). Wenn die Transformation geändert wird, wende ich sofort einige CSS-Transformationen auf die aktuelle Leinwand an (sie funktioniert sehr schnell und sorgt für eine reibungslose UI-Interaktion), während der anstehende in einem Arbeiter erneut gerendert wird. Sobald es gerendert ist, sende ich eine Nachricht vom Arbeiter, dass die neue Version fertig ist. Sobald diese Nachricht empfangen wurde, tausche ich das sichtbare und die anstehenden Leinwände aus und stelle die neuen Positionen ein. Denn nach dem Zeichnen einiger Zeilen kann ich bereits z. Überprüfen Sie die neue Pixelfarbe in derselben Zecke, was für mich bedeutet, dass sie wiedergegeben wird. Aber manchmal ist es manchmal nicht. Das neue CSS wird angewendet und die neue Leinwand wird früher sichtbar als der tatsächliche Inhalt, der für den Benutzer schrecklich aussieht. Ich denke, Commit () war hier etwas zu helfen, aber es ist aus irgendeinem Grund veraltet und meistens nicht mehr unterstützt. Überspringen des Sendens der Nachricht "Frame Rendered" mit 1 Frame mit RequestAnimationFrame () löst das Problem nicht. Es ist frustrierend, dass dieser intuitive Ansatz nicht funktioniert, da er in der Praxis sehr schnell wäre. Aber mit einer CSS -Skala sollte dieses Quadrat visuell immer gleich bleiben: https://i.imgur.com/yqfqdb2.mp4 (Warnung: viel Flackern). Es sei denn, das Rendering wird nicht synchronisiert, was tatsächlich manchmal auftritt; Der beste Weg, um es auszulösen, besteht darin, die Größe des Fensters zu ändern: https://i.imgur.com/ajmnsid.mp4 (Warnung: Noch mehr flackern). Langsame, die manchmal früher als die Leinwand aktualisiert wird, wodurch eine höhere Zahl im Inneren ist.
Code: Select all
canvas {
width: 400px;
height: 400px;
transform-origin: 0 0;
}
p {
font-size: 140px;
position: absolute;
top: 310px;
}
0
const canvasA = document.getElementById('canvasA');
const canvasB = document.getElementById('canvasB');
const worker = new Worker('worker.js', { type: 'module' });
const offscreenA = canvasA.transferControlToOffscreen();
const offscreenB = canvasB.transferControlToOffscreen();
worker.postMessage({ type: 'init', canvases: [offscreenA, offscreenB] }, [offscreenA, offscreenB]);
worker.onmessage = (e) => {
document.getElementById("currentFrame").innerText = e.data.counter.toString();
let visibleCanvas;
if (e.data.counter % 2 === 0) {
visibleCanvas = canvasA;
canvasB.style.setProperty("display", "none");
} else {
visibleCanvas = canvasB;
canvasA.style.setProperty("display", "none");
}
visibleCanvas.style.setProperty("display", "block");
const scale = 3200 / e.data.size;
visibleCanvas.style.transform = `scale(${scale})`;
requestAnimationFrame(() => worker.postMessage({ type: 'renderNext' }));
};
< /code>
Arbeiter.js:
let canvases;
let ctxs;
let counter = 0;
const defaultSize = 3200;
let size = defaultSize;
onmessage = (e) => {
if (e.data.type === 'init') {
canvases = e.data.canvases;
ctxs = canvases.map(c => c.getContext('2d'));
renderNext();
}
if (e.data.type === 'renderNext') {
renderNext();
}
};
function renderNext() {
const ctx = ctxs[counter % 2];
ctx.strokeStyle = "#00000092";
ctx.clearRect(0, 0, defaultSize, defaultSize);
size = 2 + Math.random() * (defaultSize - 2);
for (let i = 0; i < 5000; i++) {
ctx.beginPath();
ctx.moveTo(Math.random() * size, Math.random() * size);
ctx.lineTo(Math.random() * size, Math.random() * size);
ctx.stroke();
}
ctx.fillStyle = "white";
ctx.font = `bold ${size / 4}px sans-serif`;
ctx.fillText(counter.toString(), 0, size / 4);
const cnt = counter
requestAnimationFrame(() => {
postMessage({
type: 'rendered',
counter: cnt,
size: size,
})
});
counter++;
}
Ich rendere alles im Arbeiter, wenden TransferFromImageBitMap () und die neuen Stile gleichzeitig anwenden. Es scheint die Zeilen zu zeichnen, die Bitmap zu senden und die Bitmap in der Benutzeroberfläche ziemlich schnell zu zeigen. TransferToImageBitMap () ist jedoch nicht nur langsam, sondern stoppt den UI -Thread, wenn er im Hintergrund aufgerufen wird. Wie kommts? Dieser Ansatz wäre auch völlig in Ordnung, wenn die Benutzeroberfläche diese seltsame Verzögerung nicht durch etwas verursacht hätte, das in einem separaten Thread passiert. Es ist in Ordnung für mich, vor dem Umbenennen 0,5-2 Sekunden zu warten. Ich möchte die UI-Reaktionsfähigkeit einfach nicht unterbrechen. < /P>
Code: Select all
Offscreen Canvas Test
canvas {
width: 400px;
height: 400px;
transform-origin: 0 0;
transform: translateZ(0);
}
p {
font-size: 140px;
position: absolute;
top: 310px;
}
#flying-div {
position: absolute;
top: 600px;
width: 10px;
height: 10px;
background-color: mediumaquamarine;
}
0
const canvasA = document.getElementById('canvasA');
// Send offscreen to worker
const worker = new Worker('worker.js', { type: 'module' });
worker.postMessage({ type: 'renderNext' });
const ctx = canvasA.getContext('bitmaprenderer');
worker.onmessage = (e) => {
if (e.data.type === 'noRender') {
requestAnimationFrame(() => worker.postMessage({ type: 'renderNext' }));
return;
}
document.getElementById("currentFrame").innerText = e.data.counter.toString();
ctx.transferFromImageBitmap(e.data.bitmap);
const scale = 3200 / e.data.size;
canvasA.style.transform = `scale(${scale})`;
requestAnimationFrame(() => worker.postMessage({ type: 'renderNext' }));
};
const flyingDiv = document.getElementById("flying-div");
let pos = 0;
setDivPos();
function setDivPos() {
flyingDiv.style.left = `${pos}px`;
pos += 2;
if (pos > 500) {
pos = 0;
}
requestAnimationFrame(setDivPos);
}
< /code>
Arbeiter.js:
let counter = 0;
const defaultSize = 3200;
const canvas = new OffscreenCanvas(defaultSize, defaultSize);
const ctx = canvas.getContext('2d', {willReadFrequently: false});
let size = defaultSize;
onmessage = (e) => {
if (e.data.type === 'renderNext') {
queueMicrotask(renderNext)
}
};
function renderNext() {
ctx.strokeStyle = "#00000002";
ctx.clearRect(0, 0, defaultSize, defaultSize);
size = 10 + Math.random() * (defaultSize - 10);
for (let i = 0; i < 500000; i++) {
ctx.beginPath();
ctx.moveTo(Math.random() * size, Math.random() * size);
ctx.lineTo(Math.random() * size, Math.random() * size);
ctx.stroke();
}
ctx.fillStyle = "white";
ctx.font = `bold ${size / 4}px sans-serif`;
ctx.fillText(counter.toString(), 0, size / 4);
const cnt = counter
createImageBitmap(canvas).then(bm => {
postMessage({
type: 'fullyRendered',
counter: cnt,
size: size,
bitmap: bm,
}, [bm])
});
counter++;
}
Fazit
ist eine Option, um die Rendering -Geschwindigkeit zu erhalten, und die Annäherung an die ersten beiden Ansätze. 3?