重复HTML画布元素(框)以填充整个视口 [英] Repeat HTML canvas element (box) to fill whole viewport
问题描述
我有一个动画画布,可以在背景上创建噪点效果.我的代码的简短说明如下:使用 for
循环,我在画布周围随机绘制了1px X 1px的正方形(点).它会产生静态噪音(颗粒).稍后,我用 requestAnimationFrame
对其进行动画处理.
I have an animated canvas to create a noise effect on the background. Short explanation of my code is following: using for
loop I am drawing 1px X 1px squares (dots) around canvas at random. It creates a static noise (grain). Later, I animate it with requestAnimationFrame
.
我的问题是,如果将画布的原始大小设置为整个视口,则浏览器将无法处理巨大的绘图空间,并且动画会变得非常缓慢.因此,我需要将画布的原始尺寸保持较小,例如50px,并在整个视口上重复.
My problem is that if I set the original size of the canvas to whole viewport, the browser can not handle huge drawing space and animation becomes extremely sluggish. So I need to keep the original size of canvas small, like 50px and repeat it on whole viewport.
我知道我可以轻松使用 document.body.style.backgroundImage
,该代码本身会自动重复该元素,但它不适合我的业务逻辑.我需要这个canvas元素来放置页面上的其他任何元素并保持透明.仅仅设置不透明度也不太适合我.
I know I can easily use document.body.style.backgroundImage
which on itself repeats the element automatically, but it does not fit my business logic. I need this canvas element to layover any other element on the page and be transparent. Just setting opacity does not fit me as well.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.className = "canvases";
canvas.width = 500;
canvas.height = 500;
document.body.appendChild(canvas);
function generateNoise() {
window.requestAnimationFrame(generateNoise);
ctx.clearRect(0, 0, canvas.width, canvas.height);
let x;
let y;
let rgb;
for (x = 0; x < canvas.width; x++) {
for (y = 0; y < canvas.height; y++) {
rgb = Math.floor(Math.random() * 255);
ctx.fillStyle = `rgba(${rgb}, ${rgb}, ${rgb}, 0.2`;
ctx.fillRect(x, y, 1, 1);
}
}
canvas.setAttribute("style", "position: absolute;");
canvas.style.zIndex = 1;
document.body.appendChild(canvas);
}
generateNoise();
.canvases {
margin: 50px;
border: 1px solid black;
}
推荐答案
The best to generate noise on a canvas (and to work at a pixel level in general) is to use an ImageData.
Painting every pixel one by one will be extremely slow (a lot of instructions to the GPU), while a simple loop over a TypedArray is quite fast.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let image; // using let because we may change it on resize
onresize = (evt) => {
const width = canvas.width = window.innerWidth;
const height = canvas.height =window.innerHeight;
image = new ImageData(width, height);
};
onresize();
const anim = (t) => {
const arr = new Uint32Array(image.data.buffer);
for( let i = 0; i < arr.length; i++ ) {
// random color with fixed 0.2 opacity
arr[i] = (Math.random() * 0xFFFFFF) + 0x33000000;
}
ctx.putImageData(image, 0, 0);
requestAnimationFrame(anim);
};
anim();
canvas { position: absolute; top: 0; left: 0 }
<canvas></canvas>
如果它仍然太慢,您也可以仅针对画布大小的一部分生成噪点并多次绘制:
If it's still too slow, you could also generate the noise only for a portion of the canvas size and draw it multiple times:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let image; // using let because we may change it on resize
onresize = (evt) => {
const width = canvas.width = window.innerWidth;
const height = canvas.height =window.innerHeight;
image = new ImageData(width / 2, height / 2);
};
onresize();
const anim = (t) => {
ctx.clearRect(0,0,canvas.width,canvas.height);
const arr = new Uint32Array(image.data.buffer);
for( let i = 0; i < arr.length; i++ ) {
// random color with fixed 0.2 opacity
arr[i] = (Math.random() * 0xFFFFFF) + 0x33000000;
}
const width = image.width;
const height = image.height;
ctx.putImageData(image, 0, 0);
ctx.drawImage(canvas, 0, 0, width, height, width, 0, width, height);
ctx.drawImage(canvas, 0, 0, width * 2, height, 0, height, width * 2, height);
requestAnimationFrame(anim);
};
anim();
canvas { position: absolute; top: 0; left: 0 }
<canvas></canvas>
这篇关于重复HTML画布元素(框)以填充整个视口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!