有关遮罩和图层叠加的问题(附刮刮卡demo)
最近有童鞋问了在mask下graphics很多方面的bug,我今天就来一一解决一下。
首先先做个连续画图。
stage.addEventListener("stagemousedown",function (event){ shape.graphics.beginFill("#ff0000") shape.graphics.drawCircle(event.rawX,event.rawY,10); shape.graphics.endFill(); }); shape = new createjs.Shape(); container.addChild(shape);
http://www.ajexoop.com/test/graphics/test1.html
可以画出来,然后我们把它当做遮罩以后呢?
stage.addEventListener("stagemousedown",function (event){ shape.graphics.beginFill("#ff0000") shape.graphics.drawCircle(event.rawX,event.rawY,10); shape.graphics.endFill(); }); var bitmap = new createjs.Bitmap(images.back); container.addChild(bitmap); shape = new createjs.Shape(); bitmap.mask = shape;
http://www.ajexoop.com/test/graphics/test2.html
显然只能显示最后一次画的了。
要全部显示其实也很简单,只用一次beginFill不要endFill就可以了
stage.addEventListener("stagemousedown",function (event){ shape.graphics.drawCircle(event.rawX,event.rawY,10); }); var bitmap = new createjs.Bitmap(images.back); stage.addChild(bitmap); shape = new createjs.Shape(); shape.graphics.beginFill("#ff0000") bitmap.mask = shape;
http://www.ajexoop.com/test/graphics/test3.html
大家可以看到,修改了以后bug更夸张了,东西全部连一块了,关于这个bug一开始本人以为是mask的问题,但是去掉mask也一样。然后我拿flash试了一下,结果2者机制并不一样,flash可以随时endFill()不需要一直连续的draw。
//as3 flash代码 var shape:Shape = new Shape(); shape.graphics.beginFill(0xff0000) shape.graphics.drawRect(0,0,100,100) shape.graphics.endFill(); mc.mask = shape stage.addChild(shape) stage.addEventListener(MouseEvent.CLICK,clickHandler); function clickHandler(e:MouseEvent):void { shape.graphics.beginFill(0xff0000) shape.graphics.drawCircle(stage.mouseX,stage.mouseY,10); shape.graphics.endFill(); trace("ok") }
我先去查了下api,发现了closePath(闭合路径),确实这问题像是PS里用钢笔工具时没闭合路径,加上closePath试一下。
stage.addEventListener("stagemousedown",function (event){ shape.graphics.drawCircle(event.rawX,event.rawY,10).closePath(); }); var bitmap = new createjs.Bitmap(images.back); stage.addChild(bitmap); shape = new createjs.Shape(); shape.graphics.beginFill("#ff0000") bitmap.mask = shape;
http://www.ajexoop.com/test/graphics/test4.html
结果一次就成功。但是!同一个圆点双击后又有bug了(天杀的createjs团队,bug真多)
每次点击都会向第一次花的发现靠拢,怎么解决呢,那我每次点的时候重新空画一下不就好了(试了好多种方法只有这个管用),上代码:
stage.addEventListener("stagemousedown",function (event){ shape.graphics.drawCircle(event.rawX,event.rawY,10).closePath(); shape.graphics.drawCircle(0,0,0).closePath(); }); var bitmap = new createjs.Bitmap(images.back); stage.addChild(bitmap); shape = new createjs.Shape(); shape.graphics.beginFill("#ff0000"); bitmap.mask = shape;
可以看到我drawCircle(0,0,0)空画了一下,测试一下这次确实不会有问题了。
http://www.ajexoop.com/test/graphics/test5.html?v=0.0.1
接下来分享出刮刮卡的demo
http://www.ajexoop.com/test/graphics/guaguale.html?v=0.0.1
代码在这里:
//main.js var canvas,stage,images = {},txt,shape,maskTxtShape,eraseShape,container; function init() { canvas = document.getElementById("mainView"); stage = new createjs.Stage(canvas);//字容器 container = new createjs.Container(); createjs.Touch.enable(stage); stage.addEventListener("stagemousedown",startMove); stage.addEventListener("stagemouseup",endMove); txt = new createjs.Text();//字 txt.text = "一等奖"; txt.font = "bold 36px Arial"; container.addChild(txt) maskTxtShape = new createjs.Shape();//字遮罩 maskTxtShape.graphics.beginFill("#ff0000"); maskTxtShape.graphics.drawCircle(0,0,0);//什么都没有是无法遮罩的 container.mask = maskTxtShape; shape = new createjs.Shape();//灰色图层 shape.graphics.beginFill("#999999"); shape.graphics.drawRect(0,0,120,50); shape.graphics.endFill(); stage.addChild(shape); eraseShape = new createjs.Shape();//擦除图层 eraseShape.graphics.beginFill("rgba(255,255,255,1)"); eraseShape.compositeOperation = "destination-out";//叠加方式 stage.addChild(eraseShape); stage.addChild(container);//是字放在最上面而不是擦除图层,如果擦除图层在最上面会把一切都擦掉 createjs.Ticker.setFPS(30); createjs.Ticker.addEventListener("tick", stageBreakHandler); } function startMove(event) { moveHandler(event) stage.addEventListener("stagemousemove",moveHandler) } function endMove(event) { stage.removeEventListener("stagemousemove",moveHandler) } function moveHandler(event) { maskTxtShape.graphics.drawCircle(event.rawX,event.rawY,10).closePath(); eraseShape.graphics.drawCircle(event.rawX,event.rawY,10).closePath(); } function stageBreakHandler(event) { stage.update(); }
这个刮刮乐的项目,比我想象的麻烦,主要createjs的叠加方式的api居然直接用原生的(createjs的compositeOperation同原生的globalcompositeoperation),让我找了半天,还有图层关系也是颠倒的,需要好好理解一下。
最近据群友提出,这个demo还是有个小问题,就是快速刮开的时候会不连续,就像下面那样:
大家看,4个洞不连续,很多懂的人看代码会说用lineTo也就是画线代替drawCircle画圆就可以了,虽然是这么说也对,但是问题在于createjs的mask是无法支持矢量线遮罩的,所以要想连续只能在2次mousemove之间手动画圆补全路径,那有没有不需要通过数学补全去做的方法呢?也有!但是做法也不轻松,而且相对比较耗性能,上代码:
//main.js var canvas,stage,images = {},txt,shape,maskTxtShape,eraseShape,txtContainer,eraseContainer; function init() { canvas = document.getElementById("mainView"); stage = new createjs.Stage(canvas);//字容器 txtContainer = new createjs.Container(); eraseContainer = new createjs.Container(); createjs.Touch.enable(stage); stage.addEventListener("stagemousedown",startMove); stage.addEventListener("stagemouseup",endMove); txt = new createjs.Text();//字 txt.text = "一等奖"; txt.font = "bold 36px Arial"; txtContainer.addChild(txt) maskTxtShape = new createjs.Shape();//字遮罩叠加 maskTxtShape.graphics.beginFill("#ffff00"); maskTxtShape.graphics.drawCircle(0,0,0);//什么都没有是无法遮罩和叠加的 maskTxtShape.graphics.setStrokeStyle(10,"round").beginStroke("#FF0"); maskTxtShape.compositeOperation = "destination-in";//叠加方式 // txtContainer.mask = maskTxtShape;//mask无法完成对线的遮罩 maskTxtShape.cache(0,0,120,50); txtContainer.addChild(maskTxtShape); txtContainer.cache(0,0,120,50) shape = new createjs.Shape();//灰色图层 shape.graphics.beginFill("#999999"); shape.graphics.drawRect(0,0,120,50); shape.graphics.endFill(); eraseContainer.addChild(shape); eraseShape = new createjs.Shape();//擦除图层 eraseShape.graphics.setStrokeStyle(10,"round").beginStroke("#FF0"); eraseShape.compositeOperation = "destination-out";//叠加方式 eraseContainer.addChild(eraseShape); stage.addChild(eraseContainer); stage.addChild(txtContainer);//是字放在最上面而不是擦除图层,如果擦除图层在最上面会把一切都擦掉 createjs.Ticker.setFPS(30); createjs.Ticker.addEventListener("tick", stageBreakHandler); } var lostPoint = new createjs.Point() function startMove(event) { lostPoint.x = event.rawX; lostPoint.y = event.rawY; moveHandler(event) stage.addEventListener("stagemousemove",moveHandler) } function endMove(event) { stage.removeEventListener("stagemousemove",moveHandler) } function moveHandler(event) { maskTxtShape.graphics.setStrokeStyle(10,"round").beginStroke("#FF0"); maskTxtShape.graphics.moveTo(lostPoint.x,lostPoint.y) maskTxtShape.graphics.lineTo(event.rawX,event.rawY) eraseShape.graphics.setStrokeStyle(10,"round").beginStroke("#FF0"); eraseShape.graphics.moveTo(lostPoint.x,lostPoint.y) eraseShape.graphics.lineTo(event.rawX,event.rawY) lostPoint.x = event.rawX; lostPoint.y = event.rawY; maskTxtShape.updateCache(); txtContainer.updateCache(); // txtContainer.mask = maskTxtShape; } function stageBreakHandler(event) { stage.update(); }
测试地址:http://www.ajexoop.com/test/graphics/guaguale2.html
大家看上面的代码和demo,我所有画圆的逻辑都变成了画线,但是除此之外还有二个关键操作,一个是加了2个cache,一个是去掉了mask换成了叠加方式
maskTxtShape.cache(0,0,120,50);
txtContainer.cache(0,0,120,50);
为什么要用cache和叠加方式呢?因为cache了就会把线变成普通的矢量对象,就可以进行叠加方式,但是用遮罩就算用cache也不能生效(亲测)
那为什么要2次cache呢?第一次cache是转线段为普通对象,第二次cache是防止二次叠加方式冲突,没错这是个重要的知识点,同一个位置超过2个叠加方式会冲突,cache后可以解决冲突。
大家还可以看到,每次mousemove都需要2次cache,这样性能会多消耗不少,虽然说cache的面积比较小,不会消耗太多,所以还是推荐大家手动补全圆的路径。
最后放一张叠加关系图:
发表评论