How to fill pattern in canvas and curving along the shape? - javascript

I have one image like this and I want to fill with pattern like this to make result like this .
I can fill the pattern using following code but I don't know how to curve pattern properly along the collar shape because it should look like real but my result become like this. .
Here is my sample script
$(function(){
drawCanvas("body","collar","images/collar.png", 180);
function drawCanvas(overlayType, canvasID, imageSource, degreeRotate){
var canvas=document.getElementById(canvasID);
var ctx=canvas.getContext("2d");
var imgBody=new Image();
var imgPattern=new Image();
imgPattern.onload=function(){
imgBody.onload=function(){
start();
}
imgBody.src=imageSource;
}
imgPattern.src="images/pattern.png";
function start(){
ctx.drawImage(imgBody,0,0);
if(overlayType=="body"){
ctx.globalCompositeOperation="source-atop";
ctx.globalAlpha=.85;
var pattern = ctx.createPattern(imgPattern, 'repeat');
ctx.fillStyle = pattern;
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.rotate(degreeRotate * Math.PI/180);
ctx.fill();
ctx.translate(150,0);
ctx.globalAlpha=.1;
ctx.drawImage(imgBody,150,0);
}
}
}});
Can someone guide me to how to manage pattern to curve along side collar shape to look like real?

You can get this close by simply slicing and offsetting your pattern vertically
Original "collar" image:
Collar filled with curved checkered texture
**The Method:*
Create an image tiled with your checkered texture.
Draw that texture image onto a temporary canvas.
Get the image data for the temporary canvas.
For each column of pixels, offset that entire column by the curve of your collar.
A quadratic curve fits the curve of your collar well, so I used that in the example to determin the Y offset.
Put the image data back on the temporary canvas.
(You now have a curved checkered pattern to apply to your collar image).
Draw the collar image on your main canvas.
Set context.globalCompositeOperation=”source-atop”
Draw the texture from the temporary canvas onto the main canvas.
(The compositing mode will paint the texture only on the collar—not the background)
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/hdXyk/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// get canvas references (canvas=collar, canvas1=texture)
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvas1=document.getElementById("canvas1");
var ctx1=canvas1.getContext("2d");
// preload the texture and collar images before starting
var textureImg,collarImg;
var imageURLs=[];
var imagesOK=0;
var imgs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/checkered.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/collar.png");
loadAllImages();
function loadAllImages(callback){
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
img.crossOrigin="anonymous";
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK==imageURLs.length ) {
textureImg=imgs[0];
collarImg=imgs[1];
start();
}
};
img.src = imageURLs[i];
}
}
function start(){
// set both canvas dimensions
canvas.width=collarImg.width;
canvas.height=collarImg.height+5;
canvas1.width=textureImg.width;
canvas1.height=textureImg.height;
// draw the textureImg on canvas1
ctx1.drawImage(textureImg,0,0,canvas1.width,canvas1.height);
// curve the texture into a collar shaped curved
curveTexture(collarImg.width,collarImg.height);
// draw the collarImg on canvas
ctx.drawImage(collarImg,0,0);
// set compositing to source-atop
// any new drawing will ONLY fill existing non-transparent pixels
ctx.globalCompositeOperation="source-atop";
// draw the curved texture from canvas1 onto the collar of canvas
// (the existing pixels are the collar, so only the collar is filled)
ctx.drawImage(canvas1,0,0);
}
function curveTexture(w,h){
// define a quadratic curve that fits the collar bottom
// These values change if the collar image changes (+5,-32)
var x0=0;
var y0=h+5;
var cx=w/2;
var cy=h-32;
var x1=w;
var y1=h+5;
// get a,b,c for quadratic equation
// equation is used to offset columns of texture pixels
// in the same shape as the collar
var Q=getQuadraticEquation(x0,y0,cx,cy,x1,y1);
// get the texture canvas pixel data
// 2 copies to avoid self-referencing
var imageData0 = ctx1.getImageData(0,0,w,h);
var data0 = imageData0.data;
var imageData1 = ctx1.getImageData(0,0,w,h);
var data1 = imageData1.data;
// loop thru each vertical column of pixels
// Offset the pixel column into the shape of the quad-curve
for(var y=0; y<h; y++) {
for(var x=0; x<w; x++) {
// the pixel to write
var n=((w*y)+x)*4;
// the vertical offset amount
var yy=parseInt(y+h-(Q.a * x*x + Q.b*x + Q.c));
// the offset pixel to read
var nn=((w*yy)+x)*4;
// offset this pixel by the quadCurve Y value (yy)
data0[n+0]=data1[nn+0];
data0[n+1]=data1[nn+1];
data0[n+2]=data1[nn+2];
data0[n+3]=data1[nn+3];
}
}
ctx1.putImageData(imageData0,0,0);
}
// Quadratic Curve: given x coordinate, find y coordinate
function getQuadraticY(x,Q){
return(Q.a * x*x + Q.b*x + Q.c);
}
// Quadratic Curve:
// Given: start,control,end points
// Find: a,b,c in quadratic equation ( y=a*x*x+b*x+c )
function getQuadraticEquation(x0,y0,cx,cy,x2,y2){
// need 1 more point on q-curve, so calc its midpoint XY
// Note: since T=0.5 therefore TT=(1-T)=0.5 also [so could simplify]
var T=0.50;
var TT=1-T;
var x1=TT*TT*x0+2*TT*T*cx+T*T*x2;
var y1=TT*TT*y0+2*TT*T*cy+T*T*y2;
var A = ((y1-y0)*(x0-x2)
+ (y2-y0)*(x1-x0))/((x0-x2)*(x1*x1-x0*x0)
+ (x1-x0)*(x2*x2-x0*x0));
var B = ((y1-y0)-A*(x1*x1-x0*x0))/(x1-x0);
var C = y0-A*x0*x0-B*x0;
return({a:A,b:B,c:C});
}
}); // end $(function(){});
</script>
</head>
<body>
<p>"Curve" a texture by offsetting Y pixels based on Q-curve</p>
<canvas id="canvas" width=300 height=300></canvas>
<p>The temporary texture canvas</p>
<canvas id="canvas1" width=300 height=300></canvas>
</body>
</html>

You can triangulate the polygon and then bend the mesh. Then you can fill the mesh with the pattern. Here is an example of a triangulation in Java: How does this code for delaunay triangulation work?. Here is an example of a triangulation and a bit of work to remove long edges. It's a concave hull of a 2d point set.

Related

trying to colorize a simple PNG and draw inside a mask element in canvas

I tried to create an HTML canvas, place a rectangle... then INSIDE THAT rectangle, draw various shapes and an RGBa PNG... all of them clipped inside the dimensions of the rectangle. Then I tried to change the color of the PNG when you press an HTML button input. (Further comments in code.)
Heres the problems... A. You have to draw to a temporary canvas and apply "GlobalCompositeOperation source-atop" just AFTER the clipping rectangle. Everything drawn after that is successfully clipped into the rect shape. Then the whole thing is copied (drawn) to a MAIN canvas. I was told to do it this way in order for the programming to recognize MULTIPLE elements after a "composite" operation. I have to say this works BEAUTIFULLY!! but here's problem B...
To to a "getData" on an image (to change color), I think you have to place the image on a canvas, and doing all the image pixel manipulation screws up the "composite" operation, so I tried to draw the PNG to a THIRD canvas, do the pixel changes, and then draw it to the temporary canvas... adding it to the rest of the elements....THEEENNN draw it all to the main canvas. Does not work. See code. Please help, Im mad enough to chew neutronium.
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<canvas id="theCanvas" width="200" height="200" style="border:2px solid #000000;"></canvas>
<canvas id="tempCanvas" width="200" height="200" style="display:none;"></canvas>
<canvas id="anotherCanvas" width="200" height="200" style="display:none;"></canvas>
<form>
<input type="button" id="changeColor" value="Click to Change Color of Graphic">
</form>
<script type="text/javascript" src="hereWeGoAgain_GHELP.js"></script>
</body>
</html>
//------------------------------------- JS
window.addEventListener("load", eventWindowLoaded, false);
function eventWindowLoaded () {
canvasApp();
}
function canvasApp() {
var canvas = document.getElementById('theCanvas');// the main canvas, where it all goes in the end
var context = canvas.getContext('2d');
var tempCanvas = document.getElementById('tempCanvas');// the canvas to do my "source-atop" stuff...
var tempContext = tempCanvas.getContext('2d');
var anotherCanvas = document.getElementById('anotherCanvas');
var anotherContext = anotherCanvas.getContext('2d');
// ...and Im thinking I should draw the RGBA PNG here, before placing it in the temp canvas, with the other elements
var cc = document.getElementById('changeColor');
cc.addEventListener('click', function(){changeColorFunction('ff0000');}, false);
// the HTML form button to change the PNG color
var colorOfThePlacedPNG = "#000000";
var imagesToLoad = 0;
var imagesLoaded = 0;
function drawScreen() {
tempContext.fillStyle="#999999";
tempContext.fillRect(0,0,200,200); //color the whole temp canvas area grey....
tempContext.fillStyle="#2baae1";
tempContext.fillRect(30,30,140,140);//now draw a light blue rect inside....
tempContext.globalCompositeOperation="source-atop"; // now make everything drawn AFTERWARDS be clipped (masked) inside the blue rect
// when I comment out the above "global Comp Op"... everything draws to the main canvas normally...just not clipped(masked) however
tempContext.fillStyle="#f47e1f";
tempContext.fillRect(150,100,150,150);//SO heres an orange box intentionally clipped off the bottom right in the blue rect
tempContext.fillStyle="#d89bc5";
tempContext.fillRect(40,50,80,200);//AND heres a light purple rect intentionally clipped at the bottom of the blue rect
getTheImageData(); //draw PNG to another canvas, convert image data, put in tempContext
//tempContext.restore();//dont know if I need this
context.drawImage(tempCanvas, 0, 0);// and then FINALLY draw all to the main canvas
}
var loaded = function(){
imagesLoaded += 1;
if(imagesLoaded === imagesToLoad){
drawScreen();
}
}
var loadImage = function(url){
var image = new Image();
image.addEventListener("load",loaded);
imagesToLoad += 1;
image.src = url;
return image;
}
function changeColorFunction(e) {
colorOfThePlacedPNG = e;
drawScreen();
}
function getTheImageData(){
anotherContext.drawImage(testPNGimage, 0, 0);// draw to the third canvas(another canvas)
var imgData = anotherContext.getImageData(0, 0, 200, 200);
// how do i color it red? ....like #ff0000 ???
var i;
for (i = 0; i < imgData.data.length; i += 4) {
imgData.data[i] = 255 - imgData.data[i];
imgData.data[i+1] = 255 - imgData.data[i+1];
imgData.data[i+2] = 255 - imgData.data[i+2];
imgData.data[i+3] = 255;
}
tempContext.putImageData(imgData, 0, 0);
}
var testPNGimage = loadImage("test.png");// the PNG is just a 75X75px black squiggle drawn in pshop
}
You're overcomplicating things!
There is a clipping method built into canvas.
Use clipping instead of compositing and multiple canvases.
Do this and all new drawings will be clipped inside your 140x140 rect:
context.beginPath();
context.rect(30,30,140,140);
context.clip();
Here's a simplified redesign of your code:
Draw a grey rect filling the canvas.
Draw a blue 140x140 rect at [30,30].
Clip all new drawings into the blue rect with context.clip()
Draw a clipped orange rect.
Draw a clipped purple rect.
Unclip so new drawings will be visible anywhere on the canvas.
Draw the squiggle image (it's not clipped).
Use .getImageData to invert every pixel's color.
And a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/squiggle.png";
function start(){
//color the whole canvas area grey....
ctx.fillStyle="#999999";
ctx.fillRect(0,0,200,200);
//now draw a light blue rect inside....
ctx.fillStyle="#2baae1";
ctx.beginPath();
ctx.rect(30,30,140,140);
ctx.fill();
// save the unclipped context state
ctx.save();
// cause all new drawings to be clipped inside
// the blue 140x140 rect at [30,30]
ctx.clip();
//SO heres an orange box intentionally clipped off the bottom right in the blue rect
ctx.fillStyle="#f47e1f";
ctx.fillRect(150,100,150,150);
//AND heres a light purple rect intentionally clipped at the bottom of the blue rect
ctx.fillStyle="#d89bc5";
ctx.fillRect(40,50,80,200);
// restore the context state (releases clipping for new drawings)
ctx.restore();
// draw the squiggley line image -- it's not clipped in the blue rect
ctx.drawImage(img,0,0);
// invert the colors using getImageData
var imgData = ctx.getImageData(0, 0, 200, 200);
var i;
for (i = 0; i < imgData.data.length; i += 4) {
imgData.data[i] = 255 - imgData.data[i];
imgData.data[i+1] = 255 - imgData.data[i+1];
imgData.data[i+2] = 255 - imgData.data[i+2];
imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=200 height=200></canvas>

Extract part of image by its image map coordinates and display it inside a canvas

I have an image which is being displayed by a canvas and I have coordinates like this
428,361,432,374,475,399,470,472,420,
494,400,486,328,461,183,409,176,386,
178,356,182,345,270,309,192,288,072,
257,069,228,069,219,077,212,112,205,
133,207,185,201,213,203,277,215,291,
220,298,222,323,225,388,235,418,244,
412,269,393,312,387,332,388,349,407,
356,414,360
which are in an <area> tag format.
What I want to do is to extract that area from my image and display it (that extracted area) inside another canvas.
How can I do that?
Thanks
Your area points are actually point-pairs of a polygon. That polygon is the desired area of the source image.
Here's how to extract the area mapped pixels from the source image:
Calculate the bounding box of the area coordinates: minX,minY & maxX,maxY
Resize the destination canvas to the size of the bounding box:
width=maxX-minX, height=maxY-minY
Create a clipping path on the destination. The clipping path is the original area moved to the top-left of the destination canvas.
// draw the clipping path
ctx.beginPath();
ctx.moveTo(pointpairs[0]-minX,pointpairs[1]-minY);
for(var i=2;i<pointpairs.length;i+=2){
var x=pointpairs[i];
var y=pointpairs[i+1];
ctx.lineTo(x-minX,y-minY);
}
ctx.closePath();
ctx.clip();
Draw the image onto the destination canvas, but offset by minY & minY
ctx.drawImage(img,-minX,-minY);
Here's example code and a Demo:
var srcCanvas=document.getElementById("source");
var sctx=srcCanvas.getContext("2d");
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var pointpairs=[
428,361,432,374,475,399,470,472,420,
494,400,486,328,461,183,409,176,386,
178,356,182,345,270,309,192,288,072,
257,069,228,069,219,077,212,112,205,
133,207,185,201,213,203,277,215,291,
220,298,222,323,225,388,235,418,244,
412,269,393,312,387,332,388,349,407,
356,414,360];
var minX=1000000;
var minY=minX;
var maxX=-1000000;
var maxY=maxX;
for(var i=0;i<pointpairs.length;i+=2){
var x=pointpairs[i];
var y=pointpairs[i+1];
if(x<minX){minX=x;}
if(y<minY){minY=y;}
if(x>maxX){maxX=x;}
if(y>maxY){maxY=y;}
}
var img=new Image();
img.onload=start;
img.src='https://dl.dropboxusercontent.com/u/139992952/multple/transportation.jpg';
function start(){
srcCanvas.width=img.width;
srcCanvas.height=img.height;
sctx.drawImage(img,0,0);
// draw the source image
// and also stroke the text area
sctx.lineWidth=2;
sctx.strokeStyle='blue';
sctx.beginPath();
sctx.moveTo(pointpairs[0],pointpairs[1]);
for(var i=2;i<pointpairs.length;i+=2){
var x=pointpairs[i];
var y=pointpairs[i+1];
sctx.lineTo(x,y);
}
sctx.closePath();
sctx.stroke();
// resize the canvas to the <area> bounding box size
canvas.width=maxX-minX;
canvas.height=maxY-minY;
// draw the clipping path
ctx.beginPath();
ctx.moveTo(pointpairs[0]-minX,pointpairs[1]-minY);
for(var i=2;i<pointpairs.length;i+=2){
var x=pointpairs[i];
var y=pointpairs[i+1];
ctx.lineTo(x-minX,y-minY);
}
ctx.closePath();
ctx.clip();
ctx.drawImage(img,-minX,-minY);
}
body{ background-color:black; padding:10px; }
canvas{border:1px solid red;}
<canvas id="source" width=300 height=300></canvas>
<br/>
<canvas id="canvas" width=300 height=300></canvas>
This is the code that I have used to make those coordinates
function clipIt(){
// calculate the size of the user's clipping area
if(points=="")
bootbox.alert("Not selected", function() {
});
else{
var minX=10000;
var minY=10000;
var maxX=-10000;
var maxY=-10000;
for(var i=1;i<points.length;i++){
var p=points[i];
if(p.x<minX){minX=p.x;}
if(p.y<minY){minY=p.y;}
if(p.x>maxX){maxX=p.x;}
if(p.y>maxY){maxY=p.y;}
}
var width=maxX-minX;
var height=maxY-minY;
// clip the image into the user's clipping area
ctx.save();
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++){
var p=points[i];
ctx.lineTo(points[i].x,points[i].y);
}
ctx.closePath();
ctx.clip();
ctx.drawImage(img,0,0);
ctx.restore();
// create a new canvas
var c=document.createElement('canvas');
var cx=c.getContext('2d');
// resize the new canvas to the size of the clipping area
c.width=width;
c.height=height;
// draw the clipped image from the main canvas to the new canvas
cx.drawImage(canvas, minX,minY,width,height, 0,0,width,height);
// create a new Image() from the new canvas
var clippedImage=new Image();
//clippedImage.name="img"+ nameimageis
clippedImage.id="img"+ nameimageis
clippedImage.onload=function(){
// append the new image to the page
//document.body.appendChild(clippedImage );
new_parts=new_parts+1;
$("#contain").append(clippedImage)
$("#img"+nameimageis ).wrap( "<div class='ins' id='in_"+nameimageis+"'></div>" );
$("#in_"+nameimageis ).append("<input type='hidden' id='new_"+nameimageis+"' value='new'/><div class='inner' id='inner_"+nameimageis+"' onclick='removeit("+nameimageis+")'></div>");
nameimageis++;
}
clippedImage.src=c.toDataURL();
// clear the previous points
points.length=0;
// redraw the image on the main canvas for further clipping
drawImage(1);
}
}

How to split up an image in pieces and reshuffle it, using HTML, Javascript or CamanJS?

I want to create puzzle images out of original images, meaning that an image is cut into 9 pieces (3x3) and then shuffled and stored as a new image. Does anyone know which method is the best to do so and how to achieve it? Perhaps with CamanJS? Does anyone have an example code?
Canvas can do this using the clipping version of context.drawImage.
context.drawImage allows you to clip your 9 sub-pieces from the original image and then draw them anywhere on the canvas.
The clipping version of drawImage takes these arguments:
the image to be clipped: img
the [clipLeft, clipTop] in the original image where clipping starts
the [clipWidth, clipHeight] size of the sub-image to be clipped from the original image
the [drawLeft, drawTop] on the Canvas where the clipped sub-image will start drawing
the [drawWidth, drawHeight] is scaled size of the sub-image to be drawn on the canvas
If drawWidth==clipWidth and drawHeight==clipHeight, the sub-image will be drawn at the same size clipped from the original.
If drawWidth!==clipWidth and drawHeight!==clipHeight, the sub-image will be scaled and then drawn.
Here's example code and a Demo that randomly draws the clipped pieces onto the canvas. It shuffles an array to define random positions for the pieces and then draws those pieces using drawImage.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var rows=3;
var cols=3;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/sailboat.png";
function start(){
var iw=canvas.width=img.width;
var ih=canvas.height=img.height;
var pieceWidth=iw/cols;
var pieceHeight=ih/rows;
var pieces = [
{col:0,row:0},
{col:1,row:0},
{col:2,row:0},
{col:0,row:1},
{col:1,row:1},
{col:2,row:1},
{col:0,row:2},
{col:1,row:2},
{col:2,row:2},
]
shuffle(pieces);
var i=0;
for(var y=0;y<rows;y++){
for(var x=0;x<cols;x++){
var p=pieces[i++];
ctx.drawImage(
// from the original image
img,
// take the next x,y piece
x*pieceWidth, y*pieceHeight, pieceWidth, pieceHeight,
// draw it on canvas based on the shuffled pieces[] array
p.col*pieceWidth, p.row*pieceHeight, pieceWidth, pieceHeight
);
}}
}
function shuffle(a){
for(var j, x, i = a.length; i; j = Math.floor(Math.random() * i), x = a[--i], a[i] = a[j], a[j] = x);
return a;
};
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

How to get a rotated crop of an image with HTML canvas

I have a large canvas containing an image, as shown in the example below:
I have the position and rotation angle of the red rectangle:
red : {
top : top,
left : left,
width : width,
height : height,
angle : angle
}
I also have a full set of translated coordinates denoting the actual corner points of the red rotated rectangle.
Finally, I have the position of the blue rectangle relative to the red rectangle:
blue : {
left : left,
top : top,
width : width,
height : height
}
What I need to do is create a new canvas that is the size of the blue rectangle. The new canvas should contain the correctly rotated portion of the image that is contained within the blue rectangle. The resulting image should look like this:
Here is my JavaScript code so far:
var c = getCenterPoint(); // returns center x/y positions of the RED rectangle
canvas.width = blue.width;
canvas.height = blue.height;
var blueX = red.left + blue.left;
var blueY = red.top + blue.top;
var tx = blueX - c.x;
var ty = blueY - c.y;
this.cursorContext.translate(tx, ty);
this.cursorContext.rotate(angle * (Math.PI / 180));
this.cursorContext.translate(-tx, -ty);
this.cursorContext.drawImage(image, -blueX, -blueY, blue.width, blue.height);
The problem I am having is getting the correct portion of the image when the rectangle is rotated. How can I do this?
You can use a temporary canvas to clip and unrotate your blue box
Clip the boundingbox of the blue rectangle from the image
Unrotate the boundingbox so the blue rectangle is unrotated (angle==0)
Clip the extra boundingbox area away to reveal only the blue rectangle
Draw the blue rectangle to the display canvas
Here’s code and a Demo: http://jsfiddle.net/m1erickson/28EkG/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// blue rect's info
var blueX=421;
var blueY=343;
var blueWidth=81;
var blueHeight=44;
var blueAngle=-25.00*Math.PI/180;
// load the image
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/temp6.jpg";
function start(){
// create 2 temporary canvases
var canvas1=document.createElement("canvas");
var ctx1=canvas1.getContext("2d");
var canvas2=document.createElement("canvas");
var ctx2=canvas2.getContext("2d");
// get the boundingbox of the rotated blue box
var rectBB=getRotatedRectBB(blueX,blueY,blueWidth,blueHeight,blueAngle);
// clip the boundingbox of the rotated blue rect
// to a temporary canvas
canvas1.width=canvas2.width=rectBB.width;
canvas1.height=canvas2.height=rectBB.height;
ctx1.drawImage(img,
rectBB.cx-rectBB.width/2,
rectBB.cy-rectBB.height/2,
rectBB.width,
rectBB.height,
0,0,rectBB.width,rectBB.height
);
// unrotate the blue rect on the temporary canvas
ctx2.translate(canvas1.width/2,canvas1.height/2);
ctx2.rotate(-blueAngle);
ctx2.drawImage(canvas1,-canvas1.width/2,-canvas1.height/2);
// draw the blue rect to the display canvas
var offX=rectBB.width/2-blueWidth/2;
var offY=rectBB.height/2-blueHeight/2;
canvas.width=blueWidth;
canvas.height=blueHeight;
ctx.drawImage(canvas2,-offX,-offY);
} // end start
// Utility: get bounding box of rotated rectangle
function getRotatedRectBB(x,y,width,height,rAngle){
var absCos=Math.abs(Math.cos(rAngle));
var absSin=Math.abs(Math.sin(rAngle));
var cx=x+width/2*Math.cos(rAngle)-height/2*Math.sin(rAngle);
var cy=y+width/2*Math.sin(rAngle)+height/2*Math.cos(rAngle);
var w=width*absCos+height*absSin;
var h=width*absSin+height*absCos;
return({cx:cx,cy:cy,width:w,height:h});
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

drawing over an Image in HTML5 Canvas while preserving the image

In HTML5 Canvas, what's the simplest way to draw and move a line over an Image (already on the canvas), preserving the image underneath? (e.g. have a vertical line track the mouse X position)
My current canvas:
$(document).ready(function() {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 0,0);
}
imageObj.src = "http://example.com/some_image.png";
$('#myCanvas').click(doSomething);
});
You will have to do most of the ground-work with canvas which in this case you will have to implement the functionality to move the line and then redraw everything.
The steps can be:
Keep the line as an object which can self-render (method on the object)
Listen to mousemove (in this case) in order to move the line
For each move, redraw background (image) then render the line at its new position
You can redraw the background as a whole or you can optimize it to just draw over the last line.
Here is some example code of this and a live demo here:
var canvas = document.getElementById('demo'), /// canvas element
ctx = canvas.getContext('2d'), /// context
line = new Line(ctx), /// our custom line object
img = new Image; /// the image for bg
ctx.strokeStyle = '#fff'; /// white line for demo
/// start image loading, when done draw and setup
img.onload = start;
img.src = 'http://i.imgur.com/O712qpO.jpg';
function start() {
/// initial draw of image
ctx.drawImage(img, 0, 0, demo.width, demo.height);
/// listen to mouse move (or use jQuery on('mousemove') instead)
canvas.onmousemove = updateLine;
}
Now all we need to do is to have a mechnism to update the background and the line for each move:
/// updates the line on each mouse move
function updateLine(e) {
/// correct mouse position so it's relative to canvas
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
/// draw background image to clear previous line
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
/// update line object and draw it
line.x1 = x;
line.y1 = 0;
line.x2 = x;
line.y2 = canvas.height;
line.draw();
}
The custom line object is in this demo very simple:
/// This lets us define a custom line object which self-draws
function Line(ctx) {
var me = this;
this.x1 = 0;
this.x2 = 0;
this.y1 = 0;
this.y2 = 0;
/// call this method to update line
this.draw = function() {
ctx.beginPath();
ctx.moveTo(me.x1, me.y1);
ctx.lineTo(me.x2, me.y2);
ctx.stroke();
}
}
If you are not gonna do anything specific with the image itself you can also set it as a background-image using CSS. You will still need to clear the canvas before redrawing the line though.
May this is not an actual answer, just in case you need it (in the future). Working with canvas would be better (and easier) with some library. I have tried EaselJS of CreateJS and find myself loving it.
You can have a look at it EaselJS
(I have done an example allow drawing and dragging image using EaselJS long time before)
You can get your "crosshairs" by listening to mousemove events and then:
clear the canvas
draw the image
draw your line at the mouse position
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/jEc7N/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.lineWidth=2;
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var img=new Image();
img.onload=function(){
canvas.width=img.width;
canvas.height=img.height;
ctx.drawImage(img,0,0);
}
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/KoolAidMan.png";
function handleMouseMove(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(img,0,0);
ctx.beginPath();
ctx.moveTo(mouseX,0);
ctx.lineTo(mouseX,canvas.height);
ctx.moveTo(0,mouseY);
ctx.lineTo(canvas.width,mouseY);
ctx.stroke();
}
$("#canvas").mousemove(function(e){handleMouseMove(e);});
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
or just use 2 layers:
background layer has image and do not change,
top layer has line, what you can clear and redraw it lots of time without affecting background layer.

Categories