gpt4 book ai didi

javascript - 如何实现更逼真的粒子漂 float 画?

转载 作者:行者123 更新时间:2023-11-29 17:46:24 27 4
gpt4 key购买 nike

我是 Canvas 的新手,正在尝试创建看起来逼真的 float 粒子动画。

目前,我正在创建 400 个随机散布在 400x400 Canvas 上的粒子。

然后,在每个 requestAnimationFrame 中,我调整 xy 坐标,如下所示:

this.particles[i].x += Math.random() * 0.3 - 0.15;
this.particles[i].y += Math.random() * 0.3 - 0.15;

现在,我想弄清楚如何让粒子看起来更真实地漂浮。

让他们看起来更真实的一种方法是他们能走得更远。现在,它们似乎只是在振动,而不是在漂浮。实现漂移动画的一种方法是什么,这样粒子可以移动一段明显的距离但又不会完全从 Canvas 上消失?

class ParticleCanvas {
constructor(canvas, width, height) {
this.main = {
element: canvas
};

this.buffer = {
element: document.createElement("canvas")
};

this.main.context = this.main.element.getContext("2d");
this.buffer.context = this.buffer.element.getContext("2d");

this.main.element.width = this.buffer.element.width = this.width = width;
this.main.element.height = this.buffer.element.height = this.height = height;

this.makeParticles(400);
this.animate();
}

makeParticles(n) {
this.particles = [];

for (let i = 0; i < n; i++) {
this.particles.push(
new Particle(
this.width / 2 + Math.random() * this.width - this.width / 2,
this.height / 2 + Math.random() * this.height - this.height / 2,
Math.random() * 3 + 2,
"#919191",
"circle"
)
);
}
}

animate() {
this.main.context.clearRect(0, 0, this.width, this.height);
for (var i = 0; i < this.particles.length; i++) {
this.particles[i].x += Math.random() * 0.3 - 0.15;
this.particles[i].y += Math.random() * 0.3 - 0.15;
this.particles[i].draw(this.main.context);
}
requestAnimationFrame(this.animate.bind(this));
}
}

class Particle {
constructor(x, y, radius, dotColor, particleShape) {
this.x = x;
this.y = y;
this.radius = radius;
this.dotColor = dotColor;
this.particleShape = particleShape;
}

draw(context) {
context.save();
context.translate(this.x, this.y);
context.fillStyle = this.dotColor;
switch (this.particleShape) {
case "circle":
context.beginPath();
context.arc(0, 0, this.radius, 0, 2 * Math.PI);
context.fill();
break;
case "square":
context.fillRect(0, 0, this.radius * 2, this.radius * 2);
break;
}
context.restore();
}
}


new ParticleCanvas(document.querySelector("canvas"), 400, 400);
body {
margin: 0;
}
<canvas></canvas>

最佳答案

涡流和漂浮介质。

正弦波产生可用于激活粒子的简单运动。

让粒子看起来像漂浮的方法之一是将它们想象成漂浮在介质中。介质的运动使单个粒子运动。这有使彼此靠近的粒子以相似方式移动的趋势。

模拟空气。

在空气中,运动往往呈圆形,如涡流。

为了实现这一点,我们可以创建一组代表空气涡流中心的不可见涡流粒子。我们在 Canvas 周围移动漩涡。每个漩涡都有一个位置、大小和相关的强度。它们移动的区域是 Canvas 的两倍。

然后每个粒子都被每个涡流移动。粒子移动的量取决于粒子的质量(与其大小有关)以及它与涡流中心的距离。

每个粒子也有动量(保持移动的趋势)和一个抽象的浮力值,随着时间的推移使粒子上下移动(以阻止它们聚集在一个地方)。为了让运动有一点扩散,深度(远离眼睛)给运动增加了一点(非常小的)视差。

动画有 4 个步骤。

  • 更新在大致圆形的大路径中移动的漩涡。如果它们离开两侧,则将它们移动到另一侧(又名小行星空间)
  • 对于每个涡流更新每个粒子沿着切线移动到涡流。
  • 为每个粒子做阻力和浮力,并检查边界。
  • 画出粒子

魔数(Magic Number)

就许多神奇数字而言,这些类型的模拟非常锋利。在示例的顶部,我添加了一些常量来控制模拟。

您可以使用这些值,可以实现多种效果,模拟空气、水、粘液等中的粒子。

例子

整页效果最好。

// request first frame (this starts the whole shebang)
requestAnimationFrame(update);


const doFor =(c,C)=>{var i=0;while(i<c && C(i++)!==true);return i};
const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
const rand = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;
const ctx = canvas.getContext("2d");

//==================================================================
// WARNING next two control CPU load.
// Number of particles is width * height / density
const density = 1000; // particles per pixel squared WARNING Small numbers bad
const airVortexCount = 8; // number of vortex Warning big number bad
//==================================================================

const airVortexSpeed = 10; // speed of vortex movement in pixels
const vortexTurn = 0.02; // bigger values and vortex movement more circular
var airList;
const airForceCof = 1; // amount of force airVortex applys to circle
const circleDrag = 0.99; // drag is 1-circleDrag Must be less than 1
// the smaller the number the less movement in circles
const buoyancySpeed = 0.01; // Particle buoyancy speed
var circleList;
const circleMinSize = 2; // size is radius in pixels
const circleMaxSize = 4;
const floatPhaseCycleMax = 0.02; // Particle buoyancy phase shift. Bigger numbers
// create faster updown movement
var airWidth,airHeight;
var cirWidth,cirHeight;

function createParticles(){
airList = {...list};
circleList = {...list};
airList.init();
circleList.init();
doFor(airVortexCount, () => airList.add({...particle, ...air}).init());
var circleCount = (canvas.width * canvas.height) / density;
doFor(circleCount, () => circleList.add({...particle, ...circle}).init());
airWidth = canvas.width * 2;
airHeight = canvas.height * 2;
cirWidth = canvas.width + circleMaxSize * 2;
cirHeight = canvas.height + circleMaxSize * 2;
}

function updateAll(){
airList.update();
airList.each(circleList.update.bind(circleList));
circleList.update();
ctx.beginPath();
circleList.draw();
ctx.fill();
}



var w,h,cw,ch;
function update(timer){
ctx.setTransform(1,0,0,1,0,0); // reset transform
if(w !== innerWidth || h !== innerHeight){ // resize canvas if needed
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
createParticles();
}
ctx.fillStyle = "black";
ctx.fillRect(0,0,w,h);
ctx.fillStyle = "rgba(255,200,100,0.5)";
updateAll();
requestAnimationFrame(update);
}



// air is a Vortex (bad naming when I started)
const air = {
update(){
this.x += Math.cos(this.phase) * airVortexSpeed;
this.y += Math.sin(this.phase) * airVortexSpeed;
this.phase += this.phaseShift;

this.x = ((this.x % airWidth) + airWidth) % airWidth;
this.y = ((this.y % airHeight) + airHeight) % airHeight;
},
init(){
this.x = randI(canvas.width);
this.y = randI(canvas.height);
this.size2 = randI(canvas.height * canvas.width);
this.size = Math.sqrt(this.size2);
this.phase = rand(Math.PI * 2);
this.phaseShift = rand(-vortexTurn,vortexTurn);
return this;
},
}
// the things that float in air
const circle = {
init(){
this.x = randI(canvas.width);
this.y = randI(canvas.height);
this.dx = 0;
this.dy = 0;
this.size = rand(circleMinSize,circleMaxSize);
this.mass = this.size ** 3;
this.depth = rand(0.5,1);
this.floatPhase = rand(Math.PI * 2);
this.floatPhaseCycle = rand(floatPhaseCycleMax );
return this;
},
update(air){ // if air is undefined then do momentum and bouyancy
if(air === undefined){
this.dy += Math.sin(this.floatPhase)*buoyancySpeed;
this.floatPhase += this.floatPhaseCycle;
this.x += this.dx * this.depth;
this.y += this.dy * this.depth;
this.dx *= circleDrag;
this.dy *= circleDrag;
this.x = ((this.x % cirWidth) + cirWidth) % cirWidth;
this.y = ((this.y % cirHeight) + cirHeight) % cirHeight;
return;
}
var x = this.x - (air.x - airWidth / 4);
var y = this.y - (air.y - airHeight / 4);
var dist = x * x + y * y;
if(dist < air.size2){
dist = Math.sqrt(dist);
x /= dist;
y /= dist;
var force = (1 - dist / air.size) * airForceCof;

// using f = ma (force = mass * acceleration)
var a = (force / this.mass) * Math.sign(air.phaseShift);
this.dx += -y * a;
this.dy += x * a;
}
},
draw(){
var x = this.x - circleMaxSize;
var y = this.y - circleMaxSize;
ctx.moveTo(x + this.size,y);
ctx.arc(x,y,this.size,0,Math.PI * 2);
}
}
// basic particle
const particle = { x : 0, y : 0, size : 0}
// basic list handles all particle arrays
const list = {
items : [],
init(){ this.items = [] },
add(item) {
this.items.push(item);
return item;
},
each(callback) { for(const item of this.items) { callback(item) } },
update(data) { for(const item of this.items) { item.update(data) } },
draw() { for(const item of this.items) { item.draw() } },
}
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>

关于javascript - 如何实现更逼真的粒子漂 float 画?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49160780/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com