JSFiddle - React, Tailwind, and code Playground
by Gwyn Milcote
HTML
<h1>Map Painter</h1>
<p>Load a tileset image. Choose the size of your map. Click on the tileset to select a terrain tile, then choose a layer to paint on. A layer can be cleared, or filled with a certain terrain. Use <b>Import</b> and <b>Export</b> buttons at the bottom to load/get JSON code.</p>
Tileset URL: <input type='text' id='load-tileset-url' value='http://www.rpg-studio.org/wiki/images/9/92/Tileset.png'> <button id='load-tileset-btn'>Load</button>
<br>
Pixel size of each tile: <input type='number' id='load-tile-size' value='32'> | How many tiles per row? <input type='number' id='load-tile-row' value='8'>
<p id='load-tileset-message'></p>
<div id='container'>
<div class='canvas-container'>
<canvas id='canvas-tileset'></canvas>
</div>
<div id='terrain-info'></div>
<div id='layer-buttons'>
Show all layers? <input type='checkbox' id='combine-layers' checked>
---
<!--<button class='layer-button' id='layer-button-0' data-layer='0'>Combined</button>-->
<button class='layer-button' id='layer-button-1' data-layer='1'>Layer 1</button>
<button class='layer-button' id='layer-button-2' data-layer='2'>Layer 2</button>
<button class='layer-button' id='layer-button-3' data-layer='3'>Layer 3</button>
<button class='layer-button' id='layer-button-4' data-layer='4'>Layer 4</button>
<button class='layer-button' id='layer-button-5' data-layer='5'>Layer 5</button>
--- Show grid? <input type='checkbox' id='show-grid' checked>
</div>
<div id='layer-edit-info'></div>
<div>
Width: <input type='number' id='select-rows' min='10' max='100' value='10'>
Height: <input type='number' id='select-cols' min='10' max='100' value='10'>
Visual tile size: <select id='select-tileSize'>
<option>16</option>
<option>24</option>
<option selected>32</option>
<option>40</option>
...
CSS
div, img, button {
box-sizing: border-box;
}
input[type='number'] {
width: 50px;
}
textarea {
width: 90%;
display: block;
margin: auto;
max-height: 150px;
overflow: auto;
}
.canvas-container {
width: 100%;
padding: 20px;
min-height: 200px;
max-height: 600px;
overflow: auto;
background-color: silver;
text-align: center;
}
#canvas-map, #canvas-tileset {
cursor: pointer;
border: 2px solid black;
line-height: 0px;
margin: auto;
}
/*.tileset-container {
width: 100%;
padding: 10px;
min-height: 100px;
max-height: 300px;
overflow: auto;
background-color: silver;
text-align: center;
}*/
.active-layer {
background-color: gold;
}
JavaScript
class MapEditor {
constructor(){
//for(let i = 1; i < 6; i++){
// this.createCanvas(i);
//}
//this.createCanvas("Grid");
//this.createCanvas("Tileset");
this.canvasTileset = document.getElementById("canvas-tileset");
this.ctxTileset = this.canvasTileset.getContext("2d");
this.canvasMap = document.getElementById("canvas-map");
this.ctxMap = this.canvasMap.getContext("2d");
this.map = [[], [], [], [], []];
this.rows = 10;
this.cols = 10;
// tileset is currently 32px
this.mapTileSize = 32; // seen on map
this.selectedLayer = 1;
this.terrain = 1; // for painting
this.displayGrid = true;
this.combineLayers = true;
this.loadTileset(); // and then launch
}
// Setup
/*
createCanvas(name){
this["canvas" + name] = document.createElement("canvas");
this["ctx" + name] = this["canvas" + name].getContext("2d");
}
appendCanvas(name, target){
let div = document.getElementById(target);
div.innerHTML = "";
div.appendChild(this["canvas" + name]);
}
*/
// Editing
loadTileset(){
let img = new Image(),
_this = this,
url = $("#load-tileset-url").val(),
size = $("#load-tile-size").val(),
perRow = $("#load-tile-row").val();
if(!url || url.length < 8){
$("#load-tileset-message").html("Are you sure that's a valid url?");
return;
}
if(size < 1){
$("#load-tileset-message").html("Invalid tile size.");
return;
}
if(perRow < 1){
$("#load-tileset-message").html("Invalid number of tiles per row.");
return;
}
img.onload = function(){
_this.tileSize = size;
...