Заполнение сотен путей SVG с изображением вызывает серьезные проблемы с производительностью - PullRequest
1 голос
/ 10 июня 2019

Я создаю гексагональные плитки, используя SVG так:

<style>
    .tile:hover {
        fill: red;
    }
</style>

<svg width="1000" height="1000">
    <g>
        <path
            d="M-17.32050807568877,-10L0,-20L17.32050807568877,-10L17.32050807568877,10.000000000000002L0,20.000000000000004L-17.32050807568877,10.000000000000002"
            class="tile"></path>

            ...
    </g>
</svg>

У меня около 400 плиток на экране. Вы можете проверить, как это выглядит здесь: https://337706.playcode.io/ Попробуйте навести курсор на несколько плиток. Нависшие плитки должны стать красными, и нет проблем с производительностью. Также не проблема, если я залью все плитки цветом:

<style>
    .tile {
        fill: green;
    }
</style>

Я бы хотел заполнить плитки изображением, поэтому я создал шаблон:

<defs>
    <pattern patternUnits="objectBoundingBox" id="grass" width="70" height="80">
        <image href="https://i.ibb.co/9ZZgDhy/tiles.png"></image>
    </pattern>
</defs>

А затем заполните плитки этим:

<style>
    .tile {
        fill: url(#grass);
    }

    .tile:hover {
        fill: red;
    }
</style>

После этого производительность резко падает. (вы можете проверить это здесь, но имейте в виду, что это может даже вызвать сбой вашего браузера: https://337697.playcode.io/)

Я проверил профилировщик в Chrome, он говорит, что 98% времени тратится на «Составные слои».

enter image description here

Я заметил, что проблема не в самом изображении, а в <pattern>, потому что даже если я удаляю изображение и помещаю вместо него только зеленый прямоугольник, производительность остается такой же плохой. Если я уберу узор и просто заполню плитку цветом, это не проблема ...

Как мне заполнить плитки изображением и устранить эту проблему с производительностью? Есть ли лучший способ установить фоновое изображение?

Ответы [ 2 ]

0 голосов
/ 10 июня 2019

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


Вот пример использования css clip-path вместо SVG.

Обратите внимание, что вам нужно проделать немного больше работы в CSS, чтобы правильно расположить все.

Каждый шестиугольник - это квадрат, и все углы приземляются на четверть или половину. Это немного облегчает математику. Я абсолютно позиционирую каждую плитку и затем предоставляю height, width, X (left:offset) и Y (top:offset) через Javascript, когда они добавляются.

Вот кодекс , если вы хотите поиграть с ним.

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

var container = document.getElementById("wrapper");

var totalCols = 10;
var totalRows = 15;
var stepX = (1/totalCols) * 100;
var stepY = stepX * 0.75;

for (let r=0; r<totalRows; r++ ){
	for(let c=0; c<totalCols; c++){
		var offset = (r % 2) ? (stepX * 0.5) : 0;
		addHexagon(offset + c * stepX, r * stepY);
	}
}

function addHexagon (x,y) {
	var newPos = 
		'top: ' + y + '%;' +
		'left: ' + x + '%;' +
		'width: ' + stepX +'%;' +
		'height: ' + stepX + '%;'
	var el = document.createElement('div')
	el.setAttribute('class', 'el')
	el.setAttribute('style', newPos);
	container.appendChild(el);
}
#wrapper {
	width:1000px;
	height: 1000px;
	background: #333;
	position: relative;
	overflow:hidden;
}

.el {
	position: absolute;
	/* set height & width using js */
    /* make the hexagon and mask the content */
	clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
	
	/* asethetic only */
	background-image: url(https://images.unsplash.com/photo-1560146491-308b0f69a52a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=80);
	background-size: cover;
	background-position: center center;
	background-repeat: no-repeat;
}

.el:hover {
	background-color: blue;
	background-image: none;
}
<div id="wrapper"></div>
0 голосов
/ 10 июня 2019

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

Я создал карту, используя вложенный цикл для создания группы <use> элементов.Недостатком этого метода является то, что вы не можете сделать простое изменение цвета CSS при наведении.

var map = document.getElementById("map");

var SVG_NS = map.namespaceURI;
var XLINK_NS = "http://www.w3.org/1999/xlink";

var H_STEP = 34.64;
var V_STEP = 30;

var y = 0;

for (let j=0; j<20; j++) {
  var xStart = (j % 2) ? (H_STEP / 2) : 0;
  for (let i=0; i<20; i++) {
    addUse("grass", xStart + i * H_STEP, j * V_STEP);
  }
}


function addUse(defId, x, y) {
  var use = document.createElementNS(map.namespaceURI, "use");
  use.setAttributeNS(XLINK_NS, "xlink:href", "#"+defId);
  use.setAttribute("x", x);
  use.setAttribute("y", y);
  map.appendChild(use);
}
<svg width="1000" height="1000">
  <defs>
    <clipPath id="hexagon">
      <path d="M0,10 L17.32,0 L34.64,10 L34.64,30 L17.32,40 L0,30 Z"/>
    </clipPath>

    <image id="grass" href="https://i.ibb.co/9ZZgDhy/tiles.png"
           width="105" height="80" clip-path="url(#hexagon)"/>
  </defs>

  <g id="map">
  </g>
</svg>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...