recently I started playing with canvas element. Now I am able to draw lines(as many as I wish) on a canvas with mouse. You can see it here in the code: https://jsfiddle.net/saipavan579/a6L3ka8p/.
var ctx = tempcanvas.getContext('2d'),
mainctx = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height,
x1,
y1,
isDown = false;
tempcanvas.onmousedown = function(e) {
var pos = getPosition(e, canvas);
x1 = pos.x;
y1 = pos.y;
isDown = true;
}
tempcanvas.onmouseup = function() {
isDown = false;
mainctx.drawImage(tempcanvas, 0, 0);
ctx.clearRect(0, 0, w, h);
}
tempcanvas.onmousemove = function(e) {
if (!isDown) return;
var pos = getPosition(e, canvas);
x2 = pos.x;
y2 = pos.y;
ctx.clearRect(0, 0, w, h);
drawEllipse(x1, y1, x2, y2);
}
function drawEllipse(x1, y1, x2, y2) {
var radiusX = (x2 - x1) * 0.5,
radiusY = (y2 - y1) * 0.5,
centerX = x1 + radiusX,
centerY = y1 + radiusY,
step = 0.01,
a = step,
pi2 = Math.PI * 2 - step;
ctx.beginPath();
ctx.moveTo(x1,y1);
for(; a < pi2; a += step) {
ctx.lineTo(x2,y2);
}
ctx.closePath();
ctx.strokeStyle = '#000';
ctx.stroke();
}
function getPosition(e, gCanvasElement) {
var x;
var y;
x = e.pageX;
y = e.pageY;
x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;
return {x:x, y:y};
};
Now I want to draw arrow headed lines(for pointing to some specific point on a image) in the same way as I am drawing the lines. How can do that? Thank you in advance.
You can draw an arrowhead at the end of line segment [p0,p1] like this:
calculate the angle from p0 to p1 using Math.atan2.
Each side of the arrowhead starts at p1, so calculate the 2 arrow endpoints using trigonometry.
draw the [p0,p1] line segment and the 2 arrowhead line segments.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var p0={x:50,y:100};
var p1={x:250,y:50};
drawLineWithArrowhead(p0,p1,15);
function drawLineWithArrowhead(p0,p1,headLength){
// constants (could be declared as globals outside this function)
var PI=Math.PI;
var degreesInRadians225=225*PI/180;
var degreesInRadians135=135*PI/180;
// calc the angle of the line
var dx=p1.x-p0.x;
var dy=p1.y-p0.y;
var angle=Math.atan2(dy,dx);
// calc arrowhead points
var x225=p1.x+headLength*Math.cos(angle+degreesInRadians225);
var y225=p1.y+headLength*Math.sin(angle+degreesInRadians225);
var x135=p1.x+headLength*Math.cos(angle+degreesInRadians135);
var y135=p1.y+headLength*Math.sin(angle+degreesInRadians135);
// draw line plus arrowhead
ctx.beginPath();
// draw the line from p0 to p1
ctx.moveTo(p0.x,p0.y);
ctx.lineTo(p1.x,p1.y);
// draw partial arrowhead at 225 degrees
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(x225,y225);
// draw partial arrowhead at 135 degrees
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(x135,y135);
// stroke the line and arrowhead
ctx.stroke();
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>
Related
When I wrote this script to calculate the angle from 2 points (I know I can use atan2, but experiments and math learning), I got some weird results when I combined it with a triangle function I wrote. It suddenly turns around when the halfway point on the x axis is passed with the mouse, and as I don't know much about trigonometery I didn't know how to fix this issue... Here's my code:
const can = document.getElementById('can');
const ctx = can.getContext('2d');
can.width = innerWidth;
can.height = innerHeight;
var length = 200;
var origin = {x: can.width / 2, y: can.height / 2};
var mousey;
var mousex;
function triangle(x1, y1, x2, y2){
ctx.strokeStyle = "blue";
ctx.lineWidth = "2";
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(x2, y2 + (y1 - y2));
ctx.strokeStyle = "#33cc33";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2, y2 + (y1 - y2));
ctx.lineTo(x1, y1);
ctx.strokeStyle = "red";
ctx.stroke();
}
(function loop(){
ctx.clearRect(0, 0, can.width, can.height);
calc2(mousex, mousey);
requestAnimationFrame(loop);
})();
function update(){
calc2(mousex, mousey);
}
function calc2(x1, y1){
let x2 = origin.x;
let y2 = origin.y;
let opposite = y1 - y2;
let hypotenuse = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
let a = Math.asin(opposite / hypotenuse);
triangle(x2, y2, x2 + Math.cos(a) * length, y2 + Math.sin(a) * length);
}
addEventListener('mousemove', (e)=>{
mousey = e.clientY;
mousex = e.clientX;
});
<canvas id="can" width="400" height="400"></canvas>
Thanks in advance!
In function calc2(x1, y1), variable 'hypotenuse' is always a non-negative number, it can't tell if your mouse is on the left or right side. You need a boolean to store this.
The range of Math.asin() is from -pi/2 to pi/2 (-90 degrees to 90 degrees). Cos() this angle is always greater or equal to 0. In your calculations, the -90 deg relative to the horizontal red line is a straight downward line. Rotating counterclockwise from there, -45 deg is a line to lower right corner, 0 deg is the red line itself, 45 deg to upper right corner, 90 deg is straight upward. That means all possible blue lines are from starting point to the right.
The y-axis is correct. To let cos(x) produce a negative number, x should be pi/2 to pi (90 deg to 180 deg). As cos(pi-x) == -cos(x), we will flip this angle when mouse is on the left side.
const can = document.getElementById('can');
const ctx = can.getContext('2d');
can.width = innerWidth;
can.height = innerHeight;
var length = 200;
var origin = {x: can.width / 2, y: can.height / 2};
var mousey;
var mousex;
function triangle(x1, y1, x2, y2){
ctx.strokeStyle = "blue";
ctx.lineWidth = "2";
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(x2, y2 + (y1 - y2));
ctx.strokeStyle = "#33cc33";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x2, y2 + (y1 - y2));
ctx.lineTo(x1, y1);
ctx.strokeStyle = "red";
ctx.stroke();
}
(function loop(){
ctx.clearRect(0, 0, can.width, can.height);
calc2(mousex, mousey);
requestAnimationFrame(loop);
})();
function update(){
calc2(mousex, mousey);
}
function calc2(x1, y1){
let x2 = origin.x;
let y2 = origin.y;
let opposite = y1 - y2;
let hypotenuse = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
let onLeft = x1 < x2 ? true : false;
let a = Math.asin(opposite / hypotenuse);
triangle(x2, y2, x2 + Math.cos(onLeft ? Math.PI - a : a) * length, y2 + Math.sin(a) * length)
}
addEventListener('mousemove', (e)=>{
mousey = e.clientY;
mousex = e.clientX;
});
<canvas id="can" width="400" height="400"></canvas>
I need to create a pattern where 5 circles connected by lines to a middle main circle.
So I have created dynamically by rotating in some certain angle. Now I need each and every circle's x and y axis coordinates for capturing the click events on every circle.
Please help me how to find out of coordinates of every circle?
var canvas, ctx;
function createCanvasPainting() {
canvas = document.getElementById('myCanvas');
if (!canvas || !canvas.getContext) {
return false;
}
canvas.width = 600;
canvas.height = 600;
ctx = canvas.getContext('2d');
ctx.strokeStyle = '#B8D9FE';
ctx.fillStyle = '#B8D9FE';
ctx.translate(300, 250);
ctx.arc(0, 0, 50, 0, Math.PI * 2); //center circle
ctx.stroke();
ctx.fill();
drawChildCircles(5);
fillTextMultiLine('Test Data', 0, 0);
drawTextInsideCircles(5);
}
function drawTextInsideCircles(n) {
let ang_unit = Math.PI * 2 / n;
ctx.save();
for (var i = 0; i < n; i++) {
ctx.rotate(ang_unit);
//ctx.moveTo(0,0);
fillTextMultiLine('Test Data', 200, 0);
ctx.strokeStyle = '#B8D9FE';
ctx.fillStyle = '#B8D9FE';
}
ctx.restore();
}
function drawChildCircles(n) {
let ang_unit = Math.PI * 2 / n;
ctx.save();
for (var i = 0; i < n; ++i) {
ctx.rotate(ang_unit);
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(100,0);
ctx.arc(200, 0, 40, 0, Math.PI * 2);
let newW = ctx.fill();
ctx.stroke();
}
ctx.restore();
}
function fillTextMultiLine(text, x, y) {
ctx.font = 'bold 13pt Calibri';
ctx.textAlign = 'center';
ctx.fillStyle = "#FFFFFF";
// Defining the `textBaseline`…
ctx.textBaseline = "middle";
var lineHeight = ctx.measureText("M").width * 1.2;
var lines = text.split("\n");
for (var i = 0; i < lines.length; ++i) {
// console.log(lines);
if (lines.length > 1) {
if (i == 0) {
y -= lineHeight;
} else {
y += lineHeight;
}
}
ctx.fillText(lines[i], x, y);
}
}
createCanvasPainting();
<canvas id="myCanvas"></canvas>
The problem here is that you are rotating the canvas matrix and your circles are not aware of their absolute positions.
Why don't you use some simple trigonometry to determine the center of your circle and the ending of the connecting lines ?
function lineToAngle(ctx, x1, y1, length, angle) {
angle *= Math.PI / 180;
var x2 = x1 + length * Math.cos(angle),
y2 = y1 + length * Math.sin(angle);
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
return {x: x2, y: y2};
}
Ref: Finding coordinates after canvas Rotation
After that, given the xy center of your circles, calculating if a coord is inside a circle, you can apply the following formula:
Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)) < r
Ref: Detect if user clicks inside a circle
I want to create a collision region around a canvas element that enables me to interact with that element using mouse events width vanilla javascript.
To elaborate more on my problem here is the following:
at first I make an arc segment constructor with x, y, radius, beginAngle, endAngle, and a color arguments
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
/* arc class constructor */
function ArcSegment(x, y, radius, beginAngle, endAngle, segColor) {
this.x = x;
this.y = y;
this.radius = radius;
this.beginAngle = beginAngle;
this.endAngle = endAngle;
this.segColor = segColor;
this.update = function() {
this.draw();
}
this.draw = function(){
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.beginAngle, this.endAngle, false);
ctx.lineWidth = 20;
ctx.strokeStyle = this.segColor;
ctx.stroke();
}
}
Secondly, i add some value to create those arc segments
/* x, y, radius, startAngle, endAngle and color */
var centerX = canvas.width/2;
var centerY = canvas.height/2;
var radiuses = [
100,
120
];
var pi = Math.PI;
var segmentStart = [
pi/2,
0
];
var segmentRotation = [
1.4*pi,
0.2*pi
];
var segmentColors = [
"#133046",
"#15959F"
];
Then, i draw Them on the canvas.
var segment1 = new ArcSegment(centerX, centerY, radiuses[0], segmentStart[0], segmentStart[0]+segmentRotation[0], segmentColors[0]);
segment1.update();
var segment2 = new ArcSegment(centerX, centerY, radiuses[1], segmentStart[1], segmentStart[1]+segmentRotation[1], segmentColors[1]);
segment2.update();
and here is the result:
What i want now is a way to create a collision detection on top of each arc segment created, so when a mouse is clicked or moved on top of that specific arc segment
a sequence of events can occur (like a rotation animation for example or so...).
all the research i've done suggest to get the x and y value of a rectangle and calculate the distance of mouse position (mouse.x, mouse.y) and the length of the rectangle, but that method doesn't work with an arc segment with a lineWidth property.
Any help on the subject would be very appreciated.
Below is a pure mathematical approach, the key here is the code isPointInside
// Classes
function Arc(x, y, angle, arc, radius, colour, highlightColour) {
this.x = x;
this.y = y;
this.angle = angle;
this.arc = arc;
this.radius = radius;
this.colour = colour;
this.highlightColour = highlightColour;
this.highlighted = false;
this.lineWidth = 20;
}
Arc.prototype = {
isPointInside: function(x, y) {
var _x = x - this.x;
var _y = y - this.y;
var distance = Math.sqrt(_x * _x + _y * _y);
var invDistance = 1.0 / distance;
var angle = Math.acos(
_x * Math.cos(this.angle) * invDistance +
_y * Math.sin(this.angle) * invDistance
);
return distance > (this.radius - this.lineWidth/2) &&
distance < (this.radius + this.lineWidth/2) &&
angle < this.arc/2;
},
render: function(ctx) {
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.highlighted ? this.highlightColour : this.colour;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.angle - this.arc/2, this.angle + this.arc/2, false );
ctx.stroke();
}
};
// Variables
var canvas = null;
var ctx = null;
var arcs = [];
// Functions
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(0, 0, 999, 999);
for (var i = 0; i < arcs.length; ++i) {
arcs[i].render(ctx);
}
}
// Event Listeners
function onMouseMove(e) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
for (var i = 0; i < arcs.length; ++i) {
arcs[i].highlighted = arcs[i].isPointInside(x, y);
}
draw();
}
// Entry Point
onload = function() {
canvas = document.getElementById("canvas");
canvas.onmousemove = onMouseMove;
ctx = canvas.getContext("2d");
arcs.push(new Arc(190, 75, 0.2, 1.8, 60, "blue", "lime"));
arcs.push(new Arc(90, 75, 3.5, 4.2, 60, "red", "lime"));
draw();
}
<canvas id="canvas"></canvas>
I want to draw a triangle base on the intersection points of 3 circles. Is there any function or any method with JavaScript to solve this? Or I need to perform some math calculation to get exact position of the intersection point and draw the triangle myself.
function drawMap(){
var ctx = $('#map')[0].getContext("2d");
// Draw the map of my room 400cm * 300cm
ctx.fillStyle = "#ecf0f1"
ctx.beginPath();
ctx.rect(0, 0, 300, 400);
ctx.closePath();
ctx.fill();
//Draw the first circle (blue one)
ctx.fillStyle = "rgba(52, 152, 219,0.5)";
ctx.beginPath();
ctx.arc(0, 0, 200, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
//Draw the second circle(green one)
ctx.fillStyle = "rgba(46, 204, 113,0.5)";
ctx.beginPath();
ctx.arc(0, 400, 250, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
// Draw the third circle (yellow one)
ctx.fillStyle = "rgba(241, 196, 15,0.5)";
ctx.beginPath();
ctx.arc(300, 200, 280, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
Finding the intersection points which are on the circumference of all three circles
Here's how to do it:
Define 3 circles:
var A={x:0,y:0,r:200,color:"rgba(52, 152, 219,0.5)"};
var B={x:0,y:400,r:250,color:"rgba(46, 204, 113,0.5)"};
var C={x:300,y:200,r:280,color:"rgba(241, 196, 15,0.5)"};
Calculate the intersection points of the 3 circles vs each other (AB,BC,CA):
var intersections=[];
var AB=circleIntersections(A,B); // see example code below for circleIntersections()
var BC=circleIntersections(B,C);
var CA=circleIntersections(C,A);
if(AB){intersections.push(AB);}
if(BC){intersections.push(BC);}
if(CA){intersections.push(CA);}
Test each intersection point. Keep any intersection points that are in all 3 circles.
var triangle=[];
for(var i=0;i<intersections.length;i++){
var pt=intersections[i];
if(ptIsInCircle(pt,A) && ptIsInCircle(pt,B) && ptIsInCircle(pt,C)){
triangle.push(pt);
}
}
In your example code you are left with 3 intersection points that are also in all 3 circles.
But with circles positioned elsewhere you might get fewer or more than 3 intersection points.
Use context path commands to draw a polyline between the 3 discovered points.
if(triangle.length==3){
ctx.beginPath();
ctx.moveTo(triangle[0].x,triangle[0].y);
ctx.lineTo(triangle[1].x,triangle[1].y);
ctx.lineTo(triangle[2].x,triangle[2].y);
ctx.closePath();
ctx.stroke();
}
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
var A={x:0,y:0,r:200,color:"rgba(52, 152, 219,0.5)"};
var B={x:0,y:400,r:250,color:"rgba(46, 204, 113,0.5)"};
var C={x:300,y:200,r:280,color:"rgba(241, 196, 15,0.5)"};
var intersections=[];
var AB=circleIntersections(A,B);
var BC=circleIntersections(B,C);
var CA=circleIntersections(C,A);
if(AB){intersections=intersections.concat(AB);}
if(BC){intersections=intersections.concat(BC);}
if(CA){intersections=intersections.concat(CA);}
var triangle=[];
for(var i=0;i<intersections.length;i++){
var pt=intersections[i];
if(ptIsInCircle(pt,A) && ptIsInCircle(pt,B) && ptIsInCircle(pt,C)){
triangle.push(pt);
}
}
drawMap();
if(triangle.length==3){
ctx.beginPath();
ctx.moveTo(triangle[0].x,triangle[0].y);
ctx.lineTo(triangle[1].x,triangle[1].y);
ctx.lineTo(triangle[2].x,triangle[2].y);
ctx.closePath();
ctx.stroke();
}
function drawMap(){
// Draw the map of my room 400cm * 300cm
ctx.fillStyle = "#ecf0f1"
ctx.beginPath();
ctx.rect(0, 0, 300, 400);
ctx.closePath();
ctx.fill();
drawCircle(A);
drawCircle(B);
drawCircle(C);
}
function drawCircle(c){
ctx.fillStyle = c.color;
ctx.beginPath();
ctx.arc(c.x,c.y,c.r, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
// intersection points of 2 circles
function circleIntersections(c0,c1) {
var x0=c0.x;
var y0=c0.y;
var r0=c0.r;
var x1=c1.x;
var y1=c1.y;
var r1=c1.r;
// calc circles' proximity
var dx = x1 - x0;
var dy = y1 - y0;
var d = Math.sqrt((dy*dy) + (dx*dx));
// return if circles do not intersect.
if (d > (r0 + r1)) { return; }
// return if one circle is contained in the other
if (d < Math.abs(r0 - r1)) { return; }
// calc the 2 intersection points
var a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
var x2 = x0 + (dx * a/d);
var y2 = y0 + (dy * a/d);
var h = Math.sqrt((r0*r0) - (a*a));
var rx = -dy * (h/d);
var ry = dx * (h/d);
var xi = x2 + rx;
var xi_prime = x2 - rx;
var yi = y2 + ry;
var yi_prime = y2 - ry;
return([ {x:xi,y:yi}, {x:xi_prime,y:yi_prime} ]);
}
function ptIsInCircle(pt,circle){
var dx=pt.x-circle.x;
var dy=pt.y-circle.y;
var r=circle.r+1; // allow circle 1px expansion for rounding
return(dx*dx+dy*dy<=r*r);
}
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
If the centre of the circle is at (x1, y1) and radius is r then the equation of the circle is
(x - x1)^2 + (y - y1)^2 = r^2 (where ^ denotes exponentiation)
For three circles, you will get three equations. Now you have to solve pairwise the equations and get the coordinates.
Note that any two circle, if they intersect, will intersect at two points or meet at a single point. So you will need to define exactly which points of intersection will be used to draw the triangle.
Look at this answer here.
I want something like ellipse option in paint for drawing on my canvas. I have achieved this partially. The problem is I am not able to get radius of circle; currently I have hard coded it to 15. Also I want to draw an ellipse (like in paint) not exact circle.
This is my code for drawing circle on canvas using mouse events. Please help me with code to achieve my above mentioned requirements.
function tool_circle() {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.fillStyle = 'red';
var distance = Math.sqrt(Math.pow(tool.x0 - ev._x, 2) + Math.pow(tool.y0 - ev._y));
context.beginPath();
context.arc(tool.x0, tool.y0,15, 0, Math.PI * 2, false);
context.stroke();
context.fill();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
img_update();
}
};
}
I would something similar as with markE's answer however, using Bezier curve will draw ellipses but it won't give you the exact radius that you probably would need.
For that a function to draw a manual ellipse is needed, and it's rather simple -
This function will take a corner start point and and end point and draw an ellipse exactly within that boundary:
Live demo
function drawEllipse(x1, y1, x2, y2) {
var radiusX = (x2 - x1) * 0.5, /// radius for x based on input
radiusY = (y2 - y1) * 0.5, /// radius for y based on input
centerX = x1 + radiusX, /// calc center
centerY = y1 + radiusY,
step = 0.01, /// resolution of ellipse
a = step, /// counter
pi2 = Math.PI * 2 - step; /// end angle
/// start a new path
ctx.beginPath();
/// set start point at angle 0
ctx.moveTo(centerX + radiusX * Math.cos(0),
centerY + radiusY * Math.sin(0));
/// create the ellipse
for(; a < pi2; a += step) {
ctx.lineTo(centerX + radiusX * Math.cos(a),
centerY + radiusY * Math.sin(a));
}
/// close it and stroke it for demo
ctx.closePath();
ctx.strokeStyle = '#000';
ctx.stroke();
}
The demo marks the rectangle area too to show that the ellipse is exactly within it.
Draw
To handle mouse operation that will let you draw the ellipse you can do:
var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height,
x1, /// start points
y1,
isDown = false; /// if mouse button is down
/// handle mouse down
canvas.onmousedown = function(e) {
/// get corrected mouse position and store as first point
var rect = canvas.getBoundingClientRect();
x1 = e.clientX - rect.left;
y1 = e.clientY - rect.top;
isDown = true;
}
/// clear isDown flag to stop drawing
canvas.onmouseup = function() {
isDown = false;
}
/// draw ellipse from start point
canvas.onmousemove = function(e) {
if (!isDown) return;
var rect = canvas.getBoundingClientRect(),
x2 = e.clientX - rect.left,
y2 = e.clientY - rect.top;
/// clear canvas
ctx.clearRect(0, 0, w, h);
/// draw ellipse
drawEllipse(x1, y1, x2, y2);
}
A tip can be to create a top canvas on top of your main canvas and do the drawing itself there. When mouse button is released then transfer the drawing to your main canvas. This way you don't have to redraw everything when drawing a new shape.
Hope this helps!
Here's an example of how to drag-draw an oval.
Demo: http://jsfiddle.net/m1erickson/3SFJy/
Example code using 2 Bezier curves to drag-draw an oval:
<!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:0px;}
#canvas{ border:1px solid blue; }
</style>
<script>
$(function(){
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;
function drawOval(x,y){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.moveTo(startX, startY + (y-startY)/2);
ctx.bezierCurveTo(startX, startY, x, startY, x, startY + (y-startY)/2);
ctx.bezierCurveTo(x, y, startX, y, startX, startY + (y-startY)/2);
ctx.closePath();
ctx.stroke();
}
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
isDown=true;
}
function handleMouseUp(e){
if(!isDown){ return; }
e.preventDefault();
e.stopPropagation();
isDown=false;
}
function handleMouseOut(e){
if(!isDown){ return; }
e.preventDefault();
e.stopPropagation();
isDown=false;
}
function handleMouseMove(e){
if(!isDown){ return; }
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
drawOval(mouseX,mouseY);
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag to create a circle or oval</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Here's my way of drawing an ellipse onto a canvas with mouse drag.
It uses radius of 1, but dynamic scaling to get the ellipse effect :)
https://jsfiddle.net/richardcwc/wdf9cocz/
//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var scribble_canvasx = $(canvas).offset().left;
var scribble_canvasy = $(canvas).offset().top;
var scribble_last_mousex = scribble_last_mousey = 0;
var scribble_mousex = scribble_mousey = 0;
var scribble_mousedown = false;
//Mousedown
$(canvas).on('mousedown', function(e) {
scribble_last_mousex = parseInt(e.clientX-scribble_canvasx);
scribble_last_mousey = parseInt(e.clientY-scribble_canvasy);
scribble_mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function(e) {
scribble_mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function(e) {
scribble_mousex = parseInt(e.clientX-scribble_canvasx);
scribble_mousey = parseInt(e.clientY-scribble_canvasy);
if(scribble_mousedown) {
ctx.clearRect(0,0,canvas.width,canvas.height); //clear canvas
//Save
ctx.save();
ctx.beginPath();
//Dynamic scaling
var scalex = 1*((scribble_mousex-scribble_last_mousex)/2);
var scaley = 1*((scribble_mousey-scribble_last_mousey)/2);
ctx.scale(scalex,scaley);
//Create ellipse
var centerx = (scribble_last_mousex/scalex)+1;
var centery = (scribble_last_mousey/scaley)+1;
ctx.arc(centerx, centery, 1, 0, 2*Math.PI);
//Restore and draw
ctx.restore();
ctx.strokeStyle = 'black';
ctx.lineWidth = 5;
ctx.stroke();
}
//Output
$('#output').html('current: '+scribble_mousex+', '+scribble_mousey+'<br/>last: '+scribble_last_mousex+', '+scribble_last_mousey+'<br/>mousedown: '+scribble_mousedown);
});
canvas {
cursor: crosshair;
border: 1px solid #000000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="800" height="500"></canvas>
<div id="output"></div>