javascript - 来自base64编码的纯Javascript粒子排斥器png

标签 javascript debugging animation optimization particle-system

我有一些从 youtube 教程复制的 js 代码,并适用于我自己的项目来填充标题,代码按预期工作,并且当视口(viewport)小于 1200px 左右时它工作,但是当我将 firefox 放入全屏动画时不播放并且图像被拉伸(stretch),不保持其纵横比。我确实有一个 10/15 岁的 gpu,所以我猜那是我的问题的一半。该脚本使用 100x100 像素的 png 图像文件,然后将其转换为粒子颜色值。可以对此进行优化或使其运行得更好。似乎视口(viewport)越宽,动画启动所需的时间就越长,直到它最终停止并且不起作用。全屏= [2550x1440] ...
原始教程在这里:Pure Javascript Particle Animations & 将图像转换为 base64 编码在这里:Image to base64 .
HTML:

<html>
  <body>
    <canvas id="CanV"></canvas>
  </body>
</html>
CSS:
#Canv{
  position:absolute;
  top:-1px;left:-2px;
  z-index:67;
  width:100vw !important;
  max-height: 264px !important;
  min-height: 245px !important;
  filter:blur(2.27px);
}
Javascript:
window.addEventListener("DOMContentLoaded",(e)=>{
  const canv = document.getElementById('Canv');
  const ctx = canv.getContext('2d');
  canv.width = window.innerWidth;
  canv.height = window.innerHeight/ 3.85;

  let particleArray = [];
  let mouse = {
    x: null,
    y: null,
    radius: 74
  }

   window.addEventListener('mousemove',(e)=>{
     mouse.x = event.x + canv.clientLeft/2;
     mouse.y = event.y + canv.clientTop/1.2;
   });

  function drawImage(){
    let imageWidth = png.width;      //These to values crop if / sum no.
    let imageHeight = png.height;
    const data = ctx.getImageData(0, 0, imageWidth, imageHeight); //Gets img data for particles
    ctx.clearRect(0,0, canv.width, canv.height); // Clears the original img as its now being stored in the variable data.

    class Particle {
      constructor(x, y, color, size){
        this.x = x + canv.width/2 - png.width * 174,     //Chngd Ok:74
        this.y = y + canv.height/2 - png.height * 32,     //Ch<2  Ok:16
        this.color = color,
        this.size = 2.28, // Particle Size > Changed this value. from 2 i think!.
        this.baseX = x + canv.width/1.8 - png.width * 3.1,  //Chngd ok:5.1
        this.baseY = y + canv.height/1.2 - png.height * 2.8,
        this.density = (Math.random() * 14) + 2;
      }
      draw() {
         ctx.beginPath();  // this creates the sort of force field around the mouse pointer.
         ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
         ctx.closePath();
         ctx.fill();
      }
      update() {
        ctx.fillStyle = this.color;

        // Collision detection
        let dx = mouse.x - this.x;
        let dy = mouse.y - this.y;
        let distance = Math.sqrt(dx * dx + dy * dy);
        let forceDirectionX = dx / distance;
        let forceDirectionY = dy / distance;
        // Max distance, past that the force will be 0
        const maxDistance = 144;
        let force = (maxDistance - distance) / maxDistance;
        if (force < 0) force = 0;
        let directionX = (forceDirectionX * force * this.density * 0.6);
        let directionY = (forceDirectionY * force * this.density * 8.7); //Ch.this

        if (distance < mouse.radius + this.size) {
          this.x -= directionX;
          this.y -= directionY;
        } else {
          if (this.x !== this.baseX){
            let dx = this.x - this.baseX;
            this.x -= dx/54;  // Speed Particles return to ori
        } if (this.y !== this.baseY){
          let dy = this.y - this.baseY;
          this.y -= dy/17;  // Speed Particles return to ori
        }
       }
       this.draw();
      }
    }
    function init(){
      particleArray = [];
      for(let y = 0, y2 = data.height; y<y2; y++){
        for(let x =0, x2 = data.width; x<x2; x++){
          if(data.data[(y * 4 * data.width) + (x*4) + 3] > 128){
            let positionX = x + 25;
            let positionY = y + 45; // Co-ords on Canv
            let color = "rgb(" + data.data[(y * 4 * data.width) + (x * 4)] + "," +
                         data.data[(y * 4 * data.width) + (x * 4) + 1] + "," +
                         data.data[(y * 4 * data.width) + (x * 4) + 2] + ")";
            particleArray.push(new Particle(positionX * 2, positionY * 2, color));
          } /* These number effect png size but its to high */
        }
      }
    }
    function animate(){
      requestAnimationFrame(animate);
      ctx.fillStyle = 'rgba(0,0,0,.07)';
      ctx.fillRect(0,0, innerWidth, innerHeight);

      for(let i =0; i < particleArray.length; i++){
        particleArray[i].update();
      }
    }
    init();
    animate();
  }

  const png = new Image();

  png.src = "RemovedBase64StringToBig";

  window.addEventListener('load',(e)=>{
    console.log('page has loaded');
    ctx.drawImage(png, 0, 0);
    drawImage();
  })

});
通过缩短所有变量名称 > PartArr、ImgWidth、DirX、DirY 等,已经设法将其缩短了大约 100 个字符,但除了缩小它还有其他方法可以优化吗?并修复全屏问题?
我试图将它添加到 JSfiddle,所以我可以在这里链接到它,但我认为这不允许 base64 字符串,它无论如何都不会加载任何东西。 Canvas 加载,背景只是没有图像或动画。
我已经发现全屏问题的哪一部分,光标位置实际上在实际光标右侧大约 300px 处,但我仍然不知道如何解决这个问题或解决主要的滞后性能问题。猜测即使只有 100x100 也需要大量计算。
我能想到的让这个性能更好的一个选择是将它及其计算移动到它自己的专用网络 worker 中并将图像转换为 Webp,但我仍然不太了解网络 worker 或如何正确实现它们.. 将使用评论和答案中的所有建议,看看我能把什么放在一起。
我添加这些链接仅供将来引用,稍后我会回到这里:
MDN Canvas Optimizations
Html5Rocks Canvas Performance
Stack Question. Canv ~ Opti
Creating A blob From A Base 64 String in Js
次要奖金问题,
是否有最大文件大小或最大 px 尺寸,
可以base64编码吗?只是在 facebook 上问这个问题,最近有人向我发送了一个关于另一个具有多个 base64 编码图像的项目的问题,我不确定答案..

最佳答案

缩短代码对性能没有多大帮助。我正在使用火狐。要查看在 Firefox 中运行浏览器期间最占用您时间的内容,您可以阅读 Performance from MDN .

您的解决方案的问题是您的 fps 正在急剧下降。发生这种情况是因为您是 绘画每个 粒子 每一帧。想象一下,当每帧需要绘制数千个粒子时,它会有多迟缓。此绘制调用是从您的函数 Particle.draw 中调用的。 (调用如下:ctx.beginPath, ctx.arc, and ctx.closePath)。如前所述,此函数将被调用,因为 Particle.update对于 每个 框架。这是一项极其昂贵的操作。要大幅提高 fps,您可以尝试 不是 单独绘制每个粒子,而是收集所有粒子的 ImageData 然后在 rAQ 中仅将其放置在 Canvas 上一次(因此只发生一种油漆)。这个ImageData是一个包含 rgba 的对象对于 Canvas 上的每个像素。

在下面的解决方案中,我执行了以下操作:

  • 对于每个脏的粒子(已更新),修改 ImageData将被放入 Canvas
  • 然后,在整个 ImageData 之后已构建一帧,只画一次使用 putImageData 到 Canvas 上.这节省了大量调用函数所需的时间 Particle.update单独绘制每个粒子。

  • 另一个明显的解决方案是增加粒子的大小,以便需要处理的粒子像素更少(更改 ImageData)。我还稍微调整了代码,使图像始终至少为 100px 高;您可以调整数学,以便图像始终保持您的纵横比并响应窗口大小。

    这是一个工作示例:

    const canvas = document.querySelector('#canvas1')
    const ctx = canvas.getContext('2d')
    canvas.width = window.innerWidth
    canvas.height = window.innerHeight
    let canvasWidth = canvas.width
    let canvasHeight = canvas.height
    let particleArray = []
    let imageData = []
    
    // mouse
    let mouse = {
      x: null,
      y: null,
      radius: 40
    }
    
    window.addEventListener('mousemove', e => {
      mouse.x = event.x
      mouse.y = event.y
    })
    
    function drawImage(width, height) {
      let imageWidth = width
      let imageHeight = height
      const data = ctx.getImageData(0, 0, imageWidth, imageHeight)
      
      class Particle {
        constructor(x, y, color, size = 2) {
          this.x = Math.round(x + canvas.width / 2 - imageWidth * 2)
          this.y = Math.round(y + canvas.height / 2 - imageHeight * 2)
          this.color = color
          this.size = size
          
          // Records base and previous positions to repaint the canvas to its original background color
          this.baseX = Math.round(x + canvas.width / 2 - imageWidth * 2)
          this.baseY = Math.round(y + canvas.height / 2 - imageHeight * 2)
          this.previousX = null
          this.previousY = null
          this.density = (Math.random() * 100) + 2
        }
        
        stringifyColor() {
          return `rgba(${this.color.r}, ${this.color.g}, ${this.color.b}, ${this.color.a}`
        }
        
        update() {
          ctx.fillStyle = this.stringifyColor()
          
          // collision detection
          let dx = mouse.x - this.x
          let dy = mouse.y - this.y
          let distance = Math.sqrt(dx * dx + dy * dy)
          let forceDirectionX = dx / distance
          let forceDirectionY = dy / distance
          
          // max distance, past that the force will be 0
          const maxDistance = 100
          let force = (maxDistance - distance) / maxDistance
          if (force < 0) force = 0
          
          let directionX = (forceDirectionX * force * this.density)
          let directionY = (forceDirectionY * force * this.density)
          
          this.previousX = this.x
          this.previousY = this.y
          if (distance < mouse.radius + this.size) {
            this.x -= directionX
            this.y -= directionY
          } else {
          	// Rounded to one decimal number to as x and y cannot be the same (whole decimal-less integer) 
          	// as baseX and baseY by decreasing using a random number / 20
            if (Math.round(this.x) !== this.baseX) {
              let dx = this.x - this.baseX
              this.x -= dx / 20
            }
            if (Math.round(this.y) !== this.baseY) {
              let dy = this.y - this.baseY
              this.y -= dy / 20
            }
          }
        }
      }
      
      function createParticle(x, y, size) {
        if (data.data[(y * 4 * data.width) + (x * 4) + 3] > 128) {
          let positionX = x
          let positionY = y
          let offset = (y * 4 * data.width) + (x * 4)
          let color = {
            r: data.data[offset],
            g: data.data[offset + 1],
            b: data.data[offset + 2],
            a: data.data[offset + 3]
          }
          
          return new Particle(positionX * 4, positionY * 4, color, size)
        }
      }
      
      // Instead of drawing each Particle one by one, construct an ImageData that can be
      // painted into the canvas at once using putImageData()
      function updateImageDataWith(particle) {
        let x = particle.x
        let y = particle.y
        let prevX = particle.previousX
        let prevY = particle.previousY
        let size = particle.size
    
        if (prevX || prevY) {
          let prevMinY = Math.round(prevY - size)
          let prevMaxY = Math.round(prevY + size)
          let prevMinX = Math.round(prevX - size)
          let prevMaxX = Math.round(prevX + size)
    
          for (let y = prevMinY; y < prevMaxY; y++){
            for (let x = prevMinX; x < prevMaxX; x++) {
              if (y < 0 || y > canvasHeight) continue
              else if (x < 0 || x > canvasWidth) continue
              else {
                let offset = y * 4 * canvasWidth + x * 4
                imageData.data[offset] = 255
                imageData.data[offset + 1] = 255
                imageData.data[offset + 2] = 255
                imageData.data[offset + 3] = 255
              }
            }
          }
        }
    
        let minY = Math.round(y - size) 
        let maxY = Math.round(y + size) 
        let minX = Math.round(x - size) 
        let maxX = Math.round(x + size) 
    
        for (let y = minY; y < maxY; y++){
          for (let x = minX; x < maxX; x++) {
            if (y < 0 || y > canvasHeight) continue
            else if (x < 0 || x > canvasWidth) continue
            else {
              let offset = y * 4 * canvasWidth + x * 4
              imageData.data[offset] = particle.color.r
              imageData.data[offset + 1] = particle.color.g
              imageData.data[offset + 2] = particle.color.b
              imageData.data[offset + 3] = particle.color.a
            }
          }
        }
      }
      
      function init() {
        particleArray = []
        imageData = ctx.createImageData(canvasWidth, canvasHeight)
        // Initializing imageData to a blank white "page"
        for (let data = 1; data <= canvasWidth * canvasHeight * 4; data++) {
          imageData.data[data - 1] = data % 4 === 0 ? 255 : 255
        }
    
        const size = 2 // Min size is 2
        const step = Math.floor(size / 2)
        for (let y = 0, y2 = data.height; y < y2; y += step) {
          for (let x = 0, x2 = data.width; x < x2; x += step) {
            // If particle's alpha value is too low, don't record it
            if (data.data[(y * 4 * data.width) + (x * 4) + 3] > 128) {
              let newParticle = createParticle(x, y, size)
              particleArray.push(newParticle)
              updateImageDataWith(newParticle)
            }
          }
        }
      }
      
      function animate() {
        requestAnimationFrame(animate)
        
        for (let i = 0; i < particleArray.length; i++) {
          let imageDataCanUpdateKey = `${Math.round(particleArray[i].x)}${Math.round(particleArray[i].y)}`
          particleArray[i].update()
    
          updateImageDataWith(particleArray[i])
        }
        ctx.putImageData(imageData, 0, 0)
      }
      
      init()
      animate()
      
      window.addEventListener('resize', e => {
        canvas.width = innerWidth
        canvas.height = innerHeight
        canvasWidth = canvas.width
        canvasHeight = canvas.height
        init()
      })
    }
    
    const png = new Image()
    png.src = " data:image/png;base64,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"
    
    window.addEventListener('load', e => {
      // Ensuring height of image is always 100px
      let pngWidth = png.width
      let pngHeight = png.height
      
      let divisor = pngHeight / 100
      let finalWidth = pngWidth / divisor
      let finalHeight = pngHeight / divisor
      
      ctx.drawImage(png, 0, 0, finalWidth, finalHeight)
      drawImage(finalWidth, finalHeight)
    })
    #canvas1 {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
    <canvas id="canvas1"></canvas>

    关于javascript - 来自base64编码的纯Javascript粒子排斥器png,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59092312/

    相关文章:

    javascript - Rangey:获取选定节点的新方法

    ruby - 在 REPL 中调试本地 gem

    ios - UIView transitionFromView 不在容器中设置动画

    javascript - Canvas 水平旋转图像

    javascript - 属性(property)范围问题?

    javascript - 在javascript中打印对象数组并在html标签中打印

    ios - 从另一个类加载函数在 Swift IOS 中崩溃

    python - matplotlib 保存每 n 个步骤的动画

    javascript - 更改日期格式以识别日期和月份?

    ios - iOS游戏开始时的输入延迟