I am working on a project where user upload structural diagram(engineering diagram). When I user double click on the intended location on canvas the speech to text engine turns on and listen for user comments and then it draw a small circle with different colors and fill text (count) on that location. I am saving comments, counts, coordinates of arc and other things in react state and displaying the list in a component with edit and delete button. When user press the delete button. comment and other property gets deleted from the state.
I want to remove the drawn arc from the canvas. How can I do it?
I have tried clearRect. But it is not working in this case.
Please let me know.
componentDidMount() {
const img = this.refs.image;
const canvas = this.refs.canvas;
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
img.onload = () => {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.font = "40px Courier";
ctx.fillText('Drawing 1', 200, 100);
}
}
drawCircle(x, y, getcolor) {
const canvas = this.refs.canvas;
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(x, y, 8, 0, Math.PI * 2, false);
ctx.strokeStyle = "black";
ctx.stroke();
ctx.fillStyle = getcolor;
ctx.fill();
ctx.closePath();
// Text
ctx.beginPath();
ctx.font = "20px Arial"
ctx.fillStyle = "#00538a";
ctx.textAlign = "center";
ctx.fillText(this.state.count, x , y - 20);
ctx.fill();
ctx.closePath();
}
remove(id) {
this.setState({
comments: this.state.comments.filter(c => c.id !== id)
});
const canvas = this.refs.canvas;
const ctx = canvas.getContext('2d');
const arc = this.state.comments.filter(c => c.id === id);
let x = arc[0].coordinates.x;
let y = arc[0].coordinates.y
console.log("TCL: Drawing -> remove -> arc", arc[0].coordinates);
ctx.beginPath();
ctx.arc(x, y, 8, 0, Math.PI * 2, false);
ctx.clip();
ctx.clearRect(x-8, y-8, 16,16);
}
Thanks
Meet
As I mentioned in my comments the way you're trying to remove a circle from the canvas ain't gonna work.
If you call clearRect() on the canvas, it will essentially overwrite the target area including your original background image.
Instead you need to keep track of the circles - more precisely the position at which those should be drawn - using an array.
If you click the canvas -> add a circle element to an array -> clear the canvas -> draw the diagram again -> loop over the array to draw the circles on top
If you click the remove button of a circle -> search the array for this particular circle -> remove it from the array -> clear the canvas -> draw the diagram again -> loop over the array to draw the circles on top
Here's an example to illustrate what I'm talking about:
var comments = new Array();
var canvas = document.createElement("canvas");
canvas.style="float:left;"
canvas.width = 400;
canvas.height = 200;
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
function updateCanvas() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.font = "40px Courier";
ctx.fillText('Drawing 1', 200, 100);
for (var a = 0; a < comments.length; a++) {
ctx.beginPath();
ctx.arc(comments[a].x, comments[a].y, 8, 0, Math.PI * 2, false);
ctx.strokeStyle = "black";
ctx.stroke();
ctx.fillStyle = "black";
ctx.fill();
ctx.closePath();
}
}
var img = new Image();
img.onload = () => {
updateCanvas();
}
img.src = "https://picsum.photos/id/59/400/200";
function addCircle(e) {
var div = document.createElement("div");
div.innerHTML = "remove" + comments.length;
document.body.appendChild(div);
div.addEventListener("click", function(e) {
for (var a = 0; a < comments.length; a++) {
if (comments[a].div == e.target) {
comments.splice(a, 1);
break;
}
}
document.body.removeChild(e.target);
updateCanvas();
});
comments.push({
x: e.clientX,
y: e.clientY,
div: div
});
updateCanvas();
}
canvas.addEventListener("click", addCircle);
Everytime you click on the picture a 'remove' div will be created to the right of the canvas. if you click it, the associated circle will be removed.
I need one help. I need to increase the font size using canvas and Javascript. I am explaining my code below.
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
ctx.font = "40px Calibri";
var y=10;
var cnt=0;
var bg;
var logo;
var bgLoaded = false;
var logoLoaded = false;
var dataURL='';
var str='Valid:'+$('#validfrm').val()+'-'+$('#validto').val();
canvas.width = bg.width;
canvas.height = bg.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(bg, 0, 0);
ctx.drawImage(logo, 20, 130);
ctx.fillStyle = 'black';
ctx.fillText($('#copTitle').val(), 160, 70);
ctx.fillText($('#coupondiscount').val(), 190, 110);
ctx.fillText($('#couponmessage').val(), 190, 150);
ctx.fillText(str, 60, 280);
Here I have set the high font size but the small text size is coming. Please help me to resolve this issue.
What I'm asking for may be extremely easy, but I've been having quite a bit of trouble getting the intended result.
I want a shape (in this example it's squares but should work with other shapes such as circles, etc) to cut itself off when it leaves the bounds of another shape.
Basically, top image is what I want, bottom is what I currently have:
http://imgur.com/a/oQkzG
I heard this can be done with globalCompositeOperation, but am looking for any solution that will give the wanted result.
This is the current code, if you can't use JSFiddle:
// Fill the background
ctx.fillStyle = '#0A2E36';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Fill the parent rect
ctx.fillStyle = '#CCA43B';
ctx.fillRect(100, 100, 150, 150);
// Fill the child rect
ctx.fillStyle = 'red';
ctx.fillRect(200, 200, 70, 70);
// And fill a rect that should not be affected
ctx.fillStyle = 'green';
ctx.fillRect(80, 80, 50, 50);
JSFiddle Link
Since you need some kind of relation between objects - a scene graph -, you should build it now.
From your question, it seems that any child element should be drawn clipped by its parent element.
(Yes composite operation could come to the rescue, but they are handy only when drawing like 2 figures on a cleared background, things get quickly complicated otherwise, and you might have to use a back canvas, so clipping is simpler here.)
I did below a most basic class that handles the rect case, you'll see that it isn't very difficult to build.
The 'scene' is made out of a background Rect, which has two childs, the yellow and the green. And the yellow Rect has a red child.
var canvas = document.getElementById('cv');
var ctx = canvas.getContext('2d');
function Rect(fill, x, y, w, h) {
var childs = [];
this.draw = function () {
ctx.save();
ctx.beginPath();
ctx.fillStyle = fill;
ctx.rect(x, y, w, h);
ctx.fill();
ctx.clip();
for (var i = 0; i < childs.length; i++) {
childs[i].draw();
}
ctx.restore();
}
this.addChild = function (child) {
childs.push(child);
}
this.setPos = function (nx, ny) {
x = nx;
y = ny;
}
}
// background
var bgRect = new Rect('#0A2E36', 0, 0, canvas.width, canvas.height);
// One parent rect
var parentRect = new Rect('#CCA43B', 100, 100, 150, 150);
// child rect
var childRect = new Rect('red', 200, 200, 70, 70);
parentRect.addChild(childRect);
// Another top level rect
var otherRect = new Rect('green', 80, 80, 50, 50);
bgRect.addChild(parentRect);
bgRect.addChild(otherRect);
function drawScene() {
bgRect.draw();
drawTitle();
}
drawScene();
window.addEventListener('mousemove', function (e) {
var rect = canvas.getBoundingClientRect();
var x = (e.clientX - rect.left);
var y = (e.clientY - rect.top);
childRect.setPos(x, y);
drawScene();
});
function drawTitle() {
ctx.fillStyle = '#DDF';
ctx.font = '14px Futura';
ctx.fillText('Move the mouse around.', 20, 24);
}
<canvas id='cv' width=440 height=440></canvas>
I've got some code which draws a rectangle on a canvas, but I want that rectangle to change color when I hover the mouse over it.
The problem is after I've drawn the rectangle I'm not sure how I select it again to make the adjustment.
What I want to do:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
$('c.[rectangle]').hover(function(this){
this.fillStyle = 'red';
this.fill();
});
You can't do this out-of-the-box with canvas. Canvas is just a bitmap, so the hover logic has to be implemented manually.
Here is how:
Store all the rectangles you want as simple object
For each mouse move on the canvas element:
Get mouse position
Iterate through the list of objects
use isPointInPath() to detect a "hover"
Redraw both states
Example
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
rects = [
{x: 10, y: 10, w: 200, h: 50},
{x: 50, y: 70, w: 150, h: 30} // etc.
], i = 0, r;
// render initial rects.
while(r = rects[i++]) ctx.rect(r.x, r.y, r.w, r.h);
ctx.fillStyle = "blue"; ctx.fill();
canvas.onmousemove = function(e) {
// important: correct mouse position:
var rect = this.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top,
i = 0, r;
ctx.clearRect(0, 0, canvas.width, canvas.height); // for demo
while(r = rects[i++]) {
// add a single rect to path:
ctx.beginPath();
ctx.rect(r.x, r.y, r.w, r.h);
// check if we hover it, fill red, if not fill it blue
ctx.fillStyle = ctx.isPointInPath(x, y) ? "red" : "blue";
ctx.fill();
}
};
<canvas/>
This is a stable code in base of #K3N answer. The basic problem of his code is because when one box is over the another the two may get mouse hover at same time. My answer perfectly solves that adding a 'DESC' to 'ASC' loop.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
var map = [
{x: 20, y: 20, w: 60, h: 60},
{x: 30, y: 50, w: 76, h: 60}
];
var hover = false, id;
var _i, _b;
function renderMap() {
for(_i = 0; _b = map[_i]; _i ++) {
ctx.fillStyle = (hover && id === _i) ? "red" : "blue";
ctx.fillRect(_b.x, _b.y, _b.w, _b.h);
}
}
// Render everything
renderMap();
canvas.onmousemove = function(e) {
// Get the current mouse position
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left, y = e.clientY - r.top;
hover = false;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = map.length - 1, b; b = map[i]; i--) {
if(x >= b.x && x <= b.x + b.w &&
y >= b.y && y <= b.y + b.h) {
// The mouse honestly hits the rect
hover = true;
id = i;
break;
}
}
// Draw the rectangles by Z (ASC)
renderMap();
}
<canvas id="canvas"></canvas>
You may have to track the mouse on the canvas using JavaScript and see when it is over your rectangle and change the color then. See code below from my blog post
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="700" height="500" style="border:1px solid #c3c3c3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var myRect={x:150, y:75, w:50, h:50, color:"red"};
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
c.addEventListener("mousemove", function(e){
if ((e.clientX>=myRect.x)&(e.clientX<=myRect.x+myRect.w)&(e.clientY>=myRect.y)&(e.clientY<=myRect.y+myRect.h)){
myRect.color = "green";}
else{
myRect.color = "red";}
updateCanvas();
}, false);
function updateCanvas(){
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
}
</script>
</body>
</html>
I believe this is a slightly more in-depth answer that would work better for you, especially if you are interested in game design with the canvas element.
The main reason this would work better for you is because it focuses more on an OOP (object orientated programming) approach. This allows for objects to be defined, tracked and altered at a later time via some event or circumstance. It also allows for easy scaling of your code and in my opinion is just more readable and organized.
Essentially what you have here is two shapes colliding. The cursor and the individual point / object it hovers over. With basic squares, rectangles or circles this isn't too bad. But, if you are comparing two more unique shapes, you'll need to read up more on Separating Axis Theorem (SAT) and other collision techniques. At that point optimizing and performance will become a concern, but for now I think this is the optimal approach.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
const cx = width / 2;
const cy = height / 2;
const twoPie = Math.PI * 2;
const points = []; // This will be the array we store our hover points in later
class Point {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r || 0;
}
}
class HoverPoint extends Point {
constructor(x, y, r, color, hoverColor) {
super(x, y, r);
this.color = color;
this.hoverColor = hoverColor;
this.hovered = false;
this.path = new Path2D();
}
draw() {
this.hovered ? ctx.fillStyle = this.hoverColor : ctx.fillStyle = this.color;
this.path.arc(this.x, this.y, this.r, 0, twoPie);
ctx.fill(this.path);
}
}
class Cursor extends Point {
constructor(x, y, r) {
super(x, y, r);
}
collisionCheck(points) {
// This is the method that will be called during the animate function that
// will check the cursors position against each of our objects in the points array.
document.body.style.cursor = "default";
points.forEach(point => {
point.hovered = false;
if (ctx.isPointInPath(point.path, this.x, this.y)) {
document.body.style.cursor = "pointer";
point.hovered = true;
}
});
}
}
function createPoints() {
// Create your points and add them to the points array.
points.push(new HoverPoint(cx, cy, 100, 'red', 'coral'));
points.push(new HoverPoint(cx + 250, cy - 100, 50, 'teal', 'skyBlue'));
// ....
}
function update() {
ctx.clearRect(0, 0, width, height);
points.forEach(point => point.draw());
}
function animate(e) {
const cursor = new Cursor(e.offsetX, e.offsetY);
update();
cursor.collisionCheck(points);
}
createPoints();
update();
canvas.onmousemove = animate;
There is one more thing that I would like to suggest. I haven't done tests on this yet but I suspect that using some simple trigonometry to detect if our circular objects collide would preform better over the ctx.IsPointInPath() method.
However if you are using more complex paths and shapes, then the ctx.IsPointInPath() method would most likely be the way to go. if not some other more extensive form of collision detection as I mentioned earlier.
The resulting change would look like this...
class Cursor extends Point {
constructor(x, y, r) {
super(x, y, r);
}
collisionCheck(points) {
document.body.style.cursor = "default";
points.forEach(point => {
let dx = point.x - this.x;
let dy = point.y - this.y;
let distance = Math.hypot(dx, dy);
let dr = point.r + this.r;
point.hovered = false;
// If the distance between the two objects is less then their combined radius
// then they must be touching.
if (distance < dr) {
document.body.style.cursor = "pointer";
point.hovered = true;
}
});
}
}
here is a link containing examples an other links related to collision detection
I hope you can see how easily something like this can be modified and used in games and whatever else. Hope this helps.
Below code adds shadow to canvas circle on hovering it.
<html>
<body>
<canvas id="myCanvas" width="1000" height="500" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
</body>
<script>
var canvas = document.getElementById("myCanvas"),
ctx = canvas.getContext("2d"),
circle = [{
x: 60,
y: 50,
r: 40,
},
{
x: 100,
y: 150,
r: 50,
} // etc.
];
// render initial rects.
for (var i = 0; i < circle.length; i++) {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
}
canvas.onmousemove = function(e) {
var x = e.pageX,
y = e.pageY,
i = 0,
r;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < circle.length; i++) {
if ((x > circle[i].x - circle[i].r) && (y > circle[i].y - circle[i].r) && (x < circle[i].x + circle[i].r) && (y < circle[i].y + circle[i].r)) {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.shadowBlur = 10;
ctx.lineWidth = 3;
ctx.strokeStyle = 'rgb(255,255,255)';
ctx.shadowColor = 'grey';
ctx.stroke();
ctx.shadowColor = 'white';
ctx.shadowBlur = 0;
} else {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.shadowColor = 'white';
ctx.shadowBlur = 0;
}
}
};
</script>
</html>
I know this is old, but I am surprised no one has mentioned JCanvas. It adds to the simplicity of animating canvas on events. More documentation here https://projects.calebevans.me/jcanvas/docs/mouseEvents/
<html lang="en">
<head>
<!-- css and other -->
</head>
<body onload="draw();">
<canvas id = "canvas" width="500" height="500" style= border:1px solid #000000;"> </canvas>
<script>
function draw() {
$('canvas').drawRect({
layer: true,
fillStyle:'#333',
x:100, y: 200,
width: 600,
height: 400,
mouseover: function(layer) {
$(this).animateLayer(layer, {
fillStyle: 'green'
}, 1000, 'swing');
}
});
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jcanvas/21.0.1/jcanvas.js" crossorigin="anonymous"></script>
</body>
</html>
Consider this following code:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
c.addEventListener("mouseover", doMouseOver, false);//added event to canvas
function doMouseOver(e){
ctx.fillStyle = 'red';
ctx.fill();
}
DEMO
You could use canvas.addEventListener
var canvas = document.getElementById('canvas0');
canvas.addEventListener('mouseover', function() { /*your code*/ }, false);
It worked on google chrome
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
$(c).hover(function(e){
ctx.fillStyle = 'red';
ctx.fill();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="myCanvas"/>
Consider a simple canvas as
$(document).ready(function(){
draw();
});
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
}
}
How can I introduce variable into the jQuery function to draw several canvases with defined variable (e.g. color set).
In fact, I want to replace canvas id and its options (like color) with variable provided by draw(variables), e.g.draw(canvas_id, color, ...).
Example: (for creating several canvases on different DOM elements)
function draw(ccc) {
var canvas = document.getElementById(ccc);
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
}
}
draw(canvas1);
draw(canvas2);
try this:
function draw(id, clr, fill) {
var canvas = document.getElementById(id);
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.fillStyle = clr;
ctx.fillRect (fill);
}
}
Here is one way you could do it:
function draw(colors) {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
for(var i=0; i < colors.length; i ++){
ctx.fillStyle = colors[i];
ctx.fillRect (10*i, 10*i, 55, 50);
}
}
}
// define some colors in an array
var colors = ["rgb(200,0,0)","rgba(0, 0, 200, 0.5)","rgba(0, 128, 200, 0.5)"];
draw(colors);
EDIT
Here is jsfiddle example