I'd like to know if there is some way in Kinetic to have an image merged with another image on a lower layer, creating a kind of crop effect based on the shape of the "cropping" image.
The image must have a transparent background as a result, which is the main source of difficulty for me. I would otherwise just have used a mask with dragging turned off... I've made a diagram to explain what I want a bit better. Any suggestions welcome.
diagram
I've also made a quick jsfiddle, where I would like the contents of the image to be displayed inside the box. I feel like one layer is the way to go on this one.
You can use an offscreen Canvas element plus compositing to create your clipped image for use by your Kinetic.Image. Note that the offscreen canvas can be used as an image source for your Kinetic.Image.
Example code and a Demo: http://jsfiddle.net/m1erickson/ks1xxqfL/
var canvas=document.createElement('canvas');
var ctx=canvas.getContext('2d');
canvas.width=background.width;
canvas.height=background.height;
ctx.drawImage(mask,0,0,mask.width*2.25,mask.height*2.25);
ctx.globalCompositeOperation='source-in';
ctx.drawImage(background,0,0);
var support=new Kinetic.Image({
draggable:true,
image:canvas,
});
layer.add(support);
layer.draw();
Illustrations
Left: the background image,
Center: the image to be used as a clipping mask
Right: the background clipped by the mask (mask was scaled by 2.25X)
Related
I would like to be able to drag, resize and rotate an image over layers on Openlayers3. The image has to be independent from layers.
I tried rendering the image as a layer, using this:
var extent = map.getView().calculateExtent(map.getSize());
var imageLayer = new ol.layer.Image({opacity: 0.5});
var imageSource = new ol.source.ImageStatic({
url: 'https://dummyimage.com/600x400/faf7fa/0011ff',
imageExtent: extent
});
imageLayer.setSource(imageSource);
map.addLayer(imageLayer);
Unfortunately, I didn't find any clue about the possibility to drag an image layer over others layers.
Moreover, I do not want to load the image as a HTML element. I want it to be part of the OL canvas.
How can I achieve this?
I believe something like this can point you in right direction. It's not so flexible as you would like but it can help you a bit.
If you click Run code snippet in this link, it shows a cool usage of <canvas> where you can "cut-out" part of the image (just drag your mouse on the image to "cut"). I'm just curious if there is any way to save the resulting "cut-out" part of the image, as a transparent PNG (i.e. everything that is white in the canvas would be transparent).
If anyone can point me in the right direction (or tell me it's not doable), I'd appreciate it.
Yes, there is a way. Use canvas context.getImageData to get image raw data-array. Do with it(raw data) what you need(make transparent any pixel you need), and then use context.putImageData to render data on canvas. Then use var data = canvas.toDataURL("image/png") to get image data. And then you can do so: image.src = data;
Use this link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData for more info
Here's a quick tour showing how to crop & save a user-dragged cutout of an image:
Draw the image on the canvas.
Listen to mouse events and let the user define the region they want to cut out by continuously connecting new lines to the current mouse position.
Save every mouse point from #2 in an array.
When the user has finished defining their cutout region:
Clear the canvas.
Draw and fill the cutout area using the saved points in the arry.
Set context.globalCompositeOperation='source-in'. This mode will cause any new drawings to appear only where the newly drawn pixels and the existing pixels overlap and every thing else is made transparent. In common terms: New pixels will be "clipped" into the user defined cutout and new pixels will not appear outside the cutout.
Redraw the image.
The result is the second drawing of the image will appear only inside the user defined cutout. Everything else will be transparent.
Finally, you can use var url=canvasElement.toDataURL to save the cropped canvas image into a .png dataURL. If you then want an actual image from this dataURL you can var img=new Image() and set the img.src=url.
I'm not requiring a full answer to this question, just an idea on how to approach it.
Let's say that a user on my site wants to cut out the background from this image:
Normally this would be a job for some magic outline tool, but this site already carries something that would provide a perfect cutout pattern, namely this:
As you can see this car will fit perfectly over the top one.
If the user could somehow fit the bottom picture over the top one and cut out everything outside that, it would be a perfect background removal.
How do I go about building something like this? Or are there already software out that does something similar?
The bottom picture could be anything, for examle a completely black model for easier recognition, but I'd think that it would be smarter if it used the outline of the transparent .png image and cut out everything outside it.
(The picture itself doesn't need to be used either if there is some way to extract the important bits of it needed for the cutout, of course).
Here's how to do your knockout with html5 canvas
If you have an exact image that defines the desired cut and you also know the position where the cut is to be made, then you can use compositing to do you cut-out. With destination-in compositing, new drawings will keep existing pixels only where new & old pixels are non-transparent.
Here's a few notes, example code and a Demo:
Notes:
Your car on your car-only image is not exactly the size of the car on the car+background image -- the car-only is a bit wider. This causes the cut-out to have some extra pixels. But if you had exact sizing the cutout would be perfect.
Your car on your car-only image has semi-transparent shadowing. This causes the cutout to have some extra semi-transparent pixels where the shadow was on the car-only image.
Example & demo using a different exactly sized cutout with no shadow:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var bk=new Image();
bk.onload=start;
bk.src='https://dl.dropboxusercontent.com/u/139992952/multple/model-t-car.png';
var cut=new Image();
cut.crossOrigin='anonymous';
cut.onload=start;
cut.src="https://dl.dropboxusercontent.com/u/139992952/multple/model-t-cutout.png";
var imgcount=2;
function start(){
if(--imgcount>0){return;}
canvas.width=bk.width;
canvas.height=bk.height;
ctx.drawImage(bk,0,0);
ctx.globalCompositeOperation='destination-in';
ctx.drawImage(cut,125,40);
// always clean up -- reset the default compositing mode
ctx.globalCompositeOperation='source-over';
}
body{ background-color: ivory; }
canvas{border:1px solid red; margin:0 auto; }
<h4>Original with background</h4>
<img src='https://dl.dropboxusercontent.com/u/139992952/multple/model-t-car.png'>
<h4>Exactly sized cutout</h4>
<img src='https://dl.dropboxusercontent.com/u/139992952/multple/model-t-cutout.png'>
<br>
<h4>With background removed</h4>
<canvas id="canvas" width=300 height=300></canvas>
There's no easy plug-and-play way. I can think of 2 methods:
(1) SVGs. Plot the different points on the outline of the car yourself (very time-consuming), or import the car into Illustrator (or similar), export it as an SVG, and use the points it calculated for you with clip-path.
(2) PNG (or GIF).
Create a rectangle of solid color in Illustrator/Photoshop.
Paste the car image on top of it as a new layer.
Select the outline of the car.
Delete the selection from the rectangle of color. This will leave a rectangle with a transparent car-shaped hole in it.
Save the rectangle as a PNG or GIF or other format supporting transparent backgrounds.
Use CSS to overlay that PNG on various images of cars.
This is useful if, for instance, you have 5 photos of cars in different colors, all with the same dimensions and taken from the same angle, and want to display the 5 cars with the same background. No need to copy the same background 5 times in Photoshop; just re-use the PNG 5 times in CSS.
Now if you want to change the "background" (which is actually an overlay and not really a background) you need only change it in one place.
Keep in mind: The image you provided is not a perfect outline because it has a shadow.
I have a canvas <canvas></canvas> that displays a graphic of an unclean window. I need to be able to 'clean' the window with my cursor, displaying another image behind it. Just as this website uses their brushes: http://mudcu.be/sketchpad/ but rather than adding to the page, I need to remove content to display the image behind it.
Here's an example of the before and after 'rubbing out':
http://www.titaniumwebdesigns.com/images/forums/before.png http://www.titaniumwebdesigns.com/images/forums/after.png
See this complete DEMO
globalCompositeOperation is one of the most nice features in canvas api.
To achieve the desired effect, I use multiple canvas layers and globalCompositeOperation.
In this solution we have 3 layers:
Layer 1 - bottom Layer 2 - middle
Layer 3 - top
Middle and Top layers are static and only the middle layer is dynamically cleared using globalCompositeOperation.
middleCtx.globalCompositeOperation = "xor";
With globalCompositeOperation = "xor", the brush is drawn over the layer and clears the portion of canvas where it was drawn.
The final result is:
UPDATE:
To verify if the window is fully cleaned I create a hidden canvas with the same size of the others layers and drawn a black rectangle on it. When dragging the mouse over the canvas the layer 2 (middle) is cleared with a circle with transparent gradient color and now we also draw over the hidden canvas a circle with white color (might be any color different of black).
So on, we just verify the pixels of the hidden canvas and if there is no black pixels, the window is cleaned.
To get the image data we need to use something like:
imageData = context.getImageData(x, y, width, height)
and then get the pixels from the image data:
pixels = imageData.data;
The requestAnimationFrame routine is used for performance reason, because getImageData might be slow. The major change in the code is put the brush action inside an animation frame when dragging the mouse instead of do that action in each mouse move event. This allows the processor to have more time to do the pixel data verification.
Here is the modified fiddle with pixel data verification and an alert when the window is cleaned:
jsFiddle link
If you have a canvas where you have drawn a blurred image into it once, you should be able to create that effect by creating a "brush" image (an image containing a semi-transparent circle, with soft edges), and then draw that image in the canvas at the mouse coordinate using:
canvasContext.globalCompositeOperation = "destination-out";
https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
As soon as you have drawn the blurred image to the canvas, you just need to call the line above once and all drawn images after will use the specified composite operation.
I am writing a colouring game for small children, where I have a black and white image shown on a canvas initially, and as the user moves the drawing tool (mouse) over the canvas, the black and white surface gets over-painted with the colour information from the corresponding coloured image.
In particular, on every mouse move I need to copy a circular area from the coloured image to my canvas. The edge of the circle should be a little blurry to better immitate the qualities of a real drawing tool.
The question is how to accomplish this?
One way I see is to use a clipping region, but this approach does not let me have blurry edges. Or does it?
So I was thinking about using an alpha mask to do that and copy only pixels that correspond to the pixels in the mask that have non zero alpha. Is it feasible?
My suggestion is to have your drawable canvas in front of the coloured image you wish to reveal. (You could use your coloured image as a CSS background image for the canvas.)
Initially have the canvas containing the black and white image with 100% opacity. Then, when you draw, actually erase the contents of the canvas to show the image behind.
Like this:
var pos_x, pos_y, circle_radius; // initialise these appropriately
context.globalCompositeOperation = 'destination-out';
context.fillStyle = "rgba(0,0,0, 1.0)";
// And "draw" a circle (actually erase it to reveal the background image)
context.beginPath();
context.arc(pos_x, pos_y, circle_radius, 0, Math.PI*2);
context.fill();
I would probably use multiple clipping regions with varying alpha (one dab for each) to mimic the effect you are after. Render the low opacity one first (paste using drawImage) and render the rest after that till you reach alpha=1.0.
Have you considered using radial gradients that go from an opaque color to a fully transparent one?
Here is a demo from Mozilla. The circles are drawn the way you need. - https://developer.mozilla.org/samples/canvas-tutorial/4_10_canvas_radialgradient.html