图像悬浮粒子分解和合成效果
代码语言: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,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAAB3RJTUUH4gYdExYsikuu1gAAL8pJREFUeNrtnXl8HEeZ97/VPaNb9kjyKduxHR+KE8dJTOLchJgFFnaB5YZlORyW+2WThUVkXyDnskfCstwsEAgsL5tsCBCyQDglcionSWySWHLiM5Zly5JG19zd9f4xkq1jZjQz3T3VM6rv56OPrZ7u6qdK07+ueqrqeUCj0Wg0Go1GU0K6euTKrm65RLUdmvmLodqA+UpXj3wHsBfB3q4euUW1PZr5iRYABXT1yLcD/wUEgXpAC4BGCVoASkxXj3wr8AMgMHEoCvxctV2a+YkWgBLS1SPfBPyQkw8/wI8u3CjCqm3TzE+0AJSIrh75RuA2pj/8AN9WbZtm/qIFoAR0.........完整代码请登录后点击上方下载按钮下载查看
网友评论0