// Three.js - OffscreenCanvas
// from https://threejsfundamentals.org/threejs/threejs-offscreencanvas.html
'use strict'; // eslint-disable-line
function main() { /* eslint consistent-return: 0 */
const canvas = document.querySelector('#c');
if (!canvas.transferControlToOffscreen) {
canvas.style.display = 'none';
document.querySelector('#noOffscreenCanvas').style.display = '';
return;
}
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker(getWorkerBlob());
worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
function sendSize() {
worker.postMessage({
type: 'size',
width: canvas.clientWidth,
height: canvas.clientHeight,
});
}
window.addEventListener('resize', sendSize);
sendSize();
}
main();
// Helper function to create Worker Blobs for self-contained snippets
function getWorkerBlob() {
const idsToUrls = [];
const scriptElements = [...document.querySelectorAll('script[type=x-worker')];
for (const scriptElement of scriptElements) {
let text = scriptElement.text;
for (const {id, url} of idsToUrls) {
text = text.split(id).join(url);
}
const blob = new Blob([text], {type: 'application/javascript'});
const url = URL.createObjectURL(blob);
const id = scriptElement.id;
idsToUrls.push({id, url});
}
return idsToUrls.pop().url;
}
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
#noOffscreenCanvas {
display: flex;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: red;
color: white;
}
<script id="worker-offscreencanvas-cubes.js" type="x-worker">
'use strict'; // eslint-disable-line
/* global importScripts, THREE */
importScripts('https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js');
const state = {
width: 300, // canvas default
height: 150, // canvas default
};
function main(data) {
const {canvas} = data;
const renderer = new THREE.WebGLRenderer({canvas});
state.width = canvas.width;
state.height = canvas.height;
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 4;
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const offscreenCanvas = new OffscreenCanvas(256, 256);
const ctx = offscreenCanvas.getContext('2d');
ctx.fillStyle = '#FDB';
ctx.fillRect(0, 0, 256, 256);
ctx.font = '200px bold sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#666';
ctx.fillText('F', 128, 128);
const texture = new THREE.CanvasTexture(offscreenCanvas);
// texture.flipY = false;
function makeInstance(geometry, color, x) {
const material = new THREE.MeshPhongMaterial({
map: texture,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = x;
return cube;
}
const cubes = [
makeInstance(geometry, 0x44aa88, 0),
makeInstance(geometry, 0x8844aa, -2),
makeInstance(geometry, 0xaa8844, 2),
];
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = state.width;
const height = state.height;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
camera.aspect = state.width / state.height;
camera.updateProjectionMatrix();
}
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * .1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function size(data) {
state.width = data.width;
state.height = data.height;
}
const handlers = {
main,
size,
};
self.onmessage = function(e) {
const fn = handlers[e.data.type];
if (!fn) {
throw new Error('no handler for type: ' + e.data.type);
}
fn(e.data);
};
</script>
<canvas id="c"></canvas>
<div id="noOffscreenCanvas" style="display:none;">
<div>no OffscreenCanvas support</div>
</div>