HTML5 游戏的滚动板应使用 SVG 或 Canvas

Scrollable board for HTML5 GAME should use SVG's or Canvas

我想创建一个涉及平铺板的简单网页游戏。我有一组 svg 用于每个正方形的背景(即一个用于草地,一个用于石头,一个用于泥土等)。我还有 svg 用于将显示在背景上方的图层上的项目(例如树木、木头、剑)。

我有一个内存数据库,其中包含每个方块的背景、是否包含以及包含哪些项目。

我希望能够:
* 放大或缩小
* 向左或向右滚动
* 向上或向下滚动
* 在该方块的背景上方显示项目

只需要在最新版本的现代浏览器中工作

最好的方法是什么:

1。有一个 canvas 对象。获取当前缩放、最上面的 XY、canvas 宽度和 canvas 高度。遍历内存数据库中的方块,并在正确的位置打印相应的 SVG。每次滚动或缩放时重新打印整个板。

2。有一个div。获取当前缩放、最上面的 XY、canvas 宽度和 canvas 高度。遍历内存数据库中的方块并在正确的位置创建 SVG。

每次板滚动时添加新的 SVG,因为它们变得可见,当 SVG 在板上移动时删除它们。按适当的数量翻译所有现有的 SVG。

每次看板缩放都会根据新的缩放级别放大或缩小所有现有 SVG。

3。我不知道的第三种方法。

下面的示例使用两个模块 svg 来加载图像(任何图像格式都可以)和处理平移、缩放和渲染的板。它还提供了一个 onClick 事件,该事件将为您提供一个描述已单击的图块的对象。

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <style>
   body {
    background-color: black;
   }
   
   canvas {
    display: block;
    margin: 30px auto 0px auto;
    border: solid 1px white;
    border-radius: 10px;
   }
  </style>
 </head>
 <body>
  <canvas id="canvas"></canvas>
  <script type="application/javascript">
  
  var svg = function() {
  
   "use strict";
   
   var svgImages = [];
   var areImagesLoaded = true;
   var isPageLoaded = false;
   
   addEventListener("load",function() { isPageLoaded = true; isFinished(); });
   
   var exports = {
    onload: function() {},
    request: null,
    get: null
   };
   
   function isFinished() {
    if (isPageLoaded && areImagesLoaded) {
     exports.onload();
    }
   }
   
   function onSvgError() {
    this.isDone = true;
    console.warn("SVG " + this.src + " failed to load.");
   }
   
   function onSvgLoaded() {
    this.isDone = true;
    
    for (var id in svgImages) {
     if (!svgImages[id].isDone) {
      return;
     }
    }
    
    areImagesLoaded = true;
    isFinished();
   }
   
   function request(id,path) {
    if (svgImages[id]) {
     return;
    }
    
    areImagesLoaded = false;
    
    var img = document.createElement("img");
    
    img.onerror = onSvgError;
    img.onload = onSvgLoaded;
    img.isDone = false;
    img.id = id;
    img.src = path;
    
    svgImages[id] = img;
   }
   
   function get(id) {
    return svgImages[id];
   }
  
   exports.request = request;
   exports.get = get;
   
   return exports;
  
  }();
  
  var board = function() {
  
   "use strict";
   
   var canvasWidth = 0;
   var canvasHeight = 0;
   var canvas = null;
   var ctx = null;
   var frameRequested = false;
   
   var tileWidth = 0;
   var tileHeight = 0;
   var tileTypes = [];
   
   var boardWidth = 0;
   var boardHeight = 0;
   var board = [];
   var hasInitialized = false;
   
   var camera = {
    x: 0.0,
    y: 0.0,
    zoom: 1.0
   };
   
   function mapToBoard(x,y) {
    var invZoom = 1.0 / camera.zoom;
    
    return [
     (x - (canvasWidth >> 1)) * invZoom - camera.x,
     (y - (canvasHeight >> 1)) * invZoom - camera.y
    ];
   }
   
   var isMouseDragging = false;
   var mouseStartX = 0;
   var mouseStartY = 0;
   var mouseLastX = 0;
   var mouseLastY = 0;
   
   var tileEvent = {
    background: "",
    foreground: "",
    x: 0,
    y: 0
   };
   
   function onTileSelected(e) {
    
   }
   
   function onMouseDown(e) {
    isMouseDragging = true;
   
    var bounds = canvas.getBoundingClientRect();
    
    mouseStartX = mouseLastX = e.clientX - bounds.left;
    mouseStartY = mouseLastY = e.clientY - bounds.top;
   }
   
   function onMouseUp(e) {
    isMouseDragging = false;
    
    var bounds = canvas.getBoundingClientRect()
    var x = e.clientX - bounds.left - mouseStartX;
    var y = e.clientY - bounds.top - mouseStartY;
    var l = Math.sqrt(x * x + y * y);
    
    if (l < 2.0) {
     [x,y] = mapToBoard(e.clientX - bounds.left,e.clientY - bounds.top);
     
     if (x > 0 && y > 0 && x < boardWidth * tileWidth && y < boardHeight * tileHeight) {
      x = (x / tileWidth) | 0;
      y = (y / tileHeight) | 0;
      
      var tile = board[x + y * boardWidth];
      
      tileEvent.background = tile.background;
      tileEvent.foreground = tile.foreground;
      tileEvent.x = x;
      tileEvent.y = y;
     } else {
      tileEvent.background = "";
      tileEvent.foreground = "";
      tileEvent.x = -1;
      tileEvent.y = -1;
     }
    
     onTileSelected(tileEvent);
    }
   }
   
   function onMouseMove(e) {
    if (hasInitialized && isMouseDragging) {
     var bounds = canvas.getBoundingClientRect();
     var x = e.clientX - bounds.left;
     var y = e.clientY - bounds.top;
     
     camera.x += (x - mouseLastX) / camera.zoom;
     camera.y += (y - mouseLastY) / camera.zoom;
     
     mouseLastX = x;
     mouseLastY = y;
     
     requestDraw();
    }
   }
   
   function onWheel(e) {
    if (Math.sign(e.deltaY) === -1) {
     camera.zoom *= 1.1;
    } else {
     camera.zoom *= 0.9;
    }
    
    requestDraw();
   }
   
   function draw() {
    ctx.fillStyle = "gray";
    ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
    
    var _tileWidth = tileWidth * camera.zoom;
    var _tileHeight = tileHeight * camera.zoom;
    var _ox = camera.x * camera.zoom;
    var _oy = camera.y * camera.zoom;
    var _x = _ox;
    var _y = _oy;
    
    for (var x = 0; x <boardWidth; ++x) {
     for (var y = 0; y < boardHeight; ++y) {
      var index = x + y * boardWidth;
      var tile = board[index];
      var background = tileTypes[tile.background];
      var foreground = tileTypes[tile.foreground];
      
      if (background) {
       ctx.drawImage(
        background,
        _x,
        _y,
        _tileWidth,
        _tileHeight
       );
      }
      
      if (foreground) {
       ctx.drawImage(
        foreground,
        _x,
        _y,
        _tileWidth,
        _tileHeight
       );
      }
      
      _y += _tileHeight;
     }
     
     _y = _oy;
     _x += _tileWidth;
    }
    
    frameRequested = false;
   }
   
   function requestDraw() {
    if (!frameRequested) {
     frameRequested = true;
     requestAnimationFrame(draw);
    }
   }
   
   return {
    BACKGROUND: 0,
    FOREGROUND: 1,
   
    set canvas(canvasID) {
     if (!hasInitialized) {
      canvas = document.getElementById(canvasID);
      canvas.onmousedown = onMouseDown;
      canvas.onmouseup = onMouseUp;
      canvas.onmousemove = onMouseMove;
      canvas.onwheel = onWheel;
      ctx = canvas.getContext("2d");
     }
    },
    
    set canvasWidth(w) {
     if (!hasInitialized && canvas) {
      canvasWidth = canvas.width = w;
     }
    },
    
    set canvasHeight(h) {
     if (!hasInitialized && canvas) {
      canvasHeight = canvas.height = h;
     }
    },
    
    set tileWidth(w) {
     if (!hasInitialized) {
      tileWidth = w;
     }
    },
    
    set tileHeight(h) {
     if (!hasInitialized) {
      tileHeight = h;
     }
    },
    
    set width(w) {
     if (!hasInitialized) {
      boardWidth = w;
     }
    },
    
    set height(h) {
     if (!hasInitialized) {
      boardHeight = h;
     }
    },
    
    set onTileSelected(callback) {
     onTileSelected = callback;
    },
    
    get width() {
     return boardWidth;
    },
    
    get height() {
     return boardHeight;
    },
    
    get onTileSelected() {
     return onTileSelected;
    },
    
    defineTileTypes: function(types) {
     if (types.length % 2 !== 0) {
      return;
     }
    
     for (var i = 0; i < types.length; i += 2) {
      var id = types[i];
      var img = types[i + 1];
      
      tileTypes[id] = img;
     }
    },
    
    init: function() {
     camera.x = -(boardWidth >> 1) * tileWidth;
     camera.y = -(boardHeight >> 1) * tileHeight;
     ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
    
     board.length = boardWidth * boardHeight;
     
     for (var i = 0; i < board.length; ++i) {
      board[i] = {
       background: "",
       foreground: ""
      };
     }
     
     hasInitialized = true;
     requestAnimationFrame(draw);
    },
    
    set: function(type,id,x,y) {
     if (hasInitialized
     && tileTypes[id]
     && x > -1 
     && x < boardWidth
     && y > -1
     && y < boardHeight) {
      var index = x + y * boardWidth;
      
      if (type === this.BACKGROUND) {
       board[index].background = id;
      } else {
       board[index].foreground = id;
      }
      
      requestDraw();
     }
    }
   };
  
  }();
  
  void function() {
  
   "use strict";
   
   svg.request("grass","https://i.stack.imgur.com/CkvU7.png");
   svg.request("water","https://i.stack.imgur.com/an6a5.png");
   
   svg.onload = function() {
    board.canvas = "canvas";
    board.canvasWidth = 180;
    board.canvasHeight = 160;
    
    board.tileWidth = 25;
    board.tileHeight = 25;
    
    board.width = 20;
    board.height = 20;
    
    board.defineTileTypes([
     "GRASS",svg.get("grass"),
     "WATER",svg.get("water")
    ]);
    
    board.init();
    
    for (var x = 0; x < board.width; ++x) {
     for (var y = 0; y < board.height; ++y) {
      board.set(board.BACKGROUND,"WATER",x,y);
      
      if (Math.random() > 0.2) {
       board.set(board.BACKGROUND,"GRASS",x,y);
      } else {
       board.set(board.BACKGROUND,"WATER",x,y);
      }
     }
    }
   }
   
   board.onTileSelected = function(e) {
    if (e.background === "GRASS") {
           board.set(board.BACKGROUND,"WATER",e.x,e.y);
        } else {
           board.set(board.BACKGROUND,"GRASS",e.x,e.y);
        }
   }
   
  }();
  
  </script>
 </body>
</html>