KonvaJS / HTML5 canvas infinite looping tilemap. Setting camera position
Asked Answered
L

1

7

I'm trying to create an infinite looping canvas based on a main 'grid'. Scaled down fiddle here with the grid in the centre of the viewport.

JS Fiddle here

In the fiddle I have my main grid of coloured squares in the centre, I want them tiled infinitely in all directions. Obviously this isn't realistically possible, so I want to give the illusion of infinite by just redrawing the grids based on the scroll direction.

I found some good articles: https://developer.mozilla.org/en-US/docs/Games/Techniques/Tilemaps/Square_tilemaps_implementation:_Scrolling_maps

https://gamedev.stackexchange.com/questions/71583/html5-dynamic-canvas-grid-for-scrolling-a-big-map

And the best route seems to be to get the drag direction and then reset camera to that point, so the layers scroll under the main canvas viewport, thus meaning the camera can never reach the edge of the main viewport canvas.

I've worked on adding some event listeners for mouse drags :

Fiddle with mouse events

 var bMouseDown = false;
    var oPreviousCoords = {
        'x': 0,
        'y': 0
    }
    var oDelta;
    var oEndCoords;
    var newLayerTop;


    $(document).on('mousedown', function (oEvent) {
        bMouseDown = true;
        oPreviousCoords = {
            'x': oEvent.pageX,
            'y': oEvent.pageY
        }
    });

    $(document).on('mouseup', function (oEvent) {
        bMouseDown = false;

        oPreviousCoords = {
            'x': oEvent.pageX,
            'y': oEvent.pageY
        }


        oEndCoords = oDelta


        if(oEndCoords.y < -300){


            if(newLayerTop){
                newLayerTop.destroy();
            }

            layerCurentPosition = layer.position();

            newLayerTop = layer.clone();
            newLayerTop.position({
                x:  layerCurentPosition.x,
                y:  layerCurentPosition.y -1960
            });


            stage.add(newLayerTop)

            stage.batchDraw();



        }

    });

    $(document).on('mousemove', function (oEvent) {


        if (!bMouseDown) {
            return;
        }

        oDelta = {
            'x': oPreviousCoords.x - oEvent.pageX,
            'y': oPreviousCoords.y - oEvent.pageY
        }


    });

But I can't reliably work out the co-ordinates for each direction and then how to reset the camera position.

Layby answered 5/12, 2018 at 12:7 Comment(6)
A few warnings about the initial JS Findle: last_position in line 1 is never used; missing semicolon in lines 24, 37, 43, 187; unnecessary semicolon in line 47.Chantal
Neither fiddle is working for me. Both just show blank pages. Console shows warnings like Konva warning: Can not change width of layer.Octagonal
Neither fiddle is working for me too. I just see a blank page.Raleigh
Fiddle is not working for me too. I just see a blank page.Antherozoid
This fiddle: jsfiddle.net/kiksy/dvn2wyh5/25 is working, you may need to scroll to see the squares however.Layby
I've updated the mouse event fiddle to reduce the canvas size so the squares can be seen. jsfiddle.net/kiksy/025fp49w/8Layby
C
12

As you need "infinite" canvas I suggest to not use scrolling and make canvas as large as user viewport. Then you can emulate camera and on every move, you need to draw a new grid on the canvas. You just need to carefully calculate the position of the grid.

const stage = new Konva.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight,
  draggable: true
});

const layer = new Konva.Layer();
stage.add(layer);


const WIDTH = 100;
const HEIGHT = 100;

const grid = [
  ['red', 'yellow'],
  ['green', 'blue']
];

function checkShapes() {
  const startX = Math.floor((-stage.x() - stage.width()) / WIDTH) * WIDTH;
  const endX = Math.floor((-stage.x() + stage.width() * 2) / WIDTH) * WIDTH;
  
  const startY = Math.floor((-stage.y() - stage.height()) / HEIGHT) * HEIGHT;
  const endY = Math.floor((-stage.y() + stage.height() * 2) / HEIGHT) * HEIGHT;
 
  
  for(var x = startX; x < endX; x += WIDTH) {
    for(var y = startY; y < endY; y += HEIGHT) {
      const indexX = Math.abs(x / WIDTH) % grid.length;
      const indexY = Math.abs(y / HEIGHT) % grid[0].length;
      layer.add(new Konva.Rect({
        x,
        y,
        width: WIDTH,
        height: HEIGHT,
        fill: grid[indexX][indexY]
      }))
    }
  }
}


checkShapes();
layer.draw();

stage.on('dragend', () => {
  layer.destroyChildren();
  checkShapes();
  layer.draw();
})
  <script src="https://unpkg.com/konva@^2/konva.min.js"></script>
  <div id="container"></div>

If you need scrolling you can listen wheel event on stage and move into desired direction.

Carolanncarole answered 11/12, 2018 at 2:3 Comment(5)
Thanks, this isn't infinte however. The desire result is that the squares would keep looping, making it impossible to reach the edge, top bottom, left or right.Layby
The logic is explained a bit here, gamedev.stackexchange.com/questions/71583/… but I'm looking for a infinite looping tile map.Layby
Sorry, just realised that works! I was scrolling not dragging.Layby
Good, that it helps. You can add scrolling manually. Listen to wheel event and change position of the stage in it.Carolanncarole
Still tearing my hair out with this one. Trying to get text to stay in the correct place. Heres a cut down fiddle with what I mean: jsfiddle.net/kiksy/jqo2h3dx/2 The text doesn't stay in the boxes, I don't really understand why as the X Y are the same as the rectangle on redraw?Layby

© 2022 - 2024 — McMap. All rights reserved.