Arbeiter + Offscreencanvas -SynchronisationsproblemeJavaScript

Javascript-Forum
Anonymous
 Arbeiter + Offscreencanvas -Synchronisationsprobleme

Post by Anonymous »

Mit JavaScript möchte ich schwere Vektorgrafiken rendern. Beim Zoom sollte der gesamte Inhalt erneut gerendert werden. Um es visuell reibungslos zu erreichen, habe ich 3 Lösungen mit Webarbeitern ausprobiert: < /p>
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++;
}
Ein Bitmaprenderer UI -Leinwand, ein Offscreencanvas im Arbeiter
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++;
}
Ich habe eine fliegende DIV hinzugefügt, um die Verzögerungen im UI -Thread zu visualisieren: https://i.imgur.com/uqqbukl.mp4. Es ist deutlich sichtbar, dass es häufig aufhört. Aber es treten keine UI -Verzögerungen auf, und der Flug des DIV ist sehr reibungslos: https://i.imgur.com/9sx62wf.mp4.
Fazit
ist eine Option, um die Rendering -Geschwindigkeit zu erhalten, und die Annäherung an die ersten beiden Ansätze. 3?

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post