html粒子代码教程

1.

js:

var c = document.createElement('canvas'),
    ctx = c.getContext('2d'),
    w = c.width = 600,
    h = c.height = 400,
    particles = [],
    particleCount = 1000,
    particlePath = 4,
    pillars = [],
    pillarCount = 110,
    hue = 0,
    hueRange = 60,
    hueChange = 1,
    gravity = 0.1,
    lineWidth = 1,
    lineCap = 'round',
    PI = Math.PI,
    TWO_PI = PI * 2;

function rand(min, max) {
    return Math.random() * (max - min) + min;
}

function distance(a, b) {
    var dx = a.x - b.x,
        dy = a.y - b.y;
    return Math.sqrt(dx * dx + dy * dy);
}



function Particle(opt) {
    this.path = [];
    this.reset();
}

Particle.prototype.reset = function () {
    this.radius = 1;
    this.x = rand(0, w);
    this.y = 0;
    this.vx = 0;
    this.vy = 0;
    this.hit = 0;
    this.path.length = 0;
};

Particle.prototype.step = function () {
    this.hit = 0;

    this.path.unshift([this.x, this.y]);
    if (this.path.length > particlePath) {
        this.path.pop();
    }

    this.vy += gravity;

    this.x += this.vx;
    this.y += this.vy;

    if (this.y > h + 10) {
        this.reset();
    }

    var i = pillarCount;
    while (i--) {
        var pillar = pillars[i];
        if (distance(this, pillar) < this.radius + pillar.renderRadius) {
            this.vx = 0;
            this.vy = 0;
            this.vx += -(pillar.x - this.x) * rand(0.01, 0.03);
            this.vy += -(pillar.y - this.y) * rand(0.01, 0.03);
            pillar.radius -= 0.1;
            this.hit = 1;
        }
    }
};

Particle.prototype.draw = function () {
    ctx.beginPath();
    ctx.moveTo(this.x, ~~this.y);
    for (var i = 0, length = this.path.length; i < length; i++) {
        var point = this.path[i];
        ctx.lineTo(point[0], ~~point[1]);
    }
    ctx.strokeStyle = 'hsla(' + rand(hue + (this.x / 3), hue + (this.x / 3) + hueRange) + ', 50%, 30%, 0.3)';
    ctx.stroke();

    if (this.hit) {
        ctx.beginPath();
        ctx.arc(this.x, this.y, rand(1, 25), 0, TWO_PI);
        ctx.fillStyle = 'hsla(' + rand(hue + (this.x / 3), hue + (this.x / 3) + hueRange) + ', 80%, 15%, 0.1)'
        ctx.fill();
    }
};


function Pillar() {
    this.reset();
}

Pillar.prototype.reset = function () {
    this.radius = rand(50, 100);
    this.renderRadius = 0;
    this.x = rand(0, w);
    this.y = rand(h / 2 - h / 4, h);
    this.active = 0;
};

Pillar.prototype.step = function () {
    if (this.active) {
        if (this.radius <= 1) {
            this.reset();
        } else {
            this.renderRadius = this.radius;
        }
    } else {
        if (this.renderRadius < this.radius) {
            this.renderRadius += 0.5;
        } else {
            this.active = 1;
        }
    }
};

Pillar.prototype.draw = function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.renderRadius, 0, TWO_PI, false);
    ctx.fill();
};

function init() {
    ctx.lineWidth = lineWidth;
    ctx.lineCap = lineCap;

    var i = pillarCount;
    while (i--) {
        pillars.push(new Pillar());
    }

    document.body.appendChild(c);
    loop();
}


function step() {
    hue += hueChange;

    if (particles.length < particleCount) {
        particles.push(new Particle());
    }

    var i = particles.length;
    while (i--) {
        particles[i].step();
    }

    i = pillarCount;
    while (i--) {
        pillars[i].step();
    }
}

function draw() {
    ctx.fillStyle = 'hsla(0, 0%, 0%, 0.1)';
    ctx.fillRect(0, 0, w, h);

    ctx.globalCompositeOperation = 'lighter';
    var i = particles.length;
    while (i--) {
        particles[i].draw();
    }

    ctx.globalCompositeOperation = 'source-over';
    i = pillarCount;
    ctx.fillStyle = 'rgba(20, 20, 20, 0.3)';
    while (i--) {
        pillars[i].draw();
    }
}


function loop() {
    requestAnimationFrame(loop);
    step();
    draw();
}

init();

html:

<!DOCTYPE html>
<html lang="zh-cn">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>HTML5 Canvas炫酷的火焰风暴动画</title>
</head>

<body>
    <script src="canvas.js"></script>
    <canvas width="600" height="400"></canvas>
</body>

</html>

2.

html:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		body {
		  	background: #111;
		  	padding:0;
		  	margin:0;
			overflow:hidden;
		}
	</style>
</head>
<body>
	<div id="wrapper"></div>
</body>
<script>
(function(){
	'use strict';
	let wrapper, canvas, ctx, width, height, 
	Tau=Math.PI*2, PI180=Math.PI/180,
	systems=[];

/* PlanetarySystem */
	let PlanetarySystem = function(id='pSys'){
		Object.defineProperty(this, 'id',               { value:id, writable:true} );
		Object.defineProperty(this, 'x',                { value:0, writable:true });
		Object.defineProperty(this, 'y',                { value:0, writable:true });
		Object.defineProperty(this, 'allBodies',        { value:[], writable:true });
		Object.defineProperty(this, 'allBodiesLookup',  { value:{}, writable:true });    // fast id lookup for children
		Object.defineProperty(this, 'numBodies',        { value:0, writable:true });
	}
	PlanetarySystem.prototype.addBody = function(vo) {
		vo.parentSystem = this;
		vo.parentBody = vo.parentBody === null ? this : this.allBodiesLookup[vo.parentBody];
		let body = new PlanetaryBody(vo);
		body.update();
		this.allBodies.push(body);
		this.allBodiesLookup[vo.id] = body;
		this.numBodies += 1;
	}
	PlanetarySystem.prototype.setSpeedFactor = function(value){
		let body;
		for(let i=0; i<this.numBodies; i++){
			body = this.allBodies[i];
			body.setSpeedFactor(value);
		}
	}
	PlanetarySystem.prototype.update = function(){
		let body;
		for(let i=0; i<this.numBodies; i++){
			body = this.allBodies[i];
			body.update();
		}
	}
/* PlanetaryBody */
	let PlanetaryBody = function(vo){
		Object.defineProperty(this, 'id',					{ value:vo.id, writable:true} );
		Object.defineProperty(this, 'diameter',				{ value:vo.diameter, writable:true });
		Object.defineProperty(this, 'colour',				{ value:vo.colour, writable:true });
		Object.defineProperty(this, 'x',					{ value:0, writable:true });
		Object.defineProperty(this, 'y',					{ value:0, writable:true });
		Object.defineProperty(this, 'vx',					{ value:0, writable:true });
		Object.defineProperty(this, 'vy',					{ value:0, writable:true });
		Object.defineProperty(this, 'degrees',				{ value:vo.degrees, writable:true });
		Object.defineProperty(this, 'speedBase',			{ value:vo.speed, writable:true });
		Object.defineProperty(this, 'speed',				{ value:vo.speed , writable:true });
		Object.defineProperty(this, 'orbitalRadius',		{ value:vo.orbitalRadius, writable:true });
		Object.defineProperty(this, 'parentSystem',			{ value:vo.parentSystem, writable:true });
		Object.defineProperty(this, 'parentBody',			{ value:vo.parentBody, writable:true });

		return this;
	}
	PlanetaryBody.prototype.update = function(){
		let angle = this.degrees * PI180;
		this.degrees += this.speed;
		this.vx = this.orbitalRadius * Math.cos(angle);
		this.vy = this.orbitalRadius * Math.sin(angle);
		// update position
		if(this.parentBody != null){
			this.x = this.vx + this.parentBody.x;
			this.y = this.vy + this.parentBody.y;
		}
	}

/* init() */
	function init(){
		wrapper = document.querySelector('#wrapper');
		canvas = createCanvas('canvas', width, height);
		wrapper.appendChild(canvas);
		ctx = canvas.getContext('2d');
		setupEvents();
		resizeCanvas();

		/* Define new PlanetarySystem and set values */
		let system1 = new PlanetarySystem('pSys1');
		systems.push(system1);
		system1.x = width * .5;
		system1.y = height * .5;
		system1.addBody({id:'sun', diameter:5, degrees:0, speed:0, colour:'#FDFE1D', orbitalRadius:0, parentBody:null});
		for(let loop=30, i=0; i<loop; i+=1){
			system1.addBody({	id:				'ball'+i,
								diameter:		5,
								degrees:		0,
								speed:			2 + (loop * 0.15) - (i* 0.2),
								colour:			'#FDFE1D',
								orbitalRadius:	7*(i+1),
								parentBody:		'sun'});
		}
	}
	
/* Methods */
	function createCanvas(id, w, h){
		let tCanvas = document.createElement('canvas');
		tCanvas.width = w;
		tCanvas.height = h;
		tCanvas.id = id;
		return tCanvas;
	}

	function setupEvents(){
		window.onresize = resizeCanvas;
	}
	function resizeCanvas(){
		let rect = wrapper.getBoundingClientRect();
		width = window.innerWidth;
		height = window.innerHeight - rect.top -2;
		canvas.width = width;
		canvas.height = height;
		for(let i=0; i<systems.length; i++){
			systems[i].x = width * .5;
			systems[i].y = height * .5;
		}
	}

	function update(){
		for(let loop=systems.length, i=0; i<loop; i++){
			systems[i].update();
		}
	}

	function draw(){
		let system;
		let prev = null;
		for(let i=0; i<systems.length; i++){
			system = systems[i];
			let planetaryBody;
			for(let loop=system.numBodies, j=1; j<loop; j+=1) {
				planetaryBody = system.allBodies[j];
				ctx.beginPath();
				ctx.arc(planetaryBody.x, planetaryBody.y, planetaryBody.diameter, 0, Tau, false);
				ctx.fillStyle = planetaryBody.colour;
				ctx.fill();
				if(j>1){
					ctx.strokeStyle = planetaryBody.colour;
					ctx.lineWidth = 2;
					ctx.beginPath();
					ctx.moveTo(planetaryBody.x, planetaryBody.y);
					ctx.lineTo(prev.x, prev.y);
					ctx.stroke();
				}
				prev = {x:planetaryBody.x, y:planetaryBody.y};
			}
		}
	}

	function animate(){
		ctx.fillStyle = 'rgba(0,0,0, .05)';
		ctx.fillRect(0, 0, width, height);
		update();
		draw();
		requestAnimationFrame(animate);
	}
	init();
	animate();
}());
</script>
</html>

3.

html:

<!DOCTYPE html>
<html lang="zh">
 
<head>
	<meta charset="utf-8" />
	<title>光标粒子特效</title>
</head>
 
<body>
</body>
 
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/2.2.0/jquery.js"></script>
<script src="https://blog-static.cnblogs.com/files/shuiche/mouse.min.js"></script>
<script>
	$.shuicheMouse({
		type: 11,
		color: "rgba(187,67,128,1)"
	})
</script>
 
</html>

4.

html:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>砰砰砰</title>
	<style>
	body {
	  	background: #000800;
	}
	canvas {
	  	margin: 0 auto;
	  	display: block;
	}
	</style>
</head>
<body>
	<canvas></canvas>
	<div style="text-align:center;margin:50px 0; font:normal 14px/24px 'MicroSoft YaHei';color:#ffffff">
</body>
<script>
	let canvas = document.querySelector('canvas'),
		speedSelect = document.querySelector('#speed'),
		width = 300,
		height = 300,
		ctx = canvas.getContext('2d'),
		pSystemSize = 1000,
		deform = {a:100, s:0.4, min:-200, max:200, dir:1}; // a=4 is natural if not animated;

	const mcos = Math.cos,
		  msin = Math.sin,
		  mpow = Math.pow,
		  PI180=Math.PI / 180,
		  tau = Math.PI * 2;

	canvas.width = width;
	canvas.height = height;
	ctx.lineWidth = 1;

	const ParticleSystem = function(num){
		this.scalar = 8;
		this.numParticles = num;
		this.allParticles = [];
		this.x = width * .5;
		this.y = height * .5;
		this.generate();
	}
	ParticleSystem.prototype.generate = function(){
		for(let i=0; i<this.numParticles; i++){
			let vo = {};
			vo.degrees = (360 / this.numParticles) * i * PI180;
			vo.parent = this;
			vo.x = width / 2;
			vo.y = height / 2;
			vo.vx = 0;
			vo.vy = 0;
			this.allParticles.push(new Particle(vo));
		}
	}
	ParticleSystem.prototype.update = function(){
		for(let i=0; i<this.allParticles.length; i++){
			this.allParticles[i].update();
		}
	}

	const Particle = function(vo){
		this.degrees = vo.degrees;
		this.parent = vo.parent;
		this.x = vo.x;
		this.y = vo.y;
		this.vx = 0;
		this.vy = 0;
		this.colour = 'hsl(' + (Math.round((this.degrees * (180/Math.PI)))) + ',100%,50%)';
	}
	Particle.prototype.update = function(){
		// http://mathworld.wolfram.com/HeartCurve.html
		this.vx = 16 * mpow(msin(this.degrees), 3) * deform.dir;
		this.vy = ((13 * mcos(this.degrees)) - 
				   (6 * mcos(2 * this.degrees)) - // 5
				   (2 * mcos(3 * this.degrees)) -
				   (mcos(deform.a * this.degrees))) * -1;
		
		// update position
		this.x = this.vx * this.parent.scalar + this.parent.x;
		this.y = this.vy * this.parent.scalar + this.parent.y;
	}

	function update(){
		if(deform.dir === 1){
			if(deform.a > deform.min){
				deform.a -= deform.s;
			}
			else{
				flipDirection();
			}
		}
		else{
			if(deform.a < deform.max){
				deform.a += deform.s;
			}
			else{
				flipDirection();
			}
		}
		system.update();
	}
	function flipDirection(){
		deform.dir *= -1;
	}

	function draw(){
		ctx.clearRect(0, 0, width, height);
		ctx.save();
		for(let i=0; i<system.numParticles; i++){
			let p = system.allParticles[i];
			ctx.fillStyle = p.colour;
			ctx.beginPath();
			ctx.arc(p.x, p.y, 1, 0, tau, false);
			ctx.fill();
		}
		ctx.restore();
	}
	function animate(){
		update();
		draw();
		requestAnimationFrame(animate);
	}
	let system = new ParticleSystem(pSystemSize);
	animate();
</script>
</html>

5.

html:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		html, body {
		  	overflow: hidden;
		  	background: black;
		  	padding:0;
		  	margin:0;
		}
	</style>
</head>
<body>
	
</body>
<script>
"use strict";

const {
	PI,
	cos,
	sin,
	tan,
	abs,
	sqrt,
	pow,
	min,
	max,
	ceil,
	floor,
	round,
	random,
	atan2
} = Math;
const HALF_PI = 0.5 * PI;
const QUART_PI = 0.25 * PI;
const TAU = 2 * PI;
const TO_RAD = PI / 180;
const G = 6.67 * pow(10, -11);
const EPSILON = 2.220446049250313e-16;
const rand = n => n * random();
const randIn = (_min, _max) => rand(_max - _min) + _min;
const randRange = n => n - rand(2 * n);
const fadeIn = (t, m) => t / m;
const fadeOut = (t, m) => (m - t) / m;
const fadeInOut = (t, m) => {
	let hm = 0.5 * m;
	return abs((t + hm) % m - hm) / hm;
};
const dist = (x1, y1, x2, y2) => sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
const angle = (x1, y1, x2, y2) => atan2(y2 - y1, x2 - x1);
const lerp = (a, b, amt) => (1 - amt) * a + amt * b;
const vh = p => p * window.innerHeight * 0.01;
const vw = p => p * window.innerWidth * 0.01;
const vmin = p => min(vh(p), vw(p));
const vmax = p => max(vh(p), vw(p));
const clamp = (n, _min, _max) => min(max(n, _min), _max);
const norm = (n, _min, _max) => (n - _min) / (_max - _min);

Array.prototype.lerp = function(t = [], a = 0) {
	this.forEach((n, i) => (this[i] = lerp(n, t[i], a)));
};

Float32Array.prototype.get = function(i = 0, n = 0) {
	const t = i + n;

	let r = [];

	for (; i < t; i++) {
		r.push(this[i]);
	}

	return r;
};


class PropsArray {
	constructor(count = 0, props = []) {
		this.count = count;
		this.props = props;
		this.values = new Float32Array(count * props.length);
	}
	get length() {
		return this.values.length;
	}
	set(a = [], i = 0) {
		this.values.set(a, i);
	}
	setMap(o = {}, i = 0) {
		this.set(Object.values(o), i);
	}
	get(i = 0) {
		return this.values.get(i, this.props.length);
	}
	getMap(i = 0) {
		return this.get(i).reduce(
			(r, v, i) => ({
				...r,
				...{ [this.props[i]]: v }
			}),
			{}
		);
	}
	forEach(cb) {
		let i = 0;
		
		for (; i < this.length; i += this.props.length) {
			cb(this.get.call(this, i), i, this);
		}
	}
	map(cb) {
		let i = 0;
		
		for (; i < this.length; i += this.props.length) {
			this.set(cb(this.get.call(this, i), i, this), i);
		}
	}
}
!function(){"use strict";var r=.5*(Math.sqrt(3)-1),e=(3-Math.sqrt(3))/6,t=1/6,a=(Math.sqrt(5)-1)/4,o=(5-Math.sqrt(5))/20;function i(r){var e;e="function"==typeof r?r:r?function(){var r=0,e=0,t=0,a=1,o=(i=4022871197,function(r){r=r.toString();for(var e=0;e<r.length;e++){var t=.02519603282416938*(i+=r.charCodeAt(e));t-=i=t>>>0,i=(t*=i)>>>0,i+=4294967296*(t-=i)}return 2.3283064365386963e-10*(i>>>0)});var i;r=o(" "),e=o(" "),t=o(" ");for(var n=0;n<arguments.length;n++)(r-=o(arguments[n]))<0&&(r+=1),(e-=o(arguments[n]))<0&&(e+=1),(t-=o(arguments[n]))<0&&(t+=1);return o=null,function(){var o=2091639*r+2.3283064365386963e-10*a;return r=e,e=t,t=o-(a=0|o)}}(r):Math.random,this.p=n(e),this.perm=new Uint8Array(512),this.permMod12=new Uint8Array(512);for(var t=0;t<512;t++)this.perm[t]=this.p[255&t],this.permMod12[t]=this.perm[t]%12}function n(r){var e,t=new Uint8Array(256);for(e=0;e<256;e++)t[e]=e;for(e=0;e<255;e++){var a=e+~~(r()*(256-e)),o=t[e];t[e]=t[a],t[a]=o}return t}i.prototype={grad3:new Float32Array([1,1,0,-1,1,0,1,-1,0,-1,-1,0,1,0,1,-1,0,1,1,0,-1,-1,0,-1,0,1,1,0,-1,1,0,1,-1,0,-1,-1]),grad4:new Float32Array([0,1,1,1,0,1,1,-1,0,1,-1,1,0,1,-1,-1,0,-1,1,1,0,-1,1,-1,0,-1,-1,1,0,-1,-1,-1,1,0,1,1,1,0,1,-1,1,0,-1,1,1,0,-1,-1,-1,0,1,1,-1,0,1,-1,-1,0,-1,1,-1,0,-1,-1,1,1,0,1,1,1,0,-1,1,-1,0,1,1,-1,0,-1,-1,1,0,1,-1,1,0,-1,-1,-1,0,1,-1,-1,0,-1,1,1,1,0,1,1,-1,0,1,-1,1,0,1,-1,-1,0,-1,1,1,0,-1,1,-1,0,-1,-1,1,0,-1,-1,-1,0]),noise2D:function(t,a){var o,i,n=this.permMod12,f=this.perm,s=this.grad3,v=0,h=0,l=0,u=(t+a)*r,d=Math.floor(t+u),p=Math.floor(a+u),M=(d+p)*e,m=t-(d-M),c=a-(p-M);m>c?(o=1,i=0):(o=0,i=1);var y=m-o+e,w=c-i+e,g=m-1+2*e,A=c-1+2*e,x=255&d,q=255&p,D=.5-m*m-c*c;if(D>=0){var S=3*n[x+f[q]];v=(D*=D)*D*(s[S]*m+s[S+1]*c)}var U=.5-y*y-w*w;if(U>=0){var b=3*n[x+o+f[q+i]];h=(U*=U)*U*(s[b]*y+s[b+1]*w)}var F=.5-g*g-A*A;if(F>=0){var N=3*n[x+1+f[q+1]];l=(F*=F)*F*(s[N]*g+s[N+1]*A)}return 70*(v+h+l)},noise3D:function(r,e,a){var o,i,n,f,s,v,h,l,u,d,p=this.permMod12,M=this.perm,m=this.grad3,c=(r+e+a)*(1/3),y=Math.floor(r+c),w=Math.floor(e+c),g=Math.floor(a+c),A=(y+w+g)*t,x=r-(y-A),q=e-(w-A),D=a-(g-A);x>=q?q>=D?(s=1,v=0,h=0,l=1,u=1,d=0):x>=D?(s=1,v=0,h=0,l=1,u=0,d=1):(s=0,v=0,h=1,l=1,u=0,d=1):q<D?(s=0,v=0,h=1,l=0,u=1,d=1):x<D?(s=0,v=1,h=0,l=0,u=1,d=1):(s=0,v=1,h=0,l=1,u=1,d=0);var S=x-s+t,U=q-v+t,b=D-h+t,F=x-l+2*t,N=q-u+2*t,C=D-d+2*t,P=x-1+.5,T=q-1+.5,_=D-1+.5,j=255&y,k=255&w,z=255&g,B=.6-x*x-q*q-D*D;if(B<0)o=0;else{var E=3*p[j+M[k+M[z]]];o=(B*=B)*B*(m[E]*x+m[E+1]*q+m[E+2]*D)}var G=.6-S*S-U*U-b*b;if(G<0)i=0;else{var H=3*p[j+s+M[k+v+M[z+h]]];i=(G*=G)*G*(m[H]*S+m[H+1]*U+m[H+2]*b)}var I=.6-F*F-N*N-C*C;if(I<0)n=0;else{var J=3*p[j+l+M[k+u+M[z+d]]];n=(I*=I)*I*(m[J]*F+m[J+1]*N+m[J+2]*C)}var K=.6-P*P-T*T-_*_;if(K<0)f=0;else{var L=3*p[j+1+M[k+1+M[z+1]]];f=(K*=K)*K*(m[L]*P+m[L+1]*T+m[L+2]*_)}return 32*(o+i+n+f)},noise4D:function(r,e,t,i){var n,f,s,v,h,l,u,d,p,M,m,c,y,w,g,A,x,q=this.perm,D=this.grad4,S=(r+e+t+i)*a,U=Math.floor(r+S),b=Math.floor(e+S),F=Math.floor(t+S),N=Math.floor(i+S),C=(U+b+F+N)*o,P=r-(U-C),T=e-(b-C),_=t-(F-C),j=i-(N-C),k=0,z=0,B=0,E=0;P>T?k++:z++,P>_?k++:B++,P>j?k++:E++,T>_?z++:B++,T>j?z++:E++,_>j?B++:E++;var G=P-(l=k>=3?1:0)+o,H=T-(u=z>=3?1:0)+o,I=_-(d=B>=3?1:0)+o,J=j-(p=E>=3?1:0)+o,K=P-(M=k>=2?1:0)+2*o,L=T-(m=z>=2?1:0)+2*o,O=_-(c=B>=2?1:0)+2*o,Q=j-(y=E>=2?1:0)+2*o,R=P-(w=k>=1?1:0)+3*o,V=T-(g=z>=1?1:0)+3*o,W=_-(A=B>=1?1:0)+3*o,X=j-(x=E>=1?1:0)+3*o,Y=P-1+4*o,Z=T-1+4*o,$=_-1+4*o,rr=j-1+4*o,er=255&U,tr=255&b,ar=255&F,or=255&N,ir=.6-P*P-T*T-_*_-j*j;if(ir<0)n=0;else{var nr=q[er+q[tr+q[ar+q[or]]]]%32*4;n=(ir*=ir)*ir*(D[nr]*P+D[nr+1]*T+D[nr+2]*_+D[nr+3]*j)}var fr=.6-G*G-H*H-I*I-J*J;if(fr<0)f=0;else{var sr=q[er+l+q[tr+u+q[ar+d+q[or+p]]]]%32*4;f=(fr*=fr)*fr*(D[sr]*G+D[sr+1]*H+D[sr+2]*I+D[sr+3]*J)}var vr=.6-K*K-L*L-O*O-Q*Q;if(vr<0)s=0;else{var hr=q[er+M+q[tr+m+q[ar+c+q[or+y]]]]%32*4;s=(vr*=vr)*vr*(D[hr]*K+D[hr+1]*L+D[hr+2]*O+D[hr+3]*Q)}var lr=.6-R*R-V*V-W*W-X*X;if(lr<0)v=0;else{var ur=q[er+w+q[tr+g+q[ar+A+q[or+x]]]]%32*4;v=(lr*=lr)*lr*(D[ur]*R+D[ur+1]*V+D[ur+2]*W+D[ur+3]*X)}var dr=.6-Y*Y-Z*Z-$*$-rr*rr;if(dr<0)h=0;else{var pr=q[er+1+q[tr+1+q[ar+1+q[or+1]]]]%32*4;h=(dr*=dr)*dr*(D[pr]*Y+D[pr+1]*Z+D[pr+2]*$+D[pr+3]*rr)}return 27*(n+f+s+v+h)}},i._buildPermutationTable=n,"undefined"!=typeof define&&define.amd&&define(function(){return i}),"undefined"!=typeof exports?exports.SimplexNoise=i:"undefined"!=typeof window&&(window.SimplexNoise=i),"undefined"!=typeof module&&(module.exports=i)}();

"use strict";

const backgroundColor = "rgba(0,0,10,0.5)";
const baseHue = rand(360);
const rangeHue = 180;
const tentacleCount = 30;
const segmentCountMin = 10;
const segmentCountMax = 20;
const segmentLengthMin = 20;
const segmentLengthMax = 40;
const colonyRadius = 200;

let canvas;
let ctx;
let center;
let mouse;
let tick;
let simplex;
let tentacle;
let tentacles;

class Tentacle {
  constructor(x, y, segmentNum, baseLength, baseDirection) {
    this.base = [x, y];
    this.position = [x, y];
    this.target = [x, y];
    this.segmentNum = segmentNum;
    this.baseLength = baseLength;
    this.baseDirection = baseDirection;
    this.segmentProps = ["x1", "y1", "x2", "y2", "l", "d", "h"];
    this.segments = new PropsArray(segmentNum, this.segmentProps);
    this.follow = false;

    let i = this.segments.length - this.segmentProps.length;
    let x1, y1, x2, y2, l, d, h;

    l = this.baseLength;
    d = this.baseDirection;

    for (; i >= 0; i -= this.segmentProps.length) {
      x1 = x2 || this.position[0];
      y1 = y2 || this.position[1];
      x2 = x1 - l * cos(d);
      y2 = y1 - l * sin(d);
      d += 0.309;
      l *= 0.98;
      h = baseHue + fadeIn(i, this.segments.length) * rangeHue;
      this.segments.set([x1, y1, x2, y2, l, d, h], i);
    }
  }
  setCtx(ctx) {
    this.ctx = ctx;
  }
  setTarget(target) {
    this.target = target;
  }
  updateBase() {
    let t = simplex.noise3D(this.base[0] * 0.005, this.base[1] * 0.005, tick * 0.005) * TAU;

    this.base.lerp([
    this.base[0] + 20 * cos(t),
    this.base[1] + 20 * sin(t)],
    0.025);
  }
  async update() {
    let target = this.position;
    let i = this.segments.length - this.segmentProps.length;
    let promises = [];

    this.position.lerp(this.target, 0.015);
    !this.follow && this.updateBase();

    for (; i >= 0; i -= this.segmentProps.length) {
      promises.push(
      new Promise(resolve => {
        let [x1, y1, x2, y2, l, d, h] = this.segments.get(i);
        let t, n, tn;

        x1 = target[0];
        y1 = target[1];
        t = angle(x1, y1, x2, y2);
        n = simplex.noise3D(
        x1 * 0.005,
        y1 * 0.005,
        (i + tick) * 0.005);

        tn = t + n * PI * 0.0125;
        x2 = x1 + l * cos(tn);
        y2 = y1 + l * sin(tn);
        d = t;

        target = [x2, y2];

        this.segments.set([x1, y1, x2, y2, l, d], i);
        this.drawSegment(x1, y1, x2, y2, h, n, i);

        resolve();
      }));

    }

    await Promise.all(promises);
  }
  drawSegment(x1, y1, x2, y2, h, n, i) {
    const fn = fadeInOut(1 + n, 2);
    const fa = fadeInOut(i, this.segments.length);
    const a = 0.25 * (fn + fa);

    this.ctx.beginPath();
    this.ctx.strokeStyle = `hsla(${h}, 50%, 50%, ${a})`;
    this.ctx.moveTo(x2, y2);
    this.ctx.lineTo(x1, y1);
    this.ctx.stroke();
    this.ctx.beginPath();

    this.ctx.closePath();
    this.ctx.strokeStyle = `hsla(${h}, 50%, 50%, ${a + 0.5})`;
    this.ctx.arc(x1, y1, fn * 3, 0, TAU);
    this.ctx.stroke();
    this.ctx.closePath();
  }}


function setup() {
  tick = 0;
  simplex = new SimplexNoise();
  mouse = [0, 0];
  createCanvas();
  resize();
  tentacles = [];

  let i, t;

  for (i = 0; i < tentacleCount; i++) {
    t = i / tentacleCount * TAU;
    tentacle = new Tentacle(
    center[0] + colonyRadius * cos(rand(TAU)),
    center[1] + colonyRadius * sin(rand(TAU)),
    round(randIn(segmentCountMin, segmentCountMax)),
    round(randIn(segmentLengthMin, segmentLengthMax)),
    t);

    tentacle.setCtx(ctx.a);
    tentacles.push(tentacle);
  }

  loop();
}

function createCanvas() {
  canvas = {
    a: document.createElement("canvas"),
    b: document.createElement("canvas") };

  canvas.b.style = `
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
	`;
  document.body.appendChild(canvas.b);
  ctx = {
    a: canvas.a.getContext("2d"),
    b: canvas.b.getContext("2d") };

  center = [0.5 * canvas.a.width, 0.5 * canvas.a.height];
}

function resize() {
  const { innerWidth, innerHeight } = window;

  canvas.a.width = innerWidth;
  canvas.a.height = innerHeight;

  ctx.a.drawImage(canvas.b, 0, 0);

  canvas.b.width = innerWidth;
  canvas.b.height = innerHeight;

  ctx.b.drawImage(canvas.a, 0, 0);

  center[0] = 0.5 * canvas.a.width;
  center[1] = 0.5 * canvas.a.height;
}

function renderBackground() {
  ctx.a.clearRect(0, 0, canvas.b.width, canvas.b.height);
  ctx.b.fillStyle = backgroundColor;
  ctx.b.fillRect(0, 0, canvas.a.width, canvas.a.height);
}

function renderGlow() {
  ctx.b.save();
  ctx.b.filter = "blur(8px) brightness(200%)";
  ctx.b.globalCompositeOperation = "lighter";
  ctx.b.drawImage(canvas.a, 0, 0);
  ctx.b.restore();

  ctx.b.save();
  ctx.b.filter = "blur(4px) brightness(200%)";
  ctx.b.globalCompositeOperation = "lighter";
  ctx.b.drawImage(canvas.a, 0, 0);
  ctx.b.restore();
}

function renderToScreen() {
  ctx.b.save();
  ctx.b.globalCompositeOperation = "lighter";
  ctx.b.drawImage(canvas.a, 0, 0);
  ctx.b.restore();
}

async function loop() {
  tick++;

  renderBackground();

  await Promise.all(tentacles.map(tentacle => tentacle.update()));

  renderGlow();
  renderToScreen();

  window.requestAnimationFrame(loop);
}

window.addEventListener("load", setup);
window.addEventListener("resize", resize);
window.addEventListener("mousemove", e => {
  tentacles.forEach((tentacle, i) => {
    const t = i / tentacles.length * TAU;
    tentacle.setTarget([e.clientX + colonyRadius * cos(t + tick * 0.05), e.clientY + colonyRadius * sin(t + tick * 0.05)]);
    tentacle.follow = true;
  });
});
window.addEventListener("mouseout", () => {
  tentacles.forEach(tentacle => {
    tentacle.base = [
    center[0] + colonyRadius * cos(rand(TAU)),
    center[1] + colonyRadius * sin(rand(TAU))];

    tentacle.setTarget(tentacle.base);
    tentacle.follow = false;
  });
});
</script>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值