Kineticjs clip shape by mask image - javascript

I have mask image like this
How can I hide drawn elements by this mask like in photoshop (dark areas are invisible, transparent areas are shown).
It is possible to do this in raw canvas via setting globalCompositeOperation = 'destination-over', but I want it in Kineticjs

You can do something like this:
var shape = new Kinetic.Shape({
drawFunc: function(context) {
context.beginPath();
context.rect(0, 0, image.width, image.height)
context.closePath();
context.fillStrokeShape(this);
context.setAttr('globalCompositeOperation', 'destination-out');
context.drawImage(image, 0, 0);
context.setAttr('globalCompositeOperation', 'source-over');
},
fill: '#00D2FF',
});
Demo: http://jsbin.com/nebuvi/1/edit?html,js,output

As of my knowledge you can try by using opacity: 0. for your image value. or create a rectangle overlap the rectangle over your image.

Related

How to detect transparent area click OR if user draw in transparent area in canvas?

I'm drawing an image onto a canvas using drawImage. It's a PNG that is surrounded by transparent pixels, like this:
How can I detect a drawing move path in the transparent part of that image on the canvas? I want to detect if a user draws in a transparent part.
I am trying this tutorial and I did as showing in the tutorial.
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.src = "http://i.stack.imgur.com/UFBxY.png";
function draw() {
// draw original image in normal mode
ctx.drawImage(img, 10, 10);
}
<canvas id=canvas width=500 height=500></canvas>
Check it out full code on Github
Check it out live demo IonCanvas
To find out is a pixel is transparent get the pixel using ctx.getImageData and look at the alpha value.
Example
// assumes ctx is defined
// returns true if pixel is fully transparent
function isTransparent(x, y) { // x, y coordinate of pixel
return ctx.getImageData(x, y, 1, 1).data[3] === 0; // 4th byte is alpha
}

Fabric.js: Resize canvas and continue working

I am working with Fabric.js manipulation images uploaded by users.
Here is how the application works
The uploaded image can be moved and worked on on the entire canvas (grey area). Once finished, I only need the middle area.
One the user clicks save, currently I save the green area as an image, but I also need to resize the whole canvas to be only the green area.
I tried changing the width and height of the whole canvas and then redrawAll(), but the canvas goes bottom left and only a small portion of the working area is visible, the rest is empty gray.
Is it possible to take a snapshot of the green area and continuing working on the canvas ?
Thank you
You should do extensive use of viewportTransform.
let's make an example:
you have a canvas element of 1000x1000 and you want to work on an area of 500x500 pixels.
You set the green area using a green image or a green rect of 500x500 as background image, with top left 0,0. Then you set the viewportTransform of the fabric canvas to [1, 0, 0, 1, 250, 250] this will make your canvas translate to right bottom of that grey border.
When is time to save, you have 2 choices:
1) set a viewportTransform that lets you cover everything of the original canvas and export to dataurl
2) you create an offscreen canvas of 500x500px, you move the objects over there, without touching the new canvas zoom and pan ( viewport transform ) and export that canvas to dataUrl
The snippet has some number for examples, but you need to fully understand how to use and check also the toDataUrl options of fabricCanvas.
var butt = document.getElementById('butt');
butt.onclick = function() {
canvas.viewportTransform = [2, 0, 0, 2, 0, 0];
var img = document.getElementById('export');
img.src = canvas.toDataURL()
canvas.viewportTransform = [1, 0, 0, 1, 250, 250];
canvas.renderAll();
}
var canvas = new fabric.Canvas('c', { backgroundColor: 'grey' });
var rect = new fabric.Rect({ width: 500, height: 500, fill: 'green'})
canvas.backgroundImage = rect;
canvas.viewportTransform = [1, 0, 0, 1, 250, 250];
var exampleRect = new fabric.Rect({left: 5, top: 5, width: 100, height: 100, fill: 'red' });
canvas.add(exampleRect);
canvas.renderAll();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.11/fabric.min.js"></script>
<img id="export" />
<button id="butt" >export</button>
<canvas id="c" width="1000" height="1000"></canvas>

HTML 5 Canvas. How to rotate (keep rotating) only one object (of >1)?

I am trying to get a handle on rotating in canvas and I have not found an answer to my issue.
One square is to remain still. Another square to rotate at a specified point. I can either get the entire canvas to rotate at that point, or neither at all. But I cannot get one square to be static and one to rotate.
Please view my codepen demo here: http://codepen.io/richardk70/pen/EZWMXx
The javascript:
HEIGHT = 400;
WIDTH = 400;
function draw(){
var ctx = document.getElementById("canvas").getContext('2d');
ctx.clearRect(0,0,WIDTH,HEIGHT);
// draw black square
ctx.save();
ctx.fillStyle = "black";
ctx.beginPath();
ctx.fillRect(75,75,100,100);
ctx.closePath();
ctx.restore();
// attempt to rotate small, maroon square only
ctx.save();
ctx.translate(225,225);
// small maroon square
ctx.fillStyle = "maroon";
ctx.beginPath();
ctx.fillRect(-25,-25,50,50);
ctx.closePath();
ctx.rotate(.1);
ctx.translate(-225,-225);
// comment out below line to spin
// ctx.restore();
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
I know I could do layered canvases, but surely it can be done in just one canvas layer? Isn't the clock in this tutorial (https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations) doing exactly that?
Thanks for any and all help.
To rotate only one image or rendering you need to restore the canvas transform state back to the default.
This function will render your object rotated scaled and translated but not affect any other rendering.
BTW you don't need to use beginPath is the render calls start with fill or stroke such as ctx.fillRect, ctx.strokeRect, ctx.fillText, ctx.strokeText and you only need to use ctx.closePath if you need to create a line from the last path point to the previous ctx.moveTo point or first pathpoint after a ctx.beginPath call
Nor do you need to use save and restore for all rendering. You only use it if you need to get the previous canvas state back.
ctx = canvas.getContext("2d");
function drawBox(col,size,x,y,scale,rot){
ctx.fillStyle = col;
// use setTransform as it overwrites the canvas transform rather than multiply it as the other transform functions do
ctx.setTransform(scale, 0, 0, scale, x, y);
ctx.rotate(rot);
ctx.fillRect(-size / 2,-size / 2, size, size);
}
function update(time){
ctx.clearRect(0,0,512,256)
drawBox("black",100,125,125,1,0); // draw none rotated box
drawBox("maroon",50,225,125,1,time / 500); // draw rotating box
drawBox("green",25,275,100,1,-time / 250); // draw rotating box
drawBox("green",25,275,150,1,-time / 250); // draw rotating box
// after using transforms you need to reset the transform to the default
// if you plan to render anything in the default coordinate system
ctx.setTransform(1, 0 ,0 , 1, 0, 0); // reset to default transform
requestAnimationFrame(update);
}
requestAnimationFrame(update);
<canvas id="canvas" width="512" height="256"></canvas>

FabricJS prevent canvas.clipTo from clipping canvas.backgroundImage

I want to set a global clipTo in my Fabric-powered Canvas that will affect all user-added layers. I want a background image and an overlay image, which are unaffected by this clip mask.
Example:
Here's what's happening in this photo:
A canvas overlay image makes the t-shirt look naturally wrinkled. This overlay image is mostly transparent
A background image in the exact shape of the t-shirt was added, which is supposed to make the t-shirt look blue
A canvas.clipTo function was added, which clips the canvas to a rectangular shape
A user-added image (the famous Fabric pug) was added
I want the user-added image (the pug) to be limited to the rectangular area.
I do not want the background image (the blue t-shirt shape) affected by the clip area.
Is there a simple way to accomplish this? I really don't want to have to add a clipTo on every single user layer rather than one tidy global clipTo.
You can play with a JS fiddle showing the problem here.
I came here with the same need and ultimately found a solution for what I'm working on. Maybe it helps:
For SVG paths, within the clipTo function you can modify the ctx directly prior to calling render(ctx) and these changes apply outside the clipped path o. Like so:
var clipPath = new fabric.Path("M 10 10 L 100 10 L 100 100 L 10 100", {
fill: 'rgba(0,0,0,0)',
});
var backgroundColor = "rgba(0,0,0, 0.2)";
var opts = {
controlsAboveOverlay: true,
backgroundColor: 'rgb(255,255,255)',
clipTo: function (ctx) {
if (typeof backgroundColor !== 'undefined') {
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, 300, 150);
}
clipPath.render(ctx);
}
}
var canvas = new fabric.Canvas('c', opts);
canvas.add(new fabric.Rect({
width: 50,
height: 50,
left: 30,
top: 30,
fill: 'rgb(255,0,0)'
}));
You can of course add an image instead of a color, or whatever else you want done. The trick I've found is to put it in the clipTo function on the ctx directly.
here's a fiddle
One (sorta hacky) solution: set a CSS background image on your canvas element, as shown in https://jsfiddle.net/qpnvo3cL/
<canvas id="c" width="500" height="500"></canvas>
<style>
background: url('http://fabricjs.com/assets/jail_cell_bars.png') no-repeat;
</style>
<script>
var canvas = window._canvas = new fabric.Canvas('c');
canvas.clipTo = function(ctx) {
ctx.rect(100,100,100,100);
}
</script>
Have you tried clipping a fabric Group? You could make the whole shirt one canvas. The center graphics would be one Group which you clip to where you want it. The white t-shirt and the blue overlay would of course not be part of the clipped group.
Here's an example of clipping a group:
var rect = new fabric.Rect({width:100, height: 100, fill: 'red' });
var circle = new fabric.Circle({ radius: 100, fill: 'green' });
var group1 = new fabric.Group([ circle, rect ], { left: 100, top: 100 });
canvas.add(group1);
group1.clipTo = function(ctx) {
ctx.rect(50,50,200,200);
};
See this jsfiddle I made: https://jsfiddle.net/uvepfag5/4/
I find clip rather slow so I tend to use globalCompositeOperation to do masking.
If you really need to use clip then use it in conjunction with save and restore.
// ctx is canvas context 2d
// pug is the image to be clipped
// draw your background
ctx.save(); // save state
ctx.rect(100,100,100,100); // set the clip area
ctx.clip(); // apply the clip
ctx.drawImage(pug,x,y); // draw the clipped image
ctx.restore(); // remove the clipping
// draw the other layers.
or you can
// draw background
ctx.globalCompositeOperation = "xor"; // set up the mask
ctx.fillRect(100,100,100,100); // draw the mask, could be an image.
// Alpha will effect the amount of masking,
// not available with clip
ctx.globalCompositeOperation = "destination-over";
ctx.drawImage(pug,x,y); // draw the image that is masked
ctx.globalCompositeOperation = "source-over";
// draw the stuff that needs to be over everything.
The advantage of composite operations is you have control over the clipping at a per pixel level, including the amount of clipping via the pixel alpha value

Use drawImage to draw pixel data on canvas

I have a pixel-array and I would like to draw it onto the canvas, without using the putImageData, because that function ignores clip data.
So, just to make this work i've converted the pixel-array to a data-url, and then appended the url to an image which I then draw onto the canvas, like this:
var ctx = {
display: document.querySelector('canvas#display').getContext('2d'),
convert: document.querySelector('canvas#convert').getContext('2d')
}
var image_data = THE_IMAGE_DATA_THAT_I_ALREADY_HAVE;
ctx.convert.putImageData(image_data, 0, 0);
var image_data_URL = ctx.convert.canvas.toDataURL('image/png');
var converter_image = document.querySelector('img#converter-image');
converter_image.onload = function () {
ctx.display.save();
ctx.display.beginPath();
ctx.display.arc(320, 240, 240, 0, Math.PI*2, true);
ctx.display.clip();
ctx.display.drawImage(converter_image, 0, 0, 640, 480);
ctx.display.restore();
}
converter_image.src = image_data_URL;
However, this is gives a REALLY bad performance, especially since I want to do this 60/sec.
There must be another way, right?
A work around will be to draw your pixel using getImageData / putImageData in an offscreen canvas then drawing the offscreen canvas back in your canvas.
More infos :
Canvas offscreen
Pixel drawing with putImagedata (click on the blank in top of the code to launch it)

Categories