HTML5 Canvas有趣的情感粒子表情动画特效是一款比较趣味性的生气,开心,发怒,难受,伤心等表情动画。

代码如下:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>HTML5情感粒子表情特效</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<style>
html, body{
  width: 100%;
  height: 100%;
  overflow:hidden;
  margin: 0;
  padding: 0;
  background-color: #000000;
}
.container{
  width: 100%;
  height: 100%;
  margin: 0;
}
</style>
</head>
<body>
<div id="jsi-particle-container" class="container"></div>
<script>
var RENDERER = {
	init : function(){
		this.setParameters();
		this.reconstructMethods();
		this.createParticles();
		this.render();
	},
	setParameters : function(){
		this.$container = $('#jsi-particle-container');
		this.width = this.$container.width();
		this.height = this.$container.height();
		this.context = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');
		this.particles = [];
	},
	reconstructMethods : function(){
		this.render = this.render.bind(this);
	},
	createParticles : function(){
		for(var i = 0, length = Math.round(this.width / 500 * this.height / 500 * 10); i < length; i++){
			this.particles.push(new PARTICLE(this.width, this.height));
		}
	},
	render : function(){
		requestAnimationFrame(this.render);
		this.context.clearRect(0, 0, this.width, this.height);
		
		for(var i = 0, length = this.particles.length; i < length; i++){
			this.particles[i].render(this.context);
		}
		this.checkCollision();
	},
	checkCollision : function(){
		for(var i = 0, particleCount = this.particles.length; i < particleCount; i++){
			var particle = this.particles[i];
			
			for(var j = i + 1; j < particleCount; j++){
				this.particles[j].checkCollision(particle);
			}
		}
	}
};
var PARTICLE = function(width, height){
	this.width = width;
	this.height = height;
	this.init();
};
PARTICLE.prototype = {
	COLOR : 'hsl(%h, 80%, %l%)',
	RADIUS : {MIN : 25, MAX : 35},
	MASS_RATE : 0.0001,
	VELOCITY : {MIN : 2, MAX : 4},
	WALL_RESTITUTION : 1.0,
	PARTICLE_RESTITUTION : 1.0,
	MAX_FACE_INDEX : 4,
	DELTA_SCALE : 0.01,
	INIT_RADIAN : 220,
	DELTA_RADIAN : {MIN : 4, MAX : 8},
	DELTA_THETA : Math.PI / 10,
	
	init : function(){
		this.radius = this.createRandomValue(this.RADIUS);
		this.mass = Math.round(Math.pow(this.radius, 3) * this.MASS_RATE);
		this.x = this.createRandomValue({MIN : this.radius, MAX : this.width - this.radius});
		this.y = this.createRandomValue({MIN : this.radius, MAX : this.height - this.radius});
		this.previousX = this.x;
		this.previousY = this.y;
		this.radian = this.INIT_RADIAN;
		this.deltaRadian = this.createRandomValue(this.DELTA_RADIAN) | 0;
		this.color = this.COLOR.replace('%h', this.radian);
		this.vx =  this.createRandomValue(this.VELOCITY) * ((Math.random() > 0.5) ? 1 : -1);
		this.vy =  this.createRandomValue(this.VELOCITY) * ((Math.random() > 0.5) ? 1 : -1);
		this.faceIndex = this.MAX_FACE_INDEX;
		this.scale = 0;
		this.theta = 0;
	},
	createRandomValue : function(range){
		return range.MIN + Math.round((range.MAX - range.MIN) * Math.random());
	},
	moveParticle : function(){
		this.previousX = this.x;
		this.previousY = this.y;
		this.x += this.vx;
		this.y += this.vy;
		
		if(this.x <= this.radius){
			this.x = this.radius;
			this.vx *= -this.WALL_RESTITUTION;
		}else if(this.x >= this.width - this.radius){
			this.x = this.width - this.radius;
			this.vx *= -this.WALL_RESTITUTION;
		}
		if(this.y <= this.radius){
			this.y = this.radius;
			this.vy *= -this.WALL_RESTITUTION;
		}else if(this.y > this.height - this.radius){
			this.y = this.height - this.radius;
			this.vy *= -this.WALL_RESTITUTION;
		}
	},
	getParticleInfo : function(){
		return {
			x : this.x,
			y : this.y,
			previousX : this.previousX,
			previousY : this.previousY,
			vx : this.vx,
			vy : this.vy,
			radius : this.radius,
			mass : this.mass,
			color : this.COLOR.replace('%h', this.radian)
		};
	},
	setParticleInfo : function(x, y, vx, vy){
		this.previousX = this.x;
		this.previousY = this.y;
		this.x = x;
		this.y = y;
		this.vx = vx;
		this.vy = vy;
		
		if(this.radian > 0){
			this.radian = Math.max(0, this.radian - this.deltaRadian);
		}
	},
	checkCollision : function(particle){
		if(this.radian == 0){
			return;
		}
		var particle1 = this.getParticleInfo(),
			particle2 = particle.getParticleInfo(),
			dx = particle2.x - particle1.x,
			dy = particle2.y - particle1.y,
			distance = Math.sqrt(dx * dx + dy * dy);
			
		if(distance > particle1.radius + particle2.radius){
			return;
		}
		var angle = Math.atan2(dy, dx),
			axis1 = {x : 0, y : 0},
			axis2 = this.rotate(dx, dy, angle),
			v1 = this.rotate(particle1.vx, particle1.vy, angle),
			v2 = this.rotate(particle2.vx, particle2.vy, angle),
			vSum = (v1.x - v2.x) * this.PARTICLE_RESTITUTION;
			
		v1.x = ((particle1.radius - particle2.radius * this.PARTICLE_RESTITUTION) * v1.x + particle2.radius * v2.x * (1 + this.PARTICLE_RESTITUTION)) / (particle1.radius + particle2.radius);
		v2.x = v1.x + vSum;
			
		var vAbs = Math.abs(v1.x) + Math.abs(v2.x),
			overlap = (particle1.radius + particle2.radius) - Math.abs(axis1.x - axis2.x);
			
		if(axis1.x >= axis2.x){
			axis1.x += Math.abs(overlap * v1.x / vAbs);
			axis2.x -= Math.abs(overlap * v2.x / vAbs);
		}else{
			axis1.x -= Math.abs(overlap * v1.x / vAbs);
			axis2.x += Math.abs(overlap * v2.x / vAbs);
		}
		axis1 = this.rotate(axis1.x, axis1.y, -angle);
		axis2 = this.rotate(axis2.x, axis2.y, -angle);
		v1 = this.rotate(v1.x, v1.y, -angle);
		v2 = this.rotate(v2.x, v2.y, -angle);
		
		this.setParticleInfo(particle1.x + axis1.x, particle1.y + axis1.y, v1.x, v1.y);
		particle.setParticleInfo(particle1.x + axis2.x, particle1.y + axis2.y, v2.x, v2.y);
	},
	rotate : function(x, y, angle){
		var sin = Math.sin(angle),
			cos = Math.cos(angle);
		return {x : x * cos + y * sin, y : y * cos - x * sin};
	},
	render : function(context){
		this.moveParticle();
		context.save();
		var axis = this.getParticleInfo();
		
		if(this.radian == 0){
			var scale = 1 + 4 * Math.pow((1 - this.scale), 5);
			context.translate(axis.x, axis.y);
			context.scale(scale, scale);
			context.rotate(Math.PI / 5 * Math.sin(this.theta));
			context.translate(-axis.x, -axis.y);
			context.globalAlpha = Math.max(0, this.scale);
			
			if(this.scale > 0){
				this.scale -= this.DELTA_SCALE;
				this.theta += this.DELTA_THETA;
				this.theta %= Math.PI * 2;
			}else{
				this.init();
			}
		}else if(this.scale < 1){
			context.translate(axis.x, axis.y);
			context.scale(this.scale, this.scale);
			context.translate(-axis.x, -axis.y);
			context.globalAlpha = this.scale;
			this.scale = Math.min(1, this.scale + this.DELTA_SCALE * 5);
		}
		context.beginPath();
		var gradient = context.createRadialGradient(axis.x, axis.y, 0, axis.x, axis.y, axis.radius);
		gradient.addColorStop(0, axis.color.replace('%l', '60'));
		gradient.addColorStop(1, axis.color.replace('%l', '30'));
		context.fillStyle = gradient;
		context.arc(axis.x, axis.y, axis.radius, 0, Math.PI * 2, true);
		context.fill();
		context.lineWidth = 2;
		context.strokeStyle = 'hsl(0, 0%, 100%)';
		context.fillStyle = 'hsl(0, 0%, 100%)';
		
		switch(Math.min(this.MAX_FACE_INDEX, Math.floor(this.radian / (this.INIT_RADIAN / (this.MAX_FACE_INDEX + 1))))){
		case 0:
			context.beginPath();
			context.moveTo(axis.x - 14, axis.y - 10);
			context.lineTo(axis.x - 4, axis.y - 2);
			context.moveTo(axis.x + 14, axis.y - 10);
			context.lineTo(axis.x + 4, axis.y - 2);
			context.moveTo(axis.x - 8, axis.y + 15);
			context.quadraticCurveTo(axis.x, axis.y + 5, axis.x + 8, axis.y + 15);
			context.stroke();
			break;
		case 1:
			context.beginPath();
			context.moveTo(axis.x - 15, axis.y - 8);
			context.quadraticCurveTo(axis.x - 10, axis.y, axis.x - 5, axis.y - 8);
			context.moveTo(axis.x + 15, axis.y - 8);
			context.quadraticCurveTo(axis.x + 10, axis.y, axis.x + 5, axis.y - 8);
			context.moveTo(axis.x - 8, axis.y + 15);
			context.quadraticCurveTo(axis.x, axis.y + 5, axis.x + 8, axis.y + 15);
			context.stroke();
			break;
		case 2:
			context.beginPath();
			context.moveTo(axis.x - 14, axis.y - 5);
			context.lineTo(axis.x - 4, axis.y - 5);
			context.moveTo(axis.x + 14, axis.y - 5);
			context.lineTo(axis.x + 4, axis.y - 5);
			context.moveTo(axis.x - 7, axis.y + 10);
			context.lineTo(axis.x + 7, axis.y + 10);
			context.stroke();
			break;
		case 3:
			context.beginPath();
			context.arc(axis.x - 10, axis.y - 5, 3, 0, Math.PI * 2, false);
			context.arc(axis.x + 10, axis.y - 5, 3, 0, Math.PI * 2, false);
			context.fill();
			context.beginPath();
			context.moveTo(axis.x - 7, axis.y + 5);
			context.quadraticCurveTo(axis.x, axis.y + 15, axis.x + 7, axis.y + 5);
			context.stroke();
			break;
		case 4:
			context.beginPath();
			context.moveTo(axis.x - 14, axis.y - 5);
			context.quadraticCurveTo(axis.x - 10, axis.y - 15, axis.x - 6, axis.y - 5);
			context.moveTo(axis.x + 14, axis.y - 5);
			context.quadraticCurveTo(axis.x + 10, axis.y - 15, axis.x + 6, axis.y - 5);
			context.moveTo(axis.x - 7, axis.y + 5);
			context.quadraticCurveTo(axis.x, axis.y + 15, axis.x + 7, axis.y + 5);
			context.stroke();
		}
		context.restore();
	}
};
$(function(){
	RENDERER.init();
});
</script>
</body>
</html>
 
  • 本站名称:清风资源网
  • 本站永久地址:www.mcoxn.com
  • 本站提供的源码、模板、插件等等其他资源,除资源本身问题外,都不包含免费技术服务,请大家谅解!
  • 本站资源解压密码一般都为www.mcoxn.com如发现链接失效,请联系在线客服更新。
  • 本站所有代码模板仅供学习交流使用,请勿用于商业用途,及违法侵权行为使用均与本站无关。
  • 源码素材属于虚拟商品,具有可复制性,可传播性,一旦授予,不接受任何形式的退款、换货要求。
  • 本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。
  • 本站所有资源来源于公开互联网搜集和网友投稿提供,仅供个人学习研究使用,若本站收录的内容对您的版权或者利益造成损害,请提供相应的资质证明发邮件至kakbga@qq.com我们将于3个工作日内予以删除。