This is a repost from StackExchange's GameDev section, yet, I find that the problem seems more applicable to StackOverflow because it pertains more to CSS, JS, and positioning techniques rather than JavaScript topics (mostly UnityScript and Phaser) discussed on the former.
I've been making a roguelike-style game in HTML5 without using canvas (only divs) with pure JS (fiddle!). I've been trying to enlarge the tile size (font size) while keeping the player centered within the camera. For some reason, when the tile size isn't equal to the map size, the camera will be slightly off.
Note that the 3D effect is on purpose; I believe it adds some much needed depth, and just looks super cool. :D
When tileScale (line 4 in the fiddle) is 9 (very undesirable; the dots on the player's y axis should be aligned, not at a slight angle):
When tileScale is 25 (on point!):
Here's some relevant (trimmed) code:
window.onresize = function(){
game.viewportWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
game.viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
game.windowSize = Math.min(game.viewportWidth, game.viewportHeight);
// Droid Sans Mono has a ratio of 3:4 (width:height), conveniently.
// This may be problematic. I'm not sure.
game.tileWidth = game.windowSize*.6 / game.tileScale;
game.tileHeight = game.windowSize*.8 / game.tileScale;
}
// Update the camera position (needs help?)
this.updateCamera = function(){
// Get player coordinates (-.5 because we need to get the player tile center)
// times the tileWidth plus the game window (inner square) size divided by two.
var left = ((-game.player.x-.5)*game.tileWidth+game.windowSize/2)+"px";
var top = ((-game.player.y-.5)*game.tileHeight+game.windowSize/2)+"px";
game.planeContainer.style.left = left;
game.planeContainer.style.top = top;
}
How can I ensure that the dots in the center of the screen will be always lined up, instead of on a slight angle? My current evidence suggests that the position of the game.planeContainer object isn't being established correctly.
I know this is a super-tough problem, so any help will be greatly appreciated. Thanks in advance!
(Another fiddle link, in case you skimmed over the first one. :D)
Remove this line from your css stylings on .inner-text to take out the skew:
transform: translate(-50%, -50%);
Related
There's a bunch of questions on panning a background image in a canvas (i.e. to simulate a 'camera' in a game with the character in the center) - and most answers suggest using the canvas' translate method.
But since you have to re-draw the image in each frame anyway, why not just clip it? Does it matter in terms of efficiency?
In particular, is panning like this a bad idea? (This example shows a simplified pan of the camera moving in one direction)
let drawing = new Image();
drawing.src = "img_src";
drawing.onload = function () {
ctx.drawImage(drawing, 0, 0);
let pos = 0
setInterval(() => {
pos += 1
ctx.clearRect(0, 0, cnvs.width, cnvs.height);
ctx.drawImage(drawing, -pos, 0 ); // "pans" the image to the left using the option `drawImage` parameters `sx` and `sy`
}, 33);
};
Example result: fiddle
Thanks in advance!
The main advantage of using the transform matrix to control your camera is that you don't have to update all the elements in your world, you just move the world instead.
So yes, if you are willing to move only a single element (be it the background like in your case), moving only that one element might be a better choice.
But if you need several layers of elements to all move relatively to the camera, then using the transformation matrix is a better choice.
As for the perfs, I didn't ran any benchmarks on this, but I'd suspect it's exactly the same, though beware when messing with the cropping features of drawImage, at least Safari doesn't handle cropping from outside of a source canvas correctly.
I have an image which as a "ruler" (made of basic divs positioned absolute on top of the image) that are use to measure the ends of the image. Now the idea is that if you long press one of the ruler ends (the dots at the end of the line which are draggable), the image in the background would zoom in that point, and follow the dot if the user moves it. I am able to detect the long press but I cannot get the image to zoom and follow the dot once detected. The code below is where I have done the detection and now I should apply the styling to move the image. I thought of using the transition property but couldn't get it to zoom on the dot. Any help is appreciated...
Here's a codesandbox with how the ruler works: Link
Meaningful code:
const x = get('x', varToUse); //This just gives the x coordinate of the ruler end
const y = get('y', varToUse); //This just gives the y coordinate of the ruler end
const image = ruler.current.parentElement.parentElement.childNodes[1].childNodes[1];
if (zoom) {
image.style.transform = `translate(${x * 2}px, ${y * 2}px) scale(2.0)`;
} else {
image.style.transform = `scale(1.0)`;
}
This is what the ruler looks like just to get an understanding:
You can make the image a div with background-image.
.image {
background-image: url({image_url});
}
so this way you can update the image size and position easily with this properties
.image {
background-size: x y;
background-position x y;
}
I think this way is easier to do the image resizing and zoom abilities.
another way is to use a canvas library that can help you a lot they have lots of built in functions.
I think trying it without library is better for now but as it grows try to move to a canvas library
The first reason is that in the code you provided, the DOM element that is being manipulated is a div id='root'. The image should be selected.
I am playing around with a project to learn more about node.js & html canvases.
In my project I have a canvas that I want to keep a fixed bitmap size, but fill its containing div while maintaining its aspect ratio.
I have applied a size of 500x500 to my canvas element, and then applied the following style in CSS.
canvas {
display: block;
width:100%;
height:100%;
object-fit: contain;
background-color: lightgrey;
}
Inside the javascript initially fill the canvas white so I get something like the below, so far so good.
I hook into the mouse events and use them to draw lines. I use the below function to correctly scale events to the canvas.
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect(); // abs. size of element
var raw_x = evt.clientX||evt.touches[0].clientX;
var raw_y = evt.clientY||evt.touches[0].clientY;
var min_dimension = Math.min(rect.width,rect.height);
var x_offset = 0.5*(rect.width-min_dimension);
var y_offset = 0.5*(rect.height-min_dimension);
return {
x: ((raw_x - rect.left - x_offset) / min_dimension) * canvas.width,
y: ((raw_y - rect.top - y_offset) / min_dimension) * canvas.height
}
}
This works, however when drawing on the canvas when the mouse moves over a band on the right side of the image it doesn't update until the mouse leaves the band. The band is the same size as the space on the left of the canvas so I think its related but I don't know how to investigate. I have no Issues if I resize the window till there is no space on either side of the canvas bitmap (and performance is considerably faster). The below gif should make things more clear.
Does anyone have a suggestion on what could be causing this, or a better way for me to achieve the same effect.
Note: I am running chrome version 80.0.3987.149
For anyone else that comes across this, I couldn't find a good solution, beyond using JavaScript to resize the element when the window resize event occurs.
I've been working on this problem for days. I am trying to implement a "free transform" tool for svgs. Similar to that of Raphael.FreeTransform or how you would move/rotate/scale images in MS Word. (Yes, I am aware there are libraries) The following jSFiddle displays my problem: https://jsfiddle.net/hLjvrep7/12/
There are 5 functions in the jsFiddle: rotate-t. shrink-t, grow-t, shrink, grow. The functions suffixed with '-t' also apply the current rotation transformation. e.g.:
grow-t
rect.attr({height : height * 1.25, width : width * 1.25}).transform('r' + degree);
grow
rect.attr({height : height * 1.25, width : width * 1.25});
Once an svg is rotated, then scaled. If you try to rotate the svg again (after scale), the svg jumps. To see this, go top the fiddle:
Hit rotate-t twice. Svg should rotate a total of 30 degrees from the rectangles origin.
Hit grow (not grow-t) twice. Note the top left position of the svg stays the same.
Hit rotate-t once. Note the svg jumps to a different position, then rotates.
Note hitting rotate-t subsequent times will continue to rotate the image around the origin (which is what I want the first time rotate-t is clicked)
One solution I had was to apply the current rotation transformation whenever changing the height and width. This fixes my previous problem, but introduces another problem. To see an example of this, go to the fiddle, and:
Hit rotate-t twice.
Hit grow-t a couple times. Notice the svg grows, but the top left position of the rectangle moves. That's a problem for me. I want the svg to grow without the top left corner to move.
Notes on using the jsFiddle:
Any combination of rotate-t, grow-t, shrink-t will exhibit the ideal rotation behavior (about the origin, no jumping). But this also demonstrates the undesired growing and shrinking (top left position moved when svg is on angle).
Any combination pf rotate-t, grow, shrink will exhibit the ideal scaling behavior (top left corner of svg doesn't move). But this also demonstrates the undesired rotation property (will jump around after different rotations and scales).
Bottom line: I want to be able to the svg rotate around the origin. Then grow the image, while the top left position remains the same. Then rotate the svg again, around the origin without any jumping.
I am aware the how the transform function impacts the local coordinate system of the svg. I'm leaning towards using rotate-t, grow, shrink combo and simply apply some x-y offsets to remove the "jumping" effect. I would imagine there must be some sort of offset I could apply to avoid jumping or shifting during rotation or scaling, but its not clear to me how to calculate such offsets. Any help would be appreciated.
Please don't hesitate to ask anymore questions. Like I said, I've been digging into this for days. Clearly, I don't understand it all, but am somewhat intimate with what's happening and happy to explain anything in more detail.
My solutions for scale, rotate, move back and front etc:
$scope.back = function () {
if($scope.currentImage !==null) {
if($scope.currentImage.prev!=undefined) {
var bot = $scope.currentImage.prev;
$scope.currentImage.insertBefore(bot);
ft.apply();
}
}
};
//Function for moving front
$scope.front = function () {
if($scope.currentImage !==null) {
if($scope.currentImage.next!=undefined) {
var top = $scope.currentImage.next;
if($scope.currentImage.next.node.localName == "image")
$scope.currentImage.insertAfter(top);
ft.apply();
}
}
};
//ZOOM
$scope.zoomIn = function () {
if ($scope.currentImage!= null) {
var ft = paper.freeTransform($scope.currentImage);
if(ft.attrs.scale.y<4) {
$scope.currentImage.toFront();
ft.attrs.scale.y = ft.attrs.scale.y *(1.1);
ft.attrs.scale.x = ft.attrs.scale.x *(1.1);
ft.apply();
ft.updateHandles();
}
}
};
I'm working on a 2d canvas-based app using EaselJS where the user can move indefinitely on the xy-plane by dragging the background. Think google maps, except the background is a repeating tile.
The code for the movement is very simple and looks something like this:
// container - the createjs.Container being panned
// background - a createjs.Shape child of container, on which the
// background is drawn
background.onPress = function(evt) {
var x = evt.stageX, y = evt.stageY;
evt.onMouseMove = function(evt) {
// the canvas is positioned in the center of the window, so the apparent
// movement works by changing the registration point of the container in
// the opposite direction.
container.regX -= evt.stageX - x;
container.regY -= evt.stageY - y;
x = evt.stageX;
y = evt.stageY;
stage.update();
};
evt.onMouseUp = function(evt) {
// Here the background would be redrawn based on the new container coords.
// However the issue occurs even before the mouse is released.
background.redraw();
stage.update();
};
};
All works as expected until reaching 32678px (2^15) on either axis. What occurs is different in different browsers, but the point where it first happens is the same.
In Firefox, it will suddenly shift a large chunk of pixels (~100) rather than 1. It will then happen again at 65538 (2^16+2), perhaps more after that, but I haven't witnessed it. After the trouble points, the drag will continue smoothly, as expected, but remaining shifted.
In Chrome, the effect is more dramatic. The drawing breaks and results in repeated ~100px wide "stripes" of the background across the page at 32768, and does not correct itself on redraw.
Oddly, the numbers in EaselJS don't reflect the issue. The only change in the container's transform matrix is the tx or ty incrementing by 1. No other matrices change. It seems as though EaselJS is getting all the numbers right.
Can anyone shed any light this issue?
Edit:
I worked around this problem by redrawing parts of the container using a calculated regX/regY, rather than attempting to translate very large regX/regY coords on the canvas.
This question may be related
What is the maximum size for an HTML canvas?
From what I gather browsers use short int to store canvas sizes with 32,767 being the maximum possible value.
Links possibly related to your issue,
Mozilla - HTML5 Canvas slow/out of memory
https://stackoverflow.com/a/12535564/149636