How to zoom to center of canvas, not to center of context - javascript

I have a canvas, size 400 x 400. On it, i have drawn a map area, 200 x 200.
I have translated this to the center of the canvas. I can zoom in and out, all is well. But, when i pan, it zooms from the center of my map area. I want it to always zoom from the center of the canvas no matter where the map area is. I think i need to negate the pan coords somehow, but i can't figure it out.
Here is my code:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var canvas1 = document.getElementById("canvasX");
var ctxX = canvas1.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var mapW = 200;
var mapH = 200;
var panX=0;
var panY=0;
var scaleFactor=1.00;
drawTranslated();
function zoomIn(){
document.getElementById("zoomin").click();
{ scaleFactor*=1.1; drawTranslated(); };
}
function zoomOut(){
document.getElementById("zoomout").click();
{ scaleFactor/=1.1; drawTranslated(); };
}
function panUp(){
document.getElementById("panup").click();
{ panY-=25; drawTranslated(); };
}
function panDown(){
document.getElementById("pandown").click();
{ panY+=25; drawTranslated(); };
}
function panLeft(){
document.getElementById("panleft").click();
{ panX-=25; drawTranslated(); };
}
function panRight(){
document.getElementById("panright").click();
{ panX+=25; drawTranslated(); };
}
function drawTranslated(){
// canvas
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.translate(cw/2, ch/2);
ctx.translate(panX,panY);
ctx.scale(scaleFactor, scaleFactor);
ctx.fillStyle = "Green";
ctx.fillRect(mapW/-2, mapH/-2, mapW, mapH);
ctx.restore();
// canvasX
ctxX.clearRect(0,0,cw,ch);
ctxX.save();
ctxX.translate(cw/2, ch/2);
ctxX.beginPath();
ctxX.moveTo(0, 25);
ctxX.lineTo(0, -25);
ctxX.moveTo(-25, 0);
ctxX.lineTo(25, 0);
ctxX.closePath();
ctxX.lineWidth = 1;
ctxX.strokeStyle = 'Black';
ctxX.stroke();
ctxX.restore();
}
#wrapper {position: relative;}
canvas {position: absolute; border: 1px solid Black;}
<!DOCTYPE html>
<html>
<body>
<div id="wrapper">
<button id="zoomin" class="nav" title="Zoom In" onclick="zoomIn()">+</button>
<button id="zoomout" class="nav" title="Zoom Out" onclick="zoomOut()">−</button>
<button id="panup" class="nav" title="Up" onclick="panUp()">⇧</button>
<button id="pandown" class="nav" title="Down" onclick="panDown()">⇩</button>
<button id="panleft" class="nav" title="Left" onclick="panLeft()">⇦</button>
<button id="panright" class="nav" title="Right" onclick="panRight()">⇨</button>
</div>
<div id="wrapper">
<canvas id="myCanvas" width="400" height="400"></canvas>
<canvas id="canvasX" width="400" height="400"></canvas>
</div>
</body>
</html>

After some time of trying to find some equation to solve this, i think i have found the solution.
ctx.translate(panX,panY);
ctx.scale(scaleFactor, scaleFactor);
By panning first, then scaling, it will zoom from the center of the context (In this case the green square). However, simply changing it around to:
ctx.scale(scaleFactor, scaleFactor);
ctx.translate(panX,panY);
it will zoom from the center of the canvas.
It seems to do what i want it to, so unless i am mistaken, i believe this is the answer.
I have included another snippet. The only changes are those 2 lines, but i think it would be helpful for people to able to see the difference it makes.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvas1 = document.getElementById("canvasX");
var ctxX = canvas1.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
var mapW = 200;
var mapH = 200;
var panX = 0;
var panY = 0;
var scaleFactor = 1.00;
drawTranslated();
function zoomIn() {
document.getElementById("zoomin").click(); {
scaleFactor *= 1.1;
drawTranslated();
};
};
function zoomOut() {
document.getElementById("zoomout").click(); {
scaleFactor /= 1.1;
drawTranslated();
};
};
function panUp() {
document.getElementById("panup").click(); {
panY -= 25;
drawTranslated();
};
};
function panDown() {
document.getElementById("pandown").click(); {
panY += 25;
drawTranslated();
};
};
function panLeft() {
document.getElementById("panleft").click(); {
panX -= 25;
drawTranslated();
};
};
function panRight() {
document.getElementById("panright").click(); {
panX += 25;
drawTranslated();
};
};
function drawTranslated() {
// canvas
ctx.clearRect(0, 0, cw, ch);
ctx.save();
ctx.translate(cw / 2, ch / 2);
ctx.scale(scaleFactor, scaleFactor);
ctx.translate(panX, panY);
ctx.fillStyle = "Green";
ctx.fillRect(mapW / -2, mapH / -2, mapW, mapH);
ctx.restore();
// canvasX
ctxX.clearRect(0, 0, cw, ch);
ctxX.save();
ctxX.translate(cw / 2, ch / 2);
ctxX.beginPath();
ctxX.moveTo(0, 25);
ctxX.lineTo(0, -25);
ctxX.moveTo(-25, 0);
ctxX.lineTo(25, 0);
ctxX.closePath();
ctxX.lineWidth = 1;
ctxX.strokeStyle = 'Black';
ctxX.stroke();
ctxX.restore();
};
#wrapper {
position: relative;
}
canvas {
position: absolute;
border: 1px solid Black;
}
<div id="wrapper">
<button id="zoomin" class="nav" title="Zoom In" onclick="zoomIn()">+</button>
<button id="zoomout" class="nav" title="Zoom Out" onclick="zoomOut()">−</button>
<button id="panup" class="nav" title="Up" onclick="panUp()">⇧</button>
<button id="pandown" class="nav" title="Down" onclick="panDown()">⇩</button>
<button id="panleft" class="nav" title="Left" onclick="panLeft()">⇦</button>
<button id="panright" class="nav" title="Right" onclick="panRight()">⇨</button>
</div>
<div id="wrapper">
<canvas id="canvas" width="400" height="400"></canvas>
<canvas id="canvasX" width="400" height="400"></canvas>
</div>

There surely is a better way but in Fabric.js you can do
canvas.zoomToPoint(new fabric.Point(canvas.width / 2, canvas.height / 2), canvas.getZoom() / ZOOM_PERCENT);
See more here Canvas
For a more pure solution, you can also try translating the context by half the canvas size using
ctx.translate()
A fiddle that might help
http://jsfiddle.net/m1erickson/QEuw4/

Related

Show HTML Slider variable derived through javascript on HTML Page

I have a slider that changes the width of circles on a bullseye on a canvas. I want to display that bandwidth beneath the canvas. Like just a simple Current bandwidth: x
I am having trouble figuring out how to do this.
I've tried using span and documentwrite(), but part of the problem is I can't really figure out how to get the variable in the first place.
<!DOCTYPE html>
<html>
<head>
<script src="bullseye.js"></script>
<title>Bull's Eye</title>
</head>
<body>
<canvas id="testCanvas" style="border: 1px solid;" width="400" height="400"> </canvas>
<p></p>
<div style="position:absolute; top:420px; left:10px">
<label for="radius">Bandwidth</label>
<input type="range" id="width" min="5" max="50" step="5" value="25" oninput="sliderModule.drawPattern()"></input>
<p>Current Bandwidth: <span id="show"></span></p>
</div>
</body>
</html>
here is the javascript
var sliderModule = (function(win, doc) {
win.onload = init;
// canvas and context variables
var canvas;
var context;
// center of the pattern
var centerX, centerY;
function init() {
canvas = doc.getElementById("testCanvas");
context = canvas.getContext("2d");
centerX = canvas.width / 2;
centerY = canvas.height / 2;
drawPattern();
}
function drawPattern() {
var width = doc.getElementById("width").value;
var radius = 200;
var colorTog = 1;
context.clearRect(0, 0, canvas.width, canvas.height);
while (radius > 0) {
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI);
if (colorTog == 1){
context.fillStyle = 'red';
} else {
context.fillStyle = 'blue';
}
context.fill();
context.closePath();
radius = radius - width;
if (colorTog == 1){
colorTog = 0;
} else {
colorTog = 1;
}
}
}
return {
drawPattern: drawPattern
};
})(window, document);

JS Canvas: How to make rectangle go back and forth once it reaches the boundary

So I have this rectangle that animates across to the right. How can I get the rectangle to reverse it when it hits the boundaries. I'm trying to make it go back and forth.
<!DOCTYPE html>
<html>
<head>
<script type='text/javascript'>
window.onload=function(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 50;
var width = 10;
var height = 10;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(x, y, width, height);
x++;
if(x <= 490) {
setTimeout(animate, 33);
}
}
animate();
}
</script>
</head>
<body>
<canvas id="canvas" width="500" height="400"
style="border: 1px solid #000000;"></canvas>
</body>
</html>
https://codepen.io/forTheLoveOfCode/pen/wqdpeg
Is that what you need? (link to codepen above).
var canvas = document.getElementById("canvas_id");
var context = canvas.getContext('2d');
var x=5;
var y=5;
var velocity = 10;
function move(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
x =x + velocity
if ((x+50)>canvas.width || x<0){
velocity *=-1;
}
draw()
}
function draw(){
context.fillStyle = "#E80C7A";
context.strokeStyle = "#000000";
context.lineWidth = '3';
context.fillRect(x, y, 50, 100);
context.strokeRect(x, y, 50, 100);
}
setInterval(move, 100);
<html>
<body>
<canvas id = "canvas_id">
</canvas>
</body>
</html>
here's a solution with boundaries detection
window.onload=function(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 50;
var width = 10;
var height = 10;
var speed = 10; // speed
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(x, y, width, height);
if(
(x >= 500 - width && speed > 0) || // going to the right and bound reached
(x <= 0 && speed < 0) // going to the left and bound reached
) {
speed *= -1; // inverting the direction
}
x += speed;
setTimeout(animate, 33);
}
animate();
}
<canvas id="canvas" width="500" height="400"
style="border: 1px solid #000000;"></canvas>
consider using requestAnimationFrame instead of setTimeout to do this kind of work.

HTML, CSS, JS, Canvas rectangle not deleting

I would like the rectangle to move on the canvas and not copy every time.
It draws it but then the rectangle stays there.
I am a beginner with the canvas so if it is an epic fail then be prepared.
The codepen is at LINK.
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var boxWidth = 50;
var boxHeight = 50;
var bX = WIDTH / 2 - boxWidth / 2;
var bY = HEIGHT / 2 - boxHeight / 2;
function render() {
ctx.fillStyle = "white";
ctx.rect(bX, bY, boxWidth, boxHeight);
ctx.fill();
}
function control() {
bX++;
}
function main() {
control();
render();
}
main();
var run = setInterval(main, 10)
canvas {
width: 400px;
height: 400px;
background-color: black;
}
div {
text-align: center;
}
<div>
<canvas width="400px" height="400px" background-color="black" id="canvas"></canvas>
</div>
Repaint your canvas before drawing the rectangle each time - think about it. Its all done in layers.
The rectangle is "staying there" because you aren't replacing the rectangle your drew last time.
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var boxWidth = 50;
var boxHeight = 50;
var bX = WIDTH / 2 - boxWidth / 2;
var bY = HEIGHT / 2 - boxHeight / 2;
function render() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); //use clear rect instead
ctx.fillStyle = "white";
ctx.fillRect(bX, bY, boxWidth, boxHeight); //use fillRect instead of fill()
}
function control() {
bX++;
}
(function main() {
control();
render();
requestAnimationFrame(()=>main());
})()
canvas {
width: 400px;
height: 400px;
background-color: black;
}
div {
text-align: center;
}
<html>
<head></head>
<body>
<div>
<canvas width="400px" height="400px" background-color="black" id="canvas"></canvas>
</div>
</body>
</html>
Also have a look at the requestAnimationFrame() method as opposed to setInterval - it syncs up with the window's javascript timer and is less likely to cause problems.

How to restart canvas drawing?

When I click start, I get whole circle. But when I click clean and again start, previously part of the circle remaining and straight line appear.
DEMO: https://fiddle.jshell.net/1xhkfk73/
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var draw = 0;
var stepDraw = 0;
ctx.strokeStyle = "#FF0000";
ctx.translate(0.5, 0.5);
var delay = 30;
var drawing = 0;
function drawCircle(steps) {
draw = ((2 * Math.PI) / steps);
stepDraw = draw;
drawing = setInterval(function() {
ctx.arc(400, 200, 120, draw, draw, false);
ctx.stroke();
draw += stepDraw;
}, delay)
}
$("#click").click(function() {
drawCircle(120);
});
$("#clean").click(function() {
clearInterval(drawing);
ctx.clearRect(0, 0, 800, 400);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="click">start</span>
<span id="clean">clean</span>
<canvas id="myCanvas" class="center-block" width="800" height="400">
Canvas not supported!
</canvas>
Try this.
You need to have your path closed.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var draw = 0;
var stepDraw = 0;
ctx.strokeStyle = "#FF0000";
ctx.translate(0.5, 0.5);
var delay = 30;
var drawing = 0;
function drawCircle(steps) {
draw = ((2 * Math.PI) / steps);
stepDraw = draw;
drawing = setInterval(function() {
ctx.beginPath();
ctx.arc(400, 200, 120, draw, draw+stepDraw, false);
ctx.stroke();
draw += stepDraw;
}, delay)
}
$("#click").click(function() {
drawCircle(120);
});
$("#clean").click(function() {
clearInterval(drawing);
ctx.clearRect(0, 0, 800, 400);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="click">start</span>
<span id="clean">clean</span>
<canvas id="myCanvas" class="center-block" width="800" height="400">
Canvas not supported!
</canvas>

call a function onclick a button in html5

I want to call a function by touching a button in HTML5. I haveange in a canvas drawn a rect.
Here is my code:
<body>
<canvas id="canvas" width="200" height="300"></canvas>
<button id="as" type="button">Left</button></body>
<script>
var inc=10;
var c=document.getElementById("canvas");
ctx = c.getContext("2d");
ctx.fillRect(x,0,150,75);
function moveLeft(){ x+=inc }
</script>
Since you mentioned 'touch', I guess we'll need a timer. The canvas draw routine also need some fixing. Here is my version:
<html><body>
<canvas id="canvas" width="200" height="300" style='border:1px solid black'></canvas>
<button id="as" type="button" onmouseover='startMove()' onmouseout='stopMove()'>Left</button></body>
<script>
var c=document.getElementById("canvas");
var ctx = c.getContext("2d");
var inc = 10;
var timer = 0;
var x = 100;
function moveLeft(){ if ( x > 0 ) x-=inc; drawCtx(); }
function drawCtx(){ ctx.clearRect(0,0,200,300); ctx.fillStyle='black'; ctx.fillRect(x,0,50,75); }
function startMove(){ stopMove(); timer = setInterval(moveLeft, 1000); }
function stopMove(){ clearInterval(timer); }
drawCtx();
</script>
What this do is when you mouse over it will start calling moveLeft once per second (1000ms interval) until you move away the mouse.
Code is not nice, but it works and I hope it is simple enough to get the point across.
Here an example moving all directions and border checking:
HTML:
<canvas id="canvas"></canvas><br>
<button type="button" onclick="moveRect(0, -10);">move up</button><br>
<button type="button" onclick="moveRect(-10, 0);">move left</button>
<button type="button" onclick="moveRect(+10, 0);">move right</button><br>
<button type="button" onclick="moveRect(0, +10);">move down</button>
JS:
var c = null, ctx = null, x = 0, y = 0;
var width = 150, height = 75;
function moveRect(x2, y2) {
ctx.clearRect(x, y, width, height);
x += x2;
if (x < 0) x = 0;
else if (x > c.width - width) x = c.width - width;
y += y2;
if (y < 0) y = 0;
else if (y > c.height - height) y = c.height - height;
ctx.fillRect(x, y, width, height);
}
window.onload = function() {
c = document.getElementById("canvas");
ctx = c.getContext("2d");
x = 0;
moveRect(0, 0);
}
CSS:
#canvas {
width: 200;
height: 300px;
border: 1px solid red;
}
Also see this example.
You can to give fill style to make your rectangle visible or draw it border. Secondly you want to attach event to button to move the rectangle drawn on canvas. The could be done by this code and could mold the code according to your need. Demo on JsFiddle using Javascript and Demo on JsFiddle using jQuery
x =200;
$('#as').click(function()
{
var inc=10;
var c=document.getElementById("canvas");
//c.style.backgroundColor = "#ff0000";
ctx = c.getContext("2d");
ctx.fillStyle="#ff0000";
ctx.fillRect(x+5,0,15,75);
ctx.fillStyle="#ffffff";
ctx.fillRect(x,0,15,75);
x-=5;
}
);​

Categories