Для настольной игры, которую я делаю, мне нужно создать карту, состоящую из шестиугольных плит. География карты (леса, горы и т.д.) Представлена изображением плитки и как таковая. Мне нужно закрепить гексагональную область от нескольких изображений и использовать их снова и снова. Прежде чем я смогу начать разработку карты, мне нужно убедиться, что каждая отдельная плитка работает так, как ожидалось. Однако я застрял. Это код для класса Tile
var Tile = function (startX, startY, l, img) {
this.side = l; //length of the hexagon size
this.startingPoint = [startX, startY];
this.image = img;
this.incrX = Math.sqrt(3)*this.side/2;
this.incrY = this.side/2;
this.points = [
this.startingPoint,
[startX + this.incrX, startY - this.incrY],
[startX + 2*this.incrX, startY],
[startX + 2*this.incrX, startY + this.side],
[startX + this.incrX, startY + this.side + this.incrY],
[startX, startY + this.side]
]; //list of points comprising the path for making the hexagonal tile
this.middlePoint = [startX + this.incrX, startY + this.side/2];
//draws the hexagonal tile
this.show = function (context) {
context.save();
context.beginPath();
context.moveTo(this.startingPoint[0], this.startingPoint[1]); //starting at the starting point (duh)
for(var i=1; i<this.points.length; i++){
context.lineTo(this.points[i][0], this.points[i][1]); //looping through the rest of the points
}
context.closePath(); //closing the path
if(this.image){
context.clip();
context.drawImage(this.image, 50, 20);
}
context.restore();
context.stroke();
}
}
И чтобы проверить правильность кода, я пробовал рисовать 2 плитки на холсте:
var context = document.getElementById("canvas").getContext('2d');
context.canvas.height = window.innerHeight;
context.canvas.width = window.innerWidth;
var img = new Image;
img.onload = function () {
var tile = new Tile(200, 100, 50, img);
var tile2 = new Tile(200, 400, 50, img);
tile.show(context);
tile2.show(context);
}
img.src = "tree.png";
2 плитки нарисованы, но только одна из них на самом деле заполнена изображением. Кроме того, я чувствую, что этот код очень неэффективен. Метод context.save()
который я должен был использовать, - это дорогостоящая операция, и необходимость рисовать сотни фрагментов вызывает некоторые проблемы с производительностью. Правильно ли я это делаю? Что было бы более эффективным способом разработки класса Tile
чтобы избежать проблем с производительностью?
Для операций с холстом
Вторая плитка не появляется, потому что на холсте, где отображается фрагмент, создается клиппинг, размер холста имеет размеры экрана, а изображение берется из позиции (x, y) (50, 20) без масштабирования, Если изображение не доходит до места, где находится плитка, показывается содержимое плитки. И если это показывает, контент зависит от позиции плитки.
Из двух вариантов для записи данных изображения на холст, putImageData
и drawImage
, putImageData
не реализует операции рисования, которые учитывают прозрачность изображения и поэтому не могут использоваться для сопоставления непрямоугольных плит без скрытия части соседней плитки.
drawImage
может записывать содержимое изображений HTML и объектов canvas, объектов ImageBitMap, объектов SVG и т.д., но, к сожалению, в список не включены объекты ImageData, которые можно читать непосредственно из написанного холста.
Объекты ImageData могут быть преобразованы в объекты ImageBitMap, но заводская функция является асинхронной и использует обещания, поэтому вы должны подготовить плитки и преобразовать их содержимое в бит-карты перед первым использованием.
Это оставляет несколько способов достижения быстрой записи на плитки.
Загрузите объект Image. Для каждой плитки из изображения,
drawImage
чтобы указать, какую часть изображения использовать,drawImage
чтобы написать значение свойства canvas canvas в любом месте, которое вы вычисляете, и оно должно идти.Что касается 1, но после написания фрагмента на холсте, прочитайте данные изображения назад и используйте createImageBitmap, чтобы создать обещание для бит-карты. Когда все обещания для всех плиток для всех изображений выполнены, плитки можно использовать.
Создайте полосу изображений плиток в автономном режиме как строку или столбец квадратов того же размера и создайте области каждой плитки, которые не должны отображаться как прозрачные. Затем, для одного или двух, но опустив настройку области клипа.
Я проверил первый метод, чтобы убедиться, что он работал без необходимости добавления элементов холста для каждой плитки в документ. См. Фрагмент. FYI был указан размер квадрата плитки и incY
значения внутренней side
, incX
и incY
.
Метод 2, вероятно, будет использовать меньше памяти, но насколько менее неопределен. Метод 3 был только идеей. Создание полосы изображений значков одинакового размера может быть полезно в любом случае и облегчить автоматическое создание массива элементов из изображения.
Надеюсь, поможет.
function BlankTile( size) {
this.size = size;
this.xInc = size/2;
this.yInc = this.xInc * Math.tan(Math.PI/6);
this.side = size - 2* this.yInc;
this.vTab = this.side+this.yInc;
this.canvas = document.createElement("canvas");
this.canvas.height = this.size;
this.canvas.width = this.size;
var ctx = this.canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo( 0, this.yInc);
ctx.lineTo( this.xInc, 0);
ctx.lineTo( this.size, this.yInc);
ctx.lineTo( this.size, this.yInc + this.side);
ctx.lineTo( this.xInc, this.side + 2* this.yInc);
ctx.lineTo( 0, this.yInc + this.side);
ctx.closePath();
ctx.clip();
this.ctx = ctx;
}
function Tile ( size, image, x, y, width, height) {
var blank = new BlankTile( size);
blank.ctx.drawImage( image, x||0, y || 0, width || size, height || size, 0, 0, size, size);
this.canvas = blank.canvas;
this.vTab = blank.vTab;
this.size = size;
}
Tile.prototype.draw = function( context, x, y) {
context.drawImage( this.canvas, x, y);
}
// demonstrate code above:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var image = new Image();
image.onload = function() {
var tile = new Tile( 50, image);
for( var y = 0, row = 0; y < 400; y += tile.vTab, ++row) {
for( var x = 0; x < 600; x += tile.size ) {
var start = row&1 ? tile.size /2 : 0;
tile.draw( context, start + x, y);
}
}
}
image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAADAFBMVEUcjhyEpoREikTU2tQMqgSsvqwsiizs7uxklmQEsgQMmgycspzEysQErgT8+vx0nnQckhTk5uSkuqRUjlQMpgy8yrw8ijz09vQEugQMogyktqTM0sx8nnwkiiSMqozc4ty0wrQ0ijT08vRsmmwEtgQMngyctpzEzsQMrgT8/vwUlhTk6uRcklwMqgx8onwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9hQTeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAz0lEQVR42u3Wyw6CMBRFUUSw4AsElYtaUCiCPPz/z1MTjVba0iYO1LDHXdNzq52U036LVGWqSPYNEFypEIvANWJEsgRheJSZUmQawkta3k0cAnSjoZgkGFoRfSEgWwNY2T7iEZ8ApyINWCQOQdDy3CY1AXGZRxOQqSc96cl3k/jQ9d7deO9zYWYiUJrMHUtnPDCe8KavWrPXaCDa5FUp2jzO8u8KChjOvPu+IP05m4WVyB2+3L0Pfo3kz2vU3M4KUjriCbaPyl+F4G/+MJ8mF2o3RsTR4WQ1AAAAAElFTkSuQmCC"
<canvas id="canvas" style="border: thin solid blue;" height=400 width=600>
Как насчет использования одного растрового изображения со всеми изображениями на нем, а затем использовать фоновую позицию CSS для выполнения обрезки?
background-position: 13px 0;
В приведенной ниже демонстрации показано исходное изображение справа и 3 обрезанных экземпляра слева
.board {
width: 600px;
height: 600px;
border: 1px solid #ccc;
position: relative;
}
.image {
background-image: url('https://s-media-cache-ak0.pinimg.com/originals/b7/0a/79/b70a7978cdc41e84c165d46eed433dce.png');
height: 200px;
}
.image.image1 {
background-position: -13px 0px;
width: 60px;
height: 151px;
}
.image.image2 {
position: absolute;
background-position: -30px -302px;
top: 60px;
left: 70px;
z-index: 3;
width: 60px;
height: 148px;
}
.image.image3 {
position: absolute;
top: 90px;
background-position: -211px 0px;
width: 110px;
height: 150px;
}
.image.full {
position: absolute;
top: 0px;
left: 160px;
width: 210px;
height: 410px;
background-size: contain;
border: 1px solid red;
}
<div class="board">
<div class="image image1"></div>
<div class="image image2"></div>
<div class="image image3"></div>
<div class="image full"></div>
</div>