HTML5 Canvas - Rotate by anchor using a mouse - javascript

I am developing in html5's canvas element. I have the follwing code and it is draggable and resizable image. How can I turn it to rotatable by a anchor too? How can I provide live rotating by an anchor. I saw other codes samples, but do not know how to implement it.
Sampler Working:
http://jsfiddle.net/LAS8L/588/
<canvas id="canvas" width=350 height=350></canvas>
Javascript
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var startX;
var startY;
var isDown = false;
var pi2 = Math.PI * 2;
var resizerRadius = 8;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
x: 0,
y: 0
};
var imageX = 50;
var imageY = 50;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;
var cx = canvas.width / 2;
var cy = canvas.height / 2;
var w;
var h;
var r = 0;
var img = new Image();
img.onload = function () {
imageWidth = img.width;
imageHeight = img.height;
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight
draw(true, false);
}
img.src = "https://dl.dropboxusercontent.com/u/139992952/stackoverflow/facesSmall.png";
function draw(withAnchors, withBorders) {
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw the image
ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);
// optionally draw the draggable anchors
if (withAnchors) {
drawDragAnchor(imageX, imageY);
drawDragAnchor(imageRight, imageY);
drawDragAnchor(imageRight, imageBottom);
drawDragAnchor(imageX, imageBottom);
}
// optionally draw the connecting anchor lines
if (withBorders) {
ctx.beginPath();
ctx.moveTo(imageX, imageY);
ctx.lineTo(imageRight, imageY);
ctx.lineTo(imageRight, imageBottom);
ctx.lineTo(imageX, imageBottom);
ctx.closePath();
ctx.stroke();
}
}
function drawDragAnchor(x, y) {
ctx.beginPath();
ctx.arc(x, y, resizerRadius, 0, pi2, false);
ctx.closePath();
ctx.fill();
}
function anchorHitTest(x, y) {
var dx, dy;
// top-left
dx = x - imageX;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (0);
}
// top-right
dx = x - imageRight;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (1);
}
// bottom-right
dx = x - imageRight;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (2);
}
// bottom-left
dx = x - imageX;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (3);
}
return (-1);
}
function desenhe() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRotationHandle(true);
drawRect();
}
function drawRect() {
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(r);
ctx.drawImage(img, 0, 0, img.width, img.height, -imageWidth, -imageheight, w, h);
// ctx.fillStyle="yellow";
// ctx.fillRect(-w/2,-h/2,w,h);
ctx.restore();
}
function drawRotationHandle(withFill) {
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(r);
ctx.beginPath();
ctx.moveTo(0, -1);
ctx.lineTo(imageWidth + 20, -1);
ctx.lineTo(imageWidth + 20, -7);
ctx.lineTo(imageWidth + 30, -7);
ctx.lineTo(imageWidth + 30, 7);
ctx.lineTo(imageWidth + 20, 7);
ctx.lineTo(imageWidth + 20, 1);
ctx.lineTo(0, 1);
ctx.closePath();
if (withFill) {
ctx.fillStyle = "blue";
ctx.fill();
}
ctx.restore();
}
function hitImage(x, y) {
return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}
function handleMouseDown(e) {
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
draggingResizer = anchorHitTest(startX, startY);
draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}
function handleMouseUp(e) {
draggingResizer = -1;
draggingImage = false;
draw(true, false);
}
function handleMouseOut(e) {
handleMouseUp(e);
}
function handleMouseMove(e) {
if (draggingResizer > -1) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// resize the image
switch (draggingResizer) {
case 0:
//top-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageY = mouseY;
imageHeight = imageBottom - mouseY;
break;
case 1:
//top-right
imageY = mouseY;
imageWidth = mouseX - imageX;
imageHeight = imageBottom - mouseY;
break;
case 2:
//bottom-right
imageWidth = mouseX - imageX;
imageHeight = mouseY - imageY;
break;
case 3:
//bottom-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageHeight = mouseY - imageY;
break;
}
if(imageWidth<25){imageWidth=25;}
if(imageHeight<25){imageHeight=25;}
// set the image right and bottom
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;
// redraw the image with resizing anchors
draw(true, true);
} else if (draggingImage) {
imageClick = false;
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// move the image by the amount of the latest drag
var dx = mouseX - startX;
var dy = mouseY - startY;
imageX += dx;
imageY += dy;
imageRight += dx;
imageBottom += dy;
// reset the startXY for next time
startX = mouseX;
startY = mouseY;
// redraw the image with border
draw(false, true);
}
}
$("#canvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function (e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function (e) {
handleMouseOut(e);
});
I would like to work this way: http://jsfiddle.net/m1erickson/QqwKR/ but I don't know how merge the code. Can somebody help me?

Here is a function the will set the transform to scale rotate and translate an rectangle or anything.
function setTransform(ctx,x,y,scale,rotate){
var xdx = Math.cos(rotate) * scale; // create the x axis
var xdy = Math.sin(rotate) * scale;
ctx.setTransform(xdx, xdy, - xdy, xdx, x, y);
}
The canvas transform is set after that. Now just draw the object in its local space coords.
Eg draw at center of canvas double scale and 45deg rotation
setTransform(ctx,canvas.width/2,canvas.height/2,2,Math.PI /4);
ctx.strokeRect(-100,-100,200,200);
You will notice that the coordinates are negative. This is because I want the center of the box to be at the center of the canvas. If I wanted the top left of the box to be at the center
ctx.strokeRect(0,0,200,200);
or the bottom right
ctx.strokeRect(-200,-200,200,200);
Back to the centered box I can move it anywhere
setTransform(
ctx,
Math.random() * canvas.width, // random x pos
Math.random() * canvas.height, // random y pos
Math.random() * 10 + 0.1, // random scale
Math.random() * Math.PI * 2 // random rotation
);
The transform is random but I do not need to care I can still draw the box in local coordinates.
ctx.strokeRect(-100,-100,200,200);
And it will be draw where ever the transform has set it to.
Using ctx.setTransform saves the hassle of using save and restore.
If at any stage you need to return to the default transform
ctx.setTransform(1,0,0,1,0,0);
The problem you will next face is that the mouse coordinates are in canvas coordinates while the object is in its local space. You need to convert the mouse coordinates into the local object coordinates.
This is done by multiplying the mouse coordinates by the inverse of the matrix. A bit of maths here.
function getMouseLocal(mousex,mouseY,x,y,scale,rot){
var xdx = Math.cos(rotate) * scale; // create the x axis
var xdy = Math.sin(rotate) * scale;
// get the cross product of the two axies
var cross = xdx * xdx - xdy * -xdy;
// or
var cross = Math.pow(xdx,2) + Math.pow(xdy,2);
// then create the inverted axies
var ixdx = xdx / cross; // create inverted x axis
var ixdy = -xdy / cross;
var iydx = xdy / cross; // create inverted y axis
var iydy = xdx / cross;
// now remove the origin from the mouse coords
mouseX -= x;
mouseY -= y;
// multiply by the invers matrix
var localMouseX = mouseX * ixdx + mouseY * iydx;
var localMouseY = mouseX * ixdy + mouseY * iydy;
// and return the result
return {x : localMouseX, y : localMouseY};
}
Now you have the mouse coordinates in the local space. If you need to find out if the mouse is inside the box you
setTransform(ctx,100,100,2,Math.PI/4);
ctx.strokeRect(-100,-100,200,200);
var localMouse= getMouseLocal(mouseX,mouseY,100,100,2,Math.PI/4);
if(localMouse.x > -100 && localMouse.x < -100 + 200 && localMouse.y > -100 && localMouse.y < -100 + 200){
// mouse is inside the box
}
That should give you what you need.
UPDATE
I forgot you want to scale both x and y.. So below are the modified functions for scaling both X and Y axies
// sx and sy are scale x and y
function setTransform(ctx,x,y,sx,sy,rotate){
var xdx = Math.cos(rotate); // create the x axis
var xdy = Math.sin(rotate);
ctx.setTransform(xdx * sx, xdy * sx, - xdy * sy, xdx * sy, x, y);
}
And getting the mouse to local
function getMouseLocal(mousex,mouseY,x,y,sx,sy,rot){
var xdx = Math.cos(rotate); // create the x axis
var xdy = Math.sin(rotate);
// get the cross product of the two axies
var cross = xdx * sx * xdx * sy - xdy *sx * -xdy * sy;
// or
// this shortcut does not work now.
// var cross = Math.pow(xdx,2) + Math.pow(xdy,2);
// then create the inverted axies
var ixdx = (xdx * sy) / cross; // create inverted x axis
var ixdy = (-xdy * sx) / cross;
var iydx = (xdy * sy) / cross; // create inverted y axis
var iydy = (xdx * sx) / cross;
// now remove the origin from the mouse coords
mouseX -= x;
mouseY -= y;
// multiply by the invers matrix
var localMouseX = mouseX * ixdx + mouseY * iydx;
var localMouseY = mouseX * ixdy + mouseY * iydy;
// and return the result
return {x : localMouseX, y : localMouseY};
}

Related

ClearRect is removing everything on the Canvas

Trying to get this hover function working, but when I hover it clears the entire canvas. Can't figure out what the clearRect is clearing, and how I can focus it on only clearing the rectangle I've drawn in the statement. Any guidance - some of the canvas rendering simply depends on order in code.
function handleMouseMove(e) {
e.preventDefault();
e.stopPropagation();
let mouseX = e.clientX - offsetX;
let mouseY = e.clientY - offsetY;
for (var i = 0; i < t.length; i++) {
let cx = t[i].Criticality * 100 + 50;
let cy = t[i].Score + 50;
var dx = mouseX - cx;
var dy = mouseY - cy;
ctx.clearRect(0, 0, dx, dy);
if (dx * dx + dy * dy < 15 * 15) {
draw();
ctx.beginPath();
ctx.fillStyle = "white";
ctx.fillRect(mouseX, mouseY, 130, 70);
ctx.stroke();
}
}
}

Extend Line based on slope to the end of canvas/drawing area

I am trying to extend a line (from to points(X,Y)) to the end of the drawing area.
so far i found a couple of instructions on how to calculate the extension end point.
however i don't really get it done it works in one direction and breaks as soon as you reach over the middle point.
see attached code sample (the real product i am working on is in swift, but as it is not a programming language related issue, i ported it to javascript)
on the right side it seems to work, black line is the one the user selects, red one is the extension to the edge of canvas, going to the left side produces garbage.
var canvas = document.getElementById("myCanvas");
var endPoint = {
x: 200,
y: 200
};
function draw() {
//Demo only in final product user also can select the startpoint
startPoint = {
x: 150,
y: 150
}
screenMax = {
x: canvas.height,
y: canvas.width
}
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(startPoint.x, startPoint.y);
ctx.lineTo(endPoint.x, endPoint.y);
ctx.strokeStyle = "#000000";
ctx.stroke();
//Extend line to end of canvas according to slope
var slope = 1.0
var extendedPoint = {
x: 0,
y: 0
}
if (endPoint.x != startPoint.x) {
slope = (endPoint.y - startPoint.y) / (endPoint.x - startPoint.x);
extendedPoint = {
x: screenMax.x,
y: slope * (screenMax.x - endPoint.x) + endPoint.y
}
} else {
slope = 0
extendedPoint.x = endPoint.x;
extendedPoint.y = screenMax.y;
}
console.log(endPoint);
//Draw the Extension
ctx.beginPath();
ctx.moveTo(endPoint.x, endPoint.y);
ctx.lineTo(extendedPoint.x, extendedPoint.y);
ctx.strokeStyle = "#FF0000";
ctx.stroke();
}
//initial draw
draw();
//handle Mouse dOwn
canvas.onmousedown = function(e) {
handleMouseDown(e);
}
// handle the mousedown event
//Set new endpoint
function handleMouseDown(e) {
mouseX = parseInt(e.clientX);
mouseY = parseInt(e.clientY);
endPoint = {
x: mouseX,
y: mouseY
}
draw();
}
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
</body>
</html>
This function may help, takes the line x1,y1 to x2,y2 and extends it to the border defined by left,top,right,bottom returning the intercept point as {x:?,y:?}
function toBorder(x1, y1, x2, y2, left, top, right, bottom){
var dx, dy, py, vx, vy;
vx = x2 - x1;
vy = y2 - y1;
dx = vx < 0 ? left : right;
dy = py = vy < 0 ? top : bottom;
if(vx === 0){
dx = x1;
}else if(vy === 0){
dy = y1;
}else{
dy = y1 + (vy / vx) * (dx - x1);
if(dy < top || dy > bottom){
dx = x1 + (vx / vy) * (py - y1);
dy = py;
}
}
return {x : dx, y : dy}
}
Slope approach is not universal - it cannot work with vertical lines (x0=x1).
I'd use parametric representation of ray (line)
x0 = startPoint.x
x1 = endPoint.x
y0 = startPoint.y
y1 = endPoint.y
dx = x1 - x0
dy = y1 - y0
x = x0 + dx * t
y = y0 + dy * t
Now check what border will be intersected first (with smaller t value)
//prerequisites: potential border positions
if dx > 0 then
bx = width
else
bx = 0
if dy > 0 then
by = height
else
bx = 0
//first check for horizontal/vertical lines
if dx = 0 then
return ix = x0, iy = by
if dy = 0 then
return iy = y0, ix = bx
//in general case find parameters of intersection with horizontal and vertical edge
tx = (bx - x0) / dx
ty = (by - y0) / dy
//and get intersection for smaller parameter value
if tx <= ty then
ix = bx
iy = y0 + tx * dy
else
iy = by
ix = x0 + ty * dx
return ix, iy

HTML Canvas - Drag and resize uploaded image

I am doing a drawing app for a college project and currently im able to upload images into the canvas and i want to make those images resizable/draggable.
I've seen this great example and tried to apply it in my project for hours but with no success.
Is it possible to apply it to an uploaded image instead of preloaded image like in that example?
My javascript code to upload image and a fiddle. Thanks in advance.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
function el(id){return document.getElementById(id);}
function readImage() {
if ( this.files && this.files[0] ) {
var FR= new FileReader();
FR.onload = function(e) {
var img = new Image();
img.onload = function() {
ctx.drawImage(img, true, true);
};
img.src = e.target.result;
};
FR.readAsDataURL( this.files[0] );
}
}
el("fileUpload").addEventListener("change", readImage, false);
As you have provided a nice example, I just included those methods and variables in your code and added jquery then declared the var img = new Image(); variable outside of the readImage function.
That's it I did and it worked as you expected.
Here you go,
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var startX;
var startY;
var isDown = false;
var pi2 = Math.PI * 2;
var resizerRadius = 8;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
x: 0,
y: 0
};
var imageX = 50;
var imageY = 50;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;
function el(id){return document.getElementById(id);}
var img = new Image();
function readImage() {
if ( this.files && this.files[0] ) {
var FR= new FileReader();
FR.onload = function(e) {
img = new Image();
img.onload = function() {
imageWidth = img.width;
imageHeight = img.height;
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight
draw(true, false);
};
img.src = e.target.result;
};
FR.readAsDataURL( this.files[0] );
}
}
el("fileUpload").addEventListener("change", readImage, false);
function draw(withAnchors, withBorders) {
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw the image
ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);
// optionally draw the draggable anchors
if (withAnchors) {
drawDragAnchor(imageX, imageY);
drawDragAnchor(imageRight, imageY);
drawDragAnchor(imageRight, imageBottom);
drawDragAnchor(imageX, imageBottom);
}
// optionally draw the connecting anchor lines
if (withBorders) {
ctx.beginPath();
ctx.moveTo(imageX, imageY);
ctx.lineTo(imageRight, imageY);
ctx.lineTo(imageRight, imageBottom);
ctx.lineTo(imageX, imageBottom);
ctx.closePath();
ctx.stroke();
}
}
function drawDragAnchor(x, y) {
ctx.beginPath();
ctx.arc(x, y, resizerRadius, 0, pi2, false);
ctx.closePath();
ctx.fill();
}
function anchorHitTest(x, y) {
var dx, dy;
// top-left
dx = x - imageX;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (0);
}
// top-right
dx = x - imageRight;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (1);
}
// bottom-right
dx = x - imageRight;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (2);
}
// bottom-left
dx = x - imageX;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (3);
}
return (-1);
}
function hitImage(x, y) {
return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}
function handleMouseDown(e) {
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
draggingResizer = anchorHitTest(startX, startY);
draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}
function handleMouseUp(e) {
draggingResizer = -1;
draggingImage = false;
draw(true, false);
}
function handleMouseOut(e) {
handleMouseUp(e);
}
function handleMouseMove(e) {
if (draggingResizer > -1) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// resize the image
switch (draggingResizer) {
case 0:
//top-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageY = mouseY;
imageHeight = imageBottom - mouseY;
break;
case 1:
//top-right
imageY = mouseY;
imageWidth = mouseX - imageX;
imageHeight = imageBottom - mouseY;
break;
case 2:
//bottom-right
imageWidth = mouseX - imageX;
imageHeight = mouseY - imageY;
break;
case 3:
//bottom-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageHeight = mouseY - imageY;
break;
}
if(imageWidth<25){imageWidth=25;}
if(imageHeight<25){imageHeight=25;}
// set the image right and bottom
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;
// redraw the image with resizing anchors
draw(true, true);
} else if (draggingImage) {
imageClick = false;
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// move the image by the amount of the latest drag
var dx = mouseX - startX;
var dy = mouseY - startY;
imageX += dx;
imageY += dy;
imageRight += dx;
imageBottom += dy;
// reset the startXY for next time
startX = mouseX;
startY = mouseY;
// redraw the image with border
draw(false, true);
}
}
$("#canvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function (e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function (e) {
handleMouseOut(e);
});
#canvas {
border:1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type='file' id="fileUpload" />
<canvas id="canvas" width="500" height="500"></canvas>

Resizing Canvas Image Position Issues

I'm using a HTML canvas to allow a user to edit (move, resize and rotate) an image on bottle to create their own custom one - similar to how a t-shirt designer would work.
It all works (there's a slight issue with the rotate though) up until you resize the screen. Because the canvas is horizontally centred on my page, when you reduce the size (or increase) from the initial load of the page - the canvas thinks the image on is still in its original place. Meaning that you cannot click the image to move it around, you have to click to the right or left (depending on how the screen is resized).
I think there's an issue with the maths, perhaps to do with the window or canvas offset not being updated when you resize the screen, but I cannot work out what the problem is.
Example: https://jsfiddle.net/mpsx1xnj/
The code is as follows:
<div id="design-canvas">
<canvas id="bottleCanvas" width="400" height="600"></canvas>
<center><canvas id="printableCanvas" width="71" height="245"></canvas></center>
<center><canvas id="editorCanvas" width="400" height="600"></canvas></center>
<p class="tooltip">Guidelines show the printable area on the bottle.</p>
</div>
var canvas = document.getElementById("editorCanvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#editorCanvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var startX;
var startY;
var isDown = false;
var pi2 = Math.PI * 2;
var resizerRadius = 4;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
x: 0,
y: 0
};
var imageX = 0;
var imageY;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;
var ratio;
var img = new Image();
img.crossOrigin='anonymous';
img.onload = function () {
ratio = img.width / img.height;
imageWidth = 71;
imageHeight = imageWidth / ratio;
imageY = (245-imageHeight)/2;
if (imageHeight > 245) {
imageHeight = 245;
imageWidth = imageHeight * ratio;
imageY = 0;
}
imageX = ((canvas.width-imageWidth)/2);
imageY = ((canvas.height-imageHeight)/2);
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;
draw(true, false);
}
//img.src='https://dl.dropboxusercontent.com/u/139992952/multple/leftarrow.png';
function draw(withAnchors, withBorders) {
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw the image
ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);
// optionally draw the draggable anchors
if (withAnchors) {
drawDragAnchor(imageX, imageY);
drawDragAnchor(imageRight, imageY);
drawDragAnchor(imageRight, imageBottom);
drawDragAnchor(imageX, imageBottom);
}
// optionally draw the connecting anchor lines
if (withBorders) {
ctx.beginPath();
ctx.moveTo(imageX, imageY);
ctx.lineTo(imageRight, imageY);
ctx.lineTo(imageRight, imageBottom);
ctx.lineTo(imageX, imageBottom);
ctx.closePath();
ctx.stroke();
}
}
function drawDragAnchor(x, y) {
ctx.beginPath();
ctx.arc(x, y, resizerRadius, 0, pi2, false);
ctx.closePath();
ctx.fill();
}
function anchorHitTest(x, y) {
var dx, dy;
// top-left
dx = x - imageX;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (0);
}
// top-right
dx = x - imageRight;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (1);
}
// bottom-right
dx = x - imageRight;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (2);
}
// bottom-left
dx = x - imageX;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (3);
}
return (-1);
}
function hitImage(x, y) {
return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}
function handleMouseDown(e) {
offsetX = offsetX + window.pageXOffset;
offsetY = offsetY + window.pageYOffset;
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
draggingResizer = anchorHitTest(startX, startY);
draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}
function handleMouseUp(e) {
draggingResizer = -1;
draggingImage = false;
draw(true, false);
}
function handleMouseOut(e) {
handleMouseUp(e);
}
function handleMouseMove(e) {
if (draggingResizer > -1) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// resize the image
switch (draggingResizer) {
case 0:
//top-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageY = mouseY;
imageHeight = imageBottom - mouseY;
break;
case 1:
//top-right
imageY = mouseY;
imageWidth = mouseX - imageX;
imageHeight = imageWidth/ratio; //imageBottom - mouseY;
break;
case 2:
//bottom-right
imageWidth = mouseX - imageX;
imageHeight = mouseY - imageY;
break;
case 3:
//bottom-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageHeight = mouseY - imageY;
break;
}
if(imageWidth<25){imageWidth=25;}
if(imageHeight<25){imageHeight=25;}
// set the image right and bottom
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;
// redraw the image with resizing anchors
draw(true, true);
} else if (draggingImage) {
imageClick = false;
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// move the image by the amount of the latest drag
var dx = mouseX - startX;
var dy = mouseY - startY;
imageX += dx;
imageY += dy;
imageRight += dx;
imageBottom += dy;
// reset the startXY for next time
startX = mouseX;
startY = mouseY;
// redraw the image with border
draw(false, true);
}
}
$("#editorCanvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#editorCanvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#editorCanvas").mouseup(function (e) {
handleMouseUp(e);
});
$("#editorCanvas").mouseout(function (e) {
handleMouseOut(e);
});
function rotate(rotationPointX,rotationPointY,degreeRotation){
// Create an second in-memory canvas:
var mCanvas=document.createElement('canvas');
mCanvas.width=canvas.width;
mCanvas.height=canvas.height;
var mctx=mCanvas.getContext('2d');
// Draw your canvas onto the second canvas
mctx.drawImage(canvas,0,0);
// Clear your main canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// Rotate the main canvas
// set the rotation point as center of the canvas
// (but you can set any rotation point you desire)
ctx.translate(rotationPointX,rotationPointY);
// rotate by 90 degrees (==PI/2)
var radians=degreeRotation/180*Math.PI;
ctx.rotate(radians);
// Draw the second canvas back to the (now rotated) main canvas:
ctx.drawImage(mCanvas,-canvas.width/2,-canvas.height/2);
// clean up -- unrotate and untranslate
ctx.rotate(-radians);
ctx.translate(-canvas.width/2,-canvas.height/2);
}
$(window).on("resize scroll",function(e) {
offsetX = win.left;
offsetY = canvasOffset.top;
imageWidth = 71;
imageHeight = imageWidth / ratio;
imageY = (245-imageHeight)/2;
if (imageHeight > 245) {
imageHeight = 245;
imageWidth = imageHeight * ratio;
imageY = 0;
}
imageX = ((canvas.width-imageWidth)/2);
imageY = ((canvas.height-imageHeight)/2);
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;
draw(true, false);
});
$('#rotate').click(function(){
rotate((imageX + (canvas.width/2)),(imageY + (canvas.height/2,90)));
});
So my question is how would I fix this issue, so that resizing the window causes no issue?
If i understand you correctly...
Here's a solution: jsFiddle
the width and height attributes of the canvas determine the width or height of the canvas's coordinate system - the page resizing only updates css properties (to make it responsive) which means your fixed width/height attrs always stay the same, even after a resize.
so on page resize
$('canvas#editorCanvas').attr('width', $('#design-canvas').width() );
$('canvas#editorCanvas').attr('height', $('#design-canvas').height() );
make the container responsive using max-height, max-width and setting height/width to 100%

Scale Canvas Image Proportionately

I'm using the following code to create a canvas to which the user uploads an image and can the drag, resize and rotate the image onto a product. Sort of like a tshirt design tool.
My problem is, I need to scale the image proportionately, so that when the user resizes the image it doesn't look distorted or stretched.
var canvas = document.getElementById("editorCanvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#editorCanvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var startX;
var startY;
var isDown = false;
var pi2 = Math.PI * 2;
var resizerRadius = 4;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
x: 0,
y: 0
};
var imageX = 0;
var imageY;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;
var img = new Image();
img.crossOrigin='anonymous';
img.onload = function () {
var ratio = img.width / img.height;
imageWidth = 71;
imageHeight = imageWidth / ratio;
imageY = (245-imageHeight)/2;
if (imageHeight > 245) {
imageHeight = 245;
imageWidth = imageHeight * ratio;
imageY = 0;
}
imageX = ((canvas.width-imageWidth)/2);
imageY = ((canvas.height-imageHeight)/2);
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;
draw(true, false);
}
//img.src='https://dl.dropboxusercontent.com/u/139992952/multple/leftarrow.png';
function draw(withAnchors, withBorders) {
// clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw the image
ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);
// optionally draw the draggable anchors
if (withAnchors) {
drawDragAnchor(imageX, imageY);
drawDragAnchor(imageRight, imageY);
drawDragAnchor(imageRight, imageBottom);
drawDragAnchor(imageX, imageBottom);
}
// optionally draw the connecting anchor lines
if (withBorders) {
ctx.beginPath();
ctx.moveTo(imageX, imageY);
ctx.lineTo(imageRight, imageY);
ctx.lineTo(imageRight, imageBottom);
ctx.lineTo(imageX, imageBottom);
ctx.closePath();
ctx.stroke();
}
}
function drawDragAnchor(x, y) {
ctx.beginPath();
ctx.arc(x, y, resizerRadius, 0, pi2, false);
ctx.closePath();
ctx.fill();
}
function anchorHitTest(x, y) {
var dx, dy;
// top-left
dx = x - imageX;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (0);
}
// top-right
dx = x - imageRight;
dy = y - imageY;
if (dx * dx + dy * dy <= rr) {
return (1);
}
// bottom-right
dx = x - imageRight;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (2);
}
// bottom-left
dx = x - imageX;
dy = y - imageBottom;
if (dx * dx + dy * dy <= rr) {
return (3);
}
return (-1);
}
function hitImage(x, y) {
return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}
function handleMouseDown(e) {
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
draggingResizer = anchorHitTest(startX, startY);
draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}
function handleMouseUp(e) {
draggingResizer = -1;
draggingImage = false;
draw(true, false);
}
function handleMouseOut(e) {
handleMouseUp(e);
}
function handleMouseMove(e) {
if (draggingResizer > -1) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// resize the image
switch (draggingResizer) {
case 0:
//top-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageY = mouseY;
imageHeight = imageBottom - mouseY;
break;
case 1:
//top-right
imageY = mouseY;
imageWidth = mouseX - imageX;
imageHeight = imageBottom - mouseY;
break;
case 2:
//bottom-right
imageWidth = mouseX - imageX;
imageHeight = mouseY - imageY;
break;
case 3:
//bottom-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageHeight = mouseY - imageY;
break;
}
if(imageWidth<25){imageWidth=25;}
if(imageHeight<25){imageHeight=25;}
// set the image right and bottom
imageRight = imageX + imageWidth;
imageBottom = imageY + imageHeight;
// redraw the image with resizing anchors
draw(true, true);
} else if (draggingImage) {
imageClick = false;
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// move the image by the amount of the latest drag
var dx = mouseX - startX;
var dy = mouseY - startY;
imageX += dx;
imageY += dy;
imageRight += dx;
imageBottom += dy;
// reset the startXY for next time
startX = mouseX;
startY = mouseY;
// redraw the image with border
draw(false, true);
}
}
$("#editorCanvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#editorCanvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#editorCanvas").mouseup(function (e) {
handleMouseUp(e);
});
$("#editorCanvas").mouseout(function (e) {
handleMouseOut(e);
});
function rotate(rotationPointX,rotationPointY,degreeRotation){
// Create an second in-memory canvas:
var mCanvas=document.createElement('canvas');
mCanvas.width=canvas.width;
mCanvas.height=canvas.height;
var mctx=mCanvas.getContext('2d');
// Draw your canvas onto the second canvas
mctx.drawImage(canvas,0,0);
// Clear your main canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// Rotate the main canvas
// set the rotation point as center of the canvas
// (but you can set any rotation point you desire)
ctx.translate(rotationPointX,rotationPointY);
// rotate by 90 degrees (==PI/2)
var radians=degreeRotation/180*Math.PI;
ctx.rotate(radians);
// Draw the second canvas back to the (now rotated) main canvas:
ctx.drawImage(mCanvas,-canvas.width/2,-canvas.height/2);
// clean up -- unrotate and untranslate
ctx.rotate(-radians);
ctx.translate(-canvas.width/2,-canvas.height/2);
}
$('#rotate').click(function(){
rotate(canvas.width/2,canvas.height/2,90);
});
I presume there's a way of doing it, i'm just not entirely sure what would need changing or what's wrong in the code for it.
So my question is how do I change my code to allow for proportional scaling?
I actually managed to fix this by editing the maths in the handleMouseMove(e) function like so:
// resize the image
switch (draggingResizer) {
case 0:
//top-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageY = mouseY;
imageHeight = imageBottom - mouseY;
break;
case 1:
//top-right
imageY = mouseY;
imageWidth = mouseX - imageX;
imageHeight = imageWidth/ratio; //imageBottom - mouseY;
break;
case 2:
//bottom-right
imageWidth = mouseX - imageX;
imageHeight = mouseY - imageY;
break;
case 3:
//bottom-left
imageX = mouseX;
imageWidth = imageRight - mouseX;
imageHeight = mouseY - imageY;
break;
}
The place before I commented the code is the fix - this ensures the imageHeight is proportional to the imageWidth by using the initially calculated ratio.

Categories

Resources