fabric.js: move clipped Image behind fixed clipping-mask - javascript

I'm working on a small script that lets a user load a custom image into the canvas on the webpage. So far that works pretty neat. The canvas is initialized using the fabric.js script in order to let the user do some easy editing tasks.
The "uploaded" image is clipped by a simple rectangle. Now comes the tricky part: the user should then be able to move around, scale and rotate the image, whilst the rectangle stays in place; selecting the image section preferred. However, even
lockMovement = true;
or
lockMovementX = true;
lockMovementY = true;
do not keep that clipping mask in place. Any other way to achieve this?
Any help is greatly appreciated! Please find a demo here: http://jsfiddle.net/efmbrm4v/

I had the same problem and I solved it with following code:
image.clipTo = function (ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0); // Reset transformation to default for canvas
ctx.rect(
100, 100, // Just x, y position starting from top left corner of canvas
200, 200 // Width and height of clipping rect
);
ctx.restore();
};
You can try it out here: http://jsfiddle.net/Jagi/efmbrm4v/1/

Related

Skew the drawing horizontally and vertically in html5 canvas using mouse

In my code I am loading images to canvas element. Then I need to drag, resize, rotate and skew it. I managed to implement both dragging and resizing. How can I implement skew using mouse on this code? I read a helpful article on this subject (http://www.subshell.com/en/subshell/blog/image-manipulation-html5-canvas102.html). I found fabricjs plugin, but it work only one image and without drag. For example: http://fabricjs.com/matrix-transformation/ .
If you will use the transform function:
ctx.transform(1, 0.5, -0.5, 1.2, 30, 60);
This will transform all image objects assigned to the canvas element. I would like only the selected image.
My code:
https://jsfiddle.net/sjLnqk5d/2/
Here's an updated fiddle that allows you to skew any image individually by pressing Shift when clicking one of the corner handles. You may need to tweak it a little bit to have the movements appear a bit more intuitive.
The trick is indeed in using ctx.save() and ctx.restore() around the ctx.transform() call, which makes sure that the transformation (and any other property changes) only applies between the save and restore.
Here are the key changes:
Shape.prototype.draw:
...
var skewX = this.skewX;
var skewY = this.skewY;
imgNew.onload = function(){
ctx.save();
ctx.transform(1, skewX/100, skewY/100, 1, 0, 0);
ctx.drawImage(imgNew, locx, locy, width, height);
ctx.restore();
}

Javascript Scale(x,y) method doesn't work on Canvas with negative parameter

I am trying to flip an image using the scale() method.
I have this code:
ctx.save();
ctx.scale(-1, 1);
ctx.drawImage(Img, 0, 0, 100, 100);
ctx.restore();
When I put 1, 1 into the scale parameter, or any other real number, the scale function works fine. But once I put a negative parameter such as -1 (to flip image horizontally) the image doesn't draw at all. The code is in a function with a 12 fps setInterval. I've looked everywhere but can't seem to find a solution. Any help or suggestions would be appreciated. Thanks!
as being said by markE, whenever you scale, rotate or transform the canvas, the coordinates you are using will react to the change in the canvas transformation - in your case sending the image off the canvas. In order to flip the image in place, you should consider using translate with the width of the image as first parameter:
ctx.save();
//The scale will flip the whole canvas, and will move the image
//to the left (by the image width size)
ctx.scale(-1, 1);
//Using translate to move the image back to it's original origin
ctx.translate(Img.width, 0)
ctx.drawImage(Img, 0, 0, 100, 100);
ctx.restore();

Copying pixeldata between html canvases

I'm currently writing a small image editor to learn some javascript. One of the features is obviously a drawing tool. When that is used i'm drawing those pixels to another canvas on top of the actual canvas and then when the mouse is released i copy over the pixels from the top canvas to the underlying one.
This is the code that is used for the copying of the drawn pixels to the underlying canvas.
this.applySketch = function() {
this.revertStack.push(this.ctx.getImageData(0, 0, this.width, this.height));
var real = this.ctx.getImageData(0, 0, this.width, this.height);
var sketch = this.sketchctx.getImageData(0, 0, this.width, this.height);
for(var i = 0; i < sketch.data.length; i += 4) {
// check the alpha value to decide if to copy to the real canvas
if(sketch.data[i+3] > 0) {
real.data[i] = sketch.data[i];
real.data[i+1] = sketch.data[i+1];
real.data[i+2] = sketch.data[i+2];
real.data[i+3] = sketch.data[i+3];
}
}
this.ctx.putImageData(real, 0, 0);
this.sketchctx.clearRect(0, 0, this.width, this.height);
}
The problem i'm experiencing is that the pixels don't look the same when they have been copied to the other layer. Instead there is a thin contour of white pixels around whatever it is i'm copying.
Before mouse release (when the pixels are in the top layer)
http://i.imgur.com/xHvN1iF.jpg
After mouse release (when they have been copied)
http://i.imgur.com/P0sdybs.jpg
I don't really have any clue on what is going on here because it seems pretty straightforward. Could it be anything with antialiasing?
Thanks
I suppose this happens because of antialiasing. When you draw smooth line, some pixels near the line are almost transparent but not fully transparent. This fiddle illustrates the problem. So, you need to merge alpha channel more carefully. The simpliest solution is to add alpha channel value to original value instead of replacing it completely:
real.data[i+3] += sketch.data[i+3];
This fiddle shows the effect. Well, line looks thickly than original line. I suppose you should use some other formula for merging colors.

Problems clearing canvas when animating a clipping region

I'm trying to accomplish an effect similar to what you might see on the cartoon Chowder (example link) , where shapes serve as masking layers for a texture underneath that stays static. I've begun playing around with this idea by creating a render loop that clears the canvas, saves it's state, then draws a rectangular clipping region, followed by drawing the background texture that occupies the entire width and height of the canvas.
Here's the draw function:
function draw()
{
context.clearRect(0,0, 640, 480);
context.save();
x += velocityX;
y += velocityY;
context.rect(x, y, 40, 40);
context.clip();
context.drawImage(image, 0,0, 640, 480);
context.restore();
}
Basically it just runs at 60 frames per second, updating the position of the rectangle and clipping a background image inside the clipping region. (I know the code isn't structured perfectly, but I was just experimenting to see if this effect was even possible on the canvas).
http://jsfiddle.net/JERje/86/
The problem I seem to be having is that the clipping area from the previous iteration of the loop hangs around creating the weird effect that you see in the fiddle above. I've tried reordering everything in the draw() step of the loop, but the only thing that seems to work is the canvas.width = canvas.width trick for clearing the screen. I'd like to avoid this method of clearing the screen, since it doesn't seem to work in IE, and it also destroys the canvas state. clearRect() should work to clear the screen. What am I doing wrong?
You're using the same HTML5 Canvas paperback I am aren't you.
If you set up an adhoc canvas as I did on your jsfiddle like so:
var newCanvas = document.createElement('canvas');
newCanvas.getContext("2d").drawImage(image,0,0);
A function such as this would be able to hack a section out of that canvas:
context.putImageData(newCanvas.getContext("2d").getImageData(x,y,40,40),x,y);
Thus giving you the chowder effect. Good show man, good luck. Pst me if it doesn't work
EDIT: However this solution will ignore some context scaling transformations. Just be smart about how you handle scale on your own (and you really should be anyways if you want the true "chowder" effect)
So, feel pretty dumb about this, but apparently when you call rect() you also have to make sure to call closePath afterwards in order to close the clipping area. Glad I figured it out finally, now on to adding multiple layers!
Here's the working fiddle: http://jsfiddle.net/JERje/129/

Adding mask layer over fabric.js canvas

I have a problem that I'd like to add round mask on top of the editable fabric.js HTML5 canvas.
Similar problem is described in this article:
Creating an Image Mask with HTML 5 Canvas
My problem is that when I add this layer on top of the fabric.js canvas then my canvas is not editable any more inside this transparent circle. This means that the top element (the mask) captures the click/drag event, but I'd like to move the items under this mask element. Looking for ideas, how to work around this problem.
You can utilize built-in support for masks in fabric.
Here's an example of creating a circular mask at 100/100 with the radius of 200:
var canvas = new fabric.Canvas('...');
// ...
canvas.clipTo = function (ctx) {
ctx.arc(100, 100, 200, 0, Math.PI*2, true);
};

Categories