I want to be able to orient something toward the mouse on an HTML5 canvas. But when I use Math.atan2 and the other trig functions, the directions get messed up. It rotates in the opposite direction that it should and it's usually off by 90 degrees.
It will probably be easier if you see it for yourself. Here's the javascript:
var mouseX=0;
var mouseY=0;
var canvas = document.getElementById("world");
var context = canvas.getContext("2d");
function mouseMoveHandler(event) {
mouseX = event.clientX;
mouseY = event.clientY;
}
function windowResizeHandler() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
function loop() {
// Clear Screen
context.clearRect(0,0,canvas.width,canvas.height);
// Calculate the angle to the mouse
a = Math.atan2(mouseX-canvas.width/2,mouseY-canvas.height/2);
// Draw a line in the direction of the mouse
context.beginPath();
context.fillStyle = "#000000";
context.moveTo(canvas.width/2+10, canvas.height/2);
context.lineTo(canvas.width/2-10, canvas.height/2);
context.lineTo(canvas.width/2+Math.cos(a)*100, canvas.height/2+Math.sin(a)*100);
context.fill();
}
document.addEventListener('mousemove', mouseMoveHandler, false);
window.addEventListener('resize', windowResizeHandler, false);
windowResizeHandler();
setInterval(this.loop, 1000 / 30 );
And here's the HTML:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<canvas id='world'></canvas>
<script type="text/javascript" src="test.js"></script>
</body>
</html>
You can see it in action here: http://sidefofx.com/projects/stackOverflowQuestion/
How can I make the line point in the direction of the mouse?
I rechecked and what you're doing wrong (and I've done this error a few times myself) is that atan2 accepts first the y coordinate, then the x coordinate.
MDC says:
Note that the arguments to this function pass the y-coordinate first and the x-coordinate second.
So
a = Math.atan2(mouseX-canvas.width/2,mouseY-canvas.height/2);
should be
a = Math.atan2(mouseY-canvas.height/2, mouseX-canvas.width/2);
Test updated: http://jsfiddle.net/79FaY/1/
Related
I have been looking around for this function and thus far I just can't find any I can make any sense of. I already have a rotating function to make it equal to the position but slowly is proving to be a bit harder with 0-360 and all.
I am using a html canvas 2d context to render the objects on a Cartesian coordinate system .
I would like object1 to face at positionX and positionY at a turn rate (R) , fairly straightforward.
there is no need for me to supply any code since your likely going to make your own anyways. But I will anyways here you go:
let faceAt = function (thisObject,positionX,positionY) {
let desiredLocationX = positionX - thisObject.transform.x;
let desiredLocationY = positionY -thisObject.transform.y;
thisObject.transform.rotation = Math.degrees(Math.atan2(desiredLocationY, desiredLocationX));
};
The (Math.degrees) function converts radians to degrees.
This thread says it all : https://www.google.ca/amp/s/jibransyed.wordpress.com/2013/09/05/game-maker-gradually-rotating-an-object-towards-a-target/amp/
This question is quite unclear. But, I'm assuming you essentially just want to rotate an element around an arbitrary point on a HTML5 canvas.
On a canvas, you can only draw one element at a time. You can't really manipulate singular elements - for example, you can't rotate an element by itself. Instead, you'd need to rotate the entire canvas. This will always rotate around the centre of the canvas, but if you move the canvas origin, then you will draw on a different part of the canvas; thus allowing you to rotate around a point.
Check out the following example. You can click anywhere on the canvas to make the square rotate around that point. Hopefully this is what you are after:
let cv = document.getElementById("cv");
let ctx = cv.getContext("2d");
let angle = 0;
//Variables you can change:
let speed = 1; //Degrees to rotate per frame
let pointX = 250; //The x-coord to rotate around
let pointY = 250; //The y-coord to rotate around
ctx.fillStyle = "#000";
setInterval(()=>{ //This code runs every 40ms; so that the animation looks smooth
angle = (angle + speed) % 360; //Increment the angle. Bigger changes here mean that the element will rotate faster. If we go over 360deg, reset back to 0.
ctx.clearRect(0, 0, 400, 400); //Clear away the previous frame.
//Draw the point we are rotating around
ctx.beginPath();
ctx.arc(pointX,pointY,5,0,2*Math.PI);
ctx.fill();
ctx.closePath();
ctx.save(); //Save the state before we transform and rotate the canvas; so we can go back to the unrotated canvas for the next frame
ctx.translate(pointX, pointY); //Move the origin (0, 0) point of the canvas to the point to rotate around. The canvas always rotates around the origin; so this will allow us to rotate around that point
ctx.rotate(angle*Math.PI/180); //Rotate the canvas by the current angle. You can use your Math.degrees function to convert between rads / degs here.
ctx.fillStyle = "#f00"; //Draw in red. This is also restored when ctx.restore() is called; hence the point will always be black; and the square will always be red.
ctx.fillRect(0, 0, 50, 50); //Draw the item we want rotated. You can draw anything here; I just draw a square.
ctx.restore(); //Restore the canvas state
}, 40);
//Boring event handler stuff
//Move the point to where the user clicked
//Not too robust; relys on the body padding not changing
//Really just for the demo
cv.addEventListener("click", (event)=>{
pointX = event.clientX - 10;
pointY = event.clientY - 10;
});
#cv {
border:solid 1px #000; /*Just so we can see the bounds of the canvas*/
padding:0;
margin:0;
}
body {
padding:10px;
margin:0;
}
<canvas id="cv" width="400" height="400"></canvas><br>
Click on the canvas above to make the rectangle rotate around the point that was clicked.
window.onload=function(){
var c = document.getElementById('canvas'),
ctx = c.getContext('2d'),
x=0, y=0, cnt=1;
for(var i=0;i<(window.innerWidth)/10;i++){
ctx.moveTo(x, y); x+=5;
if(cnt%2){
y=5; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}else{
y=0; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}
}
}
<canvas id="canvas" style="width:100%; height:250px"></canvas>
If you run the above code then the resolution of lines in the zig-zag pattern in the if fine but in here you can see the image the resoultion of this pattern is very poor (please click on this image to view this problem):
what i have tried is that i have changed the condition (window.innerWidth)/10 to (winodw.innerWidth)/4 and x+=5 to x+=2
but what it does is that it makes the line so thick and bad that you don't want to see it.
so, what should i do to increase the resolution of the lines of the pattern?
Just make sure your canvas element is as big as you are displaying it.
i added c.width = windows.innerWidth and also c.heigth = 250 and the resolution looks correct now.
window.onload=function(){
var c = document.getElementById('canvas'),
ctx = c.getContext('2d'),
x=0, y=0, cnt=1;
c.width = window.innerWidth;
c.height = 250;
for(var i=0;i<(window.innerWidth);i++){
ctx.moveTo(x, y); x+=5;
if(cnt%2){
y=5; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}else{
y=0; cnt++;
ctx.lineTo(x, y);ctx.stroke();
}
}
}
<canvas id="canvas" style="width:100%; height:250px"></canvas>
There are a couple of things, but mostly it comes down to this: you are drawing at a width of 100%, which is stretching the default size of a canvas you are drawing in - thats why it blurs. Set your width correctly using javascript and the sharpness increases. The only thing is, a difference of 5 pixels is barely noticeable, so you have to increase your size to something more... average. I have opted for 1/100 of the windows width, but you can turn it into anything.
// For safety, use event listeners and not global window method overwriting.
// It will become useful if you have multiple scripts you want to
// execute only after loading them!
window.addEventListener('DOMContentLoaded', function(){
var c = document.getElementById('canvas'),
ctx = c.getContext('2d'),
x = 0, y = 0;
// Set the correct width and height
c.width = window.innerWidth;
c.height = window.innerWidth / 100;
// Use moveTo once, then keep drawing from your previous lineTo call
ctx.moveTo(x, y);
// You only need your x value here, once we are off screen we can stop drawing and end the for loop!
for(; x < window.innerWidth; x += window.innerWidth / 100){
// Use lineTo to create a path in memory
// You can also see if your y needs to change because y = 0 = falsy
ctx.lineTo(x, (y = y ? 0 : window.innerWidth / 100));
}
// Call stroke() only once!
ctx.stroke();
// And for safety, call closePath() as stroke does not close it.
ctx.closePath();
}, false);
<canvas id="canvas"></canvas>
<!-- Remove all styling from the canvas! Do this computationally -->
maybe you can find your ans here
Full-screen Canvas is low res
basically it summarizes that instead of setting height and width in the css, you should set it via html (inside the canvas element via width and height attr) or via javaScript.
because when doing it in css, you are basically scaling it and thus reducing the resolution, so you have to mention the actual size in html element and not scale it in css.
I am making a project where i want to draw on a canvas.
I get the draw method on mouseover and intend to draw a line while following the mouse.
It draws the line perfectly only not on the correct position.
It is made possibly bij JQuery.
var canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
ctx.beginPath();
$("#myCanvas").mousemove(function(arg)
{ ctx.lineTo(arg.pageX,arg.pageY-80);
ctx.stroke();
});
My html Canvas code:
<canvas id="myCanvas" width="500" height="500">
</canvas>
I hope it is understandable and that somebody can help me. (information the -80 at pageY is because i works better on my screen than)
Here is an optional way of reading mouse position relative to canvas:
$("#myCanvas").mousemove(function(arg) {
var pos = getMousePos(canvas, arg);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
});
function getMousePos(canvas, e) {
var rect = canvas.getBoundingClientRect(); // absolute position of canvas
return {
x: e.clientX - rect.left, // make x/y relative to canvas
y: e.clientY - rect.top
};
}
Just a side note: We will get issues with that lineTo/stroke combo BTW as the lineTo will add to path and when you stroke, the new line as well as all the other lines added will be stroked. As it is it will become slower the more lines that are drawn and anti-aliased pixels will start to appear. You can use beginPath and moveTo to solve but that is out-of-scope for this question.
Is it possible to have an array of coordinates and drag and drop an image only along those coordinates? I would like to use only javascript and not use a javascript library. I keep scratching my head and googling this forever and can't find how to do this or if it is possible.
Demo: http://jsfiddle.net/m1erickson/7vmML/
Example code:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
var isDown=false;
var startX;
var startY;
var points=[];
points.push({x:10,y:10});
points.push({x:75,y:100});
points.push({x:150,y:125});
points.push({x:125,y:200});
var imageX=-200;
var imageY=-200;
var img=new Image();
img.onload=start;
img.crossOrigin="anonymous";
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png";
function start(){
drawAll();
}
function drawAll(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.moveTo(points[0].x+4,points[0].y+4)
for(var i=1;i<points.length;i++){
var pt=points[i];
ctx.lineTo(pt.x+4,pt.y+4);
}
ctx.stroke();
//
for(var i=0;i<points.length;i++){
var pt=points[i];
ctx.fillRect(pt.x,pt.y,8,8);
}
//
ctx.drawImage(img,imageX,imageY);
}
function handleMouseDown(e){
e.preventDefault();
isDown=true;
}
function handleMouseUp(e){
e.preventDefault();
isDown=false;
}
function handleMouseOut(e){
e.preventDefault();
isDown=false;
}
function handleMouseMove(e){
if(!isDown){return;}
e.preventDefault();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
var minDistance=1000;
var minPoint=-1;
for(var i=0;i<points.length;i++){
var pt=points[i];
var dx=mouseX-pt.x;
var dy=mouseY-pt.y;
var distance=Math.sqrt(dx*dx+dy*dy);
if(distance<minDistance){
minDistance=distance;
imageX=pt.x-img.width/2;
imageY=pt.y-img.height/2;
}
}
drawAll();
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag mouse. Image will snap to nearest point.</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Note that this example code uses jQuery to listen for mouse events. If you prefer pure javascript you can use these event bindings instead:
canvas.onmousedown=handleMouseDown;
canvas.onmouseup=handleMouseUp;
canvas.onmouseout=handleMouseOut;
canvas.onmousemove=handleMouseMove;
And you can calculate mouse position like this:
function getMousePos(canvas,e) {
var rect=canvas.getBoundingClientRect();
return{ x:e.clientX-rect.left, y:e.clientY-rect.top };
}
Yes, calculate the shortest distance to each of the points and override the mouse position with the point that is closest.
Lets use a simple point array as an example:
var points = [10,200, 50,250, 100,100, 150,120, 200,240,
250,200, 300,120, 350,180, 400,150];
Now, when you move you item over you get the closest point this way:
Lets first get the mouse x and y position (for demo - in you code you would use the position of the item):
var rect = canvas.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top,
Now you can iterate the point array and get the point with shortest distance to mouse x and y:
var i = 0,
pIndex = -1,
minDist = 999999999,
dist;
/// get shortest distance
for(; i < points.length; i+=2) {
/// get distance from current point to mouse x,y
dist = getDistance(x, y, points[i], points[i+1]);
/// if less than previous distance, update
if (dist < minDist) {
minDist = dist; /// current low
pIndex = i; /// point index
}
}
pointX = points[pIndex];
pointY = points[pIndex+1];
The function for calculating distance is simple trigonometry calculating the hypotenuse:
function getDistance(x1, y1, x2, y2) {
var dx = x2 - x1,
dy = y2 - y1;
return Math.abs(Math.sqrt(dx * dx + dy * dy));
}
Now you can draw your item at (pointX, pointY).
Live demo
The example here uses a very rough point array. If you need finer resolution you can use a line smoother such as this one or interpolate the point on the line. Here is a version where line smoothing is applied to increase its resolution and smoothness (you can drop the smoothness by setting tension to 0, and you can adjust resolution by changing number of segments, see following demo):
Here is a demo where line smoothing is used
Also have in mind that you may need to optimize the check range by for example doing a rough iteration first then use the 3-4 closest points and do a finer iteration between the range those represents.
Update
To allow snapping only when is held down and moved add these lines to code:
var allowMove = false;
canvas.onmousedown = function() {
allowMove = true;
}
canvas.onmouseup = function() {
allowMove = false;
}
canvas.onmousemove = function(e) {
if (!allowMove) return;
... as before ...
Hello I'm trying to create a rectangle that grows from the side of the canvas until it fills the whole canvas, once it has done that shrink back to is original state, the approach I'm taking is using requestAnimationFrame /cancelAnimationFrame for some reason I'm not sure cancelAnimationFrame does not seem to work my code is the following one :
<script>
function grRectangle(){
var canvas = document.getElementById("paper");
var context= canvas.getContext("2d");
//var forpi = Math.PI * 2;
//context.fillStyle = "black";
context.fillRect(0,0,canvas.width,canvas.height);
var posX = 200;
var posY = 100;
var color = 0;
function draw(){
context.fillStyle = 'hsl('+ color++ + ',100%,50%)';
context.beginPath();
context.rect(0,0,posX,posY);
context.fill();
posX = posX + 0.9;
posY = posY + 0.9;
if(posX < canvas.width ){
requestAnimationFrame(draw);
} if (posX >= canvas.width){
posX = posX - 0.9;
posY = posY - 0.9;
cancelAnimationFrame(draw);
}
}
draw();
};
</script>
<body onload= "grRectangle();" >
<h1>Growing Rectangle</h1>
<canvas id = "paper" width="800" height="600">
</canvas>
Any help is kindly appreciatted
It seems to me like the code you wrote doesn't actually need a cancelAnimationFrame. I am not sure what you think it does exactly, but it seems like you misunderstood it.
The cancelAnimationFrame method is used to prevent a previous call to requestAnimationFrame from getting executed, as long as this didn't happen yet. There are really few situations where you need this.
In your case I would put that growth-per-frame constant of 0.9 into a variable. When the rectangle size reaches the upper bound, just change it to -0.9 and it will get smaller again. When it reaches the lower bound, change it again to 0.9 and it will grow again.
You will, however, not see that shrinking, because you aren't erasing your canvas. Every frame is drawn on top of the previous one. You will have to erase your canvas at the beginning of your drawing loop. To do that, move the code which fills the canvas with a black rectangle into the drawing loop (remember to set the fill-style to black).