How does negative co-ordinate work in HTML5 canvas? - javascript

function Background() {
this.speed = 1; // Redefine speed of the background for panning
// Implement abstract function
this.draw = function() {
// Pan background
this.y += this.speed;
this.context.drawImage(imageRepository.background, this.x, this.y);
// Draw another image at the top edge of the first image
this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight);
// If the image scrolled off the screen, reset
if (this.y >= this.canvasHeight)
this.y = 0;
};
}
I was trying to understand the above code which gives the logic of rendering a background image in infinite loop(giving an illusion of continuous panning).
I could not understand the following line:
this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight);
Clearly this.y - this.canvasHeight will never be > 0. How is the negative y co-ordinate interpreted by the canvas? Or put simply, what will the following code do?
ctx.drawImage(img, 0, -10);

It draws starting at -10 for the y position based on the origin.
i.e.: Assuming the default origin of 0,0 (left, top) 10 pixels off the y-axis will not be visible or you could think of it as start y at 10 pixels off screen.
(Converting comment to answer)

Related

Fix position for tranlate() while rotate() only in p5js?

Have a class with methods that draws rectangular shapes with random lengths.
However, is unable to only do rotate() on the shapes without translating ( translate() ), which translate will make the shapes draw off the canvas.
So are there anyways to make it so no translation occurs while rotating?
The code:
class rect {
constructor(range) {
this.boundary = 100;
this.x = random(this.boundary, width - this.boundary);
this.y = random(this.boundary, height - this.boundary);
this.xu = this.x + random(50, 200);
this.yu = this.y + random(50, 200);
this.range = range;
this.limit = random(-range, range);
this.rand_color1 = random(255);
this.rand_color2 = random(255);
this.rand_color3 = random(255);
}
custom_shapes() {
// how to make no translations occur while only perform rotation on shapes?
translate(this.x-this.margin,this.y-this.margin);
rotate(30);
fill(this.rand_color1, this.rand_color2, this.rand_color3)
quad(this.x, this.y, this.xu + this.limit, this.y, this.xu, this.yu, this.x, this.yu + this.limit);
}
}
If you mean that your rectangular is going of the screen when rotating, it's rotating around x = 0, y= 0 point, so i guess you could do something like:
push() //push and pop acts as a way to "seperate" any style and translate and so on...
rectMode(CENTER) // basically the middle of the rect = x , y
translate(this.x,this.y) // **OR** translate(this.x - this.rectSizeX / 2, this.y - this.rectSizeY / 2)
//quad() // if you're not using the rectMode()
pop() // also you'll have to fill() and so on in here i believe, not too sure
also if you know it's allways going to be a long or tall square, you can just use rect(x,y,xSize,ySize) // if think it's the size anyways
If you just want to separate translate() in general, just put push() and pop() around it...
Oh yeah and translate() basically just makes whatever x and y you give it into 0,0... Dunno if i said that already i'm just editing this the next day.

HTML5 Canvas atan2 off by 90 degrees

I was trying to get the green triangle to rotate about its center and orient itself towards the mouse position. I was able to accomplish this, and you can view the full code and result here:
https://codepen.io/Carpetfizz/project/editor/DQbEVe
Consider the following lines of code:
r = Math.atan2(mouseY - centerY, mouseX - centerX)
ctx.rotate(r + Math.PI/2)
I arbitrarily added Math.PI/2 to my angle calculation because without it, the rotations seemed to be 90 degrees off (by inspection). I want a better understanding of the coordinate system which atan2 is being calculated with respect to so I can justify the reason for offsetting the angle by 90 degrees (and hopefully simplify the code).
EDIT:
To my understanding, Math.atan2 is measuring the angle illustrated in blue. Shouldn't rotating both triangles that blue angle orient it towards the mouse mouse pointer (orange dot) ? Well - obviously not since it's the same angle and they are two different orientations, but I cannot seem to prove this to myself.
This is because of how the Math.atan2 works.
From MDN:
This is the counterclockwise angle, measured in radians, between the positive X axis, and the point (x, y).
In above figure, the positive X axis is the horizontal segment going from the junction to the right-most position.
To make it clearer, here is an interactive version of this diagram, where x, y values are converted to [-1 ~ 1] values.
const ctx = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height,
radius = 0.3;
ctx.textAlign = 'center';
canvas.onmousemove = canvas.onclick = e => {
// offset mouse values so they are relative to the center of our canvas
draw(as(e.offsetX), as(e.offsetY));
}
draw(0, 0);
function draw(x, y) {
clear();
drawCross();
drawLineToPoint(x, y);
drawPoint(x, y);
const angle = Math.atan2(y, x);
drawAngle(angle);
writeAngle(angle);
}
function clear() {
ctx.clearRect(0, 0, w, h);
}
function drawCross() {
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(s(0), s(-1));
ctx.lineTo(s(0), s(1));
ctx.moveTo(s(-1), s(0));
ctx.lineTo(s(0), s(0));
ctx.strokeStyle = ctx.fillStyle = '#2e404f';
ctx.stroke();
// positive X axis
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(s(0), s(0));
ctx.lineTo(s(1), s(0));
ctx.stroke();
ctx.lineWidth = 1;
ctx.font = '20px/1 sans-serif';
ctx.fillText('+X', s(1) - 20, s(0) - 10);
}
function drawPoint(x, y) {
ctx.beginPath();
ctx.arc(s(x), s(y), 10, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
ctx.font = '12px/1 sans-serif';
ctx.fillText(`x: ${x.toFixed(2)} y: ${y.toFixed(2)}`, s(x), s(y) - 15);
}
function drawLineToPoint(x, y) {
ctx.beginPath();
ctx.moveTo(s(0), s(0));
ctx.lineTo(s(x), s(y));
ctx.strokeStyle = 'red';
ctx.setLineDash([5, 5]);
ctx.stroke();
ctx.setLineDash([0]);
}
function drawAngle(angle) {
ctx.beginPath();
ctx.moveTo(s(radius), s(0));
ctx.arc(s(0), s(0), radius * w / 2,
0, // 'arc' method also starts from positive X axis (3 o'clock)
angle,
true // Math.atan2 returns the anti-clockwise angle
);
ctx.strokeStyle = ctx.fillStyle = 'blue';
ctx.stroke();
ctx.font = '20px/1 sans-serif';
ctx.fillText('∂: ' + angle.toFixed(2), s(0), s(0));
}
// below methods will add the w / 2 offset
// because canvas coords set 0, 0 at top-left corner
// converts from [-1 ~ 1] to px
function s(value) {
return value * w / 2 + (w / 2);
}
// converts from px to [-1 ~ 1]
function as(value) {
return (value - w / 2) / (w / 2);
}
<canvas id="canvas" width="500" height="500"></canvas>
So now, if we go back to your image, it currently points to the top (positive Y axis), while the angle you just measured is realtive to the x axis, so it doesn't point where you intended.
Now we know the problem, the solution is quite easy:
either apply the + Math.PI / 2 offset to your angle like you did,
either modify your original image so that it points to the positive X axis directly.
The coordinate system on canvas works with 0° pointing right. This means anything you want to point "up" must be initially drawn right.
All you need to do in this case is to change this drawing:
to
pointing "up" 0°
and you can strip the math back to what you'd expect it to be.
var ctx = c.getContext("2d"), img = new Image;
img.onload = go; img.src = "https://i.stack.imgur.com/Yj9DU.jpg";
function draw(pos) {
var cx = c.width>>1,
cy = c.height>>1,
angle = Math.atan2(pos.y - cy, pos.x - cx);
ctx.setTransform(1,0,0,1,cx, cy);
ctx.rotate(angle);
ctx.drawImage(img, -img.width>>1, -img.height>>1);
}
function go() {
ctx.globalCompositeOperation = "copy";
window.onmousemove = function(e) {draw({x: e.clientX, y: e.clientY})}
}
html, body {margin:0;background:#ccc}
#c {background:#fff}
<canvas id=c width=600 height=600></canvas>
When you do arctangents in math class, you're generally dealing with an y-axis that increases going upwards. In most computer graphics systems, however, including canvas graphics, y increases going downward. [erroneous statement deleted]
Edit: I have to admit what I wrote before was wrong for two reasons:
A change in the direction of the axis would be compensated for by adding π, not π/2.
The canvas context rotate function rotates clockwise for positive angles, and that alone should compensate for the flip of the y-axis.
I played around with a copy of your code in Plunker, and now I realize the 90° rotation simply compensates for the starting orientation of the graphic image you're drawing. If the arrowhead pointed right to start with, instead of straight up, you wouldn't need to add π/2.
I encountered the same problem and was able to achieve the desired result with a following axis 'trick':
// Default usage (works fine if your image / shape points to the RIGHT)
let angle = Math.atan2(delta_y, delta_x);
// 'Tricky' usage (works fine if your image / shape points to the LEFT)
let angle = Math.atan2(delta_y, -delta_x);
// 'Tricky' usage (works fine if your image / shape points to the BOTTOM)
let angle = Math.atan2(delta_x, delta_y);
// 'Tricky' usage (works fine if your image / shape points to the TOP)
let angle = Math.atan2(delta_x, -delta_y);

how to change the origin position to rotate a drawed line in the HTML5 Canvas

I've a little problem with canvas
I'm trying to make a paddle game , I want to rotate the upper paddle according to the mouse X-position My problem is that the drawing position is the top left of the paddle as normal and I want to change it after Drawing to be in the center of the paddle for rotation.
so the origin position of the paddle will be the center and every time the mouse moved the paddle will be rotated from the center not the top left.
here is updated function which invoked to updated the canvas.
function update() {
// Update scores
updateScore();
// Move the paddles on mouse move
// Here we will add another condition to move the upper paddle in Y-Axis
if(mouse.x && mouse.y) {
for(var i = 1; i < paddles.length; i++) {
p = paddles[i];
// the botoom paddle
if (i ==1){
p.x = mouse.x - p.w/2;
}else{
// the top paddle
ctx.save(); // saves the coordinate system
ctx.translate(W/4,H/2); // now the position (0,0) is found at (250,50)
ctx.rotate(0.30 * mouse.x); // rotate around the start point of your line
ctx.moveTo(0,0) // this will actually be (250,50) in relation to the upper left corner
ctx.lineTo(W/4,H/2) // (250,250)
ctx.stroke();
ctx.restore(); // restores the coordinate system back to (0,0)
}// end else
}//end for
}
Your translations are a little off, but it's easy to fix. Consider this alternative -- translate the context to the center of the paddle. After all, this is where you will be doing the rotation. Rotate the canvas, and then draw a horizontal line centered around the origin. I've codified my suggestion, and I've stored a few things in local variables to make it clearer.
var W = 200;
var H = 200;
var x = W / 2;
var y = H / 2;
var lineLength = 80;
ctx.save();
ctx.translate(x, y);
ctx.rotate(0.3 * mouse.X);
ctx.moveTo(-lineLength / 2,0, 0);
ctx.lineTo(lineLength / 2.0, 0);
ctx.stroke();
ctx.restore();

Move HTML5 Canvas with a background image

I want to visualize a huge diagram that is drawn in a HTML5 canvas. As depicted below, let’s imagine the world map, it’s impossible to visualize it all at the same time with a “decent” detail. Therefore, in my canvas I would like to be able to pan over it using the mouse to see the other countries that are not visible.
Does anyone know how to implement this sort of panning in a HTML5 canvas? Another feature would be the zoom in and out.
I've seen a few examples but I couldn't get them working nor they seam to address my question.
Thanks in advance!
To achieve a panning functionality with a peep-hole it's simply a matter of two draw operations, one full and one clipped.
To get this result you can do the following (see full code here):
Setup variables:
var ctx = canvas.getContext('2d'),
ix = 0, iy = 0, /// image position
offsetX = 0, offsetY = 0, /// current offsets
deltaX, deltaY, /// deltas from mouse down
mouseDown = false, /// in mouse drag
img = null, /// background
rect, /// rect position
rectW = 200, rectH = 150; /// size of highlight area
Set up the main functions that you use to set size according to window size (including on resize):
/// calc canvas w/h in relation to window as well as
/// setting rectangle in center with the pre-defined
/// width and height
function setSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
rect = [canvas.width * 0.5 - rectW * 0.5,
canvas.height * 0.5 - rectH * 0.5,
rectW, rectH]
update();
}
/// window resize so recalc canvas and rect
window.onresize = setSize;
The main function in this is the draw function. Here we draw the image on the position calculated by mouse moving (see next section).
First step to get that washed-out look is to set alpha down to about 0.2 (you could also draw a transparent rectangle on top but this is more efficient).
Then draw the complete image.
Reset alpha
Draw the peep-hole using clipping with corrected offsets for the source.
-
/// main draw
function update() {
if (img === null) return;
/// limit x/y as drawImage cannot draw with negative
/// offsets for clipping
if (ix + offsetX > rect[0]) ix = rect[0] - offsetX;
if (iy + offsetY > rect[1]) iy = rect[1] - offsetY;
/// clear background to clear off garbage
ctx.clearRect(0, 0, canvas.width, canvas.height);
/// make everything transparent
ctx.globalAlpha = 0.2;
/// draw complete background
ctx.drawImage(img, ix + offsetX, iy + offsetY);
/// reset alpha as we need opacity for next draw
ctx.globalAlpha = 1;
/// draw a clipped version of the background and
/// adjust for offset and image position
ctx.drawImage(img, -ix - offsetX + rect[0], /// sx
-iy - offsetY + rect[1], /// sy
rect[2], rect[3], /// sw/h
/// destination
rect[0], rect[1], rect[2], rect[3]);
/// make a nice sharp border by offsetting it half pixel
ctx.strokeRect(rect[0] + 0.5, rect[1] + 0.5, rect[2], rect[3]);
}
Now it's a matter of handling mouse down, move and up and calculate the offsets -
In the mouse down we store current mouse positions that we'll use for calculating deltas on mouse move:
canvas.onmousedown = function(e) {
/// don't do anything until we have an image
if (img === null) return;
/// correct mouse pos
var coords = getPos(e),
x = coords[0],
y = coords[1];
/// store current position to calc deltas
deltaX = x;
deltaY = y;
/// here we go..
mouseDown = true;
}
Here we use the deltas to avoid image jumping setting the corner to mouse position. The deltas are transferred as offsets to the update function:
canvas.onmousemove = function(e) {
/// in a drag?
if (mouseDown === true) {
var coords = getPos(e),
x = coords[0],
y = coords[1];
/// offset = current - original position
offsetX = x - deltaX;
offsetY = y - deltaY;
/// redraw what we have so far
update();
}
}
And finally on mouse up we make the offsets a permanent part of the image position:
document.onmouseup = function(e) {
/// was in a drag?
if (mouseDown === true) {
/// not any more!!!
mouseDown = false;
/// make image pos. permanent
ix += offsetX;
iy += offsetY;
/// so we need to reset offsets as well
offsetX = offsetY = 0;
}
}
For zooming the canvas I believe this is already answered in this post - you should be able to merge this with the answer given here:
Zoom Canvas to Mouse Cursor
To do something like you have requested, it is just a case of having 2 canvases, each with different z-index. one canvas smaller than the other and position set to the x and y of the mouse.
Then you just display on the small canvas the correct image based on the position of the x and y on the small canvas in relation to the larger canvas.
However your question is asking for a specific solution, which unless someone has done and they are willing to just dump their code, you're going to find it hard to get a complete answer. I hope it goes well though.

Unable to draw a rectangle in the correct position with mousemove event handler in canvas

I am attempting to make a pong clone using canvas. I however seem to have a misunderstanding how the coordinate system works in canvas in regards to events.
I have created a playerPaddle class. With this class I want the center of the paddle to be drawn exactly where the mouse is hovering:
// Paddle Object
function Paddle( h, w, x, y, fill) {
this.h = h;
this.w = w;
this.midH = h / 2;
this.x = x;
this.y = y;
this.fill = fill;
}
Paddle.prototype.draw = function(ctx) {
ctx.fillStyle = this.fill;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
Paddle.prototype.updatePos = function(y) {
this.y = y - this.midH
}
I thought the correct way to offset the paddle so that the center of the paddle is on the mousemove event would be to draw the rectangle by subtracting half the paddle height from the new position of the paddle like I am doing in the updatePos method above.
However this seems to not be working correctly as it is drawing the top of my paddle exactly where the mousemove event is occurring.
What does work though is if I subtract this.h from the y location of the event, which makes absolutely no sense to me.
Below is the code I use to instantiate the paddle, add the event handler to the canvas element itself, and the function I use to render everything.
// Paddle Globals
var paddleHeight = 50,
paddleWidth = 10,
paddleOffset = 10;
var playerPaddle = new Paddle( paddleHeight, paddleWidth, paddleOffset, midY, '#FFFFFF');
// Does the actual rendering
function render() {
ctx.clearRect(0, 0, cWidth, cHeight);
playerPaddle.draw(ctx);
}
//Event Handlers
canvas.addEventListener('mousemove', function(e) {
e.preventDefault();
var position = canvas.getBoundingClientRect();
var y = e.clientY - position.top;
playerPaddle.updatePos(y);
}, false);
Also here is an example of the behavior that I am describing: http://jsfiddle.net/u57QD/
I think I solve the problem.
Try to delete the css style on Canvas.
I think the padding-top is making you canvas downward. So it look like the y - this.midH is not working :D Good luck
Delete follow line of code in stylesheet
padding-top: 20px;

Categories

Resources