html页面显示动态效应,HTML5 Canvas虹吸和引力漩涡效应动画演示

JavaScript

语言:

JaveScriptBabelCoffeeScript

确定

/**/ /* ---- CORE ---- */

/**/ const canvas = document.createElement('canvas');

/**/ const context = canvas.getContext('2d');

/**/ let windowWidth = canvas.width = window.innerWidth;

/**/ let windowHeight = canvas.height = window.innerHeight;

/**/ let origin = { x: windowWidth * 0.5, y: windowHeight * 0.5 };

/**/ context.translate(origin.x, origin.y);

/**/ canvas.id = 'canvas';

/**/ document.body.insertBefore(canvas, document.body.firstChild);

/**/ window.onresize = () => {

/**/ windowWidth = canvas.width = window.innerWidth;

/**/ windowHeight = canvas.height = window.innerHeight;

/**/ };

/**/ /* ---- CORE END ---- */

/* ---- CREATING ZONE ---- */

/* ---- SETTINGS ---- */

const COLORS = ['#32234A', '#820263', '#D90368', '#883677', '#CA61C3'];

const props = {

PARTICLE_NUMBERS: 500,

LINE_WIDTH: 5,

DEMISE_DISTANCE: 20,

APPARITION_DISTANCE: 350,

ROTATION_FORCE: 10,

VEL_MAX: 5.5,

VEL_MIN: 0,

VEL_BRAKE_MIN: 0.65,

VEL_BRAKE_MAX: 0.95,

ATT_AMPL: 3.8, // To reduce the force of the attraction at cente,

ATT_ZONE: 0.8,

ATT_FORCE: 0.02,

};

// Helpers 1

const gui = new dat.GUI();

gui.close();

const particleNumbers = gui.add(props, 'PARTICLE_NUMBERS', 1, 1500);

gui.add(props, 'LINE_WIDTH', 1, 10);

gui.add(props, 'DEMISE_DISTANCE', 0, 100);

gui.add(props, 'APPARITION_DISTANCE', 0, windowWidth);

gui.add(props, 'ROTATION_FORCE', 0, 100);

const velocity = gui.addFolder('Velocity');

const velMin = velocity.add(props, 'VEL_MIN', 0, 80);

const velMax = velocity.add(props, 'VEL_MAX', 0, 80);

const velBrakeMin = velocity.add(props, 'VEL_BRAKE_MIN', 0, 1);

const velBrakeMax = velocity.add(props, 'VEL_BRAKE_MAX', 0, 1);

const attraction = gui.addFolder('Attraction');

attraction.add(props, 'ATT_AMPL', 0, 10);

attraction.add(props, 'ATT_ZONE', 0, 1);

attraction.add(props, 'ATT_FORCE', 0, 0.1);

Math.sqr = (a) => a * a;

Math.randomF = (min, max) => Math.random() * (max - min) + min;

const getDist = (x1, y1, x2, y2) => Math.sqrt(Math.sqr(y2 - y1) + Math.sqr(x2 - x1));

const getRandomAngle = () => Math.random() * Math.PI * 2;

const getRandomVelMax = () => Math.randomF(props.VEL_MIN, props.VEL_MAX);

const getRandomVelBrake = () => Math.randomF(props.VEL_BRAKE_MIN, props.VEL_BRAKE_MAX);

/* ---- PARTICLE ---- */

class Particle {

constructor(x, y) {

this.init(x, y);

this.life = 0;

this.color = COLORS[Math.floor(Math.random() * COLORS.length)];

}

init(x, y) {

const angle = getRandomAngle();

const radius = Math.randomF(0, props.APPARITION_DISTANCE);

this.x = x || Math.cos(angle) * radius;

this.y = y || Math.sin(angle) * radius;

this.train = [];

this.vel = {

x: 0,

y: 0,

max: getRandomVelMax(),

brake: getRandomVelBrake(),

};

}

render() {

context.beginPath();

context.strokeStyle = this.color;

context.lineWidth = props.LINE_WIDTH; // TODO augmenter la taille du trai en fonction de la distance ?

let i = this.train.length - 1;

for (i; i > 0; i--) {

context.lineTo(this.train[i].x, this.train[i].y);

}

context.stroke();

}

update() {

// init values

const dist = getDist(this.x, this.y, 0, 0);

// Init a Particle when he near the center

if (dist < props.DEMISE_DISTANCE) {

this.init();

return;

}

const norm = {

x: this.x / dist,

y: this.y / dist,

};

// calculate force

// http://www.mathopenref.com/graphfunctions.html?fx=(exp(a * (1 - x - b))) * c&xh=1&xl=0&yh=10&yl=-10&a=3.595744680851064&b=0.3&c=1.4&dh=10&dl=-4&d=5.6

const f = Math.exp(props.ATT_AMPL * (props.ATT_ZONE - (dist / windowWidth))) * props.ATT_FORCE;

const force = {

x: norm.x * f,

y: norm.y * f,

};

// update velocity

this.vel.x += force.x;

this.vel.y += force.y;

if (Math.abs(this.vel.x) > this.vel.max) this.vel.x *= this.vel.brake;

if (Math.abs(this.vel.y) > this.vel.max) this.vel.y *= this.vel.brake;

// Apply attraction

this.x -= this.vel.x;

this.y -= this.vel.y;

// Apply rotation

const rf = this.getRotationForce();

this.x += (rf.x * f);

this.y += (rf.y * f);

// Trains

this.train.push({

x: this.x,

y: this.y,

});

if (this.train.length > 10) this.train.splice(0, 1);

}

getRotationForce() {

// Get normalized orthogonal vector

const orthogonalVec = {

x: -this.y,

y: this.x,

};

const dist = getDist(orthogonalVec.x, orthogonalVec.y, this.x, this.y);

const normalizedOrthogonalVec = {

x: orthogonalVec.x / dist,

y: orthogonalVec.y / dist,

};

return {

x: normalizedOrthogonalVec.x * props.ROTATION_FORCE,

y: normalizedOrthogonalVec.y * props.ROTATION_FORCE,

};

}

}

// START

let particles = [];

function init() {

particles = [];

for (let i = 0; i < props.PARTICLE_NUMBERS; i++) {

particles.push(new Particle());

}

}

init();

// HELPER 2

particleNumbers.onChange(() => {

let i;

const nbrOfParticles = particles.length;

if (props.PARTICLE_NUMBERS > nbrOfParticles) {

for (i = nbrOfParticles; i < props.PARTICLE_NUMBERS; i++) {

particles.push(new Particle());

}

} else {

for (i = props.PARTICLE_NUMBERS; i < nbrOfParticles; i++) {

particles.splice(props.PARTICLE_NUMBERS, nbrOfParticles - props.PARTICLE_NUMBERS);

}

}

});

velMin.onChange(() => {

particles.forEach(particle => {

particle.vel.max = getRandomVelMax();

});

});

velMax.onChange(() => {

particles.forEach(particle => {

particle.vel.max = getRandomVelMax();

});

});

velBrakeMin.onChange(() => {

particles.forEach(particle => {

particle.vel.brake = getRandomVelBrake();

});

});

velBrakeMax.onChange(() => {

particles.forEach(particle => {

particle.vel.brake = getRandomVelBrake();

});

});

props.restart = init;

gui.add(props, 'restart');

// LOOP

let i;

function loop() {

for (i = 0; i < particles.length; i++) {

particles[i].update();

particles[i].render();

}

}

/* ---- CREATING ZONE END ---- */

/**/ /* ---- LOOP ---- */

/**/ function _loop(){

/**/ context.clearRect(-origin.x, -origin.y, windowWidth, windowHeight);

/**/ loop();

/**/ requestAnimationFrame(_loop);

/**/ }

/**/ window.onresize = () => {

/**/ windowWidth = canvas.width = window.innerWidth;

/**/ windowHeight = canvas.height = window.innerHeight;

/**/ origin = { x: windowWidth * 0.5, y: windowHeight * 0.5 };

/**/ context.translate(origin.x, origin.y);

/**/ };

/**/ _loop();

/**/ /* ---- LOOP END ---- */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值