图像悬浮粒子分解和合成效果

代码语言:html

所属分类:粒子

代码描述:图像悬浮粒子分解和合成效果,把鼠标放上去,然后移动,就可以看到图片分离成微小的粒子,然后慢慢的合成原图

代码标签: 分解 合成 效果

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

<style>
body {
  cursor: none;
  overflow: hidden;
}

#cursor {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 10px;
  height: 10px;
  border: 1px solid #01321f33;
  border-radius: 50%;
  box-shadow:0 0 10px #01321f;
  opacity: 0;
  transition: opacity .5s ease;
}

#canvas1 {
  position: absolute;
  top: 0;
  left: 0;
}
</style>

</head>
<body translate="no">


<div id="cursor"></div>
<canvas id="canvas1"></canvas>

<script >
const canvas = document.querySelector('#canvas1');
const cursor = document.querySelector('#cursor');
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 = [];
let raqID = null; // Cancel current requestAnimationFrame upon resize

// mouse
let mouse = {
  x: null,
  y: null,
  radius: 40 };


// Devices with mouse
window.addEventListener('mousemove', e => {
  cursor.style.opacity = 1;
  mouse.x = e.x;
  mouse.y = e.y;
  cursor.style.transform = `translate(${e.x}px, ${e.y}px)`;
});

window.addEventListener('mouseup', e => {
  cursor.style.opacity = 0;
  mouse.x = null;
  mouse.y = null;
});

// For touch devices
document.body.addEventListener('mouseleave', e => {
  cursor.style.opacity = 0;
  mouse.x = null;
  mouse.y = null;
});

window.addEventListener('touchstart', e => {
  mouse.x = e.touches[0].clientX;
  mouse.y = e.touches[0].clientY;
});

window.addEventListener('touchmove', e => {
  mouse.x = e.changedTouches[0].clientX;
  mouse.y = e.changedTouches[0].clientY;
});

window.addEventListener('touchend', e => {
  cursor.style.opacity = 0;
  mouse.x = null;
  mouse.y = null;
});

// Draw image on canvas and run animation
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 = mouse.radius * 2.5;
      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) {
    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, positionY, 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] = 207;
            imageData.data[offset + 2] = 214;
            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);
    mouse.radius = Math.round(Math.sqrt(imageWidth * imageHeight) / 10);

    // Initializing imageData to a blank pink "page" (rgba(255, 207, 214, 255))
    for (let data = 1; data <= canvasWidth * canvasHeight * 4; data++) {
      if (data % 4 === 1) imageData.data[data - 1] = 255;else
      if (data % 4 === 2) imageData.data[data - 1] = 207;else
      if (data % 4 === 3) imageData.data[data - 1] = 214;else
      if (data % 4 === 0) imageData.data[data - 1] = 255;
    }

    // Create particles and adjust imageData to paint each particle's pixel value
    const size = Math.round(Math.sqrt(imageWidth * imageHeight) / 75);
    const step = size; // Increase to add space between pixel and make it more pixelated
    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() {
    raqID = requestAnimationFrame(animate);

    // Repainting per frame
    for (let i = 0; i < particleArray.length; i++) {
      particleArray[i].update();
      updateImageDataWith(particleArray[i]);
    }
    ctx.putImageData(imageData, 0, 0);
  }

  init();
  animate();
}

const png = new Image();
png.src = " data:image/png;base64,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.........完整代码请登录后点击上方下载按钮下载查看

网友评论0