Hi I am trying to create isometric graphic app with React, mostly base on the code here.
I achieved most of the functions (ie. zoom and scroll). But hovering tiles after zooming gives me wrong mouse position (hover position).
You can see what I mean here. You can zoom with scrolling vertically.
When it is not zoomed in or out, hovering tile works correctly (tile color changes where the mouse positions). But after zooming out/in it is not working right.
Does anyone know how to get the mouse position or tile index correctly after zooming in/out?
Implemented code can be found on my Github repo here
Code snippet for getting target tile is below:
const handleHover = (x: number, y: number) => {
const { e: xPos, f: yPos } = ctx.getTransform()
const mouse_x = mouseRef.current.x - x - xPos
const mouse_y = mouseRef.current.y - y - yPos
const hoverTileX =
Math.floor(
mouse_y / Tile.TILE_HEIGHT + mouse_x / Tile.TILE_WIDTH
) - 1
const hoverTileY = Math.floor(
-mouse_x / Tile.TILE_WIDTH + mouse_y / Tile.TILE_HEIGHT
)
if (
hoverTileX >= 0 &&
hoverTileY >= 0 &&
hoverTileX < gridSize &&
hoverTileY < gridSize
) {
const renderX =
x + (hoverTileX - hoverTileY) * Tile.TILE_HALF_WIDTH
const renderY =
y + (hoverTileX + hoverTileY) * Tile.TILE_HALF_HEIGHT
renderTileHover(ctx)(renderX, renderY + Tile.TILE_HEIGHT)
}
}
I am not good at maths so I really need help...
Thank you.
I figured out how to achieve this. I will leave it here so anyone who has the same issue wont waste a lot of time for this kind of issue. My code is like this:
/**
* @param context canvas context 2d
* @param inputX mouse/touch input position x (ie. clientX)
* @param inputY mouse/touch input position y (ie. clientY)
* @returns {x, y} x and y position of inputX/Y which map scale and position are taken into account
*/
export const getTransformedPoint = (context: CanvasRenderingContext2D, inputX: number, inputY: number) => {
const transform = context.getTransform()
const invertedScaleX = DEFAULT_MAP_SCALE / transform.a
const invertedScaleY = DEFAULT_MAP_SCALE / transform.d
const transformedX = invertedScaleX * inputX - invertedScaleX * transform.e
const transformedY = invertedScaleY * inputY - invertedScaleY * transform.f
return { x: transformedX, y: transformedY }
}
/**
*
* @param startPosition position where map start rendered (Position2D has {x: number, y: number} type)
* @param inputX mouse/touch input position x (ie. clientX)
* @param inputY mouse/touch input position x (ie. clientY)
* @returns positionX, positionY: tile position x, y axis
*/
export const getTilePosition = (
startPosition: Position2D,
inputX: number,
inputY: number
): { positionX: number; positionY: number } => {
const positionX =
Math.floor((inputY - startPosition.y) / TILE_HEIGHT + (inputX - startPosition.x) / TILE_WIDTH) - 1
const positionY = Math.floor(
(inputY - startPosition.y) / TILE_HEIGHT - (inputX - startPosition.x) / TILE_WIDTH
)
return { positionX, positionY }
}
// usage
const onClick = (e: MouseEvent) => {
const { x: mouseX, y: mouseY } = getTransformedPoint(ctx, e.clientX, e.clientY)
const { positionX, positionY } = getTilePosition(startPositionRef.current, mouseX, mouseY)
// Do something with positionX and positionY...
// ie.
if (return positionX >= 0 && positionY >= 0 && positionX < GRID_SIZE && positionY < GRID_SIZE) {
// code when a user clicks a tile within the map
}
}
I referenced this for calculating the mouse position when the map is zoomed out/in.