I need to prevent the cursor from going out of the window.
I've read that it's not possible, however I've already done that with a WebGL export from a Unity project. You first had to click the canvas, then the browser would show you a notification saying that you should press 'escape' to exit and get your cursor back.
Since a Unity WebGL canvas can do it, I assume it can be done without Unity?
Use the HTML5 Pointer Lock API
https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
Not my code example below, all creds to
https://github.com/mdn/dom-examples/tree/master/pointer-lock
HTML
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>Pointer lock demo</title>
<link type="text/css" rel="stylesheet" href="style.css">
</head>
<body>
<div class="information">
<h1>Pointer lock demo</h1>
<p>This demo demonstrates usage of the pointer lock API. Click on the canvas area and your mouse will directly control the ball inside the canvas, not your mouse pointer. You can press escape to return to the standard expected state.</p>
</div>
<canvas width="640" height="360">
Your browser does not support HTML5 canvas
</canvas>
<div id="tracker"></div>
<script src="app.js"></script>
</body>
</html>
JS
// helper function
const RADIUS = 20;
function degToRad(degrees) {
var result = Math.PI / 180 * degrees;
return result;
}
// setup of the canvas
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var x = 50;
var y = 50;
function canvasDraw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#f00";
ctx.beginPath();
ctx.arc(x, y, RADIUS, 0, degToRad(360), true);
ctx.fill();
}
canvasDraw();
// pointer lock object forking for cross browser
canvas.requestPointerLock = canvas.requestPointerLock ||
canvas.mozRequestPointerLock;
document.exitPointerLock = document.exitPointerLock ||
document.mozExitPointerLock;
canvas.onclick = function() {
canvas.requestPointerLock();
};
// pointer lock event listeners
// Hook pointer lock state change events for different browsers
document.addEventListener('pointerlockchange', lockChangeAlert, false);
document.addEventListener('mozpointerlockchange', lockChangeAlert, false);
function lockChangeAlert() {
if (document.pointerLockElement === canvas ||
document.mozPointerLockElement === canvas) {
console.log('The pointer lock status is now locked');
document.addEventListener("mousemove", updatePosition, false);
} else {
console.log('The pointer lock status is now unlocked');
document.removeEventListener("mousemove", updatePosition, false);
}
}
var tracker = document.getElementById('tracker');
var animation;
function updatePosition(e) {
x += e.movementX;
y += e.movementY;
if (x > canvas.width + RADIUS) {
x = -RADIUS;
}
if (y > canvas.height + RADIUS) {
y = -RADIUS;
}
if (x < -RADIUS) {
x = canvas.width + RADIUS;
}
if (y < -RADIUS) {
y = canvas.height + RADIUS;
}
tracker.textContent = "X position: " + x + ", Y position: " + y;
if (!animation) {
animation = requestAnimationFrame(function() {
animation = null;
canvasDraw();
});
}
}
Related
I have an HTML page that allows a person to draw. Its very simple. The code for It is included down below.
I'm trying to add a button that inserts text that is editable and can be resized within the canvas element.
I have tried many options, but found no success. Is there a way to do that in Javascript?
The code I have:
<head>
<meta charset="utf-8">
<title>
Web Paint!
</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
#draw{
border-color: yellow;
}
</style>
</head>
<body>
<input id="hex" placeholder="enter hex color"></input>
<canvas id="draw"></canvas>
<script>
var canvas = document.getElementById("draw");
var ctx = canvas.getContext("2d");
//resize();
// resize canvas when window is resized
function resize() {
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
}
// initialize position as 0,0
var pos = { x: 0, y: 0 };
// new position from mouse events
function setPosition(e) {
pos.x = e.clientX-160;
pos.y = e.clientY-5;
}
function draw(e) {
if (e.buttons !== 1) return; // if mouse is not clicked, do not go further
var color = document.getElementById("hex").value;
ctx.beginPath(); // begin the drawing path
ctx.lineWidth = 20; // width of line
ctx.lineCap = "round"; // rounded end cap
ctx.strokeStyle = color; // hex color of line
ctx.moveTo(pos.x, pos.y); // from position
setPosition(e);
ctx.lineTo(pos.x, pos.y); // to position
ctx.stroke(); // draw it!
}
// add window event listener to trigger when window is resized
window.addEventListener("resize", resize);
// add event listeners to trigger on different mouse events
document.addEventListener("mousemove", draw);
document.addEventListener("mousedown", setPosition);
document.addEventListener("mouseenter", setPosition);
</script>
</body>
I'm trying to make a little game to play around with canvas. I've decided to go for Flappy Bird as there are plenty of examples online. My goal was to make the game "responsive" for all devices. If you open it in a mobile or whichever screen, you should be able to play it normally.
For this purpose I drew a background image within the canvas that will adapt the height to the screen's height and repeat it through the x-axis.
The issue I'm having is that, when I animate the bird, this one leaves a trail behind it.
As like so:
An easy fix would be clearing the canvas as explained in this post:
Canvas image leaves weird trail when moving left
But it isn't possible because of the way I'm painting my background (I paint it once and repaint it if it resizes).
Question
How do I avoid this image trail with a static background? Is it maybe possible to have like a false static background that doesn't need to be repainted?
I obviously want to do it in the canvas, without needing to have an image in the html.
This is the code with the main functionality so that you can take a look:
const cvs = document.getElementById("canvas");
const ctx = cvs.getContext("2d");
cvs.style.height = "100vh";
cvs.style.width = "100vw";
var bird = new Image();
var bg = new Image();
bird.src = "https://lh3.googleusercontent.com/prntPuix7QxlhKXx6IBtTxv7PrdEJtmzFJ4mopScNS1_klze86BVNy3PqHbQn2UQ4JyJdix3XQ=w128-h128-e365";
bg.src = "https://opengameart.org/sites/default/files/bg_5.png";
let gravity = 1;
birdPositionY = 10;
birdPositionX = 50;
const draw = () => {
ctx.drawImage(bird, birdPositionX, birdPositionY);
birdPositionY += gravity;
birdPositionX += 3;
requestAnimationFrame(draw);
};
const resizeCanvas = () => {
const {
width,
height
} = cvs.getBoundingClientRect();
cvs.height = height;
cvs.width = width;
let x = 0,
y = 0;
while (x < cvs.width && y < cvs.height) {
ctx.drawImage(bg, x, y, 360, cvs.height);
x += bg.width;
}
};
bg.onload = () => {
resizeCanvas();
};
window.addEventListener("resize", resizeCanvas, false);
draw();
<!DOCTYPE html>
<html>
<head>
<title>Flappy Bird</title>
<meta name="viewport" content="width=device-width, user-scalable=no" />
<style>
html {
overflow: hidden;
}
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="index.js"></script>
</body>
</html>
Hope it did make sense!
Thanks in advance!
Draw your background on a canvas that you will keep off-screen, then draw this canvas every frame on your visible one before you draw your moving parts.
const cvs = document.getElementById("canvas");
const ctx = cvs.getContext("2d");
// our backround canvas that we won't append in the doc ("off-screen")
const background = cvs.cloneNode();
const bg_ctx = background.getContext('2d');
cvs.style.height = "100vh";
cvs.style.width = "100vw";
var bird = new Image();
var bg = new Image();
bird.src = "https://lh3.googleusercontent.com/prntPuix7QxlhKXx6IBtTxv7PrdEJtmzFJ4mopScNS1_klze86BVNy3PqHbQn2UQ4JyJdix3XQ=w128-h128-e365";
bg.src = "https://opengameart.org/sites/default/files/bg_5.png";
let gravity = 1;
birdPositionY = 10;
birdPositionX = 50;
const draw = () => {
// first draw the background canvas
ctx.drawImage(background, 0,0);
// then your persona
ctx.drawImage(bird, birdPositionX, birdPositionY);
birdPositionY += gravity;
birdPositionX += 3;
requestAnimationFrame(draw);
};
const resizeCanvas = () => {
const {
width,
height
} = cvs.getBoundingClientRect();
// resize both canvases
cvs.height = background.height = height;
cvs.width = background.width = width;
let x = 0,
y = 0;
while (x < cvs.width && y < cvs.height) {
// draw on the off-screen canvas
bg_ctx.drawImage(bg, x, y, 360, cvs.height);
x += bg.width;
}
};
bg.onload = () => {
resizeCanvas();
draw();
};
window.addEventListener("resize", resizeCanvas, false);
<!DOCTYPE html>
<html>
<head>
<title>Flappy Bird</title>
<meta name="viewport" content="width=device-width, user-scalable=no" />
<style>
html {
overflow: hidden;
}
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="index.js"></script>
</body>
</html>
I think you could just call resizeCanvas in your draw loop to redraw the background.
EDIT: Apparently that does not work. Another option is just drawing it in the draw loop like this:
const draw = () => {
ctx.clearRect(0, 0, cvs.width, cvs.height)
let x = 0, y = 0;
while (x < cvs.width && y < cvs.height) {
ctx.drawImage(bg, x, y, 360, cvs.height);
x += bg.width;
}
ctx.drawImage(bird, birdPositionX, birdPositionY);
birdPositionY += gravity;
birdPositionX += 3;
requestAnimationFrame(draw);
};
Edit: That did not work either. The other option would be to make two canvases and overlap them with one drawing the background and one drawing the bird.
I've read many posts and gone through several tutorials on HTML5 and the canvas specifically, but so far I have been unable to replicate my exact problem. If there is an answer for it already out there, please point me in the right direction.
My ultimate goal is to make a simple Pong game. I've drawn the basic objects using JavaScript and now I am trying to get the player (left) paddle to move. The problem I am running into with my current code is that instead of moving the paddle, it fills in the area it travels to. Through various trials and error of adapting and trying different methods I don't think the paddle is being elongated (adding pixels to the height), but it seems like a new paddle object is being created rather than the one being moved.
I've looked it over and over again (you guys aren't a first-ditch effort), but can't seem to figure out what's happening. Any help would be much appreciated.
// Requests a callback 60 times per second from browser
var animate = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) { window.setTimeout(callback, 1000/60) };
// Get canvas and set context
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.fillStyle = "white";
// Settle variables for canvas width and height
var canvas_width = 500;
var canvas_height = 400;
// Set varaibles for paddle width and height
var paddle_width = 15;
var paddle_height = 75;
// Initializes variables
var playerScore = 0;
var computerScore = 0;
var player = new Player();
var computer = new Computer();
var ball = new Ball((canvas_width/2),(canvas_height/2));
// Renders the pong table
var render = function() {
player.render();
computer.render();
ball.render();
};
var update = function() {
player.update();
};
// Callback for animate function
var step = function() {
update();
render();
animate(step);
};
// Creates paddle object to build player and computer objects
function Paddle(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.x_speed = 0;
this.y_speed = 0;
};
function Player() {
this.paddle = new Paddle(1, ((canvas_height/2) - (paddle_height/2)), paddle_width, paddle_height);
};
function Computer() {
this.paddle = new Paddle((canvas_width - paddle_width - 1), ((canvas_height/2) - (paddle_height/2)), paddle_width, paddle_height);
};
// Creates ball object
function Ball(x, y) {
this.x = x;
this.y = y;
this.radius = 10;
};
// Adds render functions to objects allowing them to be drawn on canvas
Ball.prototype.render = function() {
context.beginPath();
context.arc(this.x, this.y, this.radius, Math.PI * 2, false);
context.fillStyle = "white";
context.fill();
context.closePath();
};
Paddle.prototype.render = function() {
context.fillStyle = "white";
context.fillRect(this.x, this.y, this.width, this.height);
};
Player.prototype.render = function() {
this.paddle.render();
};
// Appends a move method to Paddle prototype
Paddle.prototype.move = function(x, y) {
this.y += y;
this.y_speed = y;
};
// Updates the location of the player paddle
Player.prototype.update = function() {
for(var key in keysDown) {
var value = Number(key);
if(value == 38) {
this.paddle.move(0, -4);
} else if (value == 40) {
this.paddle.move(0, 4);
} else {
this.paddle.move(0, 0);
}
}
};
Computer.prototype.render = function() {
this.paddle.render();
};
// Draws center diving line
context.strokeStyle = "white";
context.setLineDash([5, 3]);
context.beginPath();
context.moveTo((canvas_width/2), 0);
context.lineTo((canvas_width/2), canvas_height);
context.stroke();
context.closePath();
// Draws score on canvas
context.font = "40px Arial";
context.fillText('0', (canvas_width * .23), 50);
context.fillText('0', (canvas_width * .73), 50);
window.onload = function() {
animate(step);
};
var keysDown = {};
window.addEventListener("keydown", function(event) {
keysDown[event.keyCode] = true;
});
window.addEventListener("keyup", function(event) {
delete keysDown[event.keyCode];
});
My apologies: I cut the html/css code and meant to paste it, but forgot.
pong.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Pong</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas" width="500" height="400"></canvas>
<script src="main.js"></script>
</body>
</html>
style.css:
#canvas {
background-color: black;
}
The canvas itself has no "objects", it's just a bitmap, and anything you draw on it just changes the colours of certain pixels, making it look like it's drawing "on top" of what's already there but it doesn't even do that. It just flips pixel colours.
I don't see any code that "resets" the canvas for your next frames, so you're literally just drawing the same paddle at a different height value by colouring different pixels with the paddle's colours without recolouring the original pixels using the background colour.
The easiest solution here is to add a context.clearRect(0, 0, canvas.width, canvas.height); at the start of render():
var render = function() {
// clear the canvas so we can draw a new frame
context.clearRect(0,0,canvas.width,canvas.height);
// draw the frame content to the bitmap
player.render();
computer.render();
ball.render();
};
Note that this reveals you also need to draw your scores and center line every frame. Either that, or you need to make sure your paddles (and your ball) first restore the background colour on their old position, before drawing themselves on their new position, which would require considerably more code.
I am trying to animate a line in HTML5 canvas. The line is supposed to move according to the keyboard strokes (just as the red square). Unfortunately it gets longer but it should just move sideways...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
<title>Test</title>
</head>
<body onload="init();">
<canvas id="myCanvas" width="600" height="400">Your browser does not support the canvas tag.</canvas>
<script>
var canvas;
var ctx;
var incrementX = 0;
var x = 250;
var y = 350;
function init() {
// This function is called after the page is loaded
// 1 - Get the canvas
canvas = document.getElementById('myCanvas');
// 2 - Get the context
ctx = canvas.getContext('2d');
// 3 add key listeners to the window element
window.addEventListener('keydown', handleKeydown, false);
window.addEventListener('keyup', handleKeyup, false);
// 4 - start the animation
requestId = requestAnimationFrame(animationLoop);
}
function handleKeydown(evt) {
if (evt.keyCode === 37) {
//left key
incrementX = -1;
} else if (evt.keyCode === 39) {
// right key
incrementX = 1;
}
}
function handleKeyup(evt) {
incrementX = 0;
}
function animationLoop() {
// 1 - Clear
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2 Draw
drawLine(x, y);
// 3 Move
x += incrementX;
// call again mainloop after 16.6 ms (60 frames/s)
requestId = requestAnimationFrame(animationLoop);
}
function drawLine(x, y) {
ctx.save();
ctx.translate(x, y);
ctx.moveTo(0,0);
ctx.lineTo(100,0);
ctx.stroke();
ctx.fillStyle='red';
ctx.fillRect(35,30,20,20);
ctx.restore();
}
</script>
</body>
</html>
I created circles using canvas but can't delete the same on double click.`
var canvas,
context, shapes,
dragging = false, draggingtoMove = false,
dragStartLocation,dragEndLocation,
snapshot;
var numShapes;
function initiate() {
numShapes = 100;
shapes = [];
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
canvas.addEventListener('dblclick', dblclickerase);
}
function dblclickerase(evt)
{
var i;
//We are going to pay attention to the layering order of the objects so that if a mouse down occurs over more than object,
//only the topmost one will be dragged.
var highestIndex = -1;
//getting mouse position correctly, being mindful of resizing that may have occured in the browser:
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left)*(canvas.width/bRect.width);
mouseY = (evt.clientY - bRect.top)*(canvas.height/bRect.height);
//find which shape was clicked
for (i=0; i < shapes.length; i++) {
if (hitTest(shapes[i], mouseX, mouseY)) {
//draggingtoMove = true;
if (i > highestIndex) {
// here i want to delete the circle on double click but not getting logic, I can get mouse location but how to select the circle and delete it
}
}
}
}
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left,
y = event.clientY - canvas.getBoundingClientRect().top;
return {
x: x,
y: y
};
}
function takeSnapshot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreSnapshot() {
context.putImageData(snapshot, 0, 0);
}
function draw(position) {
var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2));
var i=0;
var tempX;
var tempY;
var tempRad;
var tempR;
var tempG;
var tempB;
var tempColor;
tempRad = radius;
tempX = dragStartLocation.x;
tempY = dragStartLocation.y;
tempColor = getRndColor();
tempShape = {x:tempX, y:tempY, rad:tempRad, color:tempColor};
shapes.push(tempShape);
context.beginPath();
context.arc(tempX, tempY, tempRad, 0, 2*Math.PI, false);
//context.closePath();
context.fillStyle = tempColor;
context.fill();
i++;
}
function dragStart(evt) {
dragging = true;
//if (dragging == true) {
dragStartLocation = getCanvasCoordinates(evt);
takeSnapshot();
//}
}
function hitTest(shape,mx,my) {
var dx;
var dy;
dx = mx - shape.x;
dy = my - shape.y;
//a "hit" will be registered if the distance away from the center is less than the radius of the circular object
return (dx*dx + dy*dy < shape.rad*shape.rad);
}
function drag(event) {
var position;
if (dragging === true) {
restoreSnapshot();
position = getCanvasCoordinates(event);
draw(position);
}
}
function dragStop(event) {
dragging = false;
restoreSnapshot();
var position = getCanvasCoordinates(event);
dragEndLocation = getCanvasCoordinates(event);
draw(position);
}
function getRndColor() {
var r = 255 * Math.random() | 0,
g = 255 * Math.random() | 0,
b = 255 * Math.random() | 0;
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
function eraseCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
addEventListener("load",initiate);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<canvas id="canvas" width="1020" height="640"></canvas>
<button onclick="eraseCanvas()" style="float: right;">Reset</button>
</body>
</html>
My question is how to delete the circle when double click on same, I
added 'dblClick' eventListener but still I am only able to perform the 'clearRect'
which will only clear the rectangle from start and end location which is little odd. Another thing I can't change the color to white which will not be valid.point as if my circle overlaps another will look odd.
You can't delete what you draw on the canvas like that. Once it's drawn on the canvas, it stays there and you have no way to access it except to read the pixel data - but that won't solve your problem because you can have overlapping circles of the same color.
To solve the issue, you must keep track of drawn circles, and redraw the full canvas every time it's needed (when adding a new circle, removing an old one, etc.). That way, when you want to delete a circle, you'd simply remove it from the list of circles (a simple array would work). But the important thing is that you need to clear and redraw the full canvas.
Summary: while having your canvas constantly redrawn (either on every tick or when a user interaction happens), your click'n'drag function should only be adding the circle to the circle list (specifying data like x, y, radius, color), while double-clicking a circle would look up that circle in the list, and remove it.