【canvas】带拖尾效果的旋转扩散
使用 HTML5 canvas 制作一个圆形的拖尾,以及附加上拖尾的扩散效果
我说说的拖尾,就是拖着一个尾巴,其他简短的名词暂时想不到了。下图效果弄出来,着实费劲,还是基础太差了,好在都不难,还可以补救补救,写博客记录下,本打算弄Canvas弄个好玩的东西出来,实际做的时候,一大堆需要考虑和实现的,本人的数学也不是很好,大学都没,坐标旋转和矩阵转换看了好多才有点头绪,三角和差公式,点积和叉乘(判断图形碰撞用)也搞了好久,也没完全弄懂,实际操作还要看自己以前实现的Demo,碰撞这块,圆环是个坑,不知道咋,我百度找不到一个,还好圆环是规则图形,自己动动脑吧以前学的看的多边形碰撞仔细一缕,解决方法也想到了,暂时还没用代码实现,将来再实现吧,思路写下来,等有时间验证下。(⊙o⊙)…,还是说点正经的吧。。。
等,虽不是仙人,但依然可以指路。上面提到的一些知识,如果有兴趣可以看看这
https://www.w3cplus.com/canvas/custom-of-coordinate-transformation.html
代码正还在整理,后续给出,做这个拖尾效果用到了
createRadialGradient
,我的另一篇圆环扩散效果也用到了。现在先可以把图中右下角的实现代码和思路先弄出来,图中的旋转效果也简单,拖尾这个比较复杂,但可以调整大小和微调拖尾的一点点角度,这个调整关系用到很多位置计算,光一点点微调的功能就很费劲了,这要一点一点看着效果修改,找其中变换过程的规律,才能正常显示成我们想要的结果。
放大上图中,右下角拖尾的参数看看
图中一大堆圆和线啥的还有一块一块的。。。看不懂,搞得啥?,咋搞的?,怎么搞?。有的人初次接触,做这个效果出来也不知道用啥方便,我就来说说:图中 最左边的区域是效果实现解析 为啥这么说呢,一会就回明白;图中 *的区域是实际的效果 这个就简单许多,但实现和右边用的同一个
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]