图像悬浮粒子分解和合成效果
代码语言: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