纯 JS 实现光的魔法阵

1,213 阅读1分钟

废话开篇:简单的用 js 实现一下光的魔法阵

一、实现效果

屏幕录制2022-01-15 上午9.14.52.gif

二、步骤分析

1、全局定时任务执行

2、绘制内、外圆弧

3、绘制正、反内接三角形

三、代码总览

1、HTML 部分
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<script src="./main.js"></script>
	<style type="text/css">
		#main {
			background-color: #000;
			width: 400px;
			height: 400px;
			margin-left: auto;
			margin: auto;
			margin-top: 100px;
		}
		
		#small {
			position: absolute;
			width: 400px;
			height: 400px;
			left: 50%;
			margin-left:-200px;
			margin-top: 100px;
			opacity: 0;
			top: 0;
		}
		
		#fullScreen{
			border-top:1px solid rgba(0,0,0,0);
			background-color: #000000;
			width: 100%;
			height: 100%;
			position: relative;
		}
	</style>
	<body>
		<div id="fullScreen" onclick="show()">
			<div id="main">
				<canvas id="myCanvas" width="400" height="400">
				</canvas>
			</div>
			<div id="small">
				<canvas id="smallMyCanvas" width="400" height="400">
				</canvas>
			</div>
		</div>
	</body>
	<script type="text/javascript">
		function show(){
			window.$magic.init("myCanvas","smallMyCanvas");
		}
	</script>
</html>

</html>

2、JS 部分
(function(window){
	var magic = {};
	magic.center = null;
	magic.r = 0;
	magic.ctx = null;
	magic.smallCtx = null,
	magic.outsideAngle = 0;
	magic.withinAngle = 270;
	magic.circleDistance = 30;
	magic.upTrianglePoints = [];
	magic.downTrianglePoints = [];
	magic.triangleDrawSidePercent = 0;
	magic.drawTriangleDrawSideIndex = 0;
	magic.xDeg = 0;
	magic.smallCanvasPositionAddY = 0;
	magic.smallCanvasScaleReduce = 1;
	magic.smallCanvasId = ""
	//初始化
	magic.init = function(id,smallCanvasId){
		this.getCanvas(id)
		this.timingTask();
		this.withinUpTrianglePoints();
		this.withinDownTrianglePoints();
		this.smallCanvasId = smallCanvasId;
		this.getSmallCanvas(this.smallCanvasId);
	}
	
	//获取画布
	magic.getCanvas = function(id){
		var c= document.getElementById(id);
		this.r = c.width / 2;
		this.center = {x:this.r,y:this.r};
		this.ctx = c.getContext("2d");
		//锯齿修复
		if (window.devicePixelRatio) {
		   c.style.width = c.width + "px";
		   c.style.height = c.height + "px";
		   c.height = c.height * window.devicePixelRatio;
		   c.width = c.width * window.devicePixelRatio;
		   this.ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
		}
	};
	
	//获取小画布
	magic.getSmallCanvas = function(id){
		var c = document.getElementById(id);
		this.smallCtx = c.getContext("2d");
		//锯齿修复
		if (window.devicePixelRatio) {
		   c.style.width = c.width + "px";
		   c.style.height = c.height + "px";
		   c.height = c.height * window.devicePixelRatio;
		   c.width = c.width * window.devicePixelRatio;
		   this.smallCtx.scale(window.devicePixelRatio, window.devicePixelRatio);
		}
		var color = "FFFAE8";
		var upPoint = this.upTrianglePoints[0];
		var upLeftPoint = this.upTrianglePoints[1];
		var upRightPoint = this.upTrianglePoints[2];
		this.smallCtx.beginPath();
		this.smallCtx.moveTo(upPoint.x, upPoint.y);
		this.smallCtx.lineTo(upLeftPoint.x, upLeftPoint.y);
		this.smallCtx.lineTo(upRightPoint.x, upRightPoint.y);
		this.smallCtx.fillStyle = color;
		this.smallCtx.closePath();
		this.smallCtx.fill();
		
		var downPoint = this.downTrianglePoints[0];
		var downLeftPoint = this.downTrianglePoints[1];
		var downRightPoint = this.downTrianglePoints[2];
		this.smallCtx.beginPath();
		this.smallCtx.moveTo(downPoint.x, downPoint.y);
		this.smallCtx.lineTo(downLeftPoint.x, downLeftPoint.y);
		this.smallCtx.lineTo(downRightPoint.x, downRightPoint.y);
		this.smallCtx.fillStyle = color;
		this.smallCtx.closePath();
		this.smallCtx.fill();
	}
	
	//画圆
	magic.drawCircle = function(point,r,startAngle,endAngle,counterclockwise){
		this.ctx.strokeStyle = "#FFF";
		this.ctx.lineWidth = 0.8;
		this.ctx.beginPath();
		this.ctx.arc(point.x,point.y,r,(2*Math.PI) * startAngle / 360,(2*Math.PI) * endAngle / 360,counterclockwise);
		this.ctx.stroke();
	};
	
	//画线
	magic.drawLine = function(fromPoint,toPoint,percent){
		 // 设置线条的颜色
		 this.ctx.strokeStyle = '#FFF';
		 // 设置线条的宽度
		 this.ctx.lineWidth = 1;
		 // 绘制直线
		 this.ctx.beginPath();
		 // 起点
		 this.ctx.moveTo(fromPoint.x, fromPoint.y);
		 // 终点
		 this.ctx.lineTo(fromPoint.x + (toPoint.x - fromPoint.x) * percent,fromPoint.y + (toPoint.y - fromPoint.y) * percent);
		 this.ctx.closePath();
		 this.ctx.stroke();
	}
	
	//计算小圆内正内接三角形各个坐标点
	magic.withinUpTrianglePoints = function(){
		var withinR = this.r - this.circleDistance;
		var topPoint = {x:this.r,y:this.circleDistance};
		var leftPoint = {x:this.r - withinR * Math.sin(Math.PI / 3),y:this.r + withinR * Math.cos(Math.PI / 3)};
		var rightPoint = {x:this.r + withinR * Math.sin(Math.PI / 3),y:this.r + withinR * Math.cos(Math.PI / 3)};
		this.upTrianglePoints = [topPoint,leftPoint,rightPoint];
	}
	
	//计算小圆内倒内接三角形各个坐标点
	magic.withinDownTrianglePoints = function(){
		var withinR = this.r - this.circleDistance;
		var downPoint = {x:this.r,y:this.r * 2 - this.circleDistance};
		var leftPoint = {x:this.r - withinR * Math.sin(Math.PI / 3),y:this.r - withinR * Math.cos(Math.PI / 3)};
		var rightPoint = {x:this.r + withinR * Math.sin(Math.PI / 3),y:this.r - withinR * Math.cos(Math.PI / 3)};
		this.downTrianglePoints = [leftPoint,rightPoint,downPoint];
	}
	
	//三角形变换任务队列
	magic.drawTriangleQueue = function(time,queue){
		
		if(this.xDeg == 70){
			//小六边形升起
			this.smallCanvasPositionAddY += 0.2;
			this.smallCanvasScaleReduce -= 0.001;
			var smallDiv = document.getElementById('small');
			smallDiv.style['-webkit-transform'] = 'translateZ(50px) rotateX('+ this.xDeg +'deg) ' + 'scale('+ this.smallCanvasScaleReduce +') skew(0deg, 0deg) translate(0px, 0px)';
			smallDiv.style['margin-top'] = (100 -  this.smallCanvasPositionAddY) + 'px';
			if(this.smallCanvasPositionAddY >= 80) {
				window.clearInterval(time);
			}
			return;
		}
		
		if(this.drawTriangleDrawSideIndex == queue.length){
			
			//绘制完成倾斜角度
			var mainDiv = document.getElementById('main');
			var smallDiv = document.getElementById('small');
			//显示小六边形
			smallDiv.style['opacity'] = '1';
			this.xDeg += 1;
			mainDiv.style['-webkit-transform'] = 'translateZ(50px) rotateX('+ this.xDeg + 'deg)';
			smallDiv.style['-webkit-transform'] = 'translateZ(50px) rotateX('+ this.xDeg + 'deg)';
			return;
		}
		
		//绘制三角形各个边
		this.triangleDrawSidePercent += 0.01;
		var callBack = queue[this.drawTriangleDrawSideIndex];
		callBack();
		if(this.triangleDrawSidePercent >= 1){
			this.triangleDrawSidePercent = 0;
			this.drawTriangleDrawSideIndex += 1;
		}
	}
	
	//定时任务
	magic.timingTask = function(){
		
		var that = this;
		var time = window.setInterval(function() {
			
			//圆绘制
			if(that.outsideAngle < 360){
				that.outsideAngle += 0.2;
				that.withinAngle -= 0.2;
			}
			that.drawCircle(that.center,that.r,0,that.outsideAngle,false);
			that.drawCircle(that.center,that.r - that.circleDistance,270,that.withinAngle,true);
			if(that.outsideAngle >= 360){
				//三角形绘制任务队列
				that.drawTriangleQueue(time,[
					function(){
						that.drawLine(that.downTrianglePoints[0],that.downTrianglePoints[1],that.triangleDrawSidePercent);
					},
					function(){
						that.drawLine(that.downTrianglePoints[1],that.downTrianglePoints[2],that.triangleDrawSidePercent);
					},
					function(){
						that.drawLine(that.downTrianglePoints[2],that.downTrianglePoints[0],that.triangleDrawSidePercent);
					},
					function(){
						that.drawLine(that.upTrianglePoints[0],that.upTrianglePoints[1],that.triangleDrawSidePercent);
					},
					function(){
						that.drawLine(that.upTrianglePoints[1],that.upTrianglePoints[2],that.triangleDrawSidePercent);
					},
					function(){
						that.drawLine(that.upTrianglePoints[2],that.upTrianglePoints[0],that.triangleDrawSidePercent);
					}
				]);
			}
		},5);
	}
	
	window.$magic = magic;
})(window)

四、总结与思考

1、定时器全局只有一个,处理不同绘制阶段。

2、计算内圆的两个内接三角形的各个顶点,依次加到任务队列去执行,全部绘制完成,停止任务定时器。

3、绘制过程中加上抗锯齿逻辑,绘制更平滑。

仅为随笔交流,大神勿笑[抱拳][抱拳][抱拳]