工作繁忙之际,抽了点时间看了下canvas,今天看到了“非零环绕规则”,抱着好奇的心里写了下书上的demo看了看效果,感觉还蛮实用的。
先简单说说“非零环绕规则”原理(基本摘自书本):如果绘图路径是循环的,或是包含多个相交的子路径,那么canvas的绘图环境变量就必须要判断,当fill()方法被调用时,应该如何对当前路径进行填充。canvas在填充那种互相有交叉路径时就会使用到“非零环绕规则”。“非零环绕规则是这么来判断自我交叉情况的路径的:对于路径中的任意给定的区域,从该区域内部画一条足够长的线段,使此线段的终点完全落在路径范围之外。
接下来,将计数器初始化为0,然后,每当这条线段与路径上的直线或曲线相交时,就改变计数器的值。(这里是重点)如果是与路径的顺时针部分相交,则加1,如果是与路径的逆时针部分相交,则减1.若计数器的值最终不为0,那么此区域就在路径里面(这里理解了就好说了),在调用fill()方法时,浏览器就会对其进行填充。如果最终值是0,那么此区域就不在路径内部,浏览器也就不会对其进行填充了。
看到这差不多明白了吧,就是通过区域能够通过的路径的最终计数值来判断该区域是否会进行填充。下面以一个小demo来进行说明。
1 var canvas=document.getElementById('canvas'); 2 var context=canvas.getContext('2d'); 3 function drawTwoArcs(){ 4 context.beginPath(); 5 context.arc(300,190,150,0,Math.PI*2,false); //顺时针 6 context.arc(300,190,100,0,Math.PI*2,true); //逆时针 7 8 context.fill(); 9 context.stroke(); 10 } 11 12 function draw(){ 13 context.clearRect(0,0,context.canvas.width,context.canvas.height); 14 context.save(); 15 context.shadowColor="rgba(0,0,0,0.8)"; 16 context.shadowOffsetX=12; 17 context.shadowOffsetY=12; 18 context.shadowBlur=15; 19 20 drawTwoArcs(); 21 context.restore(); 22 } 23 24 context.fillStyle="rgba(100,140,230,0.5)"; 25 context.strokeStyle=context.fillStyle; 26 draw();
该demo中绘制了2个同心圆,内部的圆以顺时针绘制,外部的圆以逆时针绘制,程序最后对外层圆填充时,浏览器就会运用”非零环绕规则“进行填充判断。我们从里层圆内部引一条直线出来,它会与路径相交2次,两个圆的绘制方向不同,所以从里层的圆引出的线最终计数值为0,里层的圆就不会进行填充,同理可以得到外层圆与里层圆之间的部分会得到填充,最终就产生了一种剪纸图案的效果(个人感觉不怎么像剪纸,不过效果还是有点漂亮的)。
效果图: