I am making an online game and I use HTML5 canvas and Javascript to build my game.
I have some shapes and one ball that moves and when collided the shapes, the shapes should disapear.
the shapes and the ball are image and my big problem is how to detect collision between ball and shape because my shapes are rectangle, triangle, polygon and ... e.g.: This Shape
this is my code to detect collision but it just works for rectangles:
function collide(r1, r2) {
var dx = (r1.x + r1.width / 2) - (r2.x + r2.width / 2);
var dy = (r1.y + r1.height / 2) - (r2.y + r2.height / 2);
var width = (r1.width + r2.width) / 2;
var height = (r1.height + r2.height) / 2;
var crossWidth = width * dy;
var crossHeight = height * dx;
var collision = 'none';
if (Math.abs(dx) <= width && Math.abs(dy) <= height) {
if (crossWidth > crossHeight) {
collision = (crossWidth > (-crossHeight)) ? 'bottom' : 'left';
} else {
collision = (crossWidth > -(crossHeight)) ? 'right' : 'top';
}
}
return (collision);
}
Just get your collision code to work for triangles and then it will work for all shapes! Here is the formula, in Javascript code for doing just that, taken from(mostly) this question.
function sign(p1, p2, p3) {
return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
}
function inTriangle(point, trip1, trip2, trip3) {
var b1 = sign(point, trip1, trip2) < 0.0;
var b2 = sign(point, trip2, trip3) < 0.0;
var b3 = sign(point, trip3, trip1) < 0.0;
return ((b1 == b2) && (b2 == b3));
}
With this code you just have to run, inTriangle(p, v1, v2, v3), where p is the point your testing for, in collision detection that would be each of the corners of a shape, with a ball or circle just test some points on the circumference, v1; v2; v3 are the three points of a triangle your testing for.
Keep in mind that it is more efficient to test for the collision of a rectangle so if you have a shape that can be divided up like a rectangle, you should do so, instead of dividing it into triangles.
Related
This is my first post so I'm trying to make my problem as clear as possible. I'm making a game and I want to improve my collision detection. This is because I want to check what side is being hit and stop the player from moving past it without using something general like if(collision(player, enemy)) player.x = enemy.x - player.w(width) because if the player were to collide with the top it wouldn't keep the player on top.
In the code it checks if any one of the statements is true and then returns it but it doesn't tell me which statement was the one that was equal to true so I can stop the player from moving accordingly, if that makes sense. If you have a more efficient collision detection for me to use it would be greatly appreciated.
I've already tried to make a position variable to be equal to whatever side gets collided into and then stop the player from moving past it but it only works for the left side and won't let my player jump over the enemy or block.
function collision(object1, object2) {
return !(
object1.x > object2.x + object2.w ||
object1.x + object1.w < object2.x ||
object1.y > object2.y + object2.h ||
object1.y + object1.h < object2.y
)
}
//Only works for the left side
if(collision(player, enemy)) player.x = enemy.x - player.w
I expect it to be able to tell me what side is being collided into and then either stop the player from moving past/into it and for the player to be able to be on top of the block/enemy without just being pushed to the left.
You'll want to calculate the distance between the x's and y's and also use the minimum distance that they could be colliding along each axis to find the depth along both axes. Then you can pick the smaller depth and move along that one. Here's an example:
if(collision(player, enemy)){
// Most of this stuff would probably be good to keep stored inside the player
// along side their x and y position. That way it doesn't have to be recalculated
// every collision check
var playerHalfW = player.w/2
var playerHalfH = player.h/2
var enemyHalfW = enemy.w/2
var enemyHalfH = enemy.h/2
var playerCenterX = player.x + player.w/2
var playerCenterY = player.y + player.h/2
var enemyCenterX = enemy.x + enemy.w/2
var enemyCenterY = enemy.y + enemy.h/2
// Calculate the distance between centers
var diffX = playerCenterX - enemyCenterX
var diffY = playerCenterY - enemyCenterY
// Calculate the minimum distance to separate along X and Y
var minXDist = playerHalfW + enemyHalfW
var minYDist = playerHalfH + enemyHalfH
// Calculate the depth of collision for both the X and Y axis
var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX
var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY
// Now that you have the depth, you can pick the smaller depth and move
// along that axis.
if(depthX != 0 && depthY != 0){
if(Math.abs(depthX) < Math.abs(depthY)){
// Collision along the X axis. React accordingly
if(depthX > 0){
// Left side collision
}
else{
// Right side collision
}
}
else{
// Collision along the Y axis.
if(depthY > 0){
// Top side collision
}
else{
// Bottom side collision
}
}
}
}
Working example
Here's a working example that you can play around with. Use the arrow keys to move the player around.
player = {
x: 9,
y: 50,
w: 100,
h: 100
}
enemy = {
x: 100,
y: 100,
w: 100,
h: 100
}
output = document.getElementById("collisionType");
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d")
function collision(object1, object2) {
return !(
object1.x > object2.x + object2.w ||
object1.x + object1.w < object2.x ||
object1.y > object2.y + object2.h ||
object1.y + object1.h < object2.y
)
}
function draw() {
ctx.clearRect(0, 0, 400, 400)
ctx.lineWidth = "5"
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.rect(player.x, player.y, player.w, player.h);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = "blue";
ctx.rect(enemy.x, enemy.y, enemy.w, enemy.h);
ctx.stroke();
}
function handleCollision() {
if (collision(player, enemy)) {
var playerHalfW = player.w / 2
var playerHalfH = player.h / 2
var enemyHalfW = enemy.w / 2
var enemyHalfH = enemy.h / 2
var playerCenterX = player.x + player.w / 2
var playerCenterY = player.y + player.h / 2
var enemyCenterX = enemy.x + enemy.w / 2
var enemyCenterY = enemy.y + enemy.h / 2
// Calculate the distance between centers
var diffX = playerCenterX - enemyCenterX
var diffY = playerCenterY - enemyCenterY
// Calculate the minimum distance to separate along X and Y
var minXDist = playerHalfW + enemyHalfW
var minYDist = playerHalfH + enemyHalfH
// Calculate the depth of collision for both the X and Y axis
var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX
var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY
// Now that you have the depth, you can pick the smaller depth and move
// along that axis.
if (depthX != 0 && depthY != 0) {
if (Math.abs(depthX) < Math.abs(depthY)) {
// Collision along the X axis. React accordingly
if (depthX > 0) {
output.innerHTML = "left side collision"
} else {
output.innerHTML = "right side collision"
}
} else {
// Collision along the Y axis.
if (depthY > 0) {
output.innerHTML = "top side collision"
} else {
output.innerHTML = "bottom side collision"
}
}
}
} else {
output.innerHTML = "No collision"
}
}
keyStates = []
function handleKeys() {
if (keyStates[39]) {
player.x += 2 //Move right
} else if (keyStates[37]) {
player.x -= 2 //Move left
}
if (keyStates[38]) {
player.y -= 2 //Move up
}
if (keyStates[40]) {
player.y += 2 //Move down
}
}
function main() {
handleKeys();
draw();
handleCollision();
window.requestAnimationFrame(main);
}
window.onkeydown = function(e) {
keyStates[e.keyCode] = true
}
window.onkeyup = function(e) {
keyStates[e.keyCode] = false
}
main();
<h2 id="collisionType"></h2>
<canvas id="canvas" width='300' height='300'></canvas>
Reacting to the collision
Now that you know the side the collision happened on, it should be fairly trivial to decide how to react. It would be very similar to what you are currently doing for the left side just flip some signs around and change the axis.
Other Considerations
You may want to take into account your player's velocity (if it has one) otherwise the detection may fail.
If the player's velocity is too high, it might 'tunnel' through the enemy and no collision will be detected.
The player's movement can also look jittery if the velocity is not stopped upon collision
Can your objects rotate or have more than 4 sides? If so, you'll probably want to use another method as described below.
Here's a good answer to another post that talks in depth about collision engines
Other Methods
As for other collision detection methods, there's quite a few but one that comes to mind is Separating Axis Theorem which is a little more complex than what you have but will work with more complex convex shapes and rotation. It also tells you the direction and distance needed to move to resolve the collision. Here's a site that has interactive examples and goes in-depth on the subject. It doesn't appear to give a full implementation but those can be found other places.
I have been wrestling with rendering an animation that fires a projectile accurately from an "enemy" node to a "player" node in a 2D 11:11 grid (0:0 = top-left) in JS/Canvas. After a lot of reading up I've managed to get the shots close, but not quite bang on. I think my velocity function is a little out but I really don't know why. This is the trigonometric function:
this.getVelocityComponents = function(speed){
// loc (location of enemy actor) = array(2) [X_coord, Y_coord]
// des (destination (ie. player in this instance)) = array(2) [X_coord, Y_coord]
var i, sum, hyp, output = [], dis = [];
var higher = false;
for (i in loc) {
sum = 0;
if (loc[i] > des[i])
sum = loc[i] - des[i];
if (loc[i] < des[i])
sum = des[i] - loc[i];
dis.push(sum);
}
hyp = Math.sqrt(Math.pow(dis[X], 2) + Math.pow(dis[Y], 2));
if (dis[X] > dis[Y]) {
output[X] = (speed * Math.cos(dis[X]/hyp))
output[Y] = (speed * Math.sin(dis[Y]/hyp))
} else if (dis[X] < dis[Y]) {
output[X] = (speed * Math.cos(dis[Y]/hyp))
output[Y] = (speed * Math.sin(dis[X]/hyp))
}
return output;
}
and this is the instruction that tells the X and the Y of the projectile frame to advance:
var distance = [];
for (i in loc) {
var sum = 0;
if (loc[i] > des[i])
sum = loc[i] - des[i];
if (loc[i] < des[i])
sum = des[i] - loc[i];
distance.push(sum);
}
if (distance[X] > distance[Y]) {
frm[X] += (loc[X] < des[X]) ? v[X] : -v[X];
frm[Y] += (loc[Y] < des[Y]) ? v[Y] : -v[Y];
} else {
frm[Y] += (loc[Y] < des[Y]) ? v[X] : -v[X];
frm[X] += (loc[X] < des[X]) ? v[Y] : -v[Y];
}
Below is a screenshot. Blue is player, pink enemy and the yellow circles are projectiles
as you can see, it's almost on the mark.
Have I done something wrong? what do I need to do?
To calculate the direction from enemy to player you can simplify the calculations a little.
Find direction angle
var diffX = Player.x - Enemy.x, // difference in position
diffY = Player.y - Enemy.y,
angle = Math.atan2(diffY, diffX); // atan2 will give the angle in radians
Notice also difference for Y comes first for atan2 as canvas is oriented 0° pointing right.
Velocity vector
Then calculate the velocity vector using angle and speed:
// calculate velocity vector
var speed = 8,
vx = Math.cos(angle) * speed, // angle x speed
vy = Math.sin(angle) * speed;
You might want to consider using time as a factor if that is important. You can see my answer from a while back here for an example on this.
Demo
Using these calculations you will be able to always "hit" the player with the projectile (reload demo to change enemy position to random y):
var ctx = document.querySelector("canvas").getContext("2d"),
Player = {
x: 470,
y: 75
},
Enemy = {
x: 100,
y: Math.random() * 150 // reload demo to change y-position
};
// calculate angle
var diffX = Player.x - Enemy.x,
diffY = Player.y - Enemy.y,
angle = Math.atan2(diffY, diffX);
// calculate velocity vector
var speed = 8,
vx = Math.cos(angle) * speed, // angle x speed
vy = Math.sin(angle) * speed,
x = Enemy.x, // projectil start
y = Enemy.y + 50;
// render
(function loop() {
ctx.clearRect(0, 0, 500, 300);
ctx.fillRect(Player.x, Player.y, 30, 100);
ctx.fillRect(Enemy.x, Enemy.y, 30, 100);
ctx.fillRect(x - 3, y -3, 6, 6);
x += vx;
y += vy;
if (x < 500) requestAnimationFrame(loop);
})();
<canvas width=500 height=300></canvas>
The solution is much simpler than that.
What should you do ?
1) compute the vector that leads from you enemy to the player. That will be the shooting direction.
2) normalize the vector : meaning you build a vector that has a length of 1, with the same direction.
3) multiply that vector by your speed : now you have a correct speed vector, with the right norm, aimed at the player.
Below some code to help you understand :
function spawnBullet(enemy, player) {
var shootVector = [];
shootVector[0] = player[0] - enemy[0];
shootVector[1] = player[1] - enemy[1];
var shootVectorLength = Math.sqrt(Math.pow(shootVector[0], 2) + Math.pow(shootVector[1],2));
shootVector[0]/=shootVectorLength;
shootVector[1]/=shootVectorLength;
shootVector[0]*=bulletSpeed;
shootVector[1]*=bulletSpeed;
// ... here return an object that has the enemy's coordinate
// and shootVector as speed
}
Then, since you don't use time in your computations (!! wrooong !! ;-) ) you will make the bullet move with the straightforward :
bullet[0] += bullet.speed[0];
bullet[1] += bullet.speed[1];
Now the issue with fixed-step is that your game will run, say, twice slower on a 30fps device than on a 60fps device. The solution is to compute how much time elapsed since the last refresh, let's call this time 'dt'. Using that time will lead you to an update like :
bullet[0] += dt * bullet.speed[0];
bullet[1] += dt * bullet.speed[1];
and now you'll be framerate-agnostic, your game will feel the same on any device.
I am working with this library for a short time, and i have a issue that is killing me.
I have 2 cubes, one with phisy.js and another with three.js, and i have a function for rotate them when you press the A, S, D, or W key depending on the rotation of the camera, my code is like this:
var v = new THREE.Vector3(0, 0, 0);
if (keys.forward === 1)
v.x = -1;
if (keys.right === 1)
v.z = 1;
if (keys.backward === 1)
v.x = 1;
if (keys.left === 1)
v.z = -1;
//get the searched angle
var angle = camera.rotation.y + Math.atan2(v.x, v.z);
var rot = normalMesh.rotation.y;
var diff = angle - rot;
if (Math.abs(diff) > Math.PI) {
//find the shortest way to rotate
if (diff > 0)
rot += 2 * Math.PI;
else
rot -= 2 * Math.PI;
diff = angle - rot;
}
//get the /2 for a smooth rotation, i really need this
if (diff !== 0)
rot += diff / 2;
normalMesh.rotation.set(0, rot, 0);
on the three.js cube works fine, but on the physi.js cube i doesn't work.
I am created a demo for this (i can´t create it on jsfiddle because the web worker)
http://demo.cristobaldiaz.cl/test/
also i left the source code on a zip file ---> http://demo.cristobaldiaz.cl/test/issue.zip
you can check the movement function on http://demo.cristobaldiaz.cl/test/src/app.js line 95
Anyway, if you rotate the camera with the mouse, you can check that the problem occurs when you rotate the cube to the direction of the red mesh.
You can remove you rotation update from the animation loop and move it to a physical loop:
scene.addEventListener('update', function(){
actor.onRender();
})
This way it will always be in sync.
See this page https://github.com/chandlerprall/Physijs/wiki/Callbacks-&-Events for more details.
I would like to achieve this effect outlined here: http://www.html5canvastutorials.com/labs/html5-canvas-multi-touch-scale-stage-with-kineticjs/
But on a Layer in KineticJS
I have already got it working to a degree (using basically the same code as in the link), but it seems the Layer scales around the 0,0 origin point and I cannot see any doco about changing the transform origin?
How can I effectively pinch and zoom a Layer such that it scales around it center point?
I made a plugin for this kind of behavior: https://github.com/eduplus/pinchlayer
It is probably a little outdated now with the recent changes, but the logic in layerTouchMove function is most likely still sound. Here:
var touch1 = event.touches[0];
var touch2 = event.touches[1];
if (touch1 && touch2) {
self.setDraggable(false);
if (self.trans != undefined) { self.trans.stop(); }
if (self.startDistance === undefined) {
self.startDistance = self.getDistance(touch1, touch2);
self.touchPosition.x = (touch1.clientX + touch2.clientX) / 2;
self.touchPosition.y = (touch1.clientY + touch2.clientY) / 2;
self.layerPosition.x = (Math.abs(self.getX()) + self.touchPosition.x) / self.startScale;
self.layerPosition.y = (Math.abs(self.getY()) + self.touchPosition.y) / self.startScale;
}
else {
var dist = self.getDistance(touch1, touch2);
var scale = (dist / self.startDistance) * self.startScale;
if (scale < self.minScale) { scale = self.minScale; }
if (scale > self.maxScale) { scale = self.maxScale; }
self.setScale(scale, scale);
var x = (self.layerPosition.x * scale) - self.touchPosition.x;
var y = (self.layerPosition.y * scale) - self.touchPosition.y;
var pos = self.checkBounds({ x: -x, y: -y });
self.setPosition(pos.x, pos.y);
self.draw();
}
Basically it will record the starting point and the distance (how long the pinch is) and then scale and set the layer position accordingly. Hope this helps.
I'm programming a HTML5 < canvas > project that involves zooming in and out of images using the scroll wheel.
I want to zoom towards the cursor like google maps does but I'm completely lost on how to calculate the movements.
What I have: image x and y (top-left corner); image width and height; cursor x and y relative to the center of the canvas.
In short, you want to translate() the canvas context by your offset, scale() it to zoom in or out, and then translate() back by the opposite of the mouse offset. Note that you need to transform the cursor position from screen space into the transformed canvas context.
ctx.translate(pt.x,pt.y);
ctx.scale(factor,factor);
ctx.translate(-pt.x,-pt.y);
Demo: http://phrogz.net/tmp/canvas_zoom_to_cursor.html
I've put up a full working example on my website for you to examine, supporting dragging, click to zoom in, shift-click to out, or scroll wheel up/down.
The only (current) issue is that Safari zooms too fast compared to Chrome or Firefox.
I hope, these JS libraries will help you:
(HTML5, JS)
Loupe
http://www.netzgesta.de/loupe/
CanvasZoom
https://github.com/akademy/CanvasZoom
Scroller
https://github.com/zynga/scroller
As for me, I'm using loupe. It's awesome!
For you the best case - scroller.
I recently needed to archive same results as Phrogz had already done but instead of using context.scale(), I calculated each object size based on ratio.
This is what I came up with. Logic behind it is very simple. Before scaling, I calculate point distance from edge in percentages and later adjust viewport to correct place.
It took me quite a while to come up with it, hope it saves someones time.
$(function () {
var canvas = $('canvas.main').get(0)
var canvasContext = canvas.getContext('2d')
var ratio = 1
var vpx = 0
var vpy = 0
var vpw = window.innerWidth
var vph = window.innerHeight
var orig_width = 4000
var orig_height = 4000
var width = 4000
var height = 4000
$(window).on('resize', function () {
$(canvas).prop({
width: window.innerWidth,
height: window.innerHeight,
})
}).trigger('resize')
$(canvas).on('wheel', function (ev) {
ev.preventDefault() // for stackoverflow
var step
if (ev.originalEvent.wheelDelta) {
step = (ev.originalEvent.wheelDelta > 0) ? 0.05 : -0.05
}
if (ev.originalEvent.deltaY) {
step = (ev.originalEvent.deltaY > 0) ? 0.05 : -0.05
}
if (!step) return false // yea..
var new_ratio = ratio + step
var min_ratio = Math.max(vpw / orig_width, vph / orig_height)
var max_ratio = 3.0
if (new_ratio < min_ratio) {
new_ratio = min_ratio
}
if (new_ratio > max_ratio) {
new_ratio = max_ratio
}
// zoom center point
var targetX = ev.originalEvent.clientX || (vpw / 2)
var targetY = ev.originalEvent.clientY || (vph / 2)
// percentages from side
var pX = ((vpx * -1) + targetX) * 100 / width
var pY = ((vpy * -1) + targetY) * 100 / height
// update ratio and dimentsions
ratio = new_ratio
width = orig_width * new_ratio
height = orig_height * new_ratio
// translate view back to center point
var x = ((width * pX / 100) - targetX)
var y = ((height * pY / 100) - targetY)
// don't let viewport go over edges
if (x < 0) {
x = 0
}
if (x + vpw > width) {
x = width - vpw
}
if (y < 0) {
y = 0
}
if (y + vph > height) {
y = height - vph
}
vpx = x * -1
vpy = y * -1
})
var is_down, is_drag, last_drag
$(canvas).on('mousedown', function (ev) {
is_down = true
is_drag = false
last_drag = { x: ev.clientX, y: ev.clientY }
})
$(canvas).on('mousemove', function (ev) {
is_drag = true
if (is_down) {
var x = vpx - (last_drag.x - ev.clientX)
var y = vpy - (last_drag.y - ev.clientY)
if (x <= 0 && vpw < x + width) {
vpx = x
}
if (y <= 0 && vph < y + height) {
vpy = y
}
last_drag = { x: ev.clientX, y: ev.clientY }
}
})
$(canvas).on('mouseup', function (ev) {
is_down = false
last_drag = null
var was_click = !is_drag
is_drag = false
if (was_click) {
}
})
$(canvas).css({ position: 'absolute', top: 0, left: 0 }).appendTo(document.body)
function animate () {
window.requestAnimationFrame(animate)
canvasContext.clearRect(0, 0, canvas.width, canvas.height)
canvasContext.lineWidth = 1
canvasContext.strokeStyle = '#ccc'
var step = 100 * ratio
for (var x = vpx; x < width + vpx; x += step) {
canvasContext.beginPath()
canvasContext.moveTo(x, vpy)
canvasContext.lineTo(x, vpy + height)
canvasContext.stroke()
}
for (var y = vpy; y < height + vpy; y += step) {
canvasContext.beginPath()
canvasContext.moveTo(vpx, y)
canvasContext.lineTo(vpx + width, y)
canvasContext.stroke()
}
canvasContext.strokeRect(vpx, vpy, width, height)
canvasContext.beginPath()
canvasContext.moveTo(vpx, vpy)
canvasContext.lineTo(vpx + width, vpy + height)
canvasContext.stroke()
canvasContext.beginPath()
canvasContext.moveTo(vpx + width, vpy)
canvasContext.lineTo(vpx, vpy + height)
canvasContext.stroke()
canvasContext.restore()
}
animate()
})
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<canvas class="main"></canvas>
</body>
</html>
I took #Phrogz's answer as a basis and made a small library that enables canvas with dragging, zooming and rotating.
Here is the example.
var canvas = document.getElementById('canvas')
//assuming that #param draw is a function where you do your main drawing.
var control = new CanvasManipulation(canvas, draw)
control.init()
control.layout()
//now you can drag, zoom and rotate in canvas
You can find more detailed examples and documentation on the project's page
Faster
Using ctx.setTransform gives you more performance than multiple matrix calls ctx.translate, ctx.scale, ctx.translate.
No need for complex transformation inversions as and expensive DOM matrix calls tp converts point between zoomed and screen coordinate systems.
Flexible
Flexibility as you don't need to use ctx.save and ctx.restore if you are rendering content at using different transforms. Returning to the transform with ctx.setTransform rather than the potentially frame rate wreaking ctx.restorecall
Easy to invert the transform and get the world coordinates of a (screen) pixel position and the other way round.
Examples
Using mouse and mouse wheel to zoom in and out at mouse position
An example using this method to scale page content at a point (mouse) via CSS transform CSS Demo at bottom of answer also has a copy of the demo from the next example.
And an example of this method used to scale canvas content at a point using setTransform
How
Given a scale and pixel position you can get the new scale as follow...
const origin = {x:0, y:0}; // canvas origin
var scale = 1; // current scale
function scaleAt(x, y, scaleBy) { // at pixel coords x, y scale by scaleBy
scale *= scaleBy;
origin.x = x - (x - origin.x) * scaleBy;
origin.y = y - (y - origin.y) * scaleBy;
}
To position the canvas and draw content
ctx.setTransform(scale, 0, 0, scale, origin.x, origin.y);
ctx.drawImage(img, 0, 0);
To use if you have the mouse coordinates
const zoomBy = 1.1; // zoom in amount
scaleAt(mouse.x, mouse.y, zoomBy); // will zoom in at mouse x, y
scaleAt(mouse.x, mouse.y, 1 / zoomBy); // will zoom out by same amount at mouse x,y
To restore the default transform
ctx.setTransform(1,0,0,1,0,0);
The inversions
To get the coordinates of a point in the zoomed coordinate system and the screen position of a point in the zoomed coordinate system
Screen to world
function toWorld(x, y) { // convert to world coordinates
x = (x - origin.x) / scale;
y = (y - origin.y) / scale;
return {x, y};
}
World to screen
function toScreen(x, y) {
x = x * scale + origin.x;
y = y * scale + origin.y;
return {x, y};
}