咨询电话:186 7916 6165 咨询电话:186 7916 6165 (微信同号)    在线QQ:181796286
NEWS BLOG ·
学无止境
关注开优网络 关注前沿
CSS3扁平化Loading动画特效
CSS3鼠标经过,图片放大效果

HTML5 Canvas点击页面烟花绽放特效

发表日期:2017-03-30    文章编辑:南昌开优网络    浏览次数:4859    标签:HTML5

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>HTML5 Canvas点击页面烟花绽放特效 - 开优网络</title>

<style>
canvas {
	position: absolute;
	top: 0;
	left: 0;
	background-color: #111;
}
</style>

</head>
<body>
<canvas id=c></canvas><script>
var gl = c.getContext( 'webgl', { preserveDrawingBuffer: true } )
	,	w = c.width = window.innerWidth
	,	h = c.height = window.innerHeight

	,	webgl = {}
	,	opts = {
		projectileAlpha: .8,
		projectileLineWidth: 1.3,
		fireworkAngleSpan: .5,
		baseFireworkVel: 3,
		addedFireworkVel: 3,
		gravity: .03,
		lowVelBoundary: -.2,
		xFriction: .995,
		baseShardVel: 1,
		addedShardVel: .2,
		fireworks: 100,
		baseShardsParFirework: 10,
		addedShardsParFirework: 10,
		shardFireworkVelMultiplier: .3,
		initHueMultiplier: 1/360,
		runHueAdder: .1/360
	}

webgl.vertexShaderSource = `
uniform int u_mode;
uniform vec2 u_res;
attribute vec4 a_data;
varying vec4 v_color;

vec3 h2rgb( float h ){
	return clamp( abs( mod( h * 6. + vec3( 0, 4, 2 ), 6. ) - 3. ) -1., 0., 1. );
}
void clear(){
	gl_Position = vec4( a_data.xy, 0, 1 );
	v_color = vec4( 0, 0, 0, a_data.w );
}
void draw(){
	gl_Position = vec4( vec2( 1, -1 ) * ( ( a_data.xy / u_res ) * 2. - 1. ), 0, 1 );
	v_color = vec4( h2rgb( a_data.z ), a_data.w );
}
void main(){
	if( u_mode == 0 )
		draw();
	else
		clear();
}
`;
webgl.fragmentShaderSource = `
precision mediump float;
varying vec4 v_color;

void main(){
	gl_FragColor = v_color;
}
`;

webgl.vertexShader = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource( webgl.vertexShader, webgl.vertexShaderSource );
gl.compileShader( webgl.vertexShader );

webgl.fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
gl.shaderSource( webgl.fragmentShader, webgl.fragmentShaderSource );
gl.compileShader( webgl.fragmentShader );

webgl.shaderProgram = gl.createProgram();
gl.attachShader( webgl.shaderProgram, webgl.vertexShader );
gl.attachShader( webgl.shaderProgram, webgl.fragmentShader );

gl.linkProgram( webgl.shaderProgram );
gl.useProgram( webgl.shaderProgram );

webgl.dataAttribLoc = gl.getAttribLocation( webgl.shaderProgram, 'a_data' );
webgl.dataBuffer = gl.createBuffer();

gl.enableVertexAttribArray( webgl.dataAttribLoc );
gl.bindBuffer( gl.ARRAY_BUFFER, webgl.dataBuffer );
gl.vertexAttribPointer( webgl.dataAttribLoc, 4, gl.FLOAT, false, 0, 0 );

webgl.resUniformLoc = gl.getUniformLocation( webgl.shaderProgram, 'u_res' );
webgl.modeUniformLoc = gl.getUniformLocation( webgl.shaderProgram, 'u_mode' );

gl.viewport( 0, 0, w, h );
gl.uniform2f( webgl.resUniformLoc, w, h );

gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
gl.enable( gl.BLEND );

gl.lineWidth( opts.projectileLineWidth );

webgl.data = [];

webgl.clear = function(){
	
	gl.uniform1i( webgl.modeUniformLoc, 1 );
	var a = .1;
	webgl.data = [
		-1, -1, 0, a,
		 1, -1, 0, a,
		-1,  1, 0, a,
		-1,  1, 0, a,
		 1, -1, 0, a,
		 1,  1, 0, a
	];
	webgl.draw( gl.TRIANGLES );
	gl.uniform1i( webgl.modeUniformLoc, 0 );
	webgl.data.length = 0;
}
webgl.draw = function( glType ){
	
	gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( webgl.data ), gl.STATIC_DRAW );
	gl.drawArrays( glType, 0, webgl.data.length / 4 );
}

var	fireworks = []
	,	tick = 0
	,	sins = []
	,	coss = []
	,	maxShardsParFirework = opts.baseShardsParFirework + opts.addedShardsParFirework
	,	tau = 6.283185307179586476925286766559;

for( var i = 0; i < maxShardsParFirework; ++i ){
	sins[ i ] = Math.sin( tau * i / maxShardsParFirework );
	coss[ i ] = Math.cos( tau * i / maxShardsParFirework );
}

function Firework(){
	this.reset();
	this.shards = [];
	for( var i = 0; i < maxShardsParFirework; ++i )
		this.shards.push( new Shard( this ) );
}
Firework.prototype.reset = function(){
	
	var angle = -Math.PI / 2 + ( Math.random() - .5 )* opts.fireworkAngleSpan
		,	vel = opts.baseFireworkVel + opts.addedFireworkVel * Math.random();
	
	this.mode = 0;
	this.vx = vel * Math.cos( angle );
	this.vy = vel * Math.sin( angle );
	
	this.x = Math.random() * w;
	this.y = h;
	
	this.hue = tick * opts.initHueMultiplier;
	
}
Firework.prototype.step = function(){
	
	if( this.mode === 0 ){
		
		var ph = this.hue
			,	px = this.x
			,	py = this.y;
		
		this.hue += opts.runHueAdder;
	
		this.x += this.vx *= opts.xFriction;
		this.y += this.vy += opts.gravity;
		
		webgl.data.push(
			px, py, ph, opts.projectileAlpha * .2,
			this.x, this.y, this.hue, opts.projectileAlpha * .2 );
		
		if( this.vy >= opts.lowVelBoundary ){
			this.mode = 1;

			this.shardAmount = opts.baseShardsParFirework + opts.addedShardsParFirework * Math.random() | 0;

			var baseAngle = Math.random() * tau
				,	x = Math.cos( baseAngle )
				,	y = Math.sin( baseAngle )
				,	sin = sins[ this.shardAmount ]
				,	cos = coss[ this.shardAmount ];

			for( var i = 0; i < this.shardAmount; ++i ){

				var vel = opts.baseShardVel + opts.addedShardVel * Math.random();
				this.shards[ i ].reset( x * vel, y * vel )
				var X = x;
				x = x * cos - y * sin;
				y = y * cos + X * sin;
			}
		}

	} else if( this.mode === 1 ) {
		
		this.ph = this.hue
		this.hue += opts.runHueAdder;
		
		var allDead = true;
		for( var i = 0; i < this.shardAmount; ++i ){
			var shard = this.shards[ i ];
			if( !shard.dead ){
				shard.step();
				allDead = false;
			}
		}
		
		if( allDead )
			this.reset();
	}
	
}
function Shard( parent ){
	this.parent = parent;
}
Shard.prototype.reset = function( vx, vy ){
	this.x = this.parent.x;
	this.y = this.parent.y;
	this.vx = this.parent.vx * opts.shardFireworkVelMultiplier + vx;
	this.vy = this.parent.vy * opts.shardFireworkVelMultiplier + vy;
	this.starty = this.y;
	this.dead = false;
	this.tick = 1;
}
Shard.prototype.step = function(){
	
	this.tick += .05;
	
	var px = this.x
		,	py = this.y;
	
	this.x += this.vx *= opts.xFriction;
	this.y += this.vy += opts.gravity;
	
	var proportion = 1 - ( this.y - this.starty ) / ( h - this.starty );
	
	webgl.data.push(
		px, py, this.parent.ph, opts.projectileAlpha / this.tick,
		this.x, this.y, this.parent.hue, opts.projectileAlpha / this.tick );
	
	if( this.y > h )
		this.dead = true;
}

function anim(){
	
	window.requestAnimationFrame( anim )
	
	webgl.clear();
	
	++tick;
	
	if( fireworks.length < opts.fireworks )
		fireworks.push( new Firework );
	
	fireworks.map( function( firework ){ firework.step(); } );
	
	webgl.draw( gl.LINES );
}
anim();

window.addEventListener( 'resize', function(){
	
	w = c.width = window.innerWidth;
	h = c.height = window.innerHeight;
	
	gl.viewport( 0, 0, w, h );
	gl.uniform2f( webgl.resUniformLoc, w, h );
})
window.addEventListener( 'click', function( e ){
	var firework = new Firework();
	firework.x = e.clientX;
	firework.y = e.clientY;
	firework.vx = 0;
	firework.vy = 0;
	fireworks.push( firework );
});
</script>

</body>
</html>