I'm trying to create a simple, purely JS program for drawing in a canvas. I have an acceptable solution right now, but if I draw very quickly, my drawing pen isn't continuous and I get scattered circles, probably because the computer can't keep up with the pace.
var draw = false;
function yesDraw() {
draw = true;
}
function mouseCoordinates(e) {
if (draw) {
var x = e.offsetX;
var y = e.offsetY;
drawing(x, y);
}
}
function noDraw() {
draw = false;
}
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function drawing(x, y) {
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.fillStyle = "black";
ctx.fill();
}
<canvas id="myCanvas" height="400" ; width="1000" onmousedown="yesDraw()" onmousemove="mouseCoordinates(event)" onmouseup="noDraw()" onmouseout="noDraw()" style="border: solid 1px black;">Your browser does not support canvas.</canvas>
Is there any way to get a continuous flow of drawing and still keep it 100% JS?
Line segments from previous mouse position.
Rather than draw arcs draw line segments from the previous mouse position to the new one. As you don't want the line to be drawn from the end of the previous draw, you also need to indicate when a new line starts and set the previous mouse position to the current mouse.
To do this I added 3 variables lineStart, lastX,and lastY
lineStart is set to true when the mouse down event fires. In the draw function if line start is true then the lastX and lastY are set to the x,y and lineStart is set to false.
lastX, lastY hold the previous mouse position. They are set to x,y at the end of every draw call.
The line segments need to have the 2D context properties ctx.lineWidth set to the line width. To ensure the line is continuous the ctx.lineCap property is set to "round". This adds a semi circle to the start and end of the line.
Drawing past the edge
Having the pen turn off when the mouse moves out is annoying, you do this because if you don't you lose the mouse up events and the mouse keeps drawing with the mouse button up.
If you add the mouse to the document rather than the canvas, you don't have to worry about the mouse going outside the frame. You will still get the mouse up events even if the mouse is completely of the tab and browser.
Though you will have to use a slightly different way of getting the mouse coordinates, as you will want to still draw while off the canvas by at least half the draw line width. See code on how to get the mouse coordinates.
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
const r = 10; // draw radius
ctx.lineWidth = r * 2;
ctx.lineCap = "round";
ctx.fillStyle = "black";
var draw = false;
var lineStart = true;
var lastX, lastY;
function yesDraw() { draw = true; lineStart = true }
function mouseMove(e) {
const bounds = c.getBoundingClientRect();
const x = e.pageX - bounds.left - scrollX;
const y = e.pageY - bounds.top - scrollY;
if(draw && x > -r && x < c.width + r && y > -r && y < c.height + r){
drawing(x,y);
}
}
function noDraw() { draw = false }
document.addEventListener("mousemove",mouseMove);
document.addEventListener("mousedown",yesDraw);
document.addEventListener("mouseup",noDraw);
function drawing(x, y) {
if(lineStart){
lastX = x;
lastY = y;
lineStart = false;
}
ctx.beginPath();
ctx.lineTo(lastX, lastY);
ctx.lineTo(x, y);
ctx.stroke();
lastX = x;
lastY = y;
}
<canvas id="myCanvas" height="400" ; width="1000" style="border: solid 1px black;">Your browser does not support canvas.</canvas>
Use lineTo and end each line in an arc to make continuous smooth lines.
//Create Canvas
//(You can do this in HTML)
var c = document.body.appendChild(document.createElement("canvas"));
c.height = 400;
c.width = 1000;
var ctx = c.getContext("2d");
ctx.lineWidth = 20;
//Control drawing variable
var drawing = false;
c.onmousedown = c.onmouseout = c.onmouseup = function(evt) {
drawing = (evt.type === "mousedown");
};
//Keep track of last position
var oldX = null;
var oldY = null;
/**
* Draw the latest line
*
* #param {number} x
* #param {number} y
*/
function draw(x, y) {
if (drawing) {
if (oldX !== null) {
//If drawing, move to old coordinates for continual line
ctx.beginPath();
ctx.moveTo(oldX, oldY);
} else {
//Else simply move to current coordinates
ctx.moveTo(x, y);
ctx.beginPath();
}
//Draw a line
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
//Add an arc to the end of the line to make it smooth
ctx.beginPath();
ctx.arc(x, y, 0, 0, Math.PI * 2);
ctx.closePath();
ctx.stroke();
//Save new coordinates as old for next draw cycle
oldX = x;
oldY = y;
} else {
//If not drawing, cut line byt setting "old" to null
oldX = null;
oldY = null;
}
}
//Bind drawing
c.onmousemove = function(evt) {
draw(evt.offsetX, evt.offsetY);
};
canvas {
border: solid 1px black;
background-color: #eee;
}
Related
I am trying to make a highlighter,
The problem is with the transparency, maybe due to the lineCap=round, and some other reason there are many dark spots in just one line currently(it should not be). I can't explain more in words,compare the images below
Current look(when I draw 1 line):
The look I want when I draw 1 line:
The look I want when I draw 2 lines:
Gif
Current code:
lineThickess = 50;
lineColor = 'rgba(255,255,0,0.1)';
// wait for the content of the window element
// to load, then performs the operations.
// This is considered best practice.
window.addEventListener('load', () => {
resize(); // Resizes the canvas once the window loads
document.addEventListener('mousedown', startPainting);
document.addEventListener('mouseup', stopPainting);
document.addEventListener('mousemove', sketch);
window.addEventListener('resize', resize);
});
const canvas = document.querySelector('#canvas');
// Context for the canvas for 2 dimensional operations
const ctx = canvas.getContext('2d');
// Resizes the canvas to the available size of the window.
function resize() {
ctx.canvas.width = canvas.offsetWidth;
ctx.canvas.height = canvas.offsetHeight;
}
// Stores the initial position of the cursor
let coord = {
x: 0,
y: 0
};
// This is the flag that we are going to use to
// trigger drawing
let paint = false;
// Updates the coordianates of the cursor when
// an event e is triggered to the coordinates where
// the said event is triggered.
function getPosition(event) {
coord.x = event.clientX - canvas.offsetLeft;
coord.y = event.clientY - canvas.offsetTop;
}
// The following functions toggle the flag to start
// and stop drawing
function startPainting(event) {
paint = true;
getPosition(event);
}
function stopPainting() {
paint = false;
}
function sketch(event) {
if (!paint) return;
ctx.beginPath();
ctx.lineWidth = lineThickess;
// Sets the end of the lines drawn
// to a round shape.
ctx.lineCap = 'round';
ctx.strokeStyle = lineColor;
// The cursor to start drawing
// moves to this coordinate
ctx.moveTo(coord.x, coord.y);
// The position of the cursor
// gets updated as we move the
// mouse around.
getPosition(event);
// A line is traced from start
// coordinate to this coordinate
ctx.lineTo(coord.x, coord.y);
// Draws the line.
ctx.stroke();
}
html,
body {
height: 100%;
}
canvas {
width: 100%;
height: 100%;
border: 1px solid;
}
<canvas id="canvas"></canvas>
The clue
Stack overflow recommended me reviewing other posts related, and I found a clue,and code
But
As shown (and said in the answer), two different paths work well, but loops on same paths don't work as needed
The issue you are seeing is because the intersection of two lines with transparency the result is not the same transparent color, just like in the real world if you hold two sunglasses on top of each other the resulting color is not the same as the individual ones.
What you should do is use Path2D to get the same transparency over the entire object, it could be lines, arches rectangles or any other items, if they are under a common Path2D they all get the same color even on intersections.
Below is a very simple example based on your code
This is how it looks like when the lines cross:
let path = new Path2D()
document.addEventListener('mousemove', sketch)
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
ctx.lineWidth = 10
ctx.lineCap = 'round'
ctx.strokeStyle = 'rgba(255,0,255,0.3)'
function sketch(event) {
let x = event.clientX - canvas.offsetLeft
let y = event.clientY - canvas.offsetTop
path.lineTo(x, y)
}
function drawCircles() {
for (let i = 25; i<=100; i+=25)
ctx.arc(i*2, i, 5, 0, 8)
ctx.fill()
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
drawCircles()
ctx.stroke(path)
requestAnimationFrame(draw)
}
draw()
<canvas id="canvas" width=300 height=150></canvas>
Another example, a bit more complex this time, using an array of Path2D objects, and added a button to change colors, the creation of the color add a new item to the array:
let paths = []
paths.unshift({path: new Path2D(), color:'rgba(255,0,255,0.3)'})
document.addEventListener('mousemove', sketch)
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
ctx.lineWidth = 10
ctx.lineCap = 'round'
function changeColor() {
let newColor = 'rgba(0,255,255,0.3)'
if (paths[0].color == newColor)
newColor = 'rgba(255,0,255,0.3)'
paths.unshift({path:new Path2D(), color: newColor})
}
function sketch(event) {
let x = event.clientX - canvas.offsetLeft
let y = event.clientY - canvas.offsetTop
paths[0].path.lineTo(x, y)
}
function drawCircles() {
for (let i = 25; i<=100; i+=25)
ctx.arc(i*2, i, 5, 0, 8)
ctx.fill()
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
drawCircles()
paths.forEach(e => {
ctx.strokeStyle = e.color
ctx.stroke(e.path)
})
requestAnimationFrame(draw)
}
draw()
<canvas id="canvas" width=250 height=150></canvas>
<button onclick="changeColor()">Change Color</button>
This is how it looks like after changing colors a few times:
Now you have all the tools you need...
It's up to you to decide what is the end of a path and the start of a new one.
Am simulating a shape click and trying to do drag/drop inside canvas using Sahi framework.I have found some references that by looping through an array containing that shapes position/sizes , we get whether shape is clicked.While testing other website using sahi framework/native jaascript , how can it be achieved ? I wanted to select that shape inside canvas and do drag and drop inside canvas.This is my goal.
In html5 canvas, A "Shape" is defined and drawn using a Path.
You define a shape by:
Declaring that you a beginning a new path: context.beginPath
Using one or more of the path definition commands: moveTo, lineTo, arc, etc.
// define a triangle path
context.beginPath();
context.moveTo(x+50,y+50);
context.lineTo(x+100,y+100);
context.lineTo(x+0,y+100);
// see not below about .closePath()
context.closePath();
Note: context.closePath is NOT used to close a context.beginPath command -- it is NOT like a closing parenthesis! Instead, it is used to draw a line from the last coordinate to the starting coordinate -- to "enclose" the path. In our example, it draws an "enclosing" line from [x+0,y+100] to [x+50,y+50].
Just defining a path will not cause it to be drawn onto the canvas. To actually draw the shape onto the canvas you can:
Stroke the outline of the path, and / or
Fill the inside of the path.
context.stroke();
context.fill();
For example, here's how to define and draw a triangle. You can also use offset variables ([x,y] in the example) to reposition the triangle anywhere on the canvas.
// set the offset of the triangle
var x=30;
var y=40;
// define the path
context.beginPath();
context.moveTo(x+50,y+50);
context.lineTo(x+100,y+100);
context.lineTo(x+0,y+100);
context.closePath();
// stroke the path
context.stroke();
// if desired, you can also fill the inside of the path
context.fill();
To drag a shape, you must test whether the mouse is over that shape. You can "hit-test" the most recently defined shape using context.isPointInPath.
Be sure you read that carefully!
You can hit-test the most recently "defined" path. If you define and draw multiple paths then isPointInPath will only hit-test the last defined path.
if(context.isPointInPath(mouseX,mouseY)){
console.log('Yes, the mouse is in the triangle.');
}
Also note that you don't have to re-stroke the path being tested so your drawings won't be altered by the hit-testing process. So you hit-test multiple paths by:
Define one of the paths
Hit test with isPointInPath(mouseX,mouseY)
Repeat step#1 for the next path (path==shape)
Nothing drawn to the canvas can be moved -- everything is as permanent as dried paint. So to "move" a shape on canvas you clear the entire canvas and redraw the shape in its moved position:
// clear the canvas
context.clearRect(canvas.width,canvas.height);
// move the canvas by changing it's offsets
x+=20;
y+=30;
// redefine and restroke the shape
context.beginPath();
context.moveTo(x+50,y+50);
context.lineTo(x+100,y+100);
context.lineTo(x+0,y+100);
context.closePath();
context.stroke();
To make redefining and re-stroking the shape more reusable you can put the code in a function:
function myTriangle(alsoStroke){
context.beginPath();
context.moveTo(x+50,y+50);
context.lineTo(x+100,y+100);
context.lineTo(x+0,y+100);
context.closePath();
if(alsoStroke){
context.stroke();
}
}
You can read more about dragging a shape in this previous post. Since you can't move a shape you can't "drag" a shape either. You must again clear the canvas and redraw it in its newly dragged position.
To drag a shape you need to listen to 4 mouse events.
In mousedown: Check if the mouse is over the shape, and, if yes, set a flag indicating a drag has begun. To check if the mouse is over the shape, you can use canvas context's isPointInPath method which tests if an [x,y] point is inside the most recently drawn path.
In mousemove: If the dragging flag is set (indicating a drag is in process), change the position of the selected text by the distance the user has dragged and redraw the shape in its new position
In mouseup or in mouseout: The drag is over so clear the dragging flag.
Here's 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(); }
window.onresize=function(e){ reOffset(); }
var isDown=false;
var startX,startY;
var poly={
x:0,
y:0,
points:[{x:50,y:50},{x:75,y:25},{x:100,y:50},{x:75,y:125},{x:50,y:50}],
}
ctx.fillStyle='skyblue';
ctx.strokeStyle='gray';
ctx.lineWidth=3;
draw();
// listen to mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
function draw(){
ctx.clearRect(0,0,cw,ch);
define();
ctx.fill();
ctx.stroke()
}
function define(){
ctx.beginPath();
ctx.moveTo(poly.points[0].x+poly.x,poly.points[0].y+poly.y);
for(var i=0;i<poly.points.length;i++){
ctx.lineTo(poly.points[i].x+poly.x,poly.points[i].y+poly.y);
}
ctx.closePath();
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here
define();
if(ctx.isPointInPath(startX,startY)){
isDown=true;
}
}
function handleMouseUp(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mouseup stuff here
isDown=false;
}
function handleMouseOut(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mouseOut stuff here
isDown=false;
}
function handleMouseMove(e){
if(!isDown){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousemove stuff here
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
poly.x+=dx;
poly.y+=dy;
draw();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Drag the polygon</h4>
<canvas id="canvas" width=300 height=300></canvas>
[ Addition: Discovering Rectangles using context.getImageData ]
If you don't have the shape's position/size and you instead have an image of a shape then you must get the position/size by searching the pixels. Here's an example showing how to isolate rectangles by searching pixels:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw, ch;
// background definition
// OPTION: look at the top-left pixel and assume == background
// then set these vars automatically
var isTransparent = false;
var bkColor = {
r: 255,
g: 255,
b: 255
};
var bkFillColor = "rgb(" + bkColor.r + "," + bkColor.g + "," + bkColor.b + ")";
cw = canvas.width;
ch = canvas.height;
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawTestRect(30, 30, 50, 50, "1");
drawTestRect(100, 30, 50, 30, "2");
drawTestRect(170, 30, 30, 50, "3");
function drawTestRect(x, y, w, h, label) {
ctx.fillStyle = "black";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "white";
ctx.font = "24px verdana";
ctx.fillText(label, x + 10, y + 25);
}
function clipBox(data) {
var pos = findEdge(data);
if (!pos.valid) {
return;
}
var bb = findBoundary(pos, data);
alert("Found target at "+bb.x+"/"+bb.y+", size: "+bb.width+"/"+bb.height);
clipToImage(bb.x, bb.y, bb.width, bb.height);
if (isTransparent) {
// clear the clipped area
// plus a few pixels to clear any anti-aliasing
ctx.clearRect(bb.x - 2, bb.y - 2, bb.width + 4, bb.height + 4);
} else {
// fill the clipped area with the bkColor
// plus a few pixels to clear any anti-aliasing
ctx.fillStyle = bkFillColor;
ctx.fillRect(bb.x - 2, bb.y - 2, bb.width + 4, bb.height + 4);
}
}
function xyIsInImage(data, x, y) {
// find the starting index of the r,g,b,a of pixel x,y
var start = (y * cw + x) * 4;
if (isTransparent) {
return (data[start + 3] > 25);
} else {
var r = data[start + 0];
var g = data[start + 1];
var b = data[start + 2];
var a = data[start + 3]; // pixel alpha (opacity)
var deltaR = Math.abs(bkColor.r - r);
var deltaG = Math.abs(bkColor.g - g);
var deltaB = Math.abs(bkColor.b - b);
return (!(deltaR < 5 && deltaG < 5 && deltaB < 5 && a > 25));
}
}
function findEdge(data) {
for (var y = 0; y < ch; y++) {
for (var x = 0; x < cw; x++) {
if (xyIsInImage(data, x, y)) {
return ({
x: x,
y: y,
valid: true
});
}
}
}
return ({
x: -100,
y: -100,
valid: false
});
}
function findBoundary(pos, data) {
var x0 = x1 = pos.x;
var y0 = y1 = pos.y;
while (y1 <= ch && xyIsInImage(data, x1, y1)) {
y1++;
}
var x2 = x1;
var y2 = y1 - 1;
while (x2 <= cw && xyIsInImage(data, x2, y2)) {
x2++;
}
return ({
x: x0,
y: y0,
width: x2 - x0,
height: y2 - y0 + 1
});
}
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = "red";
ctx.lineWidth = 0.50;
ctx.stroke();
}
function clipToImage(x, y, w, h) {
// don't save anti-alias slivers
if (w < 3 || h < 3) {
return;
}
// save clipped area to an img element
var tempCanvas = document.createElement("canvas");
var tempCtx = tempCanvas.getContext("2d");
tempCanvas.width = w;
tempCanvas.height = h;
tempCtx.drawImage(canvas, x, y, w, h, 0, 0, w, h);
var image = new Image();
image.width = w;
image.height = h;
image.src = tempCanvas.toDataURL();
$("#clips").append(image);
}
$("#unbox").click(function () {
var imgData = ctx.getImageData(0, 0, cw, ch);
var data = imgData.data;
clipBox(data);
});
body {
background-color: ivory;
}
canvas {
border:1px solid red;
}
#clips {
border:1px solid blue;
padding:5px;
}
img {
margin:3px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="unbox">Clip next sub-image</button><br>
<canvas id="canvas" width=300 height=150></canvas><br>
<h4>Below are images clipped from the canvas above.</h4><br>
<div id="clips"></div>
[Addition: discovering the bounds of a reddishness stroked rectangle]
You can test for "reddishness" by checking if the red component value of a pixel is much larger than the green & blue component values.
function xyIsInImage(data, x, y) {
// find the starting index of the r,g,b,a of pixel x,y
var n = (y * cw + x) * 4;
return(data[n+3]>240 && // this pixel is mostly opaque
data[n]-data[n+1]>180 && // this pixel is more reddish than green
data[n]-data[n+2]>180 // this pixel is more reddish then blue
);
Then use this reddishness test to find the bounds of the reddish stroked rectangle:
Find the theoretical top-left pixel of the red rect. The discovered pixel is probably on the top of the rect. It might not be on the left of the rect because your image shows the rect's corner pixels are much less reddish. So declare the y value as the top of the rect.
Move towards the center of the rect by a few pixels.
Test each vertical pixel downward until you find the bottom reddish border of the stroked rect.
Test each horizontal pixel rightward until you find the right reddish border of the stroked rect.
Test each horizontal pixel leftward until you find the left reddish border of the stroked rect.
Now you have found the top-left and bottom-right bounds of the rect.
Here's example code and a demo using your image:
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/raw.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height;
ctx.drawImage(img,0,0);
var data=ctx.getImageData(0,0,cw,ch).data;
var edge=findEdge(data);
var top=edge.y;
var x,y,left,bottom,right;
var off=25;
for(var y=edge.y+off;y<ch;y++){if(xyIsInImage(data,edge.x+off,y)){bottom=y; break;}}
for(var x=edge.x+off;x<cw;x++){if(xyIsInImage(data,x,edge.y+off)){right=x;break;}}
for(var x=edge.x+off;x>=0;x--){if(xyIsInImage(data,x,edge.y+off)){left=x;break;}}
dot({x:left,y:top});
dot({x:right,y:bottom});
}
//
function dot(pt){
ctx.beginPath();
ctx.arc(pt.x,pt.y,4,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle='red';
ctx.fill();
ctx.strokeStyle='gold';
ctx.lineWidth=2;
ctx.stroke();
}
function xyIsInImage(data, x, y) {
// find the starting index of the r,g,b,a of pixel x,y
var n = (y * cw + x) * 4;
return(data[n+3]>240 &&
data[n]-data[n+1]>180 &&
data[n]-data[n+2]>180
);
}
function findEdge(data) {
for (var y = 0; y < ch; y++) {
for (var x = 0; x < cw; x++) {
if(xyIsInImage(data, x, y)){ return ({x:x,y:y,valid:true}); }
}
}
return ({x:-100,y:-100,valid:false});
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Red-gold dots are on top-left & bottom-right of target rect.</h4>
<canvas id="canvas" width=300 height=300></canvas>
I created circles using canvas but can't delete the same on double click.`
var canvas,
context, shapes,
dragging = false, draggingtoMove = false,
dragStartLocation,dragEndLocation,
snapshot;
var numShapes;
function initiate() {
numShapes = 100;
shapes = [];
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
canvas.addEventListener('dblclick', dblclickerase);
}
function dblclickerase(evt)
{
var i;
//We are going to pay attention to the layering order of the objects so that if a mouse down occurs over more than object,
//only the topmost one will be dragged.
var highestIndex = -1;
//getting mouse position correctly, being mindful of resizing that may have occured in the browser:
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left)*(canvas.width/bRect.width);
mouseY = (evt.clientY - bRect.top)*(canvas.height/bRect.height);
//find which shape was clicked
for (i=0; i < shapes.length; i++) {
if (hitTest(shapes[i], mouseX, mouseY)) {
//draggingtoMove = true;
if (i > highestIndex) {
// here i want to delete the circle on double click but not getting logic, I can get mouse location but how to select the circle and delete it
}
}
}
}
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left,
y = event.clientY - canvas.getBoundingClientRect().top;
return {
x: x,
y: y
};
}
function takeSnapshot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreSnapshot() {
context.putImageData(snapshot, 0, 0);
}
function draw(position) {
var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2));
var i=0;
var tempX;
var tempY;
var tempRad;
var tempR;
var tempG;
var tempB;
var tempColor;
tempRad = radius;
tempX = dragStartLocation.x;
tempY = dragStartLocation.y;
tempColor = getRndColor();
tempShape = {x:tempX, y:tempY, rad:tempRad, color:tempColor};
shapes.push(tempShape);
context.beginPath();
context.arc(tempX, tempY, tempRad, 0, 2*Math.PI, false);
//context.closePath();
context.fillStyle = tempColor;
context.fill();
i++;
}
function dragStart(evt) {
dragging = true;
//if (dragging == true) {
dragStartLocation = getCanvasCoordinates(evt);
takeSnapshot();
//}
}
function hitTest(shape,mx,my) {
var dx;
var dy;
dx = mx - shape.x;
dy = my - shape.y;
//a "hit" will be registered if the distance away from the center is less than the radius of the circular object
return (dx*dx + dy*dy < shape.rad*shape.rad);
}
function drag(event) {
var position;
if (dragging === true) {
restoreSnapshot();
position = getCanvasCoordinates(event);
draw(position);
}
}
function dragStop(event) {
dragging = false;
restoreSnapshot();
var position = getCanvasCoordinates(event);
dragEndLocation = getCanvasCoordinates(event);
draw(position);
}
function getRndColor() {
var r = 255 * Math.random() | 0,
g = 255 * Math.random() | 0,
b = 255 * Math.random() | 0;
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
function eraseCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
addEventListener("load",initiate);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<canvas id="canvas" width="1020" height="640"></canvas>
<button onclick="eraseCanvas()" style="float: right;">Reset</button>
</body>
</html>
My question is how to delete the circle when double click on same, I
added 'dblClick' eventListener but still I am only able to perform the 'clearRect'
which will only clear the rectangle from start and end location which is little odd. Another thing I can't change the color to white which will not be valid.point as if my circle overlaps another will look odd.
You can't delete what you draw on the canvas like that. Once it's drawn on the canvas, it stays there and you have no way to access it except to read the pixel data - but that won't solve your problem because you can have overlapping circles of the same color.
To solve the issue, you must keep track of drawn circles, and redraw the full canvas every time it's needed (when adding a new circle, removing an old one, etc.). That way, when you want to delete a circle, you'd simply remove it from the list of circles (a simple array would work). But the important thing is that you need to clear and redraw the full canvas.
Summary: while having your canvas constantly redrawn (either on every tick or when a user interaction happens), your click'n'drag function should only be adding the circle to the circle list (specifying data like x, y, radius, color), while double-clicking a circle would look up that circle in the list, and remove it.
I need create nice and smooth tapering brush like this:
But i have problem with mouse speed movement. how to make brush tapering no dependency with mouse movement speed. I need make brush tapering which not different then mouse move very fast and slow.
Now with diferrent speed i get this results:
var el = document.getElementById('c');
var ctx = el.getContext('2d');
var isDrawing, lastPoint;
ctx.lineWidth = 20;
el.onmousedown = function(e) {
isDrawing = true;
lastPoint = { x: e.clientX, y: e.clientY };
ctx.lineWidth = 20;
};
el.onmousemove = function(e) {
if (!isDrawing) return;
ctx.lineJoin = "round";
ctx.lineCap = "butt";
ctx.strokeStyle = "#000000";
ctx.globalAlpha = "1";
ctx.globalCompositeOperation = "source-over";
if(ctx.lineWidth >= 5) {
ctx.lineWidth -= 0.1;
}
var currentPoint = { x: e.clientX, y: e.clientY };
ctx.beginPath();
ctx.moveTo(lastPoint.x, lastPoint.y);
ctx.lineTo(currentPoint.x, currentPoint.y);
ctx.closePath();
ctx.stroke();
lastPoint = currentPoint;
};
el.onmouseup = function() {
isDrawing = false;
};
function clearit() {
ctx.clearRect(0,0, 1000, 1000);
}
canvas { border: 1px solid #ccc }
<canvas id="c" width="500" height="500"></canvas>
Instead of drawing the tapered stroke using mousemove, use mousedown to start a new stroke and use mouseup to end that new stroke. Use an array to collect all the mouse positions between mousedown and mouseup.
After mouseup you can calculate the distance the mouse has traveled since mousedown and draw a tapered polyline over that distance. You can use linear interpolation to calculate a smooth transition of the lineWidth from start to end.
Since you're using interpolation rather than mousemoves, the speed of the mouse will be taken out of the equation!
To provide feedback to the user as they are defining the line, you can draw a 1 pixel stroke during mousemove. This feedback polyline will be overwritten by the tapered polyline when they release the mouse.
I drawn a circle in canvas and put an image near the border. Now I have absolutely no idea..I want to drag the image around the circle but the top of the arrow image should always be on the border.
For Example: I drag the arrow from the top to the left at nine o'clock. Now the arrow image needs to be rotated 90 degrees.
http://jsfiddle.net/L5twk3ak/1/
canvas = document.getElementById('test');
var context = canvas.getContext("2d");
var points = [];
var radius = 55;
imageBG = new Image();
imageBG.onload = function() {context.drawImage(imageBG, 148, 100, 15, 15);};
imageBG.src = 'https://www.nanamee.com/upload/images/5945/5945_p.jpg';
for(var degree = 0; degree < 360; degree++)
{
var radians = degree * Math.PI / 179;
var x = 150 + radius * Math.cos(radians);
var y = 150 + radius * Math.sin(radians);
points.push({x : x, y : y});
}
context.beginPath();
context.moveTo(points[0].x + 4, points[0].y + 4)
for(var i = 1; i < points.length; i++)
{
var pt = points[i];
context.lineTo(pt.x + 4, pt.y + 4);
}
context.strokeStyle = "black";
context.lineWidth = 1;
context.stroke();
context.closePath();
<canvas id="test" width="400" height="400">Your browser does not support the HTML5 canvas tag.</canvas>
You need to :
Draw your Arc as we're supposed to (unless you have better plans with lineTo() )
calculate the mouse position inside the canvas - on mousemove.
calculate the resultant degree depending on Mouse Position vs. the Arc center.
cache your image for reuse
create draw functions (one for the Arc, the other for drawing the Image after translating the canvas context). That way on (click+drag) mousemove you can simply reuse them to draw your objects into Canvas.
I'll not show you how to implement the click+drag cause it's pretty trivial: you simply need to apply your draw functions if both CLICK+MOUSEMOVE are registered.
Here's the interesting calculations part:
var canvas = document.getElementById('test'); // Store in variable!
var context = canvas.getContext("2d");
var circle = {rad: 55, x:100, y:100}; // Object for ease of use
var img = {src:'//placehold.it/13x13/000', x:0 ,y:0, w:13, h:13};
var arrowImg; // Store for later Image reference
function drawArc(){
context.beginPath();
context.arc(circle.x, circle.y, circle.rad, 0, Math.PI*2, true);
context.strokeStyle = "#000";
context.lineWidth = 1;
context.stroke();
context.closePath();
}
function drawImg( deg ){
context.save(); // save before we mess with ctx translations
context.translate(circle.y, circle.x); // temporarily translate the ctx
// to the Arc center coordinates.
context.rotate(deg*Math.PI/180); // we need Radians so deg*Math.PI/180
context.drawImage(arrowImg, circle.rad-img.w, -img.h/2);
context.restore(); // restore to default
}
function calcDeg(e){ // Retrieve degree from mouse position vs. arc center
var mPos = {
x : e.pageX-canvas.offsetLeft-circle.x,
y : e.pageY-canvas.offsetTop-circle.y
};
var getAtan = Math.atan2(mPos.y, mPos.x);
return getAtan*180/Math.PI;
}
drawArc(); // Draw the ARc
arrowImg = new Image(); // Create Image Obj
arrowImg.onload = function(){ drawImg(-90) }; // onload draw the Image
arrowImg.src = img.src;
canvas.addEventListener("mousemove", function(evt){
canvas.width = canvas.width; // clear the canvas
drawArc(); // Draw Arc
drawImg( calcDeg(evt) ); // Draw Image at the calculated degree
}, false);
canvas{background:#eee;}
<canvas id="test" width="400" height="400">Your browser does not support the HTML5 canvas tag.</canvas>
Not clear? Goog, than ask