EaselJS 阿尔法蒙版过滤器
EaselJS Alpha Mask Filter
我是 Canvas 的新手。在这个 EaselJS Alpha Mask 示例中,我一直在尝试反转图像,以便初始图像清晰,而您 paint 的图像模糊;基本上,演示的反面。
我已经用它玩了几个小时,对 bitmap
var 应用过滤器并将它们从 blur
var 中删除。我所做的一切都行不通。似乎只需切换一下就可以轻松解决问题,但事实并非如此。反正不适合我。
有没有人有这方面的例子,或者知道该怎么做?我可以提供我所做的代码示例,但它基本上只是在打字机上玩猴子之类的东西。
这是他们示例中的相关代码。
<script id="editable">
var stage;
var isDrawing;
var drawingCanvas;
var oldPt;
var oldMidPt;
var displayCanvas;
var image;
var bitmap;
var maskFilter;
var cursor;
var text;
var blur;
function init() {
examples.showDistractor();
image = new Image();
image.onload = handleComplete;
image.src = "../_assets/art/flowers.jpg";
stage = new createjs.Stage("testCanvas");
//text = new createjs.Text("Loading...", "20px Arial", "#FFF");
//text.set({x: stage.canvas.width / 2, y: stage.canvas.height - 40});
//text.textAlign = "center";
}
function handleComplete() {
examples.hideDistractor();
createjs.Touch.enable(stage);
stage.enableMouseOver();
stage.addEventListener("stagemousedown", handleMouseDown);
stage.addEventListener("stagemouseup", handleMouseUp);
stage.addEventListener("stagemousemove", handleMouseMove);
drawingCanvas = new createjs.Shape();
bitmap = new createjs.Bitmap(image);
blur = new createjs.Bitmap(image);
blur.filters = [new createjs.BlurFilter(24, 24, 2), new createjs.ColorMatrixFilter(new createjs.ColorMatrix(60))];
blur.cache(0, 0, 960, 400);
//text.text = "Click and Drag to Reveal the Image.";
stage.addChild(blur, text, bitmap);
updateCacheImage(false);
cursor = new createjs.Shape(new createjs.Graphics().beginFill("#FFFFFF").drawCircle(0, 0, 25));
cursor.cursor = "pointer";
stage.addChild(cursor);
}
function handleMouseDown(event) {
oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
oldMidPt = oldPt;
isDrawing = true;
}
function handleMouseMove(event) {
cursor.x = stage.mouseX;
cursor.y = stage.mouseY;
if (!isDrawing) {
stage.update();
return;
}
var midPoint = new createjs.Point(oldPt.x + stage.mouseX >> 1, oldPt.y + stage.mouseY >> 1);
drawingCanvas.graphics.setStrokeStyle(40, "round", "round")
.beginStroke("rgba(0,0,0,0.2)")
.moveTo(midPoint.x, midPoint.y)
.curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPoint.x;
oldMidPt.y = midPoint.y;
updateCacheImage(true);
}
function handleMouseUp(event) {
updateCacheImage(true);
isDrawing = false;
}
function updateCacheImage(update) {
if (update) {
drawingCanvas.updateCache();
} else {
drawingCanvas.cache(0, 0, image.width, image.height);
}
maskFilter = new createjs.AlphaMaskFilter(drawingCanvas.cacheCanvas);
bitmap.filters = [maskFilter];
if (update) {
bitmap.updateCache(0, 0, image.width, image.height);
} else {
bitmap.cache(0, 0, image.width, image.height);
}
stage.update();
}
</script>
使用 Canvas 2D 上下文的纯 Javascript 方式 API。
您将需要创建 canvas、加载图像、创建蒙版图像和模糊图像。我已经模糊了图像,因为我不想写模糊。
对象 imageTools
中的以下函数创建 canvas/images 并加载图像。请注意 canvas 和图像可以互换。 canvas没有src,appart是一样的,不能画图。我将所有图像转换为 canvas 并将上下文附加到它们。我也称它们为图像。
/** ImageTools.js begin **/
var imageTools = (function () {
var tools = {
canvas : function (width, height) { // create a blank image (canvas)
var c = document.createElement("canvas");
c.width = width;
c.height = height;
return c;
},
createImage : function (width, height) {
var image = this.canvas(width, height);
image.ctx = image.getContext("2d");
return image;
},
loadImage : function (url, callback) {
var image = new Image();
image.src = url;
image.addEventListener('load', callback);
image.addEventListener('error', callback);
return image;
}
};
return tools;
})();
然后我使用 imageTools
加载我需要的图像并创建一个蒙版,当我有图像大小时,我将蒙版分辨率与图像分辨率相匹配
// load the images and create the mask
var imageLoadedCount = 0;
var error = false;
var maskImage;
var flowerImage = imageTools.loadImage("http://www.createjs.com/demos/_assets/art/flowers.jpg", function (event) {
if (event.type === "load") {
imageLoadedCount += 1;
} else {
error = true;
}
});
var flowerImageBlur = imageTools.loadImage("http://i.stack.imgur.com/3S5m8.jpg", function () {
if (event.type === "load") {
maskImage = imageTools.createImage(this.width, this.height);
imageLoadedCount += 1;
} else {
error = true;
}
});
我使用 requestAnimationFrame
创建一个 60FPS canvas 绘图函数,它等待图像加载,然后将 3 个图层显示到 canvas
// ctx is the main canvas context.
// drawImageCentered scales the image to fit. See Demo for code.
// draw the unblured image that will appear at the top
ctx.globalCompositeOperation = "source-over";
drawImageCentered(ctx, flowerImage, cw, ch);
drawText(ctx, "Click drag to blur the image via mask", 40 + Math.sin(time / 100), cw, ch - 30, "White");
// Mask out the parts when the mask image has pixels
ctx.globalCompositeOperation = "destination-out";
drawImageCentered(ctx, maskImage, cw, ch);
// draw the blured image only where the destination has been masked
ctx.globalCompositeOperation = "destination-atop";
drawImageCentered(ctx, flowerImageBlur, cw, ch);
它首先绘制如果没有可见的蒙版像素出现的图像。然后它绘制一些文本作为说明。
接下来是使用 destination-out
的掩码。这意味着对于蒙版中具有 alpha > 0 的像素,从目标中删除该数量的 alpha。因此,如果遮罩像素的 alpha 为 50,而目标 (canvas) 的 alpha 为 255,则使用 destination-out
渲染遮罩后该像素的结果将为 255 - 50 = 205
。这有效地在掩码上有像素的 canvas 上打了洞。
现在我们可以用模糊图像填充孔并使用 destination-atop
渲染它,这意味着只从目标 alpha 小于 255 的源(模糊图像)绘制像素
分层遮罩就完成了,我们只需要在遮罩上画画就可以了。为此,我们只监听鼠标事件,如果按下按钮,则在鼠标所在的遮罩上画一个圆圈。我的示例缩放了图像,因此那里有一些额外的工作,但基础知识如下,
// draws circle with gradient
function drawCircle(ctx, x, y, r) {
var gr = ctx.createRadialGradient(x, y, 0, x, y, r)
gr.addColorStop(1, "rgba(0,0,0,0)")
gr.addColorStop(0.5, "rgba(0,0,0,0.08)")
gr.addColorStop(0, "rgba(0,0,0,0.1)")
ctx.fillStyle = gr;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
// draw a circle on the mask where the mouse is.
drawCircle(maskImage.ctx, mouse.x, mouse.y, 20);
对于演示,有更多的代码可以使它很好地工作,但您可以选择您需要的部分。
var imageLoadedCount = 0;
var error = false;
var maskImage;
var flowerImage;
var flowerImageBlur;
/** ImageTools.js begin **/
var imageTools = (function () {
var tools = {
canvas : function (width, height) { // create a blank image (canvas)
var c = document.createElement("canvas");
c.width = width;
c.height = height;
return c;
},
createImage : function (width, height) {
var image = this.canvas(width, height);
image.ctx = image.getContext("2d");
return image;
},
loadImage : function (url, callback) {
var image = new Image();
image.src = url;
image.addEventListener('load', callback);
image.addEventListener('error', callback);
return image;
}
};
return tools;
})();
var mouse;
var demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = (function(){
var canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** MouseFull.js begin **/
if(typeof mouse !== "undefined"){ // if the mouse exists
if( mouse.removeMouse !== undefined){
mouse.removeMouse(); // remove previouse events
}
}else{
var mouse;
}
var canvasMouseCallBack = undefined; // if needed
mouse = (function(){
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
interfaceId : 0, buttonLastRaw : 0, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
getInterfaceId : function () { return this.interfaceId++; }, // For UI functions
startMouse:undefined,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
function mouseMove(e) {
var t = e.type, m = mouse;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
} else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
} else if (t === "mouseover") { m.over = true;
} else if (t === "mousewheel") { m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") { m.w = -e.detail;}
if (canvasMouseCallBack) { canvasMouseCallBack(mouse); }
e.preventDefault();
}
function startMouse(element){
if(element === undefined){
element = document;
}
mouse.element = element;
mouse.mouseEvents.forEach(
function(n){
element.addEventListener(n, mouseMove);
}
);
element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false);
}
mouse.removeMouse = function(){
if(mouse.element !== undefined){
mouse.mouseEvents.forEach(
function(n){
mouse.element.removeEventListener(n, mouseMove);
}
);
canvasMouseCallBack = undefined;
}
}
mouse.mouseStart = startMouse;
return mouse;
})();
if(typeof canvas !== "undefined"){
mouse.mouseStart(canvas);
}else{
mouse.mouseStart();
}
/** MouseFull.js end **/
// load the images and create the mask
if(imageLoadedCount === 0){
imageLoadedCount = 0;
error = false;
maskImage;
flowerImage = imageTools.loadImage("http://www.createjs.com/demos/_assets/art/flowers.jpg", function (event) {
if (event.type === "load") {
imageLoadedCount += 1;
} else {
error = true;
}
})
flowerImageBlur = imageTools.loadImage("http://i.stack.imgur.com/3S5m8.jpg", function () {
if (event.type === "load") {
maskImage = imageTools.createImage(this.width, this.height);
imageLoadedCount += 1;
} else {
error = true;
}
})
}
// set up the canvas
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;
var ch = h / 2;
// calculate time to download image using the MS algorithum. As this code is a highly gaurded secret I have obsficated it for your personal safty.
var calculateTimeToGo= (function(){var b="# SecondQMinuteQHourQDayQWeekQMonthQMomentQTick@.,Some time soon,Maybe Tomorrow.".replace(/Q/g,"@.,# ").split(","),r=Math.random,f=Math.floor,lc=0,pc=0,lt=0,lp=0;var cttg=function(a){if(lc===0){lc=100+r(r()*60);lt=f(r()*40);if(pc===0||r()<(lp/b.length)-0.2){lp=f(r()*b.length);pc=1+f(r()*10)}else{pc-=1}}else{lc-=1}a=lt;if(lp===0){a=lt;if(r()<0.01){lt-=1}}var s=b[lp].replace("#",a);if(a===1){s=s.replace("@","")}else{s=s.replace("@","s")}return s};return cttg})();
// draws circle with gradient
function drawCircle(ctx, x, y, r) {
var gr = ctx.createRadialGradient(x, y, 0, x, y, r)
gr.addColorStop(1, "rgba(0,0,0,0)")
gr.addColorStop(0.5, "rgba(0,0,0,0.08)")
gr.addColorStop(0, "rgba(0,0,0,0.1)")
ctx.fillStyle = gr;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
// draw text
function drawText(ctx, text, size, x, y, c) {
ctx.fillStyle = c;
ctx.strokeStyle = "black";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.font = size + "px Arial Black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
if (c !== "black") {
ctx.strokeText(text, x, y + 1);
}
ctx.fillText(text, x, y);
}
// draw the image to fit the current canvas size
function drawImageCentered(ctx, image, x, y) {
var scale = Math.min(w / image.width, h / image.height);
ctx.setTransform(scale, 0, 0, scale, cw, ch);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
// points for filling gaps between mouse moves.
var lastMX,lastMY;
// update function will try 60fps but setting will slow this down.
function update(time){
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore transform
ctx.clearRect(0, 0, w, h); // clear rhe canvas
// have the images loaded???
if (imageLoadedCount === 2) {
// draw the unblured image that will appear at the top
ctx.globalCompositeOperation = "source-over";
drawImageCentered(ctx, flowerImage, cw, ch);
drawText(ctx, "Click drag to blur the image via mask", 20 + Math.sin(time / 100), cw, ch - 30, "White");
// Mask out the parts when the mask image has pixels
ctx.globalCompositeOperation = "destination-out";
drawImageCentered(ctx, maskImage, cw, ch);
// draw the blured image only where the destination has been masked
ctx.globalCompositeOperation = "destination-atop";
drawImageCentered(ctx, flowerImageBlur, cw, ch);
// is the mouse down
if (mouse.buttonRaw === 1) {
// because image has been scaled need to get mouse coords on image
var scale = Math.min(w / flowerImage.width, h / flowerImage.height);
var x = (mouse.x - (cw - (maskImage.width / 2) * scale)) / scale;
var y = (mouse.y - (ch - (maskImage.height / 2) * scale)) / scale;
// draw circle on mask
drawCircle(maskImage.ctx, x, y, 20);
// if mouse is draging then draw some points between to fill the gaps
if (lastMX !== undefined) {
drawCircle(maskImage.ctx, ((x + lastMX) / 2 + x) / 2, ((y + lastMY) / 2 + y) / 2, 20);
drawCircle(maskImage.ctx, (x + lastMX) / 2, (y + lastMY) / 2, 20);
drawCircle(maskImage.ctx, ((x + lastMX) / 2 + lastMX) / 2, ((y + lastMY) / 2 + lastMY) / 2, 20);
}
// save las mouse pos on image
lastMX = x;
lastMY = y;
} else {
// undefined last mouse pos
lastMX = undefined;
}
} else {
// Laoding images so please wait.
drawText(ctx, "Please wait.", 40 + Math.sin(time / 100), cw, ch - 30, "White");
drawText(ctx, "loading images... ", 12, cw, ch, "black")
drawText(ctx, "ETA " + calculateTimeToGo(time), 14, cw, ch + 20, "black")
}
// if not restart the request animation frame
if(!STOP){
requestAnimationFrame(update);
}else{
var can = document.getElementById("canv");
if(can !== null){
document.body.removeChild(can);
}
STOP = false;
}
}
update();
}
var STOP = false; // flag to tell demo app to stop
function resizeEvent() {
var waitForStopped = function () {
if (!STOP) { // wait for stop to return to false
demo();
return;
}
setTimeout(waitForStopped, 200);
}
STOP = true;
setTimeout(waitForStopped, 100);
}
window.addEventListener("resize", resizeEvent);
demo();
/** FrameUpdate.js end **/
有几个步骤可以做到这一点。其中大部分你可能已经完成了:
1) 更改将项目添加到舞台的顺序。由于您想要显示模糊,因此请以相反的顺序添加它们。这会将模糊置于顶部。
stage.addChild(bitmap, text, blur);
2) 在updateCacheImage
方法中更改缓存或updateCached的内容:
if (update) {
blur.updateCache(0, 0, image.width, image.height);
} else {
blur.cache(0, 0, image.width, image.height);
}
这就是您可能被绊倒的地方。如果您将 blurImage
上的过滤器设置为仅 maskFilter
,它似乎不起作用。 maskFilter 正在工作,但将删除已应用的模糊和颜色过滤器。要添加 maskFilter,您必须将其与当前过滤器放在数组中。这是我的方法,它确保原来的2个过滤器完好无损,maskFilter只添加一次:
blur.filters.length = 2; // Truncate the array to 2
blur.filters.push(maskFilter); // add the new filter
在我看来,这种效果并不明显 - 所以您可能想要增加画笔的不透明度:
drawingCanvas.graphics.setStrokeStyle(40, "round", "round")
.beginStroke("rgba(0,0,0,0.5)"); // From 0.2
我是 EaselJS 中原始 AlphaMaskFilter 演示的作者 - 很高兴你发现它有用 and/or 有趣!
我是 Canvas 的新手。在这个 EaselJS Alpha Mask 示例中,我一直在尝试反转图像,以便初始图像清晰,而您 paint 的图像模糊;基本上,演示的反面。
我已经用它玩了几个小时,对 bitmap
var 应用过滤器并将它们从 blur
var 中删除。我所做的一切都行不通。似乎只需切换一下就可以轻松解决问题,但事实并非如此。反正不适合我。
有没有人有这方面的例子,或者知道该怎么做?我可以提供我所做的代码示例,但它基本上只是在打字机上玩猴子之类的东西。
这是他们示例中的相关代码。
<script id="editable">
var stage;
var isDrawing;
var drawingCanvas;
var oldPt;
var oldMidPt;
var displayCanvas;
var image;
var bitmap;
var maskFilter;
var cursor;
var text;
var blur;
function init() {
examples.showDistractor();
image = new Image();
image.onload = handleComplete;
image.src = "../_assets/art/flowers.jpg";
stage = new createjs.Stage("testCanvas");
//text = new createjs.Text("Loading...", "20px Arial", "#FFF");
//text.set({x: stage.canvas.width / 2, y: stage.canvas.height - 40});
//text.textAlign = "center";
}
function handleComplete() {
examples.hideDistractor();
createjs.Touch.enable(stage);
stage.enableMouseOver();
stage.addEventListener("stagemousedown", handleMouseDown);
stage.addEventListener("stagemouseup", handleMouseUp);
stage.addEventListener("stagemousemove", handleMouseMove);
drawingCanvas = new createjs.Shape();
bitmap = new createjs.Bitmap(image);
blur = new createjs.Bitmap(image);
blur.filters = [new createjs.BlurFilter(24, 24, 2), new createjs.ColorMatrixFilter(new createjs.ColorMatrix(60))];
blur.cache(0, 0, 960, 400);
//text.text = "Click and Drag to Reveal the Image.";
stage.addChild(blur, text, bitmap);
updateCacheImage(false);
cursor = new createjs.Shape(new createjs.Graphics().beginFill("#FFFFFF").drawCircle(0, 0, 25));
cursor.cursor = "pointer";
stage.addChild(cursor);
}
function handleMouseDown(event) {
oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
oldMidPt = oldPt;
isDrawing = true;
}
function handleMouseMove(event) {
cursor.x = stage.mouseX;
cursor.y = stage.mouseY;
if (!isDrawing) {
stage.update();
return;
}
var midPoint = new createjs.Point(oldPt.x + stage.mouseX >> 1, oldPt.y + stage.mouseY >> 1);
drawingCanvas.graphics.setStrokeStyle(40, "round", "round")
.beginStroke("rgba(0,0,0,0.2)")
.moveTo(midPoint.x, midPoint.y)
.curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPoint.x;
oldMidPt.y = midPoint.y;
updateCacheImage(true);
}
function handleMouseUp(event) {
updateCacheImage(true);
isDrawing = false;
}
function updateCacheImage(update) {
if (update) {
drawingCanvas.updateCache();
} else {
drawingCanvas.cache(0, 0, image.width, image.height);
}
maskFilter = new createjs.AlphaMaskFilter(drawingCanvas.cacheCanvas);
bitmap.filters = [maskFilter];
if (update) {
bitmap.updateCache(0, 0, image.width, image.height);
} else {
bitmap.cache(0, 0, image.width, image.height);
}
stage.update();
}
</script>
使用 Canvas 2D 上下文的纯 Javascript 方式 API。
您将需要创建 canvas、加载图像、创建蒙版图像和模糊图像。我已经模糊了图像,因为我不想写模糊。
对象 imageTools
中的以下函数创建 canvas/images 并加载图像。请注意 canvas 和图像可以互换。 canvas没有src,appart是一样的,不能画图。我将所有图像转换为 canvas 并将上下文附加到它们。我也称它们为图像。
/** ImageTools.js begin **/
var imageTools = (function () {
var tools = {
canvas : function (width, height) { // create a blank image (canvas)
var c = document.createElement("canvas");
c.width = width;
c.height = height;
return c;
},
createImage : function (width, height) {
var image = this.canvas(width, height);
image.ctx = image.getContext("2d");
return image;
},
loadImage : function (url, callback) {
var image = new Image();
image.src = url;
image.addEventListener('load', callback);
image.addEventListener('error', callback);
return image;
}
};
return tools;
})();
然后我使用 imageTools
加载我需要的图像并创建一个蒙版,当我有图像大小时,我将蒙版分辨率与图像分辨率相匹配
// load the images and create the mask
var imageLoadedCount = 0;
var error = false;
var maskImage;
var flowerImage = imageTools.loadImage("http://www.createjs.com/demos/_assets/art/flowers.jpg", function (event) {
if (event.type === "load") {
imageLoadedCount += 1;
} else {
error = true;
}
});
var flowerImageBlur = imageTools.loadImage("http://i.stack.imgur.com/3S5m8.jpg", function () {
if (event.type === "load") {
maskImage = imageTools.createImage(this.width, this.height);
imageLoadedCount += 1;
} else {
error = true;
}
});
我使用 requestAnimationFrame
创建一个 60FPS canvas 绘图函数,它等待图像加载,然后将 3 个图层显示到 canvas
// ctx is the main canvas context.
// drawImageCentered scales the image to fit. See Demo for code.
// draw the unblured image that will appear at the top
ctx.globalCompositeOperation = "source-over";
drawImageCentered(ctx, flowerImage, cw, ch);
drawText(ctx, "Click drag to blur the image via mask", 40 + Math.sin(time / 100), cw, ch - 30, "White");
// Mask out the parts when the mask image has pixels
ctx.globalCompositeOperation = "destination-out";
drawImageCentered(ctx, maskImage, cw, ch);
// draw the blured image only where the destination has been masked
ctx.globalCompositeOperation = "destination-atop";
drawImageCentered(ctx, flowerImageBlur, cw, ch);
它首先绘制如果没有可见的蒙版像素出现的图像。然后它绘制一些文本作为说明。
接下来是使用 destination-out
的掩码。这意味着对于蒙版中具有 alpha > 0 的像素,从目标中删除该数量的 alpha。因此,如果遮罩像素的 alpha 为 50,而目标 (canvas) 的 alpha 为 255,则使用 destination-out
渲染遮罩后该像素的结果将为 255 - 50 = 205
。这有效地在掩码上有像素的 canvas 上打了洞。
现在我们可以用模糊图像填充孔并使用 destination-atop
渲染它,这意味着只从目标 alpha 小于 255 的源(模糊图像)绘制像素
分层遮罩就完成了,我们只需要在遮罩上画画就可以了。为此,我们只监听鼠标事件,如果按下按钮,则在鼠标所在的遮罩上画一个圆圈。我的示例缩放了图像,因此那里有一些额外的工作,但基础知识如下,
// draws circle with gradient
function drawCircle(ctx, x, y, r) {
var gr = ctx.createRadialGradient(x, y, 0, x, y, r)
gr.addColorStop(1, "rgba(0,0,0,0)")
gr.addColorStop(0.5, "rgba(0,0,0,0.08)")
gr.addColorStop(0, "rgba(0,0,0,0.1)")
ctx.fillStyle = gr;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
// draw a circle on the mask where the mouse is.
drawCircle(maskImage.ctx, mouse.x, mouse.y, 20);
对于演示,有更多的代码可以使它很好地工作,但您可以选择您需要的部分。
var imageLoadedCount = 0;
var error = false;
var maskImage;
var flowerImage;
var flowerImageBlur;
/** ImageTools.js begin **/
var imageTools = (function () {
var tools = {
canvas : function (width, height) { // create a blank image (canvas)
var c = document.createElement("canvas");
c.width = width;
c.height = height;
return c;
},
createImage : function (width, height) {
var image = this.canvas(width, height);
image.ctx = image.getContext("2d");
return image;
},
loadImage : function (url, callback) {
var image = new Image();
image.src = url;
image.addEventListener('load', callback);
image.addEventListener('error', callback);
return image;
}
};
return tools;
})();
var mouse;
var demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = (function(){
var canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** MouseFull.js begin **/
if(typeof mouse !== "undefined"){ // if the mouse exists
if( mouse.removeMouse !== undefined){
mouse.removeMouse(); // remove previouse events
}
}else{
var mouse;
}
var canvasMouseCallBack = undefined; // if needed
mouse = (function(){
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
interfaceId : 0, buttonLastRaw : 0, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
getInterfaceId : function () { return this.interfaceId++; }, // For UI functions
startMouse:undefined,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
function mouseMove(e) {
var t = e.type, m = mouse;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
} else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
} else if (t === "mouseover") { m.over = true;
} else if (t === "mousewheel") { m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") { m.w = -e.detail;}
if (canvasMouseCallBack) { canvasMouseCallBack(mouse); }
e.preventDefault();
}
function startMouse(element){
if(element === undefined){
element = document;
}
mouse.element = element;
mouse.mouseEvents.forEach(
function(n){
element.addEventListener(n, mouseMove);
}
);
element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false);
}
mouse.removeMouse = function(){
if(mouse.element !== undefined){
mouse.mouseEvents.forEach(
function(n){
mouse.element.removeEventListener(n, mouseMove);
}
);
canvasMouseCallBack = undefined;
}
}
mouse.mouseStart = startMouse;
return mouse;
})();
if(typeof canvas !== "undefined"){
mouse.mouseStart(canvas);
}else{
mouse.mouseStart();
}
/** MouseFull.js end **/
// load the images and create the mask
if(imageLoadedCount === 0){
imageLoadedCount = 0;
error = false;
maskImage;
flowerImage = imageTools.loadImage("http://www.createjs.com/demos/_assets/art/flowers.jpg", function (event) {
if (event.type === "load") {
imageLoadedCount += 1;
} else {
error = true;
}
})
flowerImageBlur = imageTools.loadImage("http://i.stack.imgur.com/3S5m8.jpg", function () {
if (event.type === "load") {
maskImage = imageTools.createImage(this.width, this.height);
imageLoadedCount += 1;
} else {
error = true;
}
})
}
// set up the canvas
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;
var ch = h / 2;
// calculate time to download image using the MS algorithum. As this code is a highly gaurded secret I have obsficated it for your personal safty.
var calculateTimeToGo= (function(){var b="# SecondQMinuteQHourQDayQWeekQMonthQMomentQTick@.,Some time soon,Maybe Tomorrow.".replace(/Q/g,"@.,# ").split(","),r=Math.random,f=Math.floor,lc=0,pc=0,lt=0,lp=0;var cttg=function(a){if(lc===0){lc=100+r(r()*60);lt=f(r()*40);if(pc===0||r()<(lp/b.length)-0.2){lp=f(r()*b.length);pc=1+f(r()*10)}else{pc-=1}}else{lc-=1}a=lt;if(lp===0){a=lt;if(r()<0.01){lt-=1}}var s=b[lp].replace("#",a);if(a===1){s=s.replace("@","")}else{s=s.replace("@","s")}return s};return cttg})();
// draws circle with gradient
function drawCircle(ctx, x, y, r) {
var gr = ctx.createRadialGradient(x, y, 0, x, y, r)
gr.addColorStop(1, "rgba(0,0,0,0)")
gr.addColorStop(0.5, "rgba(0,0,0,0.08)")
gr.addColorStop(0, "rgba(0,0,0,0.1)")
ctx.fillStyle = gr;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
// draw text
function drawText(ctx, text, size, x, y, c) {
ctx.fillStyle = c;
ctx.strokeStyle = "black";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.font = size + "px Arial Black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
if (c !== "black") {
ctx.strokeText(text, x, y + 1);
}
ctx.fillText(text, x, y);
}
// draw the image to fit the current canvas size
function drawImageCentered(ctx, image, x, y) {
var scale = Math.min(w / image.width, h / image.height);
ctx.setTransform(scale, 0, 0, scale, cw, ch);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
// points for filling gaps between mouse moves.
var lastMX,lastMY;
// update function will try 60fps but setting will slow this down.
function update(time){
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore transform
ctx.clearRect(0, 0, w, h); // clear rhe canvas
// have the images loaded???
if (imageLoadedCount === 2) {
// draw the unblured image that will appear at the top
ctx.globalCompositeOperation = "source-over";
drawImageCentered(ctx, flowerImage, cw, ch);
drawText(ctx, "Click drag to blur the image via mask", 20 + Math.sin(time / 100), cw, ch - 30, "White");
// Mask out the parts when the mask image has pixels
ctx.globalCompositeOperation = "destination-out";
drawImageCentered(ctx, maskImage, cw, ch);
// draw the blured image only where the destination has been masked
ctx.globalCompositeOperation = "destination-atop";
drawImageCentered(ctx, flowerImageBlur, cw, ch);
// is the mouse down
if (mouse.buttonRaw === 1) {
// because image has been scaled need to get mouse coords on image
var scale = Math.min(w / flowerImage.width, h / flowerImage.height);
var x = (mouse.x - (cw - (maskImage.width / 2) * scale)) / scale;
var y = (mouse.y - (ch - (maskImage.height / 2) * scale)) / scale;
// draw circle on mask
drawCircle(maskImage.ctx, x, y, 20);
// if mouse is draging then draw some points between to fill the gaps
if (lastMX !== undefined) {
drawCircle(maskImage.ctx, ((x + lastMX) / 2 + x) / 2, ((y + lastMY) / 2 + y) / 2, 20);
drawCircle(maskImage.ctx, (x + lastMX) / 2, (y + lastMY) / 2, 20);
drawCircle(maskImage.ctx, ((x + lastMX) / 2 + lastMX) / 2, ((y + lastMY) / 2 + lastMY) / 2, 20);
}
// save las mouse pos on image
lastMX = x;
lastMY = y;
} else {
// undefined last mouse pos
lastMX = undefined;
}
} else {
// Laoding images so please wait.
drawText(ctx, "Please wait.", 40 + Math.sin(time / 100), cw, ch - 30, "White");
drawText(ctx, "loading images... ", 12, cw, ch, "black")
drawText(ctx, "ETA " + calculateTimeToGo(time), 14, cw, ch + 20, "black")
}
// if not restart the request animation frame
if(!STOP){
requestAnimationFrame(update);
}else{
var can = document.getElementById("canv");
if(can !== null){
document.body.removeChild(can);
}
STOP = false;
}
}
update();
}
var STOP = false; // flag to tell demo app to stop
function resizeEvent() {
var waitForStopped = function () {
if (!STOP) { // wait for stop to return to false
demo();
return;
}
setTimeout(waitForStopped, 200);
}
STOP = true;
setTimeout(waitForStopped, 100);
}
window.addEventListener("resize", resizeEvent);
demo();
/** FrameUpdate.js end **/
有几个步骤可以做到这一点。其中大部分你可能已经完成了:
1) 更改将项目添加到舞台的顺序。由于您想要显示模糊,因此请以相反的顺序添加它们。这会将模糊置于顶部。
stage.addChild(bitmap, text, blur);
2) 在updateCacheImage
方法中更改缓存或updateCached的内容:
if (update) {
blur.updateCache(0, 0, image.width, image.height);
} else {
blur.cache(0, 0, image.width, image.height);
}
这就是您可能被绊倒的地方。如果您将 blurImage
上的过滤器设置为仅 maskFilter
,它似乎不起作用。 maskFilter 正在工作,但将删除已应用的模糊和颜色过滤器。要添加 maskFilter,您必须将其与当前过滤器放在数组中。这是我的方法,它确保原来的2个过滤器完好无损,maskFilter只添加一次:
blur.filters.length = 2; // Truncate the array to 2
blur.filters.push(maskFilter); // add the new filter
在我看来,这种效果并不明显 - 所以您可能想要增加画笔的不透明度:
drawingCanvas.graphics.setStrokeStyle(40, "round", "round")
.beginStroke("rgba(0,0,0,0.5)"); // From 0.2
我是 EaselJS 中原始 AlphaMaskFilter 演示的作者 - 很高兴你发现它有用 and/or 有趣!