OffscreenCanvas
I managed to put all the code for working with WebGL and Three.js into a separate thread of the web worker. This accelerated the work of the site and on weak devices, friezes disappeared during the page loading.<canvas>
?<canvas>
to a web worker. In order not to open the gates of a multi-stream hell, after the transfer, the main thread loses access to this <canvas>
- only one thread will work with it.OffscreenCanvas
. npm install offscreen-canvas
entry: { 'app': './src/app.js', + 'webgl-worker': './src/webgl-worker.js' }
<link type="preload" as="script" href="./webgl-worker.js"> </head>
<canvas>
and the contents of the preload tag in the main JS file. import createWorker from 'offscreen-canvas/create-worker' const workerUrl = document.querySelector('[rel=preload][as=script]').href const canvas = document.querySelector('canvas') const worker = createWorker(canvas, workerUrl)
createWorker
in the presence of canvas.transferControlToOffscreen
load the JS file into the web worker. And in the absence of this method - as usual <script>
.webgl-worker.js
for the worker: import insideWorker from 'offscreen-canvas/inside-worker' const worker = insideWorker(e => { if (e.data.canvas) { // <canvas> } })
insideWorker
checks if it has been loaded inside the web worker. Depending on the environment, it will launch different communication systems with the main thread.insideWorker
for each new message from the main thread. Immediately after loading, createWorker
will send the first message { canvas, width, height }
to draw the first frame on <canvas>
. + import { + WebGLRenderer, Scene, PerspectiveCamera, AmbientLight, + Mesh, SphereGeometry, MeshPhongMaterial + } from 'three' import insideWorker from 'offscreen-canvas/inside-worker' + const scene = new Scene() + const camera = new PerspectiveCamera(45, 1, 0.01, 1000) + scene.add(new AmbientLight(0x909090)) + + let sphere = new Mesh( + new SphereGeometry(0.5, 64, 64), + new MeshPhongMaterial() + ) + scene.add(sphere) + + let renderer + function render () { + renderer.render(scene, camera) + } const worker = insideWorker(e => { if (e.data.canvas) { + // canvas - — , Three.js + if (!canvas.style) canvas.style = { width, height } + renderer = new WebGLRenderer({ canvas, antialias: true }) + renderer.setPixelRatio(pixelRatio) + renderer.setSize(width, height) + + render() } })
document.createElement
for loading and SVG textures. So, we will sometimes need different downloaders in a web worker and inside a regular script. To check the type of environment we have worker.isWorker
: renderer.setPixelRatio(pixelRatio) renderer.setSize(width, height) + const loader = worker.isWorker ? new ImageBitmapLoader() : new ImageLoader() + loader.load('/texture.png', mapImage => { + sphere.material.map = new CanvasTexture(mapImage) + render() + }) render()
import createWorker from 'offscreen-canvas/create-worker' const workerUrl = document.querySelector('[rel=preload][as=script]').href const canvas = document.querySelector('canvas') const worker = createWorker(canvas, workerUrl) + window.addEventListener('resize', () => { + worker.post({ + type: 'resize', width: canvas.clientWidth, height: canvas.clientHeight + }) + })
const worker = insideWorker(e => { if (e.data.canvas) { if (!canvas.style) canvas.style = { width, height } renderer = new WebGLRenderer({ canvas, antialias: true }) renderer.setPixelRatio(pixelRatio) renderer.setSize(width, height) const loader = worker.isWorker ? new ImageBitmapLoader() : new ImageLoader() loader.load('/texture.png', mapImage => { sphere.material.map = new CanvasTexture(mapImage) render() }) render() - } + } else if (e.data.type === 'resize') { + renderer.setSize(width, height) + render() + } })
OffscreenCanvas
I won OffscreenCanvas
on my website and got 100% points at Google Lighthouse. And WebGL works in all browsers, even without OffscreenCanvas
support.Source: https://habr.com/ru/post/446682/
All Articles