Как эффективно повторно использовать вырезанное изображение?

1

Для настольной игры, которую я делаю, мне нужно создать карту, состоящую из шестиугольных плит. География карты (леса, горы и т.д.) Представлена изображением плитки и как таковая. Мне нужно закрепить гексагональную область от нескольких изображений и использовать их снова и снова. Прежде чем я смогу начать разработку карты, мне нужно убедиться, что каждая отдельная плитка работает так, как ожидалось. Однако я застрял. Это код для класса 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 чтобы избежать проблем с производительностью?

  • 0
    В настоящее время изображение обрезается с использованием координат места, где оно будет нарисовано, поэтому плитка просто показывает ту часть полноэкранного изображения, которая находится под тем местом, где размещена плитка, - ни одна плитка не является такой же. Это может объяснить, почему появляется только одна ваша плитка. Мой вопрос к вам: вы хотели, чтобы плитки имели фиксированный контент, который будет повторяться снова и снова?
  • 0
    @ Traktor53 Да, контент должен быть исправлен. Вероятно, будет 4-5 различных типов плиток, но плитки одного типа должны выглядеть и вести себя одинаково.
Теги:
image
canvas

2 ответа

2
Лучший ответ

Для операций с холстом

Вторая плитка не появляется, потому что на холсте, где отображается фрагмент, создается клиппинг, размер холста имеет размеры экрана, а изображение берется из позиции (x, y) (50, 20) без масштабирования, Если изображение не доходит до места, где находится плитка, показывается содержимое плитки. И если это показывает, контент зависит от позиции плитки.

Из двух вариантов для записи данных изображения на холст, putImageData и drawImage, putImageData не реализует операции рисования, которые учитывают прозрачность изображения и поэтому не могут использоваться для сопоставления непрямоугольных плит без скрытия части соседней плитки.

drawImage может записывать содержимое изображений HTML и объектов canvas, объектов ImageBitMap, объектов SVG и т.д., но, к сожалению, в список не включены объекты ImageData, которые можно читать непосредственно из написанного холста.

Объекты ImageData могут быть преобразованы в объекты ImageBitMap, но заводская функция является асинхронной и использует обещания, поэтому вы должны подготовить плитки и преобразовать их содержимое в бит-карты перед первым использованием.

Это оставляет несколько способов достижения быстрой записи на плитки.

  1. Загрузите объект Image. Для каждой плитки из изображения,

    • создайте элемент холста (он не должен быть в DOM) прямоугольного размера плитки,
    • создать область клипа для плитки в холсте,
    • напишите раздел изображения на холст, используя последние параметры drawImage чтобы указать, какую часть изображения использовать,
    • сохранить холст как свойство объекта плитки.
    • используйте drawImage чтобы написать значение свойства canvas canvas в любом месте, которое вы вычисляете, и оно должно идти.
  2. Что касается 1, но после написания фрагмента на холсте, прочитайте данные изображения назад и используйте createImageBitmap, чтобы создать обещание для бит-карты. Когда все обещания для всех плиток для всех изображений выполнены, плитки можно использовать.

  3. Создайте полосу изображений плиток в автономном режиме как строку или столбец квадратов того же размера и создайте области каждой плитки, которые не должны отображаться как прозрачные. Затем, для одного или двух, но опустив настройку области клипа.

Я проверил первый метод, чтобы убедиться, что он работал без необходимости добавления элементов холста для каждой плитки в документ. См. Фрагмент. 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>
  • 0
    Эти идеи должны точно соответствовать моим потребностям, большое спасибо! :)
1

Как насчет использования одного растрового изображения со всеми изображениями на нем, а затем использовать фоновую позицию 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>
  • 0
    Однако, как только я это сделаю, смогу ли я получить отдельный доступ к каждому фрагменту и взаимодействовать с ним с помощью JavaScript?
  • 0
    Да, потому что каждый из них является элементом dom, поэтому вы сможете получить к нему доступ и манипулировать им в JavaScript.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню