Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/464.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 画布上大约有120000个粒子?_Javascript_Html_Canvas_Html5 Canvas_Particles - Fatal编程技术网

Javascript 画布上大约有120000个粒子?

Javascript 画布上大约有120000个粒子?,javascript,html,canvas,html5-canvas,particles,Javascript,Html,Canvas,Html5 Canvas,Particles,我有大约120000个粒子(每个粒子的大小为1px),我需要找到最好和最重要的:最快的方式来绘制到我的画布上 你会怎么做 现在我基本上把我的像素放到一个数组中,然后我在这些粒子上循环,做一些x和y计算,然后用fillRect把它们画出来。但是现在的帧速率是8-9 fps 有什么想法吗?请举例说明 多谢各位 最新更新(我的代码) 函数init(){ addEventListener(“mousemove”,onMouseMove); 设mouseX,mouseY,比值=2; const canva

我有大约120000个粒子(每个粒子的大小为1px),我需要找到最好和最重要的:最快的方式来绘制到我的画布上

你会怎么做

现在我基本上把我的像素放到一个数组中,然后我在这些粒子上循环,做一些x和y计算,然后用fillRect把它们画出来。但是现在的帧速率是8-9 fps

有什么想法吗?请举例说明

多谢各位

最新更新(我的代码)

函数init(){
addEventListener(“mousemove”,onMouseMove);
设mouseX,mouseY,比值=2;
const canvas=document.getElementById(“textCanvas”);
const context=canvas.getContext(“2d”);
canvas.width=window.innerWidth*比率;
canvas.height=window.innerHeight*比率;
canvas.style.width=window.innerWidth+“px”;
canvas.style.height=window.innerHeight+“px”;
context.imageSmoothingEnabled=false;
context.fillStyle=`rgba(255255,1)`;
setTransform(比率,0,0,比率,0,0);
const width=canvas.width;
const height=canvas.height;
context.font=“正常232px EB Garamond”;
context.fillText(“howdy”,01160);
var pixels=context.getImageData(0,0,宽度,高度);
var data32=新的UINT32阵列(像素.缓冲区);
常量粒子=新数组();
对于(变量i=0;i[
Math.round(Math.random()*(宽度-1)),
Math.round(Math.random()*(高度-1))
]);*/
移动鼠标的功能(e){
mouseX=parseInt((e.clientX-canvas.offsetLeft)*比率);
mouseY=parseInt((e.clientY-canvas.offsetTop)*比率);
}
功能帧(时间戳){
clearRect(0,0,宽度,高度);
const imageData=context.getImageData(0,0,宽度,高度);
常量数据=imageData.data;
for(设i=0;i0){
var cursorDX=p.ox-mouseX;
var cursorDY=p.oy-mouseY;
变量cursorDistanceSquared=(cursorDX*cursorDX+cursorDY*cursorDY);
cursorForce=数学最小值(10/cursorDistanceSquared,10);
cursorAngle=-Math.atan2(cursorDY,cursorDX);
}否则{
游标力=0;
橙色=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.x速度;
p、 y+=p.yVelocity;
}
请求动画帧(帧);
}
请求动画帧(帧);
}

webgl
上下文中计算着色器中的这些粒子将提供最高效的解决方案。见e。G举个例子

如果希望继续使用
2d
上下文,可以通过在屏幕外执行此操作来加快渲染粒子的速度:

  • 通过调用
    context.getImageData()
  • 通过操纵数据数组绘制像素
  • 使用
    context.putImageData()
  • 一个简化的例子:

    const output=document.getElementById(“输出”);
    const canvas=document.getElementById(“canvas”);
    const context=canvas.getContext(“2d”);
    const width=canvas.width;
    const height=canvas.height;
    const particles=Array.from({length:120000},()=>[
    Math.round(Math.random()*(宽度-1)),
    Math.round(Math.random()*(高度-1))
    ]);
    设previous=0;
    功能帧(时间戳){
    //每秒打印帧数:
    const delta=时间戳-上一个;
    先前=时间戳;
    output.textContent=`${(1000/delta).toFixed(1)}fps`;
    //绘制粒子:
    clearRect(0,0,宽度,高度);
    const imageData=context.getImageData(0,0,宽度,高度);
    常量数据=imageData.data;
    for(设i=0;i
    
    
    
    每秒移动720万个粒子 不使用webGL和着色器,并且您希望每帧的粒子数为120K 每秒60帧您需要每秒720万点的吞吐量。你需要一台速度快的机器

    Web workers多核CPU 快速解决方案。在多核机器上,web工作者为每个硬件核心提供线性性能提升。例如在8芯i上
    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);
    }
    
    // call this just once outside the animation loop.
    const imageData = this.context.getImageData(0, 0, this.width * this.ratio, this.height * this.ratio);
    // create a 32bit buffer
    const data32 = new Uint32Array(imageData.data.buffer);
    const pixel = 0xFF000000; // pixel to fill
    const width = imageData.width;
    
    
    // inside render loop
    data32.fill(0); // clear the pixel buffer
    
    // this line may be a problem I have no idea what it does. I would
    // hope its only passing a reference and not creating a copy 
    var particles = this.particleTexts[0].getParticles();
    
    var cDX,cDY,mx,my,p,cDistSqr,cForce,i;
    mx = this.mouseX | 0; // may not need the floor bitwize or 0
    my = this.mouseY | 0; // if mouse coords already integers
    
    if(mX > 0){  // do mouse test outside the loop. Need loop duplication
                 // But at 60fps thats 7.2million less if statements
        for (let i = 0; i < particles.length; i++) {
            var p = particles[i];
            p.xVelocity += 0.2 * (p.ox - p.x);
            p.yVelocity += 0.2 * (p.oy - p.y);
            p.xVelocity *= 0.55;
            p.yVelocity *= 0.55;
            data32[((p.x += p.xVelocity) | 0) + ((p.y += p.yVelocity) | 0) * width] = pixel;
        }
    }else{
        for (let i = 0; i < particles.length; i++) {
            var p = particles[i];
            cDX = p.x - mx;
            cDY = p.y - my;
            cDist = Math.sqrt(cDistSqr = cDX*cDX + cDY*cDY + 1);
            cForce = 1000 / (cDistSqr * cDist)
            p.xVelocity += cForce * cDx +  0.2 * (p.ox - p.x);
            p.yVelocity += cForce * cDY +  0.2 * (p.oy - p.y);
            p.xVelocity *= 0.55;
            p.yVelocity *= 0.55;
            data32[((p.x += p.xVelocity) | 0) + ((p.y += p.yVelocity) | 0) * width] = pixel;
    
        }
    }
    // put pixel onto the display.
    this.context.putImageData(imageData, 0, 0);
    
        const interleave = 2; // example only setup for 2 frames
                              // but can be extended to 3 or 4
    
        // create frameCount outside loop
        frameCount += 1;
    
        // do half of all particals
        for (let i = frameCount % frameCount  ; i < particles.length; i += interleave ) {
            var p = particles[i];
            cDX = p.x - mx;
            cDY = p.y - my;
            cDist = Math.sqrt(cDistSqr = cDX*cDX + cDY*cDY + 1);
            cForce = 1000 / (cDistSqr * cDist)
            p.xVelocity += cForce * cDx +  0.2 * (p.ox - p.x);
            p.yVelocity += cForce * cDY +  0.2 * (p.oy - p.y);
            p.xVelocity *= 0.55;
            p.yVelocity *= 0.55;
    
            // add pixel index to particle's property 
            p.pixelIndex = ((p.x += p.xVelocity) | 0) + ((p.y += p.yVelocity) | 0) * width;
            // write this frames pixel
            data32[p.pixelIndex] = pixel;
    
            // speculate the pixel index position in the next frame. This need to be as simple as possible.
            p.pixelIndex += (p.xVelocity | 0) + (p.yVelocity | 0) * width;
    
            p.x += p.xVelocity;  // as the next frame this particle is coasting
            p.y += p.yVelocity;  // set its position now
         }
    
         // do every other particle. Just gets the pixel index and sets it
         // this needs to remain as simple as possible.
         for (let i = (frameCount + 1) % frameCount  ; i < particles.length; i += interleave)
             data32[particles[i].pixelIndex] = pixel;
         }
    
       // for each particle after updating position
       // get index of pixel
    
       p.pixelIndex = (p.x | 0 + p.y | 0) * width;
       // add pixel
       data32[p.pixelIndex] = pixel;
    
       // now you get 8 more pixels for the price of one particle 
       var ind = p.offsetArrayIndex; 
       //  offsetArray is an array of pixel offsets both negative and positive
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       data32[p.pixelIndex + offsetArray[ind++]]  = pixel;
       // offset array arranged as sets of 8, each set of 8 is a frame in 
       // looping pre calculated offset animation
       // offset array length is 65536 or any bit mask able size.
       p.offsetArrayIndex = ind & 0xFFFF ; // ind now points at first pixel of next
                                           // set of eight pixels