有关遮罩和图层叠加的问题(附刮刮卡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的面积比较小,不会消耗太多,所以还是推荐大家手动补全圆的路径。
最后放一张叠加关系图:

发表评论