HTML5 canvas 拖尾效果(或尾巴 或方向标 或留痕。。。)

时间:2024-04-04 11:05:33

【canvas】带拖尾效果的旋转扩散

使用 HTML5 canvas 制作一个圆形的拖尾,以及附加上拖尾的扩散效果

我说说的拖尾,就是拖着一个尾巴,其他简短的名词暂时想不到了。下图效果弄出来,着实费劲,还是基础太差了,好在都不难,还可以补救补救,写博客记录下,本打算弄Canvas弄个好玩的东西出来,实际做的时候,一大堆需要考虑和实现的,本人的数学也不是很好,大学都没,坐标旋转和矩阵转换看了好多才有点头绪,三角和差公式,点积和叉乘(判断图形碰撞用)也搞了好久,也没完全弄懂,实际操作还要看自己以前实现的Demo,碰撞这块,圆环是个坑,不知道咋,我百度找不到一个,还好圆环是规则图形,自己动动脑吧以前学的看的多边形碰撞仔细一缕,解决方法也想到了,暂时还没用代码实现,将来再实现吧,思路写下来,等有时间验证下。(⊙o⊙)…,还是说点正经的吧。。。

等,虽不是仙人,但依然可以指路。上面提到的一些知识,如果有兴趣可以看看这
https://www.w3cplus.com/canvas/custom-of-coordinate-transformation.html

HTML5 canvas 拖尾效果(或尾巴 或方向标 或留痕。。。)

代码正还在整理,后续给出,做这个拖尾效果用到了 createRadialGradient ,我的另一篇圆环扩散效果也用到了。现在先可以把图中右下角的实现代码和思路先弄出来,图中的旋转效果也简单,拖尾这个比较复杂,但可以调整大小和微调拖尾的一点点角度,这个调整关系用到很多位置计算,光一点点微调的功能就很费劲了,这要一点一点看着效果修改,找其中变换过程的规律,才能正常显示成我们想要的结果。

放大上图中,右下角拖尾的参数看看

HTML5 canvas 拖尾效果(或尾巴 或方向标 或留痕。。。)

图中一大堆圆和线啥的还有一块一块的。。。看不懂,搞得啥?,咋搞的?,怎么搞?。有的人初次接触,做这个效果出来也不知道用啥方便,我就来说说:图中 最左边的区域是效果实现解析 为啥这么说呢,一会就回明白;图中 *的区域是实际的效果 这个就简单许多,但实现和右边用的同一个 createRadialGradient,然后在中心区域覆盖上想要绘制的图形就OK了,图中 最右边的区域是通过*区域剪裁出来的最小的有效区域 这个剪裁还要用到麻烦的计算。最后最右边的这个就可以拿出来批量复制,旋转,移动等渲染到要显示的地方。
这里给一个提示:在做小游戏的时候,都一定会考虑性能的问题,预渲染的技术就需要把确定的内容先处理好,最后直接拿过来用,这要比在游戏的主循环中直接动态绘制快的多。


说了那么多没用的,也该上核心代码了。。。,由于代码还在整理,所以只能把绘制拖尾效果的代码弄出来参考了,一点一点从源代码中扣出来的,也不一定能跑起来,思路我会写的,见谅海涵,如有疏漏,还请指出。

页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>拖尾</title>
</head>
	<body>
	    <div id="myCache" ></div>
	</body>
</html>

样式

 body {
     overflow: hidden;
     background: #000;
 }
 body,
 html {
     height: 100%;
     width: 100%;
     margin: 0;
     padding: 0;
 }
#myCache{
	width:400px
	height:200px;
	position: absolute;
	right:0;
	bottom:0;
	z-index:10;
	background-color: rgba(255,255,255,0.2);
}

核心代码 文中的变量名可能会有误导,还请仔细看清,我也是随手就写的变量名字

	window.cacheDiv = document.getElementById("myCache");
	
	//兼容写法
	window.RAF = (function() {
			return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
				window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
					window.setTimeout(callback, 1000 / 60);
				};
	})();
		

	window.tuowei={
		cache:{}, 
		eleConvas:null ,// 这就是上面说到的 三个区域 最左边的那个剪裁Canvas
		conts:null,//eleConvas.getContext("2d") 
		imgData:null,//剪裁后的图像数据
		imgW:0,
		imgH:0,
		id :0,
		getNextID:function (){
			this.id++;
			return this.id;
		},
		addCvs:function(w,h){
			var cvs = document.createElement("canvas");
			cvs.id=this.getNextID();
			cvs.width = w;
			cvs.height = h;
			cvs.style.border="1px solid #f00";
			window.cacheDiv.appendChild(cvs);
			this.cache[cvs.id]=cvs;
			return cvs;
		},
		// r 半径
		renderWeiba:function(r,wad){
			//这些注释 只是作者用来看的,方便查找问题,实际这下面还有比这更复杂一大段,这只是简化了一些
			//原本还想做 拖尾扩散的宽度大于圆形的直径的,实际看了下规则和实际的效果,果断放弃了
			//width r/3*4+2*r+(3-wad)*r/3*3
			//height r/3*5+3*r+(3-wad)*r/3*4
			//top r/3*2+r + (3-wad)*r/3*3 || 作用于top (2+1)
			//left r/3*2+r + (3-wad)*r/3*2	|| 作用于 left 与 right
			
			if(wad>3) wad=3;
			if(wad<1) wad=1;
			if(!(wad==1||wad==2||wad==3)) wad=3; // 3 拖尾收缩 2 收缩一点 1 最大收缩
				
			//这一段的大概是 以 半径的三分之一为基础的偏移量做的准备
			var rs = r/3;
			var zj = r*2;// 直径 看不懂的憋说话
			var wads = (3-wad)*rs; //3-wad 整体缩放偏移量
			var max_w = rs*4+zj;// rs*4 r 的半径扩大了 3分子2 加上 直径 就是红色圆的大小
			var max_h = rs*5+zj+r+wads*4; // 这个 rs*5 算起来比较坑 我都忘了
			
			var left = max_w/2; // 简单 最大的圆的半径 也就是红色圆的半径 绿色圆的中心起点位置
			var top = rs*2+r+wads*3; //  rs*2+r 绿色圆的中心距离顶部的距离 在无缩放Wads = 3 的时候 rs*2 就是给 黑色圆进行偏移的
			var top1 = top+rs*4+wads; 
			// rs*4 基于绿色圆想下偏移到红色圆的位置
			//(可能说的有点怪怪的,是以 r/3 的四倍距离来确定 拖尾 渐变填充的图形的位置)
			
			var top2 = r+wads; // 渐变填充参数的第二个圆的顶部距离(可不是第二个参数哦),wads 是使用缩放后,目标填充区域的中心 蓝色圆会变小,反而外面的那个 黑色 的圆会变大
			var r1 = r-wads;// 蓝色圆半径
			var r2 = r+wads;// 黑色圆半径
			var r3 = r+rs*2;// 红色圆半径
			
			var cont = this.addCvs(max_w,max_h).getContext("2d");
			//我喜欢叫 渐变填充器
			var grad=cont.createRadialGradient(left,top1,r1,left,top2,r2);
			grad.addColorStop(1,"rgba(255,145,188,1)");//按效果 我喜欢 从1到0 
//  		grad.addColorStop(0.8,"rgba(0,0,255,1)");//看到一点点
//  		grad.addColorStop(0.6,"rgba(0,0,0,0.7)");//1
//  		grad.addColorStop(0.4,"rgba(255,0,0,0.5)");//2
//  		grad.addColorStop(0.2,"rgba(255,255,0,0.3)");//3
//  		grad.addColorStop(0.1,"rgba(0,255,255,0.1)");//4
// 			grad.addColorStop(0,"rgba(255,0,255,0)");//5
			grad.addColorStop(0.7,"rgba(255,145,188,1)");//6
			grad.addColorStop(0,"rgba(255,255,255,0)");//7

			//上面一大堆 addColorStop 就是插入渐变的节点 
			// 第一个参数是相对渐变开始和结束的 比例 
			
			/* 没测过自己是不是色盲,就把几个简单的颜色记录下
			* 255 - - 红
			* - 255 - 绿
			* - - 255 蓝
			* - 255 255 青
			* 255 255 - 黄
			* 255 - 255 紫红
			* 255 145 188 //粉红
			*/
			
			//紧接着的这个图形就是绘制的拖尾
			cont.beginPath(); 
			cont.arc(left, top1,r3, 0, Math.PI *2);
			cont.fillStyle =grad; 
			cont.strokeStyle = "red"; // 这个用来看边框用的 下方还有个没有边框的
			cont.fill(); 
			cont.stroke();
			
			//我用的圆形,所以这代表图形的位置
			cont.beginPath(); 
			cont.arc(left, top,r, 0, Math.PI *2);
			cont.strokeStyle = "green";
			cont.stroke();
			
			//1  createRadialGradient 的第一个渐变圆的位置描画出来看
			cont.beginPath(); 
			cont.arc(left, top1 ,r1, 0, Math.PI *2);
			cont.strokeStyle = "blue";
			cont.stroke();
			
			//2 createRadialGradient 的第二个渐变圆的位置描画出来看
			cont.beginPath(); 
			cont.arc(left, top2, r2,0, Math.PI *2);
			cont.strokeStyle = "black";
			cont.stroke();
			
			//这就是我们要拖尾的效果
			var cont2 = this.addCvs(max_w,max_h).getContext("2d");
			cont2.beginPath(); 
			cont2.arc(left, top1,r3, 0, Math.PI *2);
			cont2.fillStyle =grad; 
			cont2.fill(); 
			
			//在上一个拖尾的效果上覆盖上一个图形 看上去大功告成
			cont2.beginPath(); 
			cont2.arc(left, top,r, 0, Math.PI *2);
			cont2.fillStyle ="rgba(255,145,188,1)"; 
			cont2.fill(); 
			
			//准备剪切出实际有效的区域
			var r_l = left - r;
			var r_t = top - r;
			var r_r = left + r;
			var r_b = top1 + r1;
			
			//copy 
			this.imgW = zj;
			this.imgH = r_b-r_t;
			//getImageData 获取一个指定矩形区域的图像数据
			this.imgData=cont2.getImageData(r_l,r_t,r_r,r_b); 
			this.eleConvas = this.addCvs(zj,r_b-r_t);
			this.conts = this.eleConvas.getContext("2d");
			//putImageData 将 getImageData 的图像填充到执行 x y 开始的位置。
			this.conts.putImageData(this.imgData,0,0);
			//putImageData 这个坑爹的填充不能使用旋转,烦的一批
		},
		getImgData:function(){
			_this = this;
			return{
				imgW:_this.imgW,
				imgH:_this.imgH,
				imgData:_this.imgData,
				eleConvas:_this.eleConvas,
				conts:_this.conts
			}
		}
	}
	//半径为 60 拖尾缩放为 2 级 
	window.tuowei.renderWeiba(60,2);

解析: 四个大小不同的圆分别对应着一些参数的实际位置。
红色圆: 拖尾效果是填充在一个圆形中的,其实也可以填充在矩形等图形中,这需要自己手动观察效果和寻找规律。
绿色圆: 嗯,这是个根,绿绿的。。。这个是用来看的,与实现的效果没有直接关系,换成啥都行。
黑色和蓝色这个其实是代表 createRadialGradient 开始和结束的两个圆,用另外的圆苗了下,红色圆渐变填充效果的参数位置,好仔细观察圆的位置,大小,距离,在填充区域显示的效果
其中: createRadialGradient.addColorStop 第一个参数还能实现一些其他的效果,比如月牙填充,这个填充出来的月牙可比修改 globalCompositeOperation 实现额效果要炸裂许多。

月牙部分实例

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var grd=ctx.createRadialGradient(130,100,70,100,100,100);
grd.addColorStop(1,"red");
grd.addColorStop(0,"white");
ctx.fillStyle=grd;
ctx.arc(100,100,100,0, Math.PI * 2);
ctx.fill();

/* **比如** 设置填充目标的圆半径为 50 渐变填充器 第一个圆的参数,也就是渐变开始位置大小与 目标圆 同心同大小,
渐变的的第二个圆的参数设置的半径小一些 25 ,也就是结束渐变的位置,这个最好让 x 或 y 偏移半径的一半 ,从外到
内逐渐透明 就已经很棒了,黑色背景看白色的透明效果极其明显 。
*/

关于 context.createRadialGradient 的介绍,还是要看看官方说明,自己动手试试这个贱变色的API,参数中开始或者结束的圆形区域超出屏幕一些后就不显示了,搞得我以为什么东西坏了,找半天不知道啥情况。
相关连接 W3C:http://www.w3school.com.cn/tags/canvas_createradialgradient.asp.
相关连接:https://www.cnblogs.com/tianma3798/p/5895811.html.

未完待续。。。

或发布新文章~ 或继续~ 或~ ~

如有误导请联系,我会进行修正。
邮箱 [email protected]