I am an HTML beginner, and I am trying to make a game where you could control the player with your cursor. However, the below page seemingly shows it two times farther than the cursor. Is my code wrong? If yes, where is it wrong?
Thank you very much.
const canvas = document.getElementById('screen');
const ctx = canvas.getContext('2d');
var x = 0;
var y = 0;
canvas.style.top = 0;
canvas.style.left = 0;
function drawPlayer() {
ctx.fillStyle = "yellow";
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.fillRect(x, y, 10, 10);
ctx.fillStyle = "white";
ctx.strokeStyle = "white";
ctx.lineWidth = 0;
ctx.fillRect(x + 1, y + 1, 3, 3);
ctx.fillRect(x + 6, y + 1, 3, 3);
ctx.fillStyle = "black";
ctx.strokeStyle = "black";
ctx.lineWidth = 0;
ctx.fillRect(x + 2, y + 2, 2, 2);
ctx.fillRect(x + 7, y + 2, 2, 2);
ctx.fillStyle = "red";
ctx.strokeStyle = "red";
ctx.lineWidth = 0;
ctx.fillRect(x + 2, y + 6, 6, 2);
}
function handle(event) {
x = event.offsetX;
y = event.offsetY;
}
setInterval(function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPlayer();
}, 10)
<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>Cube Quest</title>
<style>
canvas {
width: 100%;
height: 100%;
position: absolute;
}
</style>
<script src="game.js"></script>
</head>
<body>
<canvas id="screen" onmousemove="handle(event)"></canvas>
</body>
</html>
That is because you will need to define the canvas width and height manually, otherwise it falls back to the default values of 300px for width and 150px for height, i.e.:
// Specify the dimensions of the canvas
ctx.canvas.width = canvas.clientWidth;
ctx.canvas.height = canvas.clientHeight;
You might also want to consider setting the canvas width and height when the viewport is resized. See working example here:
const canvas = document.getElementById('screen');
const ctx = canvas.getContext('2d');
var x = 0;
var y = 0;
canvas.style.top = '0px';
canvas.style.left = '0px';
function setCanvasSize() {
// Specify the dimensions of the canvas
ctx.canvas.width = canvas.clientWidth;
ctx.canvas.height = canvas.clientHeight;
}
// Set dimensions at runtime
setCanvasSize();
// Set dimensions when window is resized
window.addEventListener('resize', function () {
setCanvasSize();
});
function drawPlayer() {
ctx.fillStyle = "yellow";
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.fillRect(x, y, 10, 10);
ctx.fillStyle = "white";
ctx.strokeStyle = "white";
ctx.lineWidth = 0;
ctx.fillRect(x + 1, y + 1, 3, 3);
ctx.fillRect(x + 6, y + 1, 3, 3);
ctx.fillStyle = "black";
ctx.strokeStyle = "black";
ctx.lineWidth = 0;
ctx.fillRect(x + 2, y + 2, 2, 2);
ctx.fillRect(x + 7, y + 2, 2, 2);
ctx.fillStyle = "red";
ctx.strokeStyle = "red";
ctx.lineWidth = 0;
ctx.fillRect(x + 2, y + 6, 6, 2);
}
function handle(event) {
x = event.offsetX;
y = event.offsetY;
}
setInterval(function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPlayer();
}, 10)
<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>Cube Quest</title>
<style>
canvas {
width: 100%;
height: 100%;
position: absolute;
}
</style>
<script src="game.js"></script>
</head>
<body>
<canvas id="screen" onmousemove="handle(event)"></canvas>
</body>
</html>
Personally, though, I would not try to redraw the canvas using setInterval, because you only want to redraw when mousemove is detected. Your code can be further improved by simply listening to the mousemove event on the canvas element:
const canvas = document.getElementById('screen');
const ctx = canvas.getContext('2d');
var x = 0;
var y = 0;
canvas.style.top = '0px';
canvas.style.left = '0px';
function setCanvasSize() {
// Specify the dimensions of the canvas
ctx.canvas.width = canvas.clientWidth;
ctx.canvas.height = canvas.clientHeight;
}
// Set dimensions at runtime
setCanvasSize();
// Set dimensions when window is resized
window.addEventListener('resize', function () {
setCanvasSize();
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPlayer();
});
function drawPlayer() {
ctx.fillStyle = "yellow";
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.fillRect(x, y, 10, 10);
ctx.fillStyle = "white";
ctx.strokeStyle = "white";
ctx.lineWidth = 0;
ctx.fillRect(x + 1, y + 1, 3, 3);
ctx.fillRect(x + 6, y + 1, 3, 3);
ctx.fillStyle = "black";
ctx.strokeStyle = "black";
ctx.lineWidth = 0;
ctx.fillRect(x + 2, y + 2, 2, 2);
ctx.fillRect(x + 7, y + 2, 2, 2);
ctx.fillStyle = "red";
ctx.strokeStyle = "red";
ctx.lineWidth = 0;
ctx.fillRect(x + 2, y + 6, 6, 2);
}
function handle(event) {
x = event.offsetX;
y = event.offsetY;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPlayer();
}
<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>Cube Quest</title>
<style>
canvas {
width: 100%;
height: 100%;
position: absolute;
}
</style>
</head>
<body>
<canvas id="screen" onmousemove="handle(event)"></canvas>
</body>
</html>
Related
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>
I am trying to move to translate the canvas to (100,100) but it is not moving.
And the canvas is not responsive, when I try to change the size of canvas using CSS the circles are shrinking.
How can I add a button to toggle the drawing on canvas by the circle (i.e.: when button pressed don't draw then the circle should not draw on canvas, when pressed draw it should draw)
requestAnimationFrame(animate);
var ctx = canvas1.getContext('2d');
ctx.translate(100,100);
canvas1.width = innerWidth;
canvas1.height = innerHeight;
const bgCan = copyCanvas(canvas1);
const redSize = 6, blueSize = 5; // circle sizes on pixels
const drawSpeed = 1; // when button down draw speed in pixels per frame
var X = 50, Y = 50;
var angle = 0;
var mouseButtonDown = false;
document.addEventListener('mousedown', () => mouseButtonDown = true);
document.addEventListener('mouseup', () => mouseButtonDown = false);
function copyCanvas(canvas) {
const can = Object.assign(document.createElement("canvas"), {
width: canvas.width, height: canvas.height
});
can.ctx = can.getContext("2d");
return can;
}
function circle(ctx){
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(X, Y, redSize, 0, Math.PI*2);
ctx.fill();
}
function direction(ctx){
const d = blueSize + redSize + 3;
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(d * Math.sin(angle) + X, d * Math.cos(angle) + Y, blueSize, 0, Math.PI*2);
ctx.fill();
}
function animate(){
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bgCan, 0, 0);
if (mouseButtonDown) {
circle(bgCan.ctx);
X += Math.sin(angle) * drawSpeed;
Y += Math.cos(angle) * drawSpeed;
} else {
angle += 0.1;
circle(ctx);
}
direction(ctx);
requestAnimationFrame(animate);
}
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
border-width: 1px;
border-style: solid;
border-color: Black;
}
<!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>Canvas basics</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<input onclick="change()" type="button" value="Write" id="mybutton1"></input>
<script src="script.js"></script>
</body>
</html>
the translate(100, 100) gets reset by new width/height of the canvas. It just needs to be executed after width/height is set
The easiest way is to have a window resize listener and update canvas style width/height
I'm not sure why, but for some reason canvas has a higher z-index therefor button's onclick() never fired
In the code below I've added boundaries restriction, it's affected by translate(100, 100).
requestAnimationFrame(animate);
var ctx = canvas1.getContext('2d');
canvas1.width = innerWidth;
canvas1.height = innerHeight;
ctx.translate(100, 100);
const bgCan = copyCanvas(canvas1);
const redSize = 6,
blueSize = 5; // circle sizes on pixels
const drawSpeed = 1; // when button down draw speed in pixels per frame
const drawButton = document.getElementById("mybutton1");
var draw = true;
var X = 50,
Y = 50;
var angle = 0;
var mouseButtonDown = false;
document.addEventListener('mousedown', onMouseEvent);
document.addEventListener('mouseup', onMouseEvent);
function copyCanvas(canvas) {
const can = Object.assign(document.createElement("canvas"), {
width: canvas.width,
height: canvas.height
});
can.ctx = can.getContext("2d");
return can;
}
function circle(ctx, mousedown) {
if (mousedown && !draw)
return;
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(X, Y, redSize, 0, Math.PI * 2);
ctx.fill();
}
function direction(ctx) {
const d = blueSize + redSize + 3;
ctx.fillStyle = draw ? 'blue' : 'red';
ctx.beginPath();
ctx.arc(d * Math.sin(angle) + X, d * Math.cos(angle) + Y, blueSize, 0, Math.PI * 2);
ctx.fill();
}
function animate() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bgCan, 0, 0);
if (mouseButtonDown) {
circle(bgCan.ctx, mouseButtonDown);
const x = X + Math.sin(angle) * drawSpeed,
y = Y + Math.cos(angle) * drawSpeed;
if (x > (blueSize + redSize) * 2 && x < canvas1.width - (blueSize + redSize) * 2 &&
y > (blueSize + redSize) * 2 && y < canvas1.height - (blueSize + redSize) * 2)
{
X = x;
Y = y;
}
} else {
angle += 0.1;
circle(ctx);
}
direction(ctx);
requestAnimationFrame(animate);
}
function onMouseEvent(e) {
if (e.target === drawButton) {
if (e.type == "mouseup")
{
draw = !draw;
document.getElementById("mybutton1").value = draw ? "Write" : "Move";
}
return;
}
mouseButtonDown = e.type == "mousedown";
}
window.addEventListener("resize", handleResize)
function handleResize ()
{
const ratio = canvas1.width / canvas1.height;
let h = window.innerHeight,
w = h * ratio;
if (w > window.innerWidth) {
w = window.innerWidth;
h = w / ratio;
}
canvas1.style.width = w + 'px';
canvas1.style.height = h + 'px';
};
handleResize(); // First draw
#canvas1 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-width: 1px;
border-style: solid;
border-color: Black;
z-index: -1; /* let other elements receive events */
}
<!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>Canvas basics</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1" scaleMode='fill'></canvas>
<input type="button" value="Write" id="mybutton1" />
<script src="script.js"></script>
</body>
</html>
I have a blue circle which is revolving around the red circle and both are moving to right combined.
I need to draw on the canvas on the path of the red circle moves, but I don't want the blue circle to draw.
at present the circle is moving along right but I want the circle to move in the direction of blue circle at the point of time of mouse press.
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let positionX = 100;
let positionY = 100;
let X = 50;
let Y = 50;
let angle = 0;
function circle(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(X, Y, 20, 0, Math.PI*2);
ctx.closePath();
ctx.fill();
}
function direction(){
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(positionX + X, positionY + Y, 10, 0, Math.PI*2);
ctx.closePath();
ctx.fill();
}
function animate(){
ctx.clearRect(0,0, canvas.width, canvas.height);
circle();
direction();
positionX = 35 * Math.sin(angle);
positionY = 35 * Math.cos(angle);
X+=1;
angle += 0.1;
canvas.addEventListener('onclick', function(){
});
requestAnimationFrame(animate);
}
animate();
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
<!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>Canvas basics</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<script src="script.js"></script>
</body>
</html>
I have a red circle with a blue circle circling around it. I am trying to move the red circle in the direction of the blue circle when the mouse button is pressed.
But it currently only moves once.
I would like to have it moving continually while I press the mouse button, so that it keeps moving towards the direction of where the blue circle happens to be, keeping the blue circle ahead of it (and not circling) for as long as the mouse button is down.
Here is my code. Click the mouse button to see the reaction of the red circle:
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let positionX = 100;
let positionY = 100;
let X = 50;
let Y = 50;
let angle = 0;
canvas.addEventListener('mousedown', function(){
X += positionX;
Y += positionY;
})
function circle(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(X, Y, 20, 0, Math.PI*2);
ctx.closePath();
ctx.fill();
}
function direction(){
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(positionX + X, positionY + Y, 10, 0, Math.PI*2);
ctx.closePath();
positionX = 35 * Math.sin(angle);
positionY = 35 * Math.cos(angle);
angle += 0.1;
ctx.fill();
}
function animate(){
ctx.clearRect(0,0, canvas.width, canvas.height);
circle();
direction();
requestAnimationFrame(animate);
}
animate();
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<script src="script.js"></script>
</body>
</html>
If you want to continually have the red circle move towards the blue one, for as long as the mouse button is down, then you need to maintain the state of the mouse button. You can do that by keeping a global flag updated with a handler for the mousedown and the mouseup event.
Then move the code that updates X and Y into the animation loop: make that update when the flag is true (when the mouse button is down). On the other hand, only update the angle (for the blue circle) when the mouse button is not down:
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let positionX = 100;
let positionY = 100;
let X = 50;
let Y = 50;
let angle = 0;
let mouseButtonDown = false;
document.addEventListener('mousedown', () => mouseButtonDown = true);
document.addEventListener('mouseup', () => mouseButtonDown = false);
function circle(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(X, Y, 20, 0, Math.PI*2);
ctx.closePath();
ctx.fill();
}
function direction(){
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(positionX + X, positionY + Y, 10, 0, Math.PI*2);
ctx.closePath();
positionX = 35 * Math.sin(angle);
positionY = 35 * Math.cos(angle);
ctx.fill();
}
function animate(){
if (mouseButtonDown) {
X += positionX / 10;
Y += positionY / 10;
} else {
angle += 0.1;
}
ctx.clearRect(0,0, canvas.width, canvas.height);
circle();
direction();
requestAnimationFrame(animate);
}
animate();
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<script src="script.js"></script>
</body>
</html>
How could the size of the shadow change, every n seconds? I guess you have to constantly create a new circle and eliminate the previous one? How would this be done? And also, is not there a more optimal way?
function main() {
var canvas = document.getElementsByTagName("CANVAS")[0],
ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = document.documentElement.scrollHeight;
var cW = canvas.width,
cH = canvas.height;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, cW, cH);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "#FFF";
ctx.shadowBlur = 5;
ctx.shadowColor = "#FFF";
ctx.arc(cW/2, cH/2, 50, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
}
window.addEventListener("load", main);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<canvas></canvas>
</body>
</html>
Create a setInterval & an array of colors. Use Math.random to randomly select any color.
Modify the main function to accept colors as two parameters.
function main(bckColor, circleColor) {
var canvas = document.getElementsByTagName("CANVAS")[0],
ctx = canvas.getContext("2d");
if (canvas.height) {
canvas.height = 0;
}
canvas.width = window.innerWidth;
canvas.height = document.documentElement.scrollHeight;
var cW = canvas.width,
cH = canvas.height;
ctx.fillStyle = bckColor || "#000";
//ctx.fillStyle = "red" ;
ctx.fillRect(0, 0, cW, cH);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = circleColor || "#FFF";
ctx.shadowBlur = 5;
ctx.shadowColor = "#FFF";
ctx.arc(cW / 2, cH / 2, 50, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
}
var colorArray = ['red', 'green', 'yellow', 'blue', 'pink', 'brown', 'orange']
var changeColor = setInterval(function() {
let bckColor = colorArray[Math.floor(Math.random() * 7 + 1)];
let circleColor = colorArray[Math.floor(Math.random() * 7 + 1)];
main(bckColor, circleColor)
}, 2000)
window.addEventListener("load", main);
<canvas></canvas>
instead of animating the canvas itself with a redraw every x seconds, i would suggest using an overlay , create a circle div, position it over the canvas' circle and animate its shadow using css, you'll have all the controls you want like the shadow's color and distance, animation speed ..etc.
here's a fiddle ( the overlay is better positionned )
function main() {
var canvas = document.getElementsByTagName("CANVAS")[0],
ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = document.documentElement.scrollHeight;
var cW = canvas.width,
cH = canvas.height;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, cW, cH);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "#FFF";
ctx.shadowBlur = 5;
ctx.shadowColor = "#FFF";
ctx.arc(cW / 2, cH / 2, 50, 0, 2 * Math.PI, false);
ctx.fill();
ctx.closePath();
// position the overlay over the circle
overlay.style.top = (cH / 2 - 99) + 'px'
overlay.style.left = (cW / 2 - 50) + 'px'
}
window.addEventListener("load", main);
main()
const animationDuration = 2;
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
overlay.style.animationDuration = animationDuration + 's';
setInterval(function() {
overlay.style.color = getRandomColor()
}, animationDuration * 1000)
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
#overlay {
border-radius: 50%;
background: none;
animation: shadow linear infinite;
margin: auto;
transform: translate(0, 50%);
color: green;
position: absolute;
z-index: 99;
width: 100px;
height: 93px;
}
#keyframes shadow {
0% 100% {
box-shadow: 0 0 0
}
50% {
box-shadow: 0 0 50px 20px
}
}
<canvas></canvas>
<div id="overlay">
</div>