Making a analog clock by using html canvas - javascript

Below is my preliminary Javascript code for making a analog clock. My main problem is I don't know how to clear the "previous second lines" on the clock surface:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<script>
setInterval(timing, 1000);
var canvas1 = document.createElement("canvas");
canvas1.id = "canvas-1";
document.body.appendChild(canvas1);
canvas1.width = 500;
canvas1.height = 500;
canvas1.style.backgroundColor = "#3d3d3b";
var radius = (canvas1.height/2) * 0.9;
var ctx = canvas1.getContext("2d");
ctx.beginPath();
ctx.arc(250,250,radius,0,2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.beginPath();
ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
ctx.beginPath();
ctx.lineWidth = radius * 0.05;
ctx.stroke();
ctx.font = "40px Georgia"
ctx.textBaseline="middle";
ctx.textAlign="center";
for (i=1;i<13;i++){
ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
}
function timing(){
const d = new Date();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.01;
ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.03;
ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.05;
ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
ctx.stroke();
}
</script>
</body>
</html>
I have tried to use "ctx.globalCompositeOperation = "destination-over";", however not successful:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<script>
setInterval(timing, 1000);
var canvas1 = document.createElement("canvas");
canvas1.id = "canvas-1";
document.body.appendChild(canvas1);
canvas1.width = 500;
canvas1.height = 500;
canvas1.style.backgroundColor = "#3d3d3b";
var radius = (canvas1.height/2) * 0.9;
var ctx = canvas1.getContext("2d");
ctx.beginPath();
ctx.arc(250,250,radius,0,2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.beginPath();
ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
ctx.beginPath();
ctx.lineWidth = radius * 0.05;
ctx.stroke();
ctx.font = "40px Georgia"
ctx.textBaseline="middle";
ctx.textAlign="center";
for (i=1;i<13;i++){
ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
}
function timing(){
const d = new Date();
ctx.beginPath();
ctx.arc(250,250,radius,0,2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.globalCompositeOperation = "destination-over";
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineTo(250+(Math.sin((d.getSeconds()-1)*Math.PI/30)*radius*0.85), 250-Math.cos((d.getSeconds()-1)*Math.PI/30)*radius*0.85);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.01;
ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.03;
ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.05;
ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
ctx.stroke();
}
</script>
</body>
</html>
Could you tell me how to clear these "previous second lines" by using globalCompositeOperation if such function can really do in my case? Thanks.
The reason i believe it is possible to do it through globalCompositeOperation, is because i had tried some test as below:
<html>
<body>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
</canvas>
<button onclick="myFunction()">Click me</button>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(50, 50, 50, 0, 2*Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(90,90);
ctx.stroke();
function myFunction() {
ctx.beginPath();
ctx.arc(50, 50, 50, 0, 2*Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.globalCompositeOperation = "destination-over";
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(90,90);
ctx.stroke();}
</script>
</body>
</html>

The globalCompositeOperation property cannot really be used for this purpose.
You can however do this:
Create a second canvas element that overlays the first (using position: absolute). It is transparent, so the other canvas will be seen through it.
After drawing the background on the original canvas, switch the context (ctx) to the second canvas, so that the timing function will only deal with the overlayed canvas
In the timing function, start by clearing that overlay canvas
setInterval(timing, 1000);
// Create second canvas that will overlay the first
var canvas2 = document.createElement("canvas");
canvas2.width = 500;
canvas2.height = 500;
canvas2.style.position = "absolute";
document.body.appendChild(canvas2);
var canvas1 = document.createElement("canvas");
canvas1.id = "canvas-1";
document.body.appendChild(canvas1);
canvas1.width = 500;
canvas1.height = 500;
canvas1.style.backgroundColor = "#3d3d3b";
var radius = (canvas1.height/2) * 0.9;
var ctx = canvas1.getContext("2d");
ctx.beginPath();
ctx.arc(250,250,radius,0,2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.beginPath();
ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
ctx.beginPath();
ctx.lineWidth = radius * 0.05;
ctx.stroke();
ctx.font = "40px Georgia"
ctx.textBaseline="middle";
ctx.textAlign="center";
for (i=1;i<13;i++){
ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
}
// Switch the context to the overlayed canvas
ctx = canvas2.getContext("2d");
function timing(){
// Clear the second canvas (only)
ctx.clearRect(0, 0, 500, 500);
const d = new Date();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.01;
ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.03;
ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.05;
ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
ctx.stroke();
}

You should probably be re-drawing the clockface for every new date that you render. I broke it down into individual pieces and used Promises but sure these were not strictly necessary.
(() => {
let cnvs;
let ctxt;
let radius;
const buildcanvas = () => new Promise((resolve, reject) => {
cnvs = document.createElement("canvas");
cnvs.id = "canvas-1";
cnvs.width = 500;
cnvs.height = 500;
cnvs.style.backgroundColor = "#3d3d3b";
document.body.appendChild(cnvs);
resolve(true)
});
const buildclockface = () => new Promise((resolve, reject) => {
radius = (cnvs.height / 2) * 0.9;
ctxt = cnvs.getContext("2d");
ctxt.beginPath();
ctxt.arc(250, 250, radius, 0, 2 * Math.PI);
ctxt.fillStyle = "white";
ctxt.fill();
ctxt.beginPath();
ctxt.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
ctxt.fillStyle = '#333';
ctxt.fill();
ctxt.beginPath();
ctxt.lineWidth = radius * 0.05;
ctxt.stroke();
ctxt.font = "40px Georgia"
ctxt.textBaseline = "middle";
ctxt.textAlign = "center";
for (i = 1; i < 13; i++) {
ctxt.fillText(
i.toString(),
250 + (Math.sin(i * Math.PI / 6) * radius * 0.8),
250 - (Math.cos(i * Math.PI / 6) * radius * 0.8)
);
}
resolve(true)
});
const showtime = (d) => new Promise((resolve, reject) => {
let d = new Date();
buildclockface();
secondhand(d);
minutehand(d);
hourhand(d);
});
const secondhand = (d) => {
ctxt.beginPath();
ctxt.moveTo(250, 250);
ctxt.lineWidth = radius * 0.01;
ctxt.lineTo(250 + (Math.sin(d.getSeconds() * Math.PI / 30) * radius * 0.85), 250 - Math.cos(d.getSeconds() * Math.PI / 30) * radius * 0.85);
ctxt.stroke();
}
const minutehand = (d) => {
ctxt.beginPath();
ctxt.moveTo(250, 250);
ctxt.lineWidth = radius * 0.03;
ctxt.lineTo(250 + (Math.sin(d.getMinutes() * Math.PI / 30) * radius * 0.78), 250 - Math.cos(d.getMinutes() * Math.PI / 30) * radius * 0.78);
ctxt.stroke();
}
const hourhand = (d) => {
ctxt.beginPath();
ctxt.moveTo(250, 250);
ctxt.lineWidth = radius * 0.05;
ctxt.lineTo(250 + (Math.sin(d.getHours() * Math.PI / 6) * radius * 0.7), 250 - Math.cos(d.getHours() * Math.PI / 6) * radius * 0.7);
ctxt.stroke();
}
buildcanvas()
.then(bool => setInterval(showtime, 1000))
.catch(err => alert(err))
})();

classic case of redraw the background here, nice little program though :)
setInterval(timing, 1000);
var canvas1 = document.createElement("canvas");
canvas1.id = "canvas-1";
document.body.appendChild(canvas1);
var radius = (canvas1.height/2) * 0.9;
var ctx = canvas1.getContext("2d");
function timing(){
canvas1.width = 500;
canvas1.height = 500;
canvas1.style.backgroundColor = "#3d3d3b";
ctx.beginPath();
ctx.arc(250,250,radius,0,2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.beginPath();
ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
ctx.beginPath();
ctx.lineWidth = radius * 0.05;
ctx.stroke();
ctx.font = "40px Georgia"
ctx.textBaseline="middle";
ctx.textAlign="center";
for (i=1;i<13;i++){
ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
}
const d = new Date();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.01;
ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.03;
ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.05;
ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
ctx.stroke();
}//timing
i just reread your post and you were asking about globalCompositeOperation
if you really dont want to redraw the background, and use globalCompositeOperation,
the globalCompositeOperation appears to operate only on filled areas, ie not lines
you would have to store your draw coordinates for each hand, redraw each hand at the old position - to remove it, then calculate new draw coordinates, draw and save
but check this out
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width=500;
canvas.height=500;
var ctx = canvas.getContext("2d");
ctx.globalCompositeOperation = 'xor';
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineWidth = 1;
ctx.lineTo(250,250);
ctx.strokeStyle = '#ff0000'
ctx.stroke();
ctx.beginPath();
ctx.moveTo(50,250);
ctx.lineWidth = 1;
ctx.lineTo(250,50);
ctx.strokeStyle = '#ff0000'
ctx.stroke();
ctx.fillStyle='blue';
ctx.fillRect(20,20,60,60);
seems to show it wouldnt work for your clock hands

After some further research of the globalCompositeOperation, I find it is necessary to add another instruction to tell the program that the globalCompositeOperation restore to its previous state once the "previous second line" is cleared, therefore i further modified my program as below, and eventually it proof that using globalCompositeOperation can solve the problem. However, I had to admit that simply redraw the clock background each time should be better solution in this case.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analog Clock-4 (using globalCompositeOperation)</title>
</head>
<body>
<script>
var canvas1 = document.createElement("canvas");
canvas1.id = "canvas-1";
document.body.appendChild(canvas1);
canvas1.width = 500;
canvas1.height = 500;
canvas1.style.backgroundColor = "#3d3d3b";
var radius = (canvas1.height/2) * 0.9;
var ctx = canvas1.getContext("2d");
setInterval(timing, 1000);
function timing(){
const d = new Date();
ctx.beginPath();
ctx.arc(250,250,radius,0,2*Math.PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.beginPath();
ctx.arc(250, 250, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
ctx.beginPath();
ctx.lineWidth = radius * 0.05;
ctx.stroke();
ctx.font = "40px Georgia"
ctx.textBaseline="middle";
ctx.textAlign="center";
for (i=1;i<13;i++){
ctx.fillText(i.toString(), 250+(Math.sin(i*Math.PI/6)*radius*0.8), 250-Math.cos(i*Math.PI/6)*radius*0.8);
}
ctx.globalCompositeOperation = "destination-over";
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineTo(250+(Math.sin((d.getSeconds()-1)*Math.PI/30)*radius*0.5), 250-Math.cos((d.getSeconds()-1)*Math.PI/30)*radius*0.5);
ctx.stroke();
ctx.globalCompositeOperation = "source-over";
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.01;
ctx.lineTo(250+(Math.sin(d.getSeconds()*Math.PI/30)*radius*0.85), 250-Math.cos(d.getSeconds()*Math.PI/30)*radius*0.85);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.03;
ctx.lineTo(250+(Math.sin(d.getMinutes()*Math.PI/30)*radius*0.78), 250-Math.cos(d.getMinutes()*Math.PI/30)*radius*0.78);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(250,250);
ctx.lineWidth = radius*0.05;
ctx.lineTo(250+(Math.sin(d.getHours()*Math.PI/6)*radius*0.7), 250-Math.cos(d.getHours()*Math.PI/6)*radius*0.7);
ctx.stroke();
}
</script>
</body>
</html>

Related

Multiple Javascript Clocks

I followed a tutorial for building a JS analogue clock and now I'd like to create an identical second clock on the page to experiment with.
When I've tried adding a second clock it overwrites the first clock - I believe this is because the variables ctx and radius are set globally.
Working example:
var canvas = document.getElementById("cambs_clock");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90;
//drawClock();
setInterval(drawClock, 1000);
function drawClock()
{
drawFace(ctx, radius);
drawNumbers(ctx, radius);
drawTime(ctx, radius);
}
I tried turning the block of code that generates the ctx and radius variables into a function and adding params to the drawClock function but that resulted in no clocks being drawn whatsoever:
function setupClock(clock)
{
var canvas = document.getElementById("cambs_clock");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90;
setInterval(drawClock(ctx, radius), 1000);
}
//drawClock();
//setInterval(drawClock, 1000);
setupClock();
function drawClock(ctx,radius)
{
drawFace(ctx, radius);
drawNumbers(ctx, radius);
drawTime(ctx, radius);
}
function drawFace(ctx, radius) {
var grad;
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
grad = ctx.createRadialGradient(0, 0 ,radius * 0.95, 0, 0, radius * 1.05);
grad.addColorStop(0, '#333');
grad.addColorStop(0.5, 'black');
grad.addColorStop(1, '#333');
ctx.strokeStyle = grad;
ctx.lineWidth = radius*0.05;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, radius * 0.1, 0, 2 * Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
}
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius * 0.15 + "px arial";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
ctx.rotate(ang);
ctx.translate(0, -radius * 0.85);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius * 0.85);
ctx.rotate(-ang);
}
}
function drawTime(ctx, radius){
var now = new Date();
var hour = now.getHours();
var minute = now.getMinutes();
var second = now.getSeconds();
//hour
hour = hour%12;
hour = (hour*Math.PI/6)+(minute*Math.PI/(6*60))+(second*Math.PI/(360*60));
drawHand(ctx, hour, radius*0.5, radius*0.07);
//minute
minute = (minute*Math.PI/30)+(second*Math.PI/(30*60));
drawHand(ctx, minute, radius*0.8, radius*0.07);
// second
second = (second*Math.PI/30);
drawHand(ctx, second, radius*0.9, radius*0.02);
}
function drawHand(ctx, pos, length, width) {
ctx.beginPath();
ctx.lineWidth = width;
ctx.lineCap = "round";
ctx.moveTo(0,0);
ctx.rotate(pos);
ctx.lineTo(0, -length);
ctx.stroke();
ctx.rotate(-pos);
}
</script>
The above code results in no clocks being drawn.
How would I be able to expand this code to accommodate multiple clocks?
(The end game is to have several clocks for different timezones in case that makes a difference)
You have tried the following:
function setupClock(clock)
{
var canvas = document.getElementById("cambs_clock");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90;
setInterval(() => drawClock(ctx, radius), 1000);
}
setupClock();
Notice that that function has an argument, clock which is not used. Furthermore, with document.getElementById("cambs_clock"); you always target the same canvas. So what you need in your case are two canvas's like
<canvas id="clock1"></canvas>
<canvas id="clock2"></canvas>
And the following script
function setupClock(clock)
{
var canvas = document.getElementById(clock);
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = radius * 0.90;
setInterval(drawClock(ctx, radius), 1000);
}
setupClock('clock1');
setupClock('clock2');
You can pass the radius as well as an argument if you like:
function setupClock(clock, radius) { ... }
setupClock('clock2', 20);
Endless possibilities, good luck!
Change
setInterval(drawClock(ctx, radius), 1000);
to
setInterval(() => {
drawClock(ctx, radius);
}, 1000);

Using Canvas via JavaScript, how can I draw a picture X number of times and within Y parameters?

I'm trying to draw a smiley face X number of times, and then the smiley face are Y radius from the center of the canvas. I also want to add a function where it allows the drawing to stay within the canvas, not outside as well as two functions to allow maximum number of smiley face in the circle and the maximum radius it can go up to. Eventually, I want my final product to end up looking something like this: https://imgur.com/VvDcFXq. I am new to Canvas and any help is greatly appreciated
<!DOCTYPE>
<html lang="en">
<meta charset="UTF-8">
<head>
<title>CPSC 1045 Assignment 7 - Smiley Rotator</title>
</head>
<body>
<h1>CPSC 1045 Assignment 7 - Simley Rotator</h1>
<p>Enter a number of smiles to draw<input type="number" min="0" max="9" id="NumberofSmiles"></p>
<p>Enter how far from the center of the canvas to draw them<input type="number" min="0" max="151" id="radius"></p>
<button id="draw">Draw</button><br>
<canvas id="myCanvas" height="400" width="400" style="border: 1px solid black">
<script>
let c, ctx, pos, centerX, centerY, radius, eyeRadius, eyeXOffset, eyeYOffset
document.getElementById("draw").onclick = checkNumber;
document.getElementById("draw").onclick = checkRadius;
function placement() {
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
centerX = c.width / 2;
centerY = c.height / 2;
radius = 70;
eyeRadius = 10;
eyeXOffset = 25;
eyeYOffset = 20;
reset();
}
function drawFace(){
// Draw the yellow circle
ctx.beginPath();
ctx.arc(centerX + pos.left, centerY + pos.top, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'yellow';
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.closePath();
}
function drawEyes(){
// Draw the eyes
let eyeX = centerX + pos.left - eyeXOffset;
let eyeY = centerY + pos.top - eyeYOffset;
ctx.beginPath();
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'black';
ctx.fill();
ctx.closePath();
ctx.beginPath();
eyeX = centerX + pos.left + eyeXOffset;
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'black';
ctx.fill();
ctx.closePath();
}
function drawMouth(){
// Draw the mouth
ctx.beginPath();
ctx.arc(centerX + pos.left, centerY + pos.top, 50, 0, Math.PI, false);
ctx.stroke();
ctx.closePath();
}
function draw(x,y) {
clear();
drawFace();
drawEyes();
drawMouth();
}
function clear() {
ctx.clearRect(0, 0, c.width, c.height);
}
function checkNumber() {
var input = document.getElementById("NumberofSmiles").value;
if (input > 9) {
alert("You have enter an invalid number");
}
}
function checkRadius() {
var inputs = document.getElementById("radius").value;
if (inputs > 150) {
alert("You have entered an invalid radius");
}
}
function checkmyvalue() {
checkRadius();
checkNumber();
}
</script>
</body>
</html>
I've tried to save as much as I could from your code.
Since you want to rotate the smileys I draw them around the origin of the canvas and then I translate to the position and rotate the context:
ctx.translate(pos.left,pos.top)
ctx.rotate(Angle);
Another change I've made: I've changed the radius of the smiley because I thought it was too big but you can change it back at what you want. Everything else will scale proportionally.
I hope this is what you need.
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
let center = {};
center.x = c.width / 2;
center.y = c.height / 2;
let face_radius = 30;
let eyeRadius = face_radius / 7;
let mouth_radius = face_radius * 0.7;
let eyeXOffset = face_radius * 0.36;
let eyeYOffset = face_radius * 0.28;
function drawFace() {
// Draw the yellow circle
ctx.beginPath();
ctx.arc(0, 0, face_radius, 0, 2 * Math.PI, false);
ctx.fillStyle = "yellow";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
}
function drawEyes() {
// Draw the eyes
let eyeX = - eyeXOffset;
let eyeY = - eyeYOffset;
ctx.beginPath();
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = "black";
ctx.fill();
ctx.closePath();
ctx.beginPath();
eyeX = eyeXOffset;
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = "black";
ctx.fill();
ctx.closePath();
}
function drawMouth() {
// Draw the mouth
ctx.beginPath();
ctx.arc(0, 0, mouth_radius, 0, Math.PI, false);
ctx.stroke();
ctx.closePath();
}
function clear() {
ctx.clearRect(0, 0, c.width, c.height);
}
function drawSmiley(pos,Angle) {
ctx.save();
ctx.translate(pos.left,pos.top)
ctx.rotate(Angle);
drawFace();
drawEyes();
drawMouth();
ctx.restore();
}
function checkNumber() {
let n = parseInt(NumberofSmiles.value);
if (n > 0 && n < 9) {
return n;
} else {
alert("You have enter an invalid number");
clear();
}
}
function checkRadius() {
let R = parseInt(_radius.value);
let maxR = c.width/2 - face_radius
if (R > 0 && R < maxR) {
return R;
} else {
alert("The radius has to be smaller than "+ maxR );
clear();
}
}
function checkmyvalue() {
let R = checkRadius();
let N = checkNumber();
let angle = 2 * Math.PI / N;
clear();
for (let i = 0; i < N; i++) {
let Angle = angle * i;
let pos = {};
pos.left = center.x + R * Math.cos(Angle);
pos.top = center.y + R * Math.sin(Angle);
drawSmiley(pos,Angle);
}
}
draw.addEventListener("click", checkmyvalue);
canvas{border:1px solid}
<h1>CPSC 1045 Assignment 7 - Simley Rotator</h1>
<p>Enter a number of smiles to draw<input type="number" min="0" max="9" id="NumberofSmiles"></p>
<p>Enter how far from the center of the canvas to draw them<input type="number" min="0" max="151" id="_radius"></p>
<button id="draw">Draw</button><br>
<canvas id="myCanvas" height="400" width="400" style="border: 1px solid black">

In Canvas via JavaScript, How Can I move an object to a different coordinate?

I have here A simple "game" with controls, and the reset button to reset the image drawn (smiley face) back to the center. I have tried this code with this an empty circle and I was able to move it around via the buttons without any problems. I proceeded to decorate the circle and now I am not able to move my drawn image. Any feedback or solution would be appreciated.
<!DOCTYPE>
<html lang="en">
<meta charset="UTF-8">
<body>
<button id="reset">Reset</button></br>
<canvas id="myCanvas" width="250" height="250" style="border:1px solid#000000;"></canvas></br>
<button id="left">Left</button>
<button id="up">Up</button>
<button id="down">Down</button>
<button id="right">Right</button>
<script>
let c, ctx, pos, centre = { x: 95, y: 50 }
window.onload = function drawSmile(){
let c = document.getElementById("myCanvas");
let ctx = c.getContext("2d");
let centerX = c.width / 2;
let centerY = c.height / 2;
let radius = 70;
let eyeRadius = 10;
let eyeXOffset = 25;
let eyeYOffset = 20;
// draw the yellow circle
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'yellow';
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw the eyes
ctx.beginPath();
var eyeX = centerX - eyeXOffset;
var eyeY = centerY - eyeXOffset;
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
var eyeX = centerX + eyeXOffset;
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'black';
ctx.fill();
// draw the mouth
ctx.beginPath();
ctx.arc(centerX, centerY, 50, 0, Math.PI, false);
ctx.stroke();
reset();
}
function draw(){
clear();
ctx.beginPath();
ctx.arc(centre.x + pos.left, centre.y + pos.top, 40, 0, 2*Math.PI);
ctx.stroke();
}
function clear(){
ctx.clearRect(0, 0, 2000, 1000);
}
function reset() {
pos = { left: 0, top: 0 }
draw();
}
function up() {
ctx.clearRect(0, 0, 2000, 1000);
pos.top-= 20;
draw();
}
function down() {
pos.top+= 20;
draw();
}
function left() {
pos.left-= 20;
draw();
}
function right() {
pos.left+= 20;
draw();
}
document.getElementById("reset").onclick = reset;
document.getElementById("up").onclick = up;
document.getElementById("down").onclick = down;
document.getElementById("left").onclick = left;
document.getElementById("right").onclick = right;
</script>
</body>
</html>
Do check the below code. It is similar to the last drawing of empty circle, I have just included the correct position calculations, and included closePath() method at appropriate places.
let c
, ctx
, pos
, centerX
, centerY
, radius
, eyeRadius
, eyeXOffset
, eyeYOffset
window.onload = function() {
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
centerX = c.width / 2;
centerY = c.height / 2;
radius = 70;
eyeRadius = 10;
eyeXOffset = 25;
eyeYOffset = 20;
reset();
}
function drawFace(){
// Draw the yellow circle
ctx.beginPath();
ctx.arc(centerX + pos.left, centerY + pos.top, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'yellow';
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.closePath();
}
function drawEyes(){
// Draw the eyes
var eyeX = centerX + pos.left - eyeXOffset;
var eyeY = centerY + pos.top - eyeYOffset;
ctx.beginPath();
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'black';
ctx.fill();
ctx.closePath();
ctx.beginPath();
eyeX = centerX + pos.left + eyeXOffset;
ctx.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'black';
ctx.fill();
ctx.closePath();
}
function drawMouth(){
// Draw the mouth
ctx.beginPath();
ctx.arc(centerX + pos.left, centerY + pos.top, 50, 0, Math.PI, false);
ctx.stroke();
ctx.closePath();
}
function draw() {
clear();
drawFace();
drawEyes();
drawMouth();
}
function clear() {
ctx.clearRect(0, 0, c.width, c.height);
}
function reset() {
pos = {
left: 0,
top: 0
}
draw();
}
function up() {
pos.top -= 20;
draw();
}
function down() {
pos.top += 20;
draw();
}
function left() {
pos.left -= 20;
draw();
}
function right() {
pos.left += 20;
draw();
}
document.getElementById("reset").onclick = reset;
document.getElementById("up").onclick = up;
document.getElementById("down").onclick = down;
document.getElementById("left").onclick = left;
document.getElementById("right").onclick = right;
<!DOCTYPE>
<html lang="en">
<meta charset="UTF-8">
<body>
<button id="reset">Reset</button><br>
<canvas id="myCanvas" width="250" height="250" style="border:1px solid#000000;"></canvas><br>
<button id="left">Left</button>
<button id="up">Up</button>
<button id="down">Down</button>
<button id="right">Right</button>
</body>
</html>

Moving and rotating with HTML5

What needs to be done:
The pink object should move from the left to the right (by itself). And then when it's 5px from the edge it should rotate 90 degrees.
Does anyone know how to do this?
I haven't been learning javascript for a long time, and it's the first time I'm creating something using HTML5. So it's all new. I really hope you can help me understand the code better and how I can make it move and rotate.
<!DOCTYPE html>
<html>
<head>
<script>
var canvas, ctx;
window.onload = function draw() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
var height = 90;
var width = 40;
var radius = width / 2;
ctx.clearRect(0,0, canvas.width, canvas.height);
ctx.fillStyle = "#FFE2E8";
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(70,20);
ctx.arc(70,40,20, -Math.PI/2, Math.PI/2);
ctx.lineTo(20,60);
ctx.lineTo(20,20);
ctx.closePath;
ctx.fill();
ctx.stroke();
requestAnimationFrame(draw);
}
function init() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
draw();
}
</script>
</head>
<body>
<canvas id="myCanvas" width="400" height="300" style="background:#00CC66">
</canvas>
</body>
</html>
var canvas, ctx;
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
var radius = 20;
var width = 70;
var margin = 5;
var offsetx = -20
var translate = {
x: 0,
y: 0,
xmax: canvas.width - margin - width + offsetx,
ymax: canvas.height - margin - width
};
var rotate = 0;
var to_radians = Math.PI / 180;
function draw() {
ctx.save();
ctx.translate(translate.x, translate.y);
if (translate.x >= translate.xmax) {
translate.x = translate.xmax;
if (rotate >= 90) {
rotate = 90;
} else {
rotate++;
}
ctx.rotate(rotate * to_radians);
} else {
translate.x++;
}
var height = 90;
var width = 40;
var radius = width / 2;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFE2E8";
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(70, 20);
ctx.arc(70, 40, 20, -Math.PI / 2, Math.PI / 2);
ctx.lineTo(20, 60);
ctx.lineTo(20, 20);
ctx.closePath;
ctx.fill();
ctx.stroke();
ctx.restore();
requestAnimationFrame(draw);
}
function init() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
draw();
}
init();
<canvas id="myCanvas" width="400" height="300" style="background:#00CC66">
</canvas>

Trying to make 2 happy/sad faces with canvas

I'm currently trying to make 1 happy face and 1 sad face with canvas but the problem is I can't get 2 faces to appear, only one.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My Site's Title</title>
</head>
<body>
<canvas id="myDrawing" width="800" height="200" style="border:1px solid #EEE">
</canvas>
<script>
var canvas = document.getElementById("myDrawing");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 0;
var endAngle = 2 * Math.PI;
function drawFace() {
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.stroke();
ctx.fillStyle = "yellow";
ctx.fill();
}
function drawSmile(){
var x = canvas.width / 2;
var y = 150
var radius = 40;
var startAngle = 1.1 * Math.PI;
var endAngle = 1.9 * Math.PI;
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.lineWidth = 7;
// line color
ctx.strokeStyle = 'black';
ctx.stroke();
}
function drawEyes(){
var centerX = 40;
var centerY = 0;
var radius = 10;
// save state
ctx.save();
// translate context so height is 1/3'rd from top of enclosing circle
ctx.translate(canvas.width / 2, canvas.height / 3);
// scale context horizontally by 50%
ctx.scale(.5, 1);
// draw circle which will be stretched into an oval
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// restore to original state
ctx.restore();
// apply styling
ctx.fillStyle = 'black';
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
//left eye
var centerX = -40;
var centerY = 0;
var radius = 10;
// save state
ctx.save();
// translate context so height is 1/3'rd from top of enclosing circle
ctx.translate(canvas.width / 2, canvas.height / 3);
// scale context horizontally by 50%
ctx.scale(.5, 1);
// draw circle which will be stretched into an oval
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// restore to original state
ctx.restore();
// apply styling
ctx.fillStyle = 'black';
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
drawFace()
function drawHappyFace(){
drawFace();
drawEyes();
drawSmile();
}
drawHappyFace();
// SECOND FACE - HAPPY FACE
<canvas id="canvas" width="200" height="200" style="border:1px solid #EEE">
</canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 0;
var endAngle = 2 * Math.PI;
function drawFace() {
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.stroke();
ctx.fillStyle = "yellow";
ctx.fill();
}
function drawSmile(){
var x = canvas.width / 2;
var y = 150
var radius = 40;
var startAngle = 1.9 * Math.PI;
var endAngle = 1.1 * Math.PI;
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.lineWidth = 7;
// line color
ctx.strokeStyle = 'black';
ctx.stroke();
}
function drawEyes(){
var centerX = 40;
var centerY = 0;
var radius = 10;
// save state
ctx.save();
// translate context so height is 1/3'rd from top of enclosing circle
ctx.translate(canvas.width / 2, canvas.height / 3);
// scale context horizontally by 50%
ctx.scale(.5, 1);
// draw circle which will be stretched into an oval
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// restore to original state
ctx.restore();
// apply styling
ctx.fillStyle = 'black';
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
//left eye
var centerX = -40;
var centerY = 0;
var radius = 10;
// save state
ctx.save();
// translate context so height is 1/3'rd from top of enclosing circle
ctx.translate(canvas.width / 2, canvas.height / 3);
// scale context horizontally by 50%
ctx.scale(.5, 1);
// draw circle which will be stretched into an oval
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// restore to original state
ctx.restore();
// apply styling
ctx.fillStyle = 'black';
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
drawFace()
function drawHappyFace(){
drawFace();
drawEyes();
drawSmile();
}
drawHappyFace();
</script>
</body>
</html>
</body>
</html>
I cna only get one of the faces to appear at once, for some reason, but I want both at the same time!
you can't have multiple functions with the same name because the second one overrides (hides) the first one; doesn't matter if they are in the same <script> tag or not (they can even be in different files)
change the name of the functions for the second face if you are such a beginner, but you should provide arguments to the function which will let you choose between multiple canvases and shapes using single function
it can look like this:
http://jsfiddle.net/Y5rUH/2/

Categories

Resources