I am trying to get the mouse position on a transformed canvas. Here is my resize method:
window.addEventListener('resize', resize);
function resize() {
screenWidth = window.innerWidth;
screenHeight = window.innerHeight;
scaleFillNative = MathMAX(screenWidth / maxScreenWidth, screenHeight / maxScreenHeight);
mainCanvas.width = screenWidth;
mainCanvas.height = screenHeight;
mainContext.setTransform(scaleFillNative, 0, 0, scaleFillNative, Math.floor((screenWidth - (maxScreenWidth * scaleFillNative)) / 2),
Math.floor((screenHeight - (maxScreenHeight * scaleFillNative)) / 2));
}
The maxScreenWidth and maxScreenHeight represents the native screen dimensions that the canvas should be transformed to.
The actual resizing works fine. The issue however is that I am trying to render a circle at the mouse position on the canvas. The mouse position is set as follows:
window.addEventListener('mousemove', gameInput, false);
var mouseX, mouseY;
function gameInput(e) {
e.preventDefault();
e.stopPropagation();
mouseX = e.clientX;
mouseY = e.clientY;
}
And it is then rendered like this:
renderCircle(mouseX / scaleFillNative, mouseY / scaleFillNative, 10);
The x position is rendered correctly. However when I resize the window so that the width is less than the height, it no longer renders at the correct x location. The y position is always offset.
I don't know exactly what you have tried so far, but for a basic mouse coordinate to transformed canvas (non skewed), you'll have to do
mouseX = (evt.clientX - canvas.offsetLeft - translateX) / scaleX;
mouseY = (evt.clientY - canvas.offsetTop - translateY) / scaleY;
But canvas.offsetXXX doesn't take scroll amount into account, so this demo uses getBoundingRect instead.
var ctx = canvas.getContext('2d');
window.addEventListener('resize', resize);
// you probably have these somewhere
var maxScreenWidth = 1800,
maxScreenHeight = 1200,
scaleFillNative, screenWidth, screenHeight;
// you need to set available to your mouse move listener
var translateX, translateY;
function resize() {
screenWidth = window.innerWidth;
screenHeight = window.innerHeight;
// here you set scaleX and scaleY to the same variable
scaleFillNative = Math.max(screenWidth / maxScreenWidth, screenHeight / maxScreenHeight);
canvas.width = screenWidth;
canvas.height = screenHeight;
// store these values
translateX = Math.floor((screenWidth - (maxScreenWidth * scaleFillNative)) / 2);
translateY = Math.floor((screenHeight - (maxScreenHeight * scaleFillNative)) / 2);
ctx.setTransform(scaleFillNative, 0, 0, scaleFillNative, translateX, translateY);
}
window.addEventListener('mousemove', mousemoveHandler, false);
function mousemoveHandler(e) {
// Note : I don't think there is any event default on mousemove, no need to prevent it
// normalize our event's coordinates to the canvas current transform
// here we use .getBoundingRect() instead of .offsetXXX
// because we also need to take scroll into account,
// in production, store it on debounced(resize + scroll) events.
var rect = canvas.getBoundingClientRect();
var mouseX = (e.clientX - rect.left - translateX) / scaleFillNative,
mouseY = (e.clientY - rect.top - translateY) / scaleFillNative;
ctx.fillRect(mouseX - 5, mouseY - 5, 10, 10);
}
// an initial call
resize();
<canvas id="canvas"></canvas>
Related
So I have two objects. SquareA and SquareB. SquareA follows the movement of the cursor. Now I want to make SquareB mirror the movement of SquareA but in reverse.
Example:
SquareA goes left, SquareB goes right.
SquareA goes down, SquareB goes up.
I currently have this code to make squareB follow squareA
// update squareA to mouse
squareA.x = mouseX - (squareB.width * .5);
squareA.y = mouseY - (squareB.height * .5);
// update squareB to squareA
squareB.x = squareA.x;
squareB.y = squareA.y - (squareA.height * 2);
But I can't think of a way to reverse squareB's movement.
Here's the pen for the problem
https://codepen.io/ricjonsu098/pen/xxZrvZP?editors=0010
Full solution:
// create a canvas to work with
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
// style canvas
canvas.id = "canvas";
canvas.width = 400;
canvas.height = 400;
canvas.setAttribute("style", "border: 1px solid black;");
// get 2D context
var context = canvas.getContext('2d');
var squareA = {x:10, y:30, width: 25, height: 25 };
var squareB = {x:10, y:30, width: 25, height: 25 };
// place holders for mouse x,y position
var mouseX = 0;
var mouseY = 0;
var centerX = canvas.width/2;
var centerY = canvas.height/2;
// Render Loop
function update() {
// update squareA to mouse
squareA.x = mouseX - (squareB.width * .5);
squareA.y = mouseY - (squareB.height * .5);
// update squareB to squareA
squareB.x = centerX - (squareA.x - centerX);
squareB.y = centerY - (squareA.y - centerY);
// clear the canvas
canvas.width = canvas.width;
// draw first rectangle
context.fillStyle = "blue";
context.fillRect (squareA.x,squareA.y,squareA.width,squareA.height);
// draw second rectangle
context.fillStyle = "green";
context.fillRect (squareB.x,squareB.y,squareB.width,squareB.height);
}
// update mouse position
canvas.onmousemove = function(e) {
mouseX = e.offsetX;
mouseY = e.offsetY;
update();
}
Note that I got rid of the interval. You can just update whne the mouse moves.
I hope this helps, 400 is canvas size
// update squareA to mouse
squareA.x = mouseX;
squareA.y = mouseY;
// update squareB to squareA
squareB.x = 400 - squareA.x;
squareB.y = 400 - squareA.y;
i've made a little script where a canvas is drawn dynamically according to user input(type:number), it needs work but the issue is that on android devices, when the user hide the soft keyboard, the canvas is erased. is it a way to prevent that ? Thx for any help
here is the code
var form = document.querySelector("form"),
canvas = document.getElementById("canvas"),
positions = [];
if (!canvas) {
alert("Impossible de récupérer le canvas");
}
window.addEventListener("resize", function () {
if (window.innerWidth < 500) {
canvas.width = window.innerWidth * 0.8;
canvas.height = window.innerWidth * 0.8;
} else {
canvas.width = 600;
canvas.height = 600;
}
});
var context = canvas.getContext("2d");
if (!context) {
alert("Impossible de récupérer le contexte");
}
form.addEventListener("input", function () {
var table = form.table.value,
modulo = form.modulo.value,
centerX = canvas.width / 2,
centerY = canvas.height / 2,
rayon = (canvas.width - 5) / 2;
context.lineWidth = 2;
context.strokeStyle = "#21A8A3";
context.clearRect(0, 0, canvas.width, canvas.width);
for (var i = modulo; i >= 0; i -= 1) {
var angle = (2 * Math.PI / modulo) * i - (Math.PI / 2);
positions[i] = {
x: centerX + rayon * Math.cos(angle),
y: centerY + rayon * Math.sin(angle)
};
}
context.beginPath();
context.arc(centerX, centerY, rayon, 0, Math.PI * 2);
var j = positions.length - 1;
for (j; j >= 0; j -= 1) {
var next = j * table;
context.moveTo(positions[j].x, positions[j].y);
context.lineTo(positions[next % modulo].x, positions[next % modulo].y);
}
context.stroke();
});
and the url :http://multiplier.hyperion-web.com/
I didn't tried on an Android device, but your problem is more likely due to your bad resize event listener.
This handler do resize your canvas at each resize event, but it doesn't call again your drawing functions, which leaves you with an empty canvas, since modifying the width and height of a canvas resets its context (it does clear it, but also resets any properties like fillStyle, transformation matrix, clipping area etc).
Calling and hiding the soft keyboard will fire a resize event.
So the solution for you is to fix your event handler so that it does redraw everything after it has set the new dimensions of your canvas.
For this you'll have to store your drawing function in a separate variable, then attach it to your input event, and make a call to it in your resize event listener.
Ps : even setting the width or height attributes to the same value they were will reset the whole context
So I'm using the function from this tutorial
http://www.html5rocks.com/en/tutorials/casestudies/gopherwoord-studios-resizing-html5-games/#disqus_thread
it looks like this -
function resizeGame() {
var gameArea = document.getElementById('canvas-container');
var widthToHeight = 11 / 6;
var newWidth = window.innerWidth;
var newHeight = window.innerHeight;
var newWidthToHeight = newWidth / newHeight;
if (newWidthToHeight > widthToHeight) {
newWidth = newHeight * widthToHeight;
gameArea.style.height = newHeight + 'px';
gameArea.style.width = newWidth + 'px';
} else {
newHeight = newWidth / widthToHeight;
gameArea.style.width = newWidth + 'px';
gameArea.style.height = newHeight + 'px';
}
gameArea.style.marginTop = (-newHeight / 2) + 'px';
gameArea.style.marginLeft = (-newWidth / 2) + 'px';
var gameCanvas = document.getElementById('ctx');
gameCanvas.width = newWidth;
gameCanvas.height = newHeight;
HEIGHT = newHeight;
WIDTH = newWidth
}
So everything that I use WIDTH and HEIGHT to position on the canvas work fine. But there's other things that I have in my game that just use an x and y and don't change with the canvas.
if(drawBall){
ctx.save();
ctx.beginPath();
ctx.arc(ball.x, ball.y, (ball.diameter/2) - 5,0, 2*Math.PI);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.restore();
}
What can I do to my ball.x and ball.y to make it so it correctly changes with the canvas?
It all comes down to ratio's. Knowing that you can always re position things when the screen re-sizes.
var ratio = { x:1, y: 1 };
function start(){
ratio.x = canvas.width / canvas.clientWidth;
ratio.y = canvas.height / canvas.clientHeight;
drawSomething();
}
function drawSomething(){
context.rect(rectX * ratio.x, rectY * ratio.y, rectWidth * ratio.x, rectHeight * ratio.y);
/// stroke and all that good stuff
}
now listen for window change
window.onresize = function(){
// get new ratio's and draw again
start();
}
With this it shouldn't matter what re-size the user does the canvas will draw at the same relative position and size.
I have a canvas that scales to the 16:9 aspect ratio. However the mouse does not take the offset (black background) into account.
The dotted green line marks the canvas. At the edge the mouse should display: Mouse : 0% | 23% rather than Mouse : 7% | 23%.
The problem seems to occur here:
var x = Math.floor( mouse.x / canvas.width * 100 );
var y = Math.floor( mouse.y / canvas.height * 100 );
It works fine when I go into fullscreen mode. (because there is no offset) But otherwise it does not take the offset into account.
Additional Code:
CSS:
#aspectoverlay {
/* 16:9 */
width: 100vw;
height: 56.25vw; /* 100/56.25 = 1.778 */
border: 1px #AFFF00 dashed;
max-height: 100vh;
max-width: 177.78vh; /* 16/9 = 1.778 */
margin: auto;
position: absolute;
top:0;bottom:0; /* vertical center */
left:0;right:0; /* horizontal center */
z-index: 100;
opacity: 1;
}
JS:
var canvas = document.getElementById('aspectoverlay');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext('2d');
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
canvas.addEventListener('mousemove', function(e){
mouse.x = e.x;
mouse.y = e.y;
});
var x = Math.floor( mouse.x / canvas.width * 100 );
var y = Math.floor( mouse.y / canvas.height * 100 );
ctx.fillText("Mouse : " + x + "% | " + y + "%", 50,50);
You just need to take the offsets of the element into account like so
mouse.x = e.x - this.offsetLeft;
mouse.y = e.y - this.offsetTop;
And since you are using CSS to resize and setting the canvas to the innerHeight and Width, you can't use the canvas width and height for calculations anymore you need to use offsetWidth and offsetHeight
var x = Math.floor(mouse.x / canvas.offsetWidth * 100);
var y = Math.floor(mouse.y / canvas.offsetHeight * 100);
Live Demo
Full Screen
var canvas = document.getElementById('aspectoverlay');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext('2d'),
mouse = {
x: 0,
y: 0
};
ctx.fillStyle = "#fff";
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.fillStyle = "#fff";
}
canvas.addEventListener('mousemove', function (e) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
mouse.x = e.x - this.offsetLeft;
mouse.y = e.y - this.offsetTop;
var x = Math.floor(mouse.x / canvas.offsetWidth * 100);
var y = Math.floor(mouse.y / canvas.offsetHeight * 100);
ctx.fillText("Mouse : " + x + "% | " + y + "%", 50, 50);
});
My autoresize canvas not works with kineticjs, the console say me that canvas.width and canvas.height is not defined.
Any people could help me.
Thank you .
Put the function and the call down.
function resize() {
console.log("ddddddddddddd");
//stage.setWidth((window.innerWidth / 100) * 80);
var canvas = document.getElementById('Contenedor');
console.log(canvas, canvas.height, canvas.width);
if(window.innerWidth < 1280){
var canvasRatio = canvas.height / canvas.width;
console.log(canvasRatio);
var windowRatio = window.innerHeight / window.innerWidth;
console.log(windowRatio);
var width;
var height;
if (windowRatio < canvasRatio) {
height = window.innerHeight;
width = height / canvasRatio;
} else {
width = window.innerWidth;
height = width * canvasRatio;
}
canvas.style.width = (parseFloat(width)*0.75) + 'px';
canvas.style.height = (parseFloat(height)*0.75) + 'px';
}else{
canvas.style.width=987+'px';
canvas.style.height=544+'px';
}
};
window.addEventListener('resize', resize, false);
What you can do is set the height of the canvas with respect to the width.
And use stage.setScale() and stage.setSize() to dynamically change the size of the stage
function resize() {
var canvasWidthRatio = 0.6; // canvas width to viewport width ratio
var viewPortWidth = window.innerWidth;
var canvasWidth = viewPortWidth * canvasWidthRatio;
var canvasHeight = canvasWidth * 0.6,
scale = stage.getScale(),
stageWidth = stage.getWidth(),
stageHeight = stage.getHeight(),
scaleFactor = Math.min(canvasWidth / stageWidth, canvasHeight / stageHeight),
newScale = scale * scaleFactor;
stage.setScale(newScale);
stage.setSize(canvasWidth, canvasHeight);
stage.draw();
}
window.addEventListener('resize', resize, false);
I think kineticjs sets the canvas dimensions.
set the stage width and height and don't touch the canvas directly.
Hello and sorry my bad english, also thank you to help me. I work with the code of VijayB, but with his code I have problems with multiply the object scale with a float.. I realease a few changes and for me are working.
I put the code for someone that have my own problem.
function resize() {
var __canvasWidthRatio = 0.6;
console.log( __canvasWidthRatio) ; // canvas width to viewport width ratio
var __viewPortWidth = window.innerWidth;
console.log(window.innerWidth);
var __canvasWidth = __viewPortWidth * __canvasWidthRatio;
console.log(__canvasWidth);
var aspec = 769 / 544;
var __canvasHeight = aspec *__canvasWidth * __canvasWidthRatio;
var __scale = ui.stage.getScale();
var __stageWidth = ui.stage.getWidth();
var __stageHeight = ui.stage.getHeight();
var __scaleFactor = Math.min( __canvasWidth / __stageWidth, __canvasHeight / __stageHeight);
stage.escala= __scale.x *__scaleFactor;
ui.scale=__scale.x *__scaleFactor;
stage.setScale(stage.escala,stage.escala);
stage.setSize(__canvasWidth, __canvasHeight);
stage.draw();
}
window.addEventListener('resize', resize, false);