用图片填充数百条SVG路径会导致严重的性能问题

时间:2019-06-10 14:54:35

标签: javascript html css performance svg

我正在像这样使用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 :(得分:0)

使用裁剪后的图像应该更快。

我已经使用嵌套循环创建了地图,以创建一堆<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>

答案 1 :(得分:0)

我认为,用这么多SVG填充模式的复合过程要求太多。我基于这个write-up来思考。我不确定是否有改善的方法,但是还有其他方法可以实现您想要的目标。


下面是使用css clip-path代替SVG的示例。

请注意,您需要在CSS中做更多的工作才能正确放置所有内容。

每个六边形是一个正方形,并且所有角都落在四分之一或一半上。这使数学变得容易一些。我绝对定位每个图块,然后在附加JavaScript时通过Javascript提供heightwidth,X(left:offset)和Y(top:offset)。

如果您想玩的话,这里是Codepen

即使有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>

相关问题