HTML5 context.clip() using image - javascript

Is there a way to use an image as a clipping mask instead of creating a shape like this:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
// Clip a rectangular area
ctx.rect(50,20,200,120);
ctx.stroke();
ctx.clip();
I have tried to context.drawImage('myimg.png') on top of the context and clip but that did not work.

You can only directly clip using a path.
If you have an image that you wish to clip by, you can probably achieve this by drawing your content in another canvas, and then using globalCompositeOperation combined with drawImage (with the mask) to remove the bits you don't want.
You would then then use .drawImage again (possibly with a different globalCompositeOperation) to merge that clipped image with your original content.
See for example http://www.html5canvastutorials.com/advanced/html5-canvas-global-composite-operations-tutorial/

Related

Clipping image to an svg

Is there a way to clip an image to an SVG shape with a CanvasRenderingContext2D?
I'm trying to use different SVG shapes to display various parts of an image on demand.
For example – an SVG with an <ellipse> element (or an equivalent <path>) would allow me to show a circular portion of the image.
CanvasRenderingContext2D.clip() seems to be the close to what I need, but I can't find any information about how to use it with an SVG, or alternatively how to draw an SVG as a path.
Another direction I'm thinking about is saving the clipping area as a <path> element and manually transforming it to CanvasRenderingContext2D equivalent methods such as lineTo and arc.
Three steps:
Draw the SVG to your canvas.
Set context.globalCompositeOperation = 'source-in';
Draw the image.
The image will only be drawn where the SVG has already filled in pixels with colour, effectively clipping the image to whatever has already been drawn.
You can either set the globalCompositeOperation back to 'source-over' (the default), or use context.save() before and context.restore() after to put the canvas back into a "normal" drawing mode.
#Kaiido has some good suggestions for you (see his comment to the question).
Draw the SVG to canvas & use globalCompositeOperation instead of clipping. But Firefox has a bug in applying gCO directly to an svg image, which force you to first draw your svg on a second canvas ; and that IE prior to Edge will taint the canvas when an svg is drawn to it.
Parse your svg first and then use the canvas API to draw those shapes (path commands are quite similar so it's not so hard and library like fabricjs can even handle it in a nice way for you)
Another option is to convert your SVG drawings to .png format and use that image + globalCompositeOperation to clip your image inside the .png shape. This avoids the cross-browser problems with SVG.
But, if your clipping shapes are just simple SVG paths (ovals, etc), then you might forget SVG and draw your path using canvas path commands.
I'll repost a previous SO Q&A to illustrate clipping inside a canvas path:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/kidwallpaper.jpg";
function start(){
// resize the canvas to equal the image size
var iw=img.width;
var ih=img.height;
cw=canvas.width=iw;
ch=canvas.height=ih;
// calculate the scaling needed to max the display of the image
// inside the oval
if(iw>ih){
var scaleX=iw/ih
var scaleY=1;
var r=ih/2;
}else{
var scaleX=1;
var scaleY=ih/iw;
var r=iw/2;
}
// scale so the circle (arc) becomes an oval
ctx.scale(scaleX,scaleY);
ctx.arc(cw/scaleX/2,ch/scaleY/2,r,0,Math.PI*2);
ctx.fill();
// undo the scaling
ctx.scale(1/scaleX,1/scaleY);
// draw the image centered inside the oval using compositing
ctx.globalCompositeOperation='source-atop';
ctx.drawImage(img,cw/2-img.width/2,ch/2-img.height/2);
ctx.globalCompositeOperation='source-atop';
}
body{ background-color: black; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
You cannot do this directly. You can:
Draw the SVG into a canvas and use that for compositing to clip. Drawback is that some browsers has limited support due to security (external links, tainting the canvas and so on).
Parse the SVG manually
Here is an example of how you can build a simple parser for SVG. It cannot be used for generic use but assumes you know the SVG in question. You can build on top of this to support different units, transform lists etc.
The path that is extracted can be stored on a Path2D object instead of directly as shown below (Path2D may need poly-fill in some browsers), or store custom objects/arrays etc. This is entirely up to you.
The example is more a proof-of-concept.
Example SVG parser to canvas Path2D
var ctx = c.getContext("2d"),
mask = document.getElementById("mask"); // get a SVG element
// some random graphics
for(var i=30,r=Math.random;i--;) {
ctx.fillStyle = "hsl(" + (360*r()) + ",50%,50%)";ctx.fillRect(280*r(),120*r(),50,50)}
// parse SVG element
if (mask.localName) {
switch(mask.localName) {
case "rect":
ctx.rect(v("x"), v("y"), v("width"), v("height"));
break;
case "ellipse":
// need polyfill in some browsers
ctx.ellipse(v("cx"), v("cy"), v("rx"), v("ry"), 0, 6.28);
break;
// more cases here
}
// use path from SVG to clip
ctx.globalCompositeOperation = "destination-in";
ctx.fill();
}
// helper - obtains a numeric value for SVG element property
function v(name) {return mask[name].baseVal.value}
#c, svg {border:1px solid #777}
<h4>SVG (showing mask)</h4>
<svg xmlns="http://www.w3.org/2000/svg"
width="300" height="150">
<rect id="mask" x="50" y="30" width="200" height="100" />
</svg>
<h4>Canvas (mask applied from SVG)</h4>
<canvas id=c></canvas>

KineticJS - Patterns and Fills on Images

I have a canvas that I'm creating with KineticJS and I am adding transparent PNG images to that canvas. When stacked on top of each other, this makes one image of an outfit with all the different parts.
What I then want to do is allow the user to click on a pattern and then change a specific piece of that outfit with that pattern. So I need to fill in the non-transparent parts of one of the images with that pattern. I found a way to do this that didn't use KineticJS and it looks something like this:
ctx.globalCompositeOperation = 'source-in';
var ptrn = ctx.createPattern(fabricA, 'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 375, 260);
My question is, is there a way to do the same steps outlined above with KineticJS?
Also, I did first try to just do this without using KineticJS, but when I applied the above code to the layer, it filled in all of the images because they were all on the same layer. So I'm guessing that I will need to change my code to either use multiple layers or to add the images to groups in a single layer. Is my thinking right here? And which would be the better option for what I'm trying to accomplish? Multiple Layers? Or Multiple Groups on a single Layer?
Thanks for any help that anyone can provide.
If you want to do custom drawing then use the KineticJS Shape Object
This is a KineticJS object that lets you control exactly how it's drawn.
You create your overlays using your compositing method. Then put that drawing code in a function and give that function to Kinetic Shape's drawFunc.
Here's a skeleton of Kinetic.Shape:
var outfit1 = new Kinetic.Shape({
drawFunc: function(canvas) {
// you are passed a canvas to draw your custom shape on
// so new-up a context and get drawing!
var context = canvas.getContext();
context.beginPath();
// Draw stuff--including your composited overlays
// You can use any canvas.context drawing commands
},
id:"myCustomOutfit"
});
You can get started with an example here: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-shape-tutorial/

Get elements from canvas

I would like to export my canvas onto a PDF and only render the elements added to the canvas. For example:
I have this canvas, with a background-image set to it.
http://i49.tinypic.com/n7lv.png
This is my result when I render it to PDF (using Bytescout library)
http://i50.tinypic.com/346ud7m.png
This is how I want it to end up as:
http://i50.tinypic.com/2q1s9hv.png
Meaning, I want it to end up with no rounded corners, without the background image. The canvas is done using the fabric framework. My idea is to get all the elements added to the canvas, except background image, then render the PDF from there. Any guidelines? Is that the way to go?
You simply redraw the entire scene, omitting the parts you don't want to write to a PDF.
If you don't feel like keeping track of everything to redraw, then create a second, in-memory canvas (document.createElement('canvas')) and do every drawing operation to that canvas instead of your normal one, then draw that canvas onto your normal one as the user edits instead of drawing directly onto your normal canvas.
The old way:
// First you round the corners permanently by making a clipping region:
ctx.roundedRect(etc)
ctx.clip();
//then a user draws something onto normal canvas, like an image
ctx.drawImage(myImage, 0, 0);
The new way:
// make a hidden canvas:
var hiddenCanvas = document.createElement('canvas');
var hCtx = hiddenCanvas.getContext('2d');
// First you round the corners permanently by making a clipping region:
ctx.roundedRect(etc)
ctx.clip();
//then a user draws something onto HIDDEN canvas, like an image
// This image never gets its corners cut
hCtx.drawImage(myImage, 0, 0);
// Then you draw the hidden canvas onto your normal one:
ctx.drawImage(hiddenCanvas, 0, 0);
When its time to print, you use your hidden canvas, which does not have a background image and does not have clipped corners.

Canvas shows the text blurred

I am trying to draw a sharp thin rectangle using canvas control.I want the canvas background to get loaded with an image and in foreground with some text .
Though i am able to set the color and text they are somehow appearing blurred.Is there a way to fix this issue ?
And i want to apply an image as background image for each canvas rectangle i will draw.These rectangels will appear a in a div control.So wherever there is a canvas rectangle i want its background image to be filled with the image i choose.entire div will not be filled with canvas rectangles but only half of it.Is there a way whether we can have one image for all the canvas rectangles i will draw or do ineed to draw it for every rectangle ?
html:
<div id="divBoard" >
<canvas id="canvasBoard" />
</div>
javascript:
canvas = document.getElementById("canvasBoard");
if (canvas.getContext) {
var context = canvas.getContext("2d");
}
context.fillStyle = "green";
context.fillRect(x, y, width,height);
context.font = "5px tahoma";
context.fillStyle = "black";
context.fillText("cell"+i, x, y + width);
this is the image displayed after executing my code
I have experienced the same issue with fonts not rendering as sharply on a canvas. In my project I did a workaround by placing the font in a separate DIV and overlaying it on the canvas using an appropriate z-index. This approach worked very well for me.
Have you tried adding 0.5 to x and y? The html5 canvas uses antialiasing so if you want "crisp" lines, you need to draw "in between" the pixels.
You will find a good explanation on how this works in mozilla developer reference page for lineWidth
PS. also see this question

copy non transparent pixels only to HTML5 canvas

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

Categories

Resources