move player object towards cursor direction in canvas (slither.io style) - javascript

I've found similar questions but not exact. I can do this with mouse onclick but I'm not sure how to handle it with just 'mousemove' event.
my current situation JSFiddle for example
I'm trying to make it so the screen is translated towards the mouse while moving my player, not only on mousemove. But I'm not sure how else to go about this.
I have a player object that updates when mouse is moved.
function Player (x,y) {
this.x = x;
this.y = y;
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x,this.y,30,0,Math.PI * 2, false);
ctx.fillStyle = 'black';
ctx.fill();
}
this.update = function () {
this.x = playerX;
this.y = playerY;
this.draw();
}
}
this update function doesn't allow contstant movment, but when I try to implement something it ends up creating large amounts of lag - clearly I'm taking a wrong approach.
At the moment, I'm just detection where the mouse is in compared to the center of the screen.
document.addEventListener('mousemove', (event) => {
moveWorld(event);
});
function moveWorld(e){
var x = e.clientX;
var y = e.clientY;
// playerSpeed is 3
var centerX = window.innerWidth / 2
var centerY = window.innerHeight / 2
// move south
if (y < centerY) {
ctx.translate (0,playerSpeed);
playerY -= playerSpeed;
}
// move e
if (x < centerX) {
ctx.translate (playerSpeed,0);
playerX -=playerSpeed;
}
// move north
if (y > centerY) {
ctx.translate (0,-playerSpeed);
playerY += playerSpeed;
}
// move west
if (x > centerX) {
ctx.translate (-playerSpeed,0);
playerX +=playerSpeed;
}
drawWorld();
}
and finally to render it all.
function drawWorld(){
requestAnimationFrame(drawWorld); // refresh world
// CLEAR
ctx.clearRect(0,0,innerHeight,innerHeight);
// world
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 50, 50);
ctx.fillRect(110, 30, 50, 50);
// player
player.update();
}
document.addEventListener('mousemove', (event) => {
moveWorld(event);
});
drawWorld();

Assume mouse is at center of screen
On mousemove, update the mouse coordinates
Run moveWorld in a loop using the last known mouse coordinates for directions.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var playerX = canvas.width / 2;
var playerY = canvas.height / 2;
$(window).on("resize", function () {
$("#login,#game,#canvas").width( $(this).width());
$("#login,#game,#canvas").height( $(this).height());
playerX = canvas.width / 2;
playerY = canvas.height / 2;
}).resize();
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var playerSpeed = 3;
var mouseX = centerX;
var mouseY = centerY;
function mouseUpdate(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function moveWorld(){
var x = mouseX;
var y = mouseY;
var centerX = window.innerWidth / 2
var centerY = window.innerHeight / 2
// move south
if (y < centerY) {
ctx.translate (0,playerSpeed);
playerY -= playerSpeed;
}
// move e
if (x < centerX) {
ctx.translate (playerSpeed,0);
playerX -=playerSpeed;
}
// move north
if (y > centerY) {
ctx.translate (0,-playerSpeed);
playerY += playerSpeed;
}
// move west
if (x > centerX) {
ctx.translate (-playerSpeed,0);
playerX +=playerSpeed;
}
drawWorld();
requestAnimationFrame(moveWorld);
}
var player = new Player (playerX, playerY);
var counter = 0;
function drawWorld() {
// CLEAR
ctx.clearRect(0,0,innerHeight,innerHeight);
// world
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 50, 50);
ctx.fillRect(110, 30, 50, 50);
// player
player.update();
}
document.addEventListener('mousemove', (event) => {
mouseUpdate(event);
});
requestAnimationFrame(moveWorld);
function Player (x,y) {
this.x = x;
this.y = y;
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x,this.y,30,0,Math.PI * 2, false);
ctx.fillStyle = 'black';
ctx.fill();
}
this.update = function () {
this.x = playerX;
this.y = playerY;
this.draw();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>

Updated
As you are trying to develop a Slither.io system, I assume that you are looking for Slither.io Clone made by Loonride available on Github
Are you trying to achieve something like the following ? Phaser 3 : pointer - move event
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
var player = null;
function preload ()
{
this.load.spritesheet('balls', 'https://labs.phaser.io/assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
}
function create ()
{
player = this.add.image(0,0, 'balls', Phaser.Math.Between(0, 5));
this.input.on('pointermove', function (pointer) {
player.x = pointer.x;
player.y = pointer.y;
}, this);
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.15.1/dist/phaser.min.js"></script>
<div id="phaser-example" style="overflow: hidden;"></div>

The right way to do this kind of thing is to let the browser handle the 'frame tick' by using window.requestAnimationFrame.
Here's my fiddle:
https://jsfiddle.net/r5e2ht1v/4/
var mouseX, mouseY;
document.addEventListener('mousemove', (event) => {
mouseX = event.clientX;
mouseY = event.clientY;
});
function drawWorld(){
// CLEAR
ctx.clearRect(0,0,innerHeight,innerHeight);
// world
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 50, 50);
ctx.fillRect(110, 30, 50, 50);
// player
player.update();
moveWorld();
window.requestAnimationFrame(drawWorld);
}
window.requestAnimationFrame(drawWorld);
There are some more performance optimisations you can use here:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas

Related

HTML canvas shooting game: not shooting where I click and not linked when moving the player

I'm creating a canvas game and I'm working on the shooting aspect of the game. I've created a player that can move and a shooting function but I have two issues:
I would like to click in the canvas and have a projectile send to the click location. What is happening is that the click trigger location is disconnected from the canvas so that if I click around the top left of the page I can aim where I shoot.
I have have enabled movement with the player but I'm not sure how to link the movement of the player and update the origin of the projectile. I have set up movement functions for both player and projectile but I'm not sure where I'm going wrong.
Any help would be greatly appreciated!
https://codepen.io/blacksunmachine/pen/mdmxLxg
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
canvas.width = innerWidth / 2; // this takes up the whole page horizontally
canvas.height = innerHeight / 2; // this takes up the whole page verticallly
class Player {
constructor(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
draw() {
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fillStyle = this.color;
context.fill();
}
moveLeft() {
this.x -= 30;
if (this.x <= 0) {
this.x = 0;
}
}
moveRight() {
this.x += 30;
if (this.x >= canvas.width) {
this.x = canvas.width;
}
}
moveUp() {
this.y -= 30;
if (this.y <= 0) {
this.y = 0;
}
}
moveDown() {
this.y += 30;
if (this.y >= canvas.height) {
this.y = canvas.height;
}
}
}
// player position
let newPlayer1 = new Player(canvas.width / 2, canvas.height / 2, 10, "blue");
class Projectile {
constructor(x, y, radius, color, velocity) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.velocity = velocity;
}
draw() {
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fillStyle = this.color;
context.fill();
}
update() {
this.draw();
this.x = this.x + this.velocity.x;
this.y = this.y + this.velocity.y;
}
moveLeft() {
this.x -= 30;
if (this.x <= 0) {
this.x = 0;
}
}
moveRight() {
this.x += 30;
if (this.x >= canvas.width) {
this.x = canvas.width;
}
}
moveUp() {
this.y -= 30;
if (this.y <= 0) {
this.y = 0;
}
}
moveDown() {
this.y += 30;
if (this.y >= canvas.height) {
this.y = canvas.height;
}
}
}
function animate() {
requestAnimationFrame(animate);
context.clearRect(0, 0, canvas.width, canvas.height);
newPlayer1.draw();
projectiles.forEach((projectile) => {
projectile.update();
});
}
const projectile = new Projectile(
canvas.width / 2,
canvas.height / 2,
10,
"red",
{
x: -1,
y: -1,
}
);
const projectiles = [];
// need to link shooting with movement of the character
addEventListener("click", (event) => {
console.log(event)
const angle = Math.atan2(
event.clientY - canvas.height / 2,
event.clientX - canvas.width / 2
); // detemine direction of click
console.log(angle); // show where is being clicked
const velocity = {
// speed and direction of click
x: Math.cos(angle) * 20,
y: Math.sin(angle) * 20,
};
// from original position (in the middle of the screen) move a projectile with velocity
projectiles.push(
new Projectile(canvas.width / 2, canvas.height / 2, 10, "red", velocity)
);
});
// difference in X and Y when moving the character - not using this at the moment
// let deltaX = 0;
// let deltaY = 0;
// movement keys for player 1
window.addEventListener("keydown", handleKeyDown);
function handleKeyDown(event) {
switch (
event.keyCode // position of letters might change when on different layouts - same in every language
) {
case 37: // left
newPlayer1.moveLeft();
projectile.moveLeft()
break;
case 38: // up
newPlayer1.moveUp();
projectile.moveUp()
break;
case 39: // right
newPlayer1.moveRight();
projectile.moveRight()
break;
case 40: // down
newPlayer1.moveDown();
projectile.moveDown()
break;
default:
console.log("you cannot move player 1 like that");
}
console.log(event);
}
animate();
Here is the code of what i understood in your question.
canvas.addEventListener('click',(e)=>{
try{
let x=e.clientX;
let y=e.clientY;
let angle=Math.atan2(y-player.y-(player.rad/2),x-player.x-(player.rad/2));
x=player.x+(player.rad/2);
y=player.y+(player.rad/2);
let vx=Math.cos(angle)*15;
let vy=Math.sin(angle)*15;
projectileArray.push(new Projectile(x,y,vx,vy));
}catch(e){}
});
Variables explaination
player.rad is the radius of player
Here player is the object of the Player class
This function fires when you click on the canvas
The 15 value which i added in the line 8 and 9 of my code is just the velocity of the bullet
new Projectile(x,y,cx,vy) is the bullet which takes the current x, y position which is the players position and vx, vy after resolving the components of x and y of the click position from the player
projectileArray is just an array containing the bullets fired for rendering all of the bullets on the canvas
Actually this code is what i created for making my own shooting game. If you want to see how it works just click on this link to play it.
Shooter Game
This link is provided strictly for you to understand how it works and not for publicising my game.

Stop OutSide Drag And Drop On HTML5 Canvas And Restrict Overlap Of Arc On Drag On Canvas

I have the following problems:
I want to stop drag and drop outside of the canvas.
Stop overlapping of circles on drag(stop drag a circle over another circle).
This is my code which can be found for testing purposes in this link
// get canvas related references
var mouseIsDown = false;
var lastX = 0;
var lastY = 0;
var circles = [];
var offsetX = 0;
var offsetY = 0;
var canvas = null;
var ctx = null;
var canvasWidth = 0;
var canvasHeight = 0;
var count = 0;
makeCircle(20, 20, "salmon");
function makeCircle(x, y, fill){
var i = 0;
for(i =0 ; i< 5 ;i++){
var circle = {
x: x,
y: y,
r: 20,
isDragging: false,
fill: fill
}
circles.push(circle);
y = y + 20;
}
}
var i =0;
for(i = 0; i < circles.length;i++){
updateCanvas();
}
addEventsToCanvas()
// an array of objects that define different rectangles
function drawImageScaled(img) {
var canvas = ctx.canvas;
offsetX = canvas.getBoundingClientRect().left;
offsetY = canvas.getBoundingClientRect().top;
canvasWidth = canvas.width;
canvasHeight = canvas.height;
var hRatio = canvas.width / img.width;
var vRatio = canvas.height / img.height;
var ratio = Math.min(hRatio, vRatio);
var centerShift_x = (canvas.width - img.width * ratio) / 2;
var centerShift_y = (canvas.height - img.height * ratio) / 2;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, img.width, img.height,
centerShift_x, centerShift_y, img.width * ratio, img.height * ratio);
if (circles) {
for (var i = 0; i < circles.length; i++) {
var circle = circles[i];
drawCircle(circle);
ctx.fillStyle = circle.fill;
ctx.fill();
ctx.stroke();
}
}
}
function drawCircle(circle) {
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.beginPath();
ctx.fillStyle = circle.fill;
ctx.strokeStyle = "black";
ctx.font = "20px Georgia";
ctx.lineWidth = 10;
ctx.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI, false);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "#ffffff";
ctx.fill();
}
function updateCanvas(){
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
let img = new Image();
img.onload = drawImageScaled(img);
img.setAttribute('crossOrigin', 'anonymous');
img.src = "https://images.unsplash.com/photo-1532619675605-1ede6c2ed2b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1950&q=80"
}
function addEventsToCanvas(){
// listen for mouse events
canvas.onmousedown = handleMouseDown;
canvas.onmouseup = handleMouseUp;
canvas.onmousemove = handleMouseMove;
}
function handleMouseDown(e){
// tell the browser we're handling this mouse event
e.preventDefault();
e.stopPropagation();
let mouseX = parseInt(e.clientX - offsetX);
let mouseY = parseInt(e.clientY - offsetY);
// mousedown stuff here
lastX = mouseX;
lastY = mouseY;
for (var i = 0; i < circles.length; i++) {
var circle = circles[i];
var dx = circle.x - mouseX;
var dy = circle.y - mouseY;
if (dx * dx + dy * dy < circle.r * circle.r) {
circles[i].isDragging = true;
mouseIsDown = true;
}
}
}
function handleMouseUp(e){
// tell the browser we're handling this mouse event
e.preventDefault();
e.stopPropagation();
// mouseup stuff here
mouseIsDown = false;
for (var i = 0; i < circles.length; i++) {
circles[i].isDragging = false;
}
}
function handleMouseMove(e){
if (!mouseIsDown) {
return;
}
// tell the browser we're handling this mouse event
e.preventDefault();
e.stopPropagation();
let mouseX = parseInt(e.clientX - offsetX);
let mouseY = parseInt(e.clientY - offsetY);
// mousemove stuff here
for (var i = 0; i < circles.length; i++) {
var circle = circles[i];
if (circle.isDragging) {
//move
circle.x += (mouseX - lastX);
circle.y += (mouseY - lastY);
}
}
lastX = mouseX;
lastY = mouseY;
updateCanvas()
}
This is the output
To stop drag and drop outside the canvas you simply need to handle the mouseLeave event. From your example i updated your addEventsToCanvas function like this :
function addEventsToCanvas(){
// listen for mouse events
canvas.onmousedown = handleMouseDown;
canvas.onmouseup = handleMouseUp;
canvas.onmousemove = handleMouseMove;
canvas.onmouseleave = function(event) {mouseIsDown = false;}
}
Then for collisions it is just some maths at drawing time. Here is the code to handle collisions with canvas border and others circle :
function drawCircle(circle) {
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.beginPath();
ctx.fillStyle = circle.fill;
ctx.strokeStyle = "black";
ctx.font = "20px Georgia";
ctx.lineWidth = 10;
//avoid outside canvas x and y
circle.x = Math.min(circle.x, canvas.width-circle.r);
circle.x = Math.max(circle.x, circle.r);
circle.y = Math.min(circle.y, canvas.height-circle.r);
circle.y = Math.max(circle.y, circle.r);
//then check if circles are not too close
if(circle.isDragging) {
circles.forEach(function(c) {
if(c!=circle) {
//calculate distance
let dist = Math.sqrt(Math.pow(Math.abs(c.x-circle.x), 2) + Math.pow(Math.abs(c.y-circle.y), 2));
if(dist<circle.r*2) {
let angle = Math.atan2(c.y - circle.y, c.x - circle.x);
circle.x = c.x - Math.cos(angle)*circle.r*2;
circle.y = c.y - Math.sin(angle)*circle.r*2;
}
}
});
}
ctx.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI, false);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "#ffffff";
ctx.fill();
}
You can test the whole code on JSFiddle
EDIT : I also modified the mouse move handler by replacing
circle.x += (mouseX - lastX);
circle.y += (mouseY - lastY);
by
circle.x = mouseX;
circle.y = mouseY;
It permit to place the circle center at mouse position everytime it is possible.

how to draw control on canvas using mobile touch while having scroll on screen

I'm trying to create a mobile app in which user can draw multiple controls (rectangle) on a canvas.
It works fine if canvas fits the screen, but the controls are not drawn well when I increase the canvas height and width out of the screen (a scroll appear when i increase the size of canvas more than the size of mobile screen).
How to fix this in scroll mode?
Use Chrome Inspect mobile view (iPhone 6/7/8) to test this code
Use Pointer drag to draw controls
<html>
<body>
<canvas id="canvas_transparent" height="3000" width="1000" style="border: 1px solid black;"></canvas>
</body>
</html>
<script type="text/javascript">
var canvas = document.getElementById("canvas_transparent");
var ctx = canvas.getContext("2d");
var endX;
var startX;
var endY;
var startY;
var positions = [];
//--------------------------------------------------------
canvas.addEventListener("touchstart", function(e) {
mousePos = getTouchPos(canvas, e);
startX = mousePos.x;
startY = mousePos.y;
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousedown", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);
//---------------------------------------------------------
canvas.addEventListener("touchend", function(e) {
positions.push({
s_x: startX,
s_y: startY,
e_x: endX,
e_y: endY
});
drawAllControls();
console.log(positions);
var mouseEvent = new MouseEvent("mouseup", {});
canvas.dispatchEvent(mouseEvent);
}, false);
//---------------------------------------------------------
canvas.addEventListener("touchmove", function(e) {
var touch = e.touches[0];
endX = touch.clientX;
endY = touch.clientY;
drawSquare()
var mouseEvent = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);
//--------------------------------------------------------
// Get the position of a touch relative to the canvas
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].clientX - rect.left,
y: touchEvent.touches[0].clientY - rect.top
};
}
//--------------------------------------------------------
function drawSquare() {
// creating a square
var w = endX - startX;
var h = endY - startY;
var offsetX = (w < 0) ? w : 0;
var offsetY = (h < 0) ? h : 0;
var width = Math.abs(w);
var height = Math.abs(h);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.rect(startX + offsetX, startY + offsetY, width, height);
ctx.fillStyle = "#222222";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
//----------------------------------------------------------
function drawAllControls() {
for (var i = 0; i < positions.length; i++) {
var w = positions[i].e_x - positions[i].s_x;
var h = positions[i].e_y - positions[i].s_y;
var offsetX = (w < 0) ? w : 0;
var offsetY = (h < 0) ? h : 0;
var width = Math.abs(w);
var height = Math.abs(h);
ctx.beginPath();
ctx.rect(positions[i].s_x + offsetX, positions[i].s_y + offsetY, width, height);
ctx.fillStyle = "#222222";
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
}
</script>
clientX/clientY do not factor in scroll offset. I think you want pageX/pageY
https://developer.mozilla.org/en-US/docs/Web/API/Touch
// Get the position of a touch relative to the canvas
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].pageX - rect.left,
y: touchEvent.touches[0].pageY - rect.top
};
}
Hi i think this will resolve your problem. Put this in the head tag
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
working link page

Limit values between two points on an arc?

I'm trying to adapt the code from a previous question on circular dial controls. The concept is pretty similar to this one, except I would like to define a range in which the dial cannot be selected. Consider the volume controls/dials in hardware; they often have these 'dead zones' where they can't be turned:
How can I replicate this in JavaScript? Here's the adapted code so far:
function Dial(size) {
var dial = this;
var canvas = document.querySelector('#c');
var ctx = canvas.getContext("2d");
var pi2 = Math.PI*2;
this.from = 0.75 * Math.PI;
this.to = 0.25 * Math.PI;
this.value = this.from;
var radius = size / 2 - 10;
this.draw = function() {
ctx.save();
ctx.clearRect(0,0,size,size);
ctx.translate(size/2,size/2);
ctx.beginPath();
ctx.strokeStyle = "silver";
ctx.lineWidth = 2;
ctx.arc(0, 0, radius, this.from, this.to);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = "green";
ctx.strokeStyle = "black";
ctx.arc(-radius*Math.sin(this.value),
-radius*Math.cos(this.value),
8, 0, 2*Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
};
var getMousePos = function(canvas, evt) {
return {
x: event.pageX - canvas.offsetLeft,
y: event.pageY - canvas.offsetTop
};
};
var inBounds = function(pos) {
return Math.hypot(
size / 2 - radius * Math.sin(dial.value) - pos.x,
size / 2 - radius * Math.cos(dial.value) - pos.y
) <= 8;
};
canvas.addEventListener("mousemove", function(evt) {
var pos = getMousePos(canvas, evt);
if (dial.markerMoving) {
if (pos.x == size/2 && pos.y == size/2)
return;
dial.value = Math.atan2(size/2-pos.x,size/2-pos.y);
}
dial.draw();
}, false);
canvas.addEventListener("mousedown", function(evt) {
var pos = getMousePos(canvas, evt);
dial.markerMoving = inBounds(pos);
}, false);
canvas.addEventListener("mouseup", function(evt) {
dial.markerMoving = false;
}, false);
this.draw();
};
new Dial(150);
<canvas id="c"></canvas>
Bonus points if you can work out how to display a 'range' on the selection - from the starting point on the dial to the selection point.
Turns out it was pretty straightforward, using Math.abs.
function Dial(size) {
var dial = this;
var canvas = document.querySelector('#c');
var ctx = canvas.getContext("2d");
var pi2 = Math.PI*2;
this.from = 0.75 * Math.PI;
this.to = 0.25 * Math.PI;
this.value = this.from;
var radius = size / 2 - 10;
this.draw = function() {
ctx.save();
ctx.clearRect(0,0,size,size);
ctx.translate(size/2,size/2);
ctx.beginPath();
ctx.strokeStyle = "silver";
ctx.lineWidth = 2;
ctx.arc(0, 0, radius, this.from, this.to);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = "green";
ctx.strokeStyle = "black";
ctx.arc(-radius*Math.sin(this.value),
-radius*Math.cos(this.value),
8, 0, 2*Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
};
var getMousePos = function(canvas, evt) {
return {
x: event.pageX - canvas.offsetLeft,
y: event.pageY - canvas.offsetTop
};
};
var inBounds = function(pos) {
return Math.hypot(
size / 2 - radius * Math.sin(dial.value) - pos.x,
size / 2 - radius * Math.cos(dial.value) - pos.y
) <= 8;
};
canvas.addEventListener("mousemove", function(evt) {
var pos = getMousePos(canvas, evt);
if (dial.markerMoving) {
if (pos.x == size/2 && pos.y == size/2) {
return;
}
var radians = Math.atan2(size/2-pos.x,size/2-pos.y);
if (Math.abs(radians) < dial.from) {
dial.value = radians;
dial.draw();
}
}
}, false);
canvas.addEventListener("mousedown", function(evt) {
var pos = getMousePos(canvas, evt);
dial.markerMoving = inBounds(pos);
}, false);
canvas.addEventListener("mouseup", function(evt) {
dial.markerMoving = false;
}, false);
this.draw();
};
new Dial(150);
<canvas id="c"></canvas>

Drawing a rectangle on Canvas

I am trying to create a simple canvas program where the user can consistently create new shapes. This one is just a basic rectangle creator (I am hoping to expand it more to circles, lines, and maybe even other stuff). Right now though I have created something that is working in a really weird way.
<html>
<head>
<meta chartset="utf-8">
<title>Dragging a square</title>
<script type="text/javascript">
var canvas, context, startX, endX, startY, endY;
var mouseIsDown = 0;
function init() {
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mousemove", mouseXY, false);
document.body.addEventListener("mouseup", mouseUp, false);
}
function mouseUp() {
mouseIsDown = 0;
//mouseXY();
}
function mouseDown() {
mouseIsDown = 1;
startX = event.clientX;
startY = event.clientY;
mouseXY();
}
function mouseXY(eve) {
if (!eve) {
var eve = event;
}
endX = event.pageX - canvas.offsetLeft;
endY = event.pageY - canvas.offsetTop;
drawSquare();
}
function drawSquare() {
// creating a square
var width = Math.abs(startX - endX);
var height = Math.abs(startY - endY);
context.beginPath();
context.rect(startX, startY, width, height);
context.fillStyle = "yellow";
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
}
</script>
</head>
<body onload="init()">
<canvas id="canvas" width="400" height="400" style="border: 1px solid black; cursor: pointer;"></canvas>
</body>
</html>
Sorry about the slightly weird formatting when I copy and pasted my code. I think the problem is my mouseXY function. What I want is the user to click somewhere on the canvas a drag the mouse to create a rectangle, when the user lets go that is the end of that operation and they can create a whole new rectangle right after. At this point the program kind of just lets me click and create a new rectangle but if I let go of the mouse button it doesn't stop, in fact I have to click again to make it stop which then creates a new rectangle. I am still very new to this and I am having a lot of trouble with this, I will continue to work on this and if I figure it out I will let the site know. Thank you and have a great day!
Well I got this to work (thanks to #Ken) but now I am trying to solve a new problem. I want to be able to put multiple rectangles on the canvas. I created a function that represents the Rectangle and then created a draw function within the rectangle function to draw out a rectangle. I created a new function called addShape() that ideally creates the rectangle object and pushes into an array called square and drawShapes() that is supposed to erase everything on the canvas and redraws everything. Here is what I have so far:
<html>
<head>
<meta chartset="utf-8">
<title>Dragging a square</title>
<script type="text/javascript">
function Rectangle(canvas, x, y, width, height,color) {
//this.context = canvas.getContext("2d");
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.draw = function() {
this.context.globalAlpha = 0.85;
this.context.beginPath();
this.context.rect(this.x, this.y, this.width, this.height);
this.context.fillStyle = this.color;
this.context.strokeStyle = "black";
this.context.lineWidth = 1;
this.context.fill();
this.context.stroke();
};
};
// hold the canvas and context variable, as well as the
// starting point of X and Y and the end ones
var canvas, context, startX, endX, startY, endY;
var mouseIsDown = 0;
// An array that holds all the squares
var squares = [];
window.onload = function() {
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.addEventListener("mousedown", mouseDown, false);
canvas.addEventListener("mousemove", mouseXY, false);
canvas.addEventListener("mouseup", mouseUp, false);
}
function mouseUp(eve) {
if (mouseIsDown !== 0) {
mouseIsDown = 0;
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
//Square(); //update on mouse-up
addShape(); // Update on mouse-up
}
}
function mouseDown(eve) {
mouseIsDown = 1;
var pos = getMousePos(canvas, eve);
startX = endX = pos.x;
startY = endY = pos.y;
// Square(); //update
addShape();
}
function mouseXY(eve) {
if (mouseIsDown !== 0) {
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
//Square();
addShape();
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function addShape() {
var w = endX - startX;
var h = endY - startY;
var offsetX = (w < 0) ? w : 0;
var offsetY = (h < 0) ? h : 0;
var width = Math.abs(w);
var height = Math.abs(h);
var s = new Rectangle(startX + offsetX, startY + offsetY, width, height, "yellow");
squares.push(s);
// Update the display
drawShapes();
}
function drawShapes() {
context.clearRect(0,0,canvas.width,canvas.height);
for (var i = 0; i < squares.length; i++) {
var shape = squares[i];
shape.draw();
};
}
function clearCanvas() {
squares = [];
drawShapes();
}
</script>
</head>
<body onload="addShape()">
<canvas id="canvas" width="400" height="400" style="border: 1px solid black; cursor: pointer;"></canvas><br>
<button onclick="clearCanvas()">Clear Canvas</button>
</body>
</html>
I am pretty sure I broke the original code... thank you for any help!
You need to modify a couple of things in the code: (edit: there are many issues with this code. I went through some of them inline here, but haven't tested. If you put it in a fiddle it's easier for us to check)..
Fiddle
When mouse down occur initialize both start and end points. Call a common draw function that is not dependent on the event itself:
function mouseDown(eve) {
mouseIsDown = 1;
var pos = getMousePos(canvas, eve);
startX = endX = pos.x;
startY = endY = pos.y;
drawSquare(); //update
}
At mouse up, only register if isMouseDown is true, else this function will handle all incoming up-events (as you have attatched it to document, which is correct - window could have been used too):
function mouseUp(eve) {
if (mouseIsDown !== 0) {
mouseIsDown = 0;
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
drawSquare(); //update on mouse-up
}
}
Only draw if mouseisdown is true:
function mouseXY(eve) {
if (mouseIsDown !== 0) {
var pos = getMousePos(canvas, eve);
endX = pos.x;
endY = pos.y;
drawSquare();
}
}
In addition you will need to clear the previous area of the rectangle before drawing a new or else it won't show when you draw a bigger rectangle and then move the mouse back to draw a smaller one.
For simplicity you can do:
function drawSquare() {
// creating a square
var width = Math.abs(startX - endX);
var height = Math.abs(startY - endY);
context.clearRect(0, 0, context.width, context.height);
//or use fillRect if you use a bg color
context.beginPath();
context.rect(startX, startY, width, height);
context.fillStyle = "yellow";
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
}
Use this for mouse position:
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}

Categories

Resources