【发布时间】:2018-01-19 20:24:31
【问题描述】:
我有大约 120 000 个粒子(每个粒子大小为 1px),我需要找到最好的和最重要的:在画布上绘制的最快方法。
你会怎么做?
现在我基本上是将像素放入一个数组中,然后循环这些粒子,进行一些 x 和 y 计算并使用 fillRect 将它们绘制出来。但是现在的帧率是 8-9 fps。
有什么想法吗?请举例。
谢谢
最新更新(我的代码)
function init(){
window.addEventListener("mousemove", onMouseMove);
let mouseX, mouseY, ratio = 2;
const canvas = document.getElementById("textCanvas");
const context = canvas.getContext("2d");
canvas.width = window.innerWidth * ratio;
canvas.height = window.innerHeight * ratio;
canvas.style.width = window.innerWidth + "px";
canvas.style.height = window.innerHeight + "px";
context.imageSmoothingEnabled = false;
context.fillStyle = `rgba(255,255,255,1)`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
const width = canvas.width;
const height = canvas.height;
context.font = "normal normal normal 232px EB Garamond";
context.fillText("howdy", 0, 160);
var pixels = context.getImageData(0, 0, width, height).data;
var data32 = new Uint32Array(pixels.buffer);
const particles = new Array();
for(var i = 0; i < data32.length; i++) {
if (data32[i] & 0xffff0000) {
particles.push({
x: (i % width),
y: ((i / width)|0),
ox: (i % width),
oy: ((i / width)|0),
xVelocity: 0,
yVelocity: 0,
a: pixels[i*4 + 3] / 255
});
}
}
/*const particles = Array.from({length: 120000}, () => [
Math.round(Math.random() * (width - 1)),
Math.round(Math.random() * (height - 1))
]);*/
function onMouseMove(e){
mouseX = parseInt((e.clientX-canvas.offsetLeft) * ratio);
mouseY = parseInt((e.clientY-canvas.offsetTop) * ratio);
}
function frame(timestamp) {
context.clearRect(0, 0, width, height);
const imageData = context.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < particles.length; i++) {
const particle = particles[i];
const index = 4 * Math.round((particle.x + particle.y * width));
data[index + 0] = 0;
data[index + 1] = 0;
data[index + 2] = 0;
data[index + 3] = 255;
}
context.putImageData(imageData, 0, 0);
for (let i = 0; i < particles.length; i++) {
const p = particles[i];
var homeDX = p.ox - p.x;
var homeDY = p.oy - p.y;
var cursorForce = 0;
var cursorAngle = 0;
if(mouseX && mouseX > 0){
var cursorDX = p.ox - mouseX;
var cursorDY = p.oy - mouseY;
var cursorDistanceSquared = (cursorDX * cursorDX + cursorDY * cursorDY);
cursorForce = Math.min(10/cursorDistanceSquared,10);
cursorAngle = -Math.atan2(cursorDY, cursorDX);
}else{
cursorForce = 0;
cursorAngle = 0;
}
p.xVelocity += 0.2 * homeDX + cursorForce * Math.cos(cursorAngle);
p.yVelocity += 0.2 * homeDY + cursorForce * Math.sin(cursorAngle);
p.xVelocity *= 0.55;
p.yVelocity *= 0.55;
p.x += p.xVelocity;
p.y += p.yVelocity;
}
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
【问题讨论】:
-
如果不想使用 3D 上下文,可以先调用
context.getImageData(),然后操作返回的图像数组中的像素,最后使用context.putImageData()将它们放回去 -
@le_m 嗯,好的。你能看看我更新的代码并举个例子吗?我没有真正关注
-
你可以将速度翻倍,第二个循环中的数学有点草率。平方数
(homeDX * homeDX)比Math.pow(homeDX)快 你可以避免所有三角函数atan2,sin.cos。你有var homeAngle = Math.atan2(homeDY,homeDX);然后homeForce * Math.cos(homeAngle)和y。删除homeAngle,homeForce,并将homeForce * Math.cos(homeAngle)替换为0.2 * homeDX,对于y0.2 * homeDY,它的作用完全相同,减少了2 个变量和3 个触发调用。与cursorAngle类似,在同一循环中绘制像素作为计算可以节省时间。用于像素的 Uint32Array -
@Blindman67 感谢您指出这一点。我确实更新了我的本地版本,是的,它节省了一些调用,并且像以前一样工作。但是,fps 仍然太低,我想我需要找到 le_m 建议的方法。请参阅我对他的帖子的评论。
-
@Blindman67 是的,我知道,有很多要点...请查看我的代码,看看您是否可以看到我需要更改的任何内容以获得我期望的结果在运动?粒子在 le_m 建议下以大约 30 fps 的速度运行,但它们的运动不再像以前那样了..?
标签: javascript html canvas html5-canvas particles