You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让SVG中<symbol>随动态<polygon>实现相对定位?

问题:SVG <use>引用symbol无法随六边形网格同步位置

问题描述

通过PHP数组生成SVG六边形网格,每个<polygon>后跟随<use>引用<symbol>。网格及填充色渲染正常,但所有<use>元素始终显示在视口同一位置,无法与对应六边形保持相对位置。

问题代码示例

PHP生成代码

$points = array(
    "247.8,37.9 223.8,37.9 211.7,58.9 223.8,79.9 247.8,79.9 259.8,58.9",
    "247.8,79.9 223.8,79.9 211.7,100.9 223.8,121.9 247.8,121.9 259.8,100.9",
    "211.7,100.9 187.7,100.9 175.7,121.9 187.7,143 211.7,143 223.8,121.9",
    "211.7,143 187.7,143 175.7,164 187.7,185 211.7,185 223.8,164",
    "211.7,185 187.7,185 175.7,206 187.7,227 211.7,227 223.8,206",
    "211.7,227 187.7,227 175.7,248 187.7,269 211.7,269 223.8,248",
    "211.7,269 187.7,269 175.7,290 187.7,311.1 211.7,311.1 223.8,290",
    "211.7,311.1 187.7,311.1 175.7,332.1 187.7,353.1 211.7,353.1 223.8,332.1"
);

echo '<svg version="1.1" viewBox="0 0 2410 1192" x="0" y="0" preserveAspectRatio="none" width="100%">';
$hex = 0;
// Symbol
echo '<symbol>
    <path id="hills" style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.03431px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
    d="m 234.27969,88.879347 c -2.45345,0.0538 -5.06817,2.298827 -7.87745,7.163223 l 15.63905,0.523621 c -2.40829,-4.9797 -4.981,-7.744138 
    -7.7616,-7.686754 z m 7.59808,8.001824 c -2.19129,0.03587 -4.68463,1.308966 -7.33231,4.522159 -3.83449,-1.16121 -7.72778,1.0103 
    -11.72419,9.24363 l 22.08548,0.14794 c -2.78806,-3.67548 -5.58581,-6.87639 -8.40893,-8.51612 l 13.63896,3.34992 c -0.79213,-4.08344 
    -3.93464,-8.819257 -8.25901,-8.747797 z" /></symbol>';

foreach ($points as $p) {
    echo '<polygon class="st6" points="'.$p.' " ';
    getFill($hydro,$landfill,$hex); // 假设该函数已定义,负责输出fill属性
    echo 'id="'.$hex.'" stroke="#000000" stroke-width="1"/>';
    echo '<use xlink:href="#hills"/>';
    $hex++;
}
echo '</svg>';

输出的SVG片段

<svg version="1.1" viewBox="0 0 2410 1192" x="0" y="0" preserveAspectRatio="none" width="100%">
  <symbol>
      <path id="hills" style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.03431px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 234.27969,88.879347 c -2.45345,0.0538 -5.06817,2.298827 -7.87745,7.163223 l 15.63905,0.523621 c -2.40829,-4.9797 -4.981,-7.744138 
    -7.7616,-7.686754 z m 7.59808,8.001824 c -2.19129,0.03587 -4.68463,1.308966 -7.33231,4.522159 -3.83449,-1.16121 -7.72778,1.0103 
    -11.72419,9.24363 l 22.08548,0.14794 c -2.78806,-3.67548 -5.58581,-6.87639 -8.40893,-8.51612 l 13.63896,3.34992 c -0.79213,-4.08344 
    -3.93464,-8.819257 -8.25901,-8.747797 z"></path>
  </symbol>
    <polygon class="st6" points="247.8,37.9 223.8,37.9 211.7,58.9 223.8,79.9 247.8,79.9 259.8,58.9 " fill="#fdf898" id="0" stroke="#000000" stroke-width="1"></polygon><use xlink:href="#hills"></use>
    <polygon class="st6" points="247.8,79.9 223.8,79.9 211.7,100.9 223.8,121.9 247.8,121.9 259.8,100.9 " fill="#66D3FA" id="1" stroke="#000000" stroke-width="1"></polygon>
    <use xlink:href="#hills"></use>
</svg>

解决方案

1. 修复<symbol>定义

  • 给<symbol>添加viewBox属性,定义图案的独立坐标系范围,确保引用时能正确缩放和定位。
  • 将内部<path>的坐标改为相对原点(0,0)的位置,避免使用绝对大坐标导致固定偏移。

2. 计算六边形中心坐标

每个六边形的中心可通过顶点坐标平均值计算:取所有顶点的x坐标总和除以顶点数,y坐标同理,得到中心(x,y)。

3. 给<use>添加定位属性

在<use>元素上添加x和y属性,值为对应六边形的中心坐标,再通过transform调整图案使其在六边形内居中。

修改后的代码示例

修正后的PHP代码

$points = array(
    "247.8,37.9 223.8,37.9 211.7,58.9 223.8,79.9 247.8,79.9 259.8,58.9",
    "247.8,79.9 223.8,79.9 211.7,100.9 223.8,121.9 247.8,121.9 259.8,100.9",
    "211.7,100.9 187.7,100.9 175.7,121.9 187.7,143 211.7,143 223.8,121.9",
    "211.7,143 187.7,143 175.7,164 187.7,185 211.7,185 223.8,164",
    "211.7,185 187.7,185 175.7,206 187.7,227 211.7,227 223.8,206",
    "211.7,227 187.7,227 175.7,248 187.7,269 211.7,269 223.8,248",
    "211.7,269 187.7,269 175.7,290 187.7,311.1 211.7,311.1 223.8,290",
    "211.7,311.1 187.7,311.1 175.7,332.1 187.7,353.1 211.7,353.1 223.8,332.1"
);

// 计算六边形中心坐标的函数
function getHexCenter($pointsStr) {
    $points = explode(' ', $pointsStr);
    $xSum = 0;
    $ySum = 0;
    $count = count($points);
    foreach ($points as $point) {
        list($x, $y) = explode(',', $point);
        $xSum += (float)$x;
        $ySum += (float)$y;
    }
    return array(
        'x' => $xSum / $count,
        'y' => $ySum / $count
    );
}

echo '<svg version="1.1" viewBox="0 0 2410 1192" x="0" y="0" preserveAspectRatio="none" width="100%">';
$hex = 0;
// 修正后的Symbol:添加viewBox,路径坐标改为相对原点
echo '<symbol id="hillsSymbol" viewBox="0 0 40 40">
    <path style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.03431px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
    d="m 20,10 c -2.45,0.05 -5.07,2.3 -7.88,7.16 l 15.64,0.52 c -2.41,-4.98 -4.98,-7.74 -7.76,-7.69 z m 7.6,8 c -2.19,0.04 -4.68,1.31 -7.33,4.52 c -3.83,-1.16 -7.73,1.01 -11.72,9.24 l 22.09,0.15 c -2.79,-3.68 -5.59,-6.88 -8.41,-8.52 l 13.64,3.35 c -0.79,-4.08 -3.93,-8.82 -8.26,-8.75 z" /></symbol>';

foreach ($points as $p) {
    $center = getHexCenter($p);
    echo '<polygon class="st6" points="'.$p.' " ';
    getFill($hydro,$landfill,$hex);
    echo 'id="'.$hex.'" stroke="#000000" stroke-width="1"/>';
    // <use>添加x/y定位,并用translate调整图案居中
    echo '<use xlink:href="#hillsSymbol" x="'.$center['x'].'" y="'.$center['y'].'" transform="translate(-20, -20)"/>';
    $hex++;
}
echo '</svg>';

关键修改说明

  • <symbol>新增id="hillsSymbol"和viewBox="0 0 40 40",定义了图案的40x40独立坐标系。
  • 内部<path>的坐标从原绝对位置改为相对(0,0)的位置,适配viewBox范围。
  • 新增getHexCenter函数计算每个六边形的中心坐标。
  • <use>元素添加x、y属性定位到六边形中心,并用transform="translate(-20, -20)"将图案左上角移到中心,实现居中显示。

内容的提问来源于stack exchange,提问作者David White

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 11:59:52