I want to draw one circle and a character with shadow on a canvas in a HTML page while loading the page and recreate the image on a button click. I am using this code:
window.onload = function() {
draw();
};
function draw(){
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
var width = c.width;
var height = c.height;
//DRAW A CIRCLE
var centerX = Math.floor((Math.random() * width));
var centerY = Math.floor((Math.random() * height));
var radius = Math.floor(Math.random() * 50);
var color = '#f11';
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
//DRAW A CHARACTER WITH SHADOW
var c = "S";
ctx.font = "300% Verdana";
ctx.shadowBlur = 20;
ctx.shadowColor = "black";
ctx.shadowOffsetX = 20;
ctx.shadowOffsetY = 20;
ctx.fillStyle = "#111";
ctx.fillText(c, 10, 90);
}
In HTML I am calling draw function onclick() event of a button named Refresh.
For the first time it is giving desired output by drawing one circle and a character with shadow. As I click on the Refresh button it is drawing both the objects with shadow. I dont want to draw shadow of the circle. Can anyone please tell me the mistake I'm doing here.
You may want to use the CanvasRenderingContext2D.save() method :
window.onload = function() {
draw();
};
document.getElementById("canvas").addEventListener('click', draw);
function draw(){
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
var width = c.width;
var height = c.height;
//DRAW A CIRCLE
var centerX = Math.floor((Math.random() * width));
var centerY = Math.floor((Math.random() * height));
var radius = Math.floor(Math.random() * 50);
var color = '#f11';
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
//DRAW A CHARACTER WITH SHADOW
//save the actual context
ctx.save();
var c = "S";
ctx.font = "300% Verdana";
ctx.shadowBlur = 20;
ctx.shadowColor = "black";
ctx.shadowOffsetX = 20;
ctx.shadowOffsetY = 20;
ctx.fillStyle = "#111";
ctx.fillText(c, 10, 90);
//restore it
ctx.restore();
}
canvas{border:1px solid;}
<canvas id="canvas" width="400" height="200"></canvas>
Related
So in my code I'm drawing an arrow in a fullscreen canvas. After one second it will be deleted by my clear canvas function which works fine. Now a circle shall be drawn. Also works perfectly fine. After that I want to clear the canvas again but it doesn't work anymore. Does anyone have an idea why it only works once?
Many thanks, any answer will help!
function generateRandomNumber() {
var minangle = 0;
var maxangle = 2 * Math.PI;
randangle = Math.random() * (maxangle - minangle) + minangle;
return randangle;
};
function createArrowAngle() {
var currentangle = generateRandomNumber();
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var x1 = centerX + 50 * Math.cos(currentangle);
var y1 = centerY + 50 * Math.sin(currentangle);
var x2 = centerX + 50 * Math.cos(currentangle + Math.PI);
var y2 = centerY + 50 * Math.sin(currentangle + Math.PI);
return [x1, y1, x2, y2]
}
function drawCircle(circleColour) {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 20;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = circleColour;
context.fill();
context.lineWidth = 20;
context.strokeStyle = circleColour;
context.stroke();
}
function drawArrow(fromx, fromy, tox, toy, colourarrow) {
//variables to be used when creating the arrow
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var headlen = 3;
var angle = Math.atan2(toy - fromy, tox - fromx);
//starting path of the arrow from the start square to the end square and drawing the stroke
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
ctx.beginPath();
ctx.moveTo(tox, toy);
ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7));
//path from the side point of the arrow, to the other side point
ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
ctx.lineTo(tox, toy);
ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7));
//draws the paths created above
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
ctx.fillStyle = colourarrow
ctx.fill();
}
function clearcanvas1(canvastoclear) {
var canvas = document.getElementById(canvastoclear),
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
try {
var canvas = document.getElementById("myCanvas");
canvas.width = innerWidth;
canvas.height = innerHeight;
var differentcolours = ['#FFA500', '#FFFF00', '#FF0000', '#0000FF', '#008000', '#EE82EE', '#40E0D0', '#FFFFFF'];
var angles = createArrowAngle();
//draw an arrow after 1 second
drawArrow(angles[0], angles[1], angles[2], angles[3], differentcolours[7]);
//clear canvas after 1 second --> this works
setTimeout(function() {
clearcanvas1("myCanvas")
}, 1000);
//draw a circle after 4 seconds --> this works
setTimeout(function() {
drawCircle(differentcolours[7])
}, 4000);
//clear canvas after 1 second --> this doesn't work
setTimeout(function() {
clearcanvas1("myCanvas")
}, 1000);
} catch (err) {
document.getElementById("demo").innerHTML = err.message;
}
* {
margin: 0;
padding: 0;
}
body,
html {
height: 100%;
}
#myCanvas {
position: absolute;
width: 100%;
height: 100%;
}
<body bgcolor='black'>
<canvas id="myCanvas" oncl></canvas>
<p id="demo" style="color: white" oncl></p>
</body>
The problem is you assumed setTimeout will wait for the function to execute, and it doesn't,
//draw an arrow after 1 second
drawArrow(angles[0],angles[1],angles[2],angles[3],differentcolours[7]);
//clear canvas after 1 second --> this works
setTimeout(function(){clearcanvas1("myCanvas")},1000);
//draw a circle after 4 seconds --> this works
setTimeout(function(){drawCircle(differentcolours[7])},4000);
//clear canvas after 1 second --> this doesn't work
setTimeout(function(){clearcanvas1("myCanvas")},1000);
You expected to draw the arrow, and then after 1 second to clear the canvas, and then after 4 seconds draw the circle, and then after 1 second clear the canvas, and what's happening is, it draws the arrow, and then it clear twice the canvas after 1 second, and 3 seconds after that it draws the circle.
I would change the last timeout to 5000, and you'll get the assumed functionality
//draw an arrow after 1 second
drawArrow(angles[0],angles[1],angles[2],angles[3],differentcolours[7]);
//clear canvas after 1 second --> this works
setTimeout(function(){clearcanvas1("myCanvas")},1000);
//draw a circle after 4 seconds --> this works
setTimeout(function(){drawCircle(differentcolours[7])},4000);
//clear canvas after 1 second --> this doesn't work
setTimeout(function(){clearcanvas1("myCanvas")},5000);
You can see it here in action
function generateRandomNumber() {
var minangle = 0;
var maxangle = 2*Math.PI;
randangle = Math.random() * (maxangle- minangle) + minangle;
return randangle;
};
function createArrowAngle() {
var currentangle=generateRandomNumber();
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var x1=centerX+50*Math.cos(currentangle);
var y1=centerY+50*Math.sin(currentangle);
var x2=centerX+50*Math.cos(currentangle+Math.PI);
var y2=centerY+50*Math.sin(currentangle+Math.PI);
return [x1, y1, x2, y2]
}
function drawCircle(circleColour) {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 20;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = circleColour;
context.fill();
context.lineWidth = 20;
context.strokeStyle = circleColour;
context.stroke();
}
function drawArrow(fromx, fromy, tox, toy, colourarrow){
//variables to be used when creating the arrow
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var headlen = 3;
var angle = Math.atan2(toy-fromy,tox-fromx);
//starting path of the arrow from the start square to the end square and drawing the stroke
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
ctx.beginPath();
ctx.moveTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//path from the side point of the arrow, to the other side point
ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
ctx.lineTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//draws the paths created above
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
ctx.fillStyle = colourarrow
ctx.fill();
}
function clearcanvas1(canvastoclear)
{
var canvas = document.getElementById(canvastoclear),
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
try {
var canvas = document.getElementById("myCanvas");
canvas.width = innerWidth;
canvas.height = innerHeight;
var differentcolours = ['#FFA500','#FFFF00','#FF0000','#0000FF','#008000','#EE82EE','#40E0D0','#FFFFFF'];
var angles=createArrowAngle();
//draw an arrow after 1 second
drawArrow(angles[0],angles[1],angles[2],angles[3],differentcolours[7]);
//clear canvas after 1 second --> this works
setTimeout(function(){clearcanvas1("myCanvas")},1000);
//draw a circle after 4 seconds --> this works
setTimeout(function(){drawCircle(differentcolours[7])},4000);
//clear canvas after 1 second --> this doesn't work
setTimeout(function(){clearcanvas1("myCanvas")},5000);
}
catch(err) {
document.getElementById("demo").innerHTML = err.message;
}
* { margin: 0; padding: 0;}
body, html { height:100%; }
#myCanvas {
position:absolute;
width:100%;
height:100%;
}
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body bgcolor='black'>
<canvas id="myCanvas" oncl></canvas>
<p id="demo" style="color: white" oncl></p>
</body>
</html>
Remember that calls are asynchronous.
You only need this
//draw an arrow after 1 second
drawArrow(angles[0], angles[1], angles[2], angles[3], differentcolours[7]);
//clear canvas after 1 second --> this works
setTimeout(function () { clearcanvas1("myCanvas") }, 1000);
//draw a circle after 4 seconds --> this works
setTimeout(function () {
drawCircle(differentcolours[7])
setTimeout(function () { clearcanvas1("myCanvas") }, 1000);
}, 4000);
How can I stop filling my circle at some point?
Like only fill 50% of the circle or 25% of the circle and leave the left over. Just like progress bar.
below is the code Im using but it is filling entire circle.
Kindly please give me suggestions.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
ctx.lineCap = "round";
var y = ch - 10;
var drawingColor = "#0092f9";
animate();
function animate() {
if (y > 0) {
requestAnimationFrame(animate);
}
ctx.clearRect(0, 0, cw, ch);
ctx.save();
drawContainer(0, null, null);
drawContainer(15, drawingColor, null);
drawContainer(7, "white", "white");
ctx.save();
ctx.clip();
drawLiquid();
ctx.restore();
ctx.restore();
y--;
}
function drawLiquid() {
ctx.beginPath();
ctx.moveTo(0, y);
for (var x = 0; x < 300; x += 1) {
ctx.quadraticCurveTo(x + 5, y+5, x + 5, y);
}
ctx.lineTo(cw, ch);
ctx.lineTo(0, ch);
ctx.closePath();
ctx.fillStyle = drawingColor;
ctx.fill();
}
function drawContainer(linewidth, strokestyle, fillstyle) {
ctx.beginPath();
ctx.arc(cw/2, ch/2,cw/2-30,0,2*Math.PI);
ctx.lineWidth = linewidth;
ctx.strokeStyle = strokestyle;
ctx.stroke();
if (fillstyle) {
ctx.fillStyle = fillstyle;
ctx.fill();
}
}
<canvas id="canvas" width=200 height=200></canvas>
I know zit at javascript but: Just change the limit of y in animate. I put 100 instead of 0 and it fills half the circle.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
ctx.lineCap = "round";
var y = ch - 10;
var drawingColor = "#0092f9";
animate();
function animate() {
if (y > 100) {
requestAnimationFrame(animate);
}
ctx.clearRect(0, 0, cw, ch);
ctx.save();
drawContainer(0, null, null);
drawContainer(15, drawingColor, null);
drawContainer(7, "white", "white");
ctx.save();
ctx.clip();
drawLiquid();
ctx.restore();
ctx.restore();
y--;
}
function drawLiquid() {
ctx.beginPath();
ctx.moveTo(0, y);
for (var x = 0; x < 300; x += 1) {
ctx.quadraticCurveTo(x + 5, y+5, x + 5, y);
}
ctx.lineTo(cw, ch);
ctx.lineTo(0, ch);
ctx.closePath();
ctx.fillStyle = drawingColor;
ctx.fill();
}
function drawContainer(linewidth, strokestyle, fillstyle) {
ctx.beginPath();
ctx.arc(cw/2, ch/2,cw/2-30,0,2*Math.PI);
ctx.lineWidth = linewidth;
ctx.strokeStyle = strokestyle;
ctx.stroke();
if (fillstyle) {
ctx.fillStyle = fillstyle;
ctx.fill();
}
}
<canvas id="canvas" width=200 height=200></canvas>
I am trying to insert a text at the middle of a circle drawn with HTML Canvas, but its always appearing a little above the center. (I tried baseline, but it didn't work). I don't want to hard-coded the fillText co-ordinates.
var canvas = document.getElementById('completionStatus');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 200;
context.beginPath();
context.fillStyle = '#B8D9FE';
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fill();
context.strokeStyle = '#ffffff';
context.font = 'bold 130pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'white';
context.fillText('70%', centerX, centerY);
context.stroke();
<canvas id="completionStatus" width="400" height="400"></canvas>
Here's a fiddle : http://jsfiddle.net/wku0j922/2/
Simply use the textBaseline property on the context. When both textBaseline is set to middle and textAlign is set to center you can place it exactly at the position of your circle center and it will be centered across the board.
var c1 = document.getElementById('c1');
var c2 = document.getElementById('c2');
var ct1 = c1.getContext('2d');
var ct2 = c2.getContext('2d');
c1.width = c2.width = 200;
c1.height = c2.height = 200;
ct1.font = ct2.font = '20px Helvetica';
ct1.textAlign = ct2.textAlign = 'center';
ct2.textBaseline = 'middle';
ct1.beginPath();
ct1.arc(100,100,99,0,Math.PI * 2);
ct1.stroke();
ct1.closePath();
ct2.beginPath();
ct2.arc(100,100,99,0,Math.PI * 2);
ct2.stroke();
ct2.closePath();
ct1.fillText('textBaseline not set', 100, 100);
ct2.fillText('textBaseline middle', 100, 100);
<canvas id="c1"></canvas>
<canvas id="c2"></canvas>
Here is it implemented in your snippet:
var canvas = document.getElementById('completionStatus');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 200;
context.beginPath();
context.fillStyle = '#B8D9FE';
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fill();
context.strokeStyle = '#ffffff';
context.font = 'bold 130pt Calibri';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillStyle = 'white';
context.fillText('70%', centerX, centerY);
context.stroke();
<canvas id="completionStatus" width="400" height="400"></canvas>
Docs: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
Declaring the textBaseline prior to defining the fillText seems to work.
var canvas = document.getElementById('completionStatus');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 200;
context.beginPath();
context.fillStyle = '#B8D9FE';
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fill();
context.strokeStyle = '#ffffff';
context.font = 'bold 130pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'white';
// Defining the `textBaseline`…
context.textBaseline = "middle";
context.fillText('70%', centerX, centerY);
context.stroke();
#completionStatus {
border: 1px solid #000;
}
<canvas id="completionStatus" width="500" height="500"></canvas>
Canvas puts the text baseline in vertical centrum.
To truly center the text, you need to compensate for the font size:
context.font = 'bold ' + radius + 'px Calibri';
context.fillText('70%', centerX, centerY + Math.floor(radius/4));
I changed the centerY property to 300 and it seemed to have worked.
context.fillText('70%', centerX, 300);
I have this code to show wind direction on html canvas:
var x, y, r, ctx, radians;
ctx = window.compass.getContext("2d");
radians = 0.0174533 * (10 - 90);
x = ctx.canvas.width / 2;
y = ctx.canvas.height / 2;
r = x * 0.8;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height );
ctx.beginPath();
ctx.strokeStyle = "red"
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.lineCap = 'square';
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
ctx.lineWidth = 10;
ctx.moveTo(x, y);
ctx.lineTo(x + r * Math.cos(radians), y + r * Math.sin(radians));
ctx.stroke();
I want to add arrowhead to the start point of the line, i.e
x = ctx.canvas.width / 2;
y = ctx.canvas.height / 2;
how can I do this?
Creating a reusable wind-arrow is fairly straightforward:
Create your shadowed arrow on an in-memory canvas:
var memCanvas=document.createElement('canvas')
Draw the in-memory canvas onto the visible canvas:
ctx.drawImage(memCanvas,x,y);
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;
var cx=150;
var cy=150;
var angle=0;
var arrow=makeArrow(100,10,20,20,'red',4);
requestAnimationFrame(animate);
function animate(time){
ctx.clearRect(0,0,cw,ch);
drawArrow(cx,cy,angle);
angle+=Math.PI/180;
requestAnimationFrame(animate);
}
function drawArrow(cx,cy,radianAngle){
ctx.translate(cx,cy);
ctx.rotate(radianAngle);
ctx.drawImage(arrow,-arrow.width/2,-arrow.height/2);
ctx.rotate(-radianAngle);
ctx.translate(-cx,-cy);
}
function makeArrow(length,lineHeight,arrowLength,arrowHeight,fillcolor,shadowoffset){
var lineTop=(arrowHeight-lineHeight)/2;
var arrowLeft=length-arrowLength;
var c=document.createElement('canvas');
var ctx=c.getContext('2d');
c.width=length+shadowoffset;
c.height=arrowHeight+shadowoffset;
ctx.shadowOffsetX = shadowoffset;
ctx.shadowOffsetY = shadowoffset;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
ctx.beginPath();
ctx.moveTo(0,lineTop);
ctx.lineTo(arrowLeft,lineTop);
ctx.lineTo(arrowLeft,0);
ctx.lineTo(length,arrowHeight/2);
ctx.lineTo(arrowLeft,arrowHeight);
ctx.lineTo(arrowLeft,lineTop+lineHeight);
ctx.lineTo(0,lineTop+lineHeight);
ctx.closePath();
ctx.fillStyle=fillcolor;
ctx.fill();
return(c);
}
body{ background-color:white; padding:10px; }
canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
Seemed simple enough to draw circles and text in an HTML5 canvas, but I get non-intuitive behavior. The circles get drawn nice and pretty, then the more circles I draw, the older circles become more and more octagonal shaped. Very strange to me... Also, the text disappears from the old circles and only appears on the last circle drawn. What's the proper way to do this with canvases?
$("#circles_container").on("click", "#circles_canvas", function(event) {
var canvas = document.getElementById('circles_canvas');
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var w = 16;
var x = event.pageX;
var y = Math.floor(event.pageY-$(this).offset().top);
ctx.fillStyle = "rgb(200,0,0)";
ctx.arc(x, y, w/2, 0, 2 * Math.PI, false);
ctx.fill();
ctx = canvas.getContext("2d");
ctx.font = '8pt Calibri';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText('0', x, y+3);
}
});
Just add this near the start of your function :
ctx.beginPath();
You were drawing a path always longer.
Demo in Stack Snippets & JS Fiddle (click on the canvas)
var canvas = document.getElementById('circles_canvas');
canvas.addEventListener("click", function(event) {
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var w = 16;
var x = Math.floor(event.pageX-this.offsetLeft);
var y = Math.floor(event.pageY-this.offsetTop);
ctx.beginPath();
ctx.fillStyle = "rgb(200,0,0)";
ctx.arc(x, y, w/2, 0, 2 * Math.PI, false);
ctx.fill();
ctx.font = '8pt Calibri';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText('0', x, y+3);
}
})
canvas {
border: 1px solid black;
}
<h1>Click Canvas Below</h1>
<canvas id="circles_canvas"></canvas>