马黑黑 发表于 2026-6-12 21:44

SVG内部图像自适应宽高的实现

<style>
    .artBox { margin: 20px auto; font: normal 18px/26px sans-serif; }
    .artBox p { margin: 10px 0; }
    .artBox mark { padding: 2px 6px; background: #ccc; }
</style>

<div class="artBox">
    <p>通常,SVG可视子元素使用像素为单位定义自己的尺寸或路径相关数据,不支持百分比或相对单位的表达方法。为此,在SVG中所绘制的图案,最终尺寸是固定的。实践中,在 SVG 标签宽高可变的情况下,如果希望其内部可视子元素宽高尺寸能跟随SVG宽高的变化而变化,最简单的实现方法是给 SVG 标签加上 viewBox 属性,这样,子元素图像仅参照 viewBox 属性值所规范的范围绘制图形,而 viewBox 属性则能顺应 SVG 的尺寸。试看以下示例:SVG 通过 CSS 设置会跟随浏览器窗口的变化而变化(使用了相对尺寸单位),其内的 circle(圆)以 SVG 的 viewBox 为绘制参照,因此它的尺寸也会因为浏览器窗口的尺寸变化而变化,预览效果时可手动改变浏览器窗口的尺寸查看变化效果——</p>
    <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    svg {
      display: block;
      margin: 20px auto;
      width: 30vw;
      height: 30vw;
      border: 1px solid gray;
    }
&lt;/style&gt;

&lt;svg viewBox="0 0 400 400"&gt;
    &lt;circle cx="200" cy="200" r="190" fill="pink"&gt;
&lt;/svg&gt;
        </pre></div>
        <p>核心原理是利用了 viewBox 属性,该属性用以定义绘图区域,其值的后两位数是绘图区域的宽与高,viewBox 属性按这个宽高比例去适配 SVG 的尺寸(因此要求 SVG 的宽高设置最好符合这个宽高比)。</p>
        <p>并非 SVG 标签自身才拥有 viewBox 属性,SVG内部,不可视子元素 symbol 也能设置此属性。symbol 标签如果设置了 viewBox 属性,则其内的图案均以之为绘图参照,然后通过 use 标签实例化得以渲染,实例化图案设置 width 和 height 属性的有效性依据于 symbol 的 viewBox 是否存在,换言之,symbol 如果设置了 viewBox 则 use 就可以设置宽高(基于 SVG 的尺寸或 SVG viewBox——如有),use 缺省宽高的话就是 use 所在 SVG 或其 ViewBox 的宽高尺寸。</p>
        <p>通过 symbol + use 方式同样可以可以灵活方便地设计和绘制图案。下例的 symbol 设置有 viewBox、内部绘制一个 polyline 图案,图案在实例化时设置了描边等属性,还加入了 stroke-dashoffset 动画。同样地,图案也可以跟随浏览器窗口的尺寸变化而动态变化——</p>
        <div class="hEdiv" data-prev="1"><pre class="hEpre">
&lt;style&gt;
    svg {
      display: block;
      margin: auto;
      width: 80vw;
      height: 45vw;
      border: 1px dashed gray;
      use {
            fill: none;
            stroke: rgba(128, 0, 0, .25);
            stroke-width: 2;
            stroke-linecap: round;
            stroke-linejoin: round;
            stroke-dasharray: 4 2;
            stroke-dashoffset: 0;
              animation: move 4s linear infinite;
              &:nth-of-type(2) { stroke: rgba(128, 0, 0, .55); }
              &:nth-of-type(3) { stroke: rgba(128, 0, 0, .75); }
      }
    }
    @keyframes move {
      to { stroke-dashoffset: -90; }
    }
&lt;/style&gt;

&lt;svg&gt;
        &lt;symbol id="sb" viewBox="0 0 160 90"&gt;
          &lt;polyline points="2 2,30 88,150 10" /&gt;
        &lt;/symbol&gt;
        &lt;use href="#sb" x="0" y="0"&gt;&lt;/use&gt;
        &lt;use href="#sb" x="50" y="0"&gt;&lt;/use&gt;
        &lt;use href="#sb" x="100" y="0"&gt;&lt;/use&gt;
&lt;/svg&gt;
        </pre></div>
        <p>小结:实现SVG图案自适应窗口尺寸,核心原理是,其一, viewBox 属性,既可用于 SVG 自身也可以用于 symbol 子元素;其二,SVG 自身宽高应是可变的,可以考虑使用百分比或相对单位。</>
</div>

<script type="module">
    import hl from 'https://638183.freep.cn/638183/web/js/hl.js';
    const divs = document.querySelectorAll('.hEdiv');
    const pres = document.querySelectorAll('.hEpre');
    divs.forEach((div,idx) => hl.hl(div, pres));
</script>

杨帆 发表于 2026-6-12 22:25

实用的知识,震撼的演示,谢谢马老师经典讲授与精彩示范{:4_180:}

马黑黑 发表于 2026-6-12 22:43

杨帆 发表于 2026-6-12 22:25
实用的知识,震撼的演示,谢谢马老师经典讲授与精彩示范

{:4_191:}

红影 发表于 2026-6-12 22:55

viewBox 之前就没怎么学清楚,好像可以等比例缩放来填充规定的空间,但好像还能裁剪,就被弄糊涂了,也许是因为显示区域的缘故而造成的裁剪效果吧。我是没学透彻{:5_102:}

红影 发表于 2026-6-12 22:56

第二个实例里的动态很惊艳,漂亮{:4_187:}

马黑黑 发表于 2026-6-13 08:01

红影 发表于 2026-6-12 22:56
第二个实例里的动态很惊艳,漂亮

还好吧

马黑黑 发表于 2026-6-13 08:03

红影 发表于 2026-6-12 22:55
viewBox 之前就没怎么学清楚,好像可以等比例缩放来填充规定的空间,但好像还能裁剪,就被弄糊涂了,也许是 ...
记住这个就行:如果设置了 viewBox,绘制的图像以它规定的坐标数据为参照,绘制的起止、范围均由它规定。举个栗子:

如果 viewBox="0 0 100 100",那么,你绘制的图形不要超越 0~100,超出的部分会看不到。此时,如果 SVG 设置了 width="400" height="400",那么,图形会按 1:4 缩放(包括任何数据都会这样缩放,例如 stroke-width,设为 1 的描边尺寸会变为 4)

红影 发表于 2026-6-13 12:30

马黑黑 发表于 2026-6-13 08:01
还好吧

看到黑黑把这个效果做成帖子了,特别漂亮{:4_187:}

红影 发表于 2026-6-13 12:32

马黑黑 发表于 2026-6-13 08:03
记住这个就行:如果设置了 viewBox,绘制的图像以它规定的坐标数据为参照,绘制的起止、范围均由它规定。 ...

嗯嗯,这个倒是知道的,当viewBox的长宽比SVG的小,图形就会被整体放大,反之会缩小。

马黑黑 发表于 2026-6-13 13:44

红影 发表于 2026-6-13 12:32
嗯嗯,这个倒是知道的,当viewBox的长宽比SVG的小,图形就会被整体放大,反之会缩小。

那就行了,达到目的了

马黑黑 发表于 2026-6-13 13:45

红影 发表于 2026-6-13 12:30
看到黑黑把这个效果做成帖子了,特别漂亮

还好,还不太差

红影 发表于 2026-6-13 22:19

马黑黑 发表于 2026-6-13 13:44
那就行了,达到目的了

更细致的海没学好,或者说没记住{:4_173:}

红影 发表于 2026-6-13 22:19

马黑黑 发表于 2026-6-13 13:45
还好,还不太差

岂止不差,简直是非常好呢{:4_187:}

马黑黑 发表于 2026-6-13 22:56

红影 发表于 2026-6-13 22:19
岂止不差,简直是非常好呢

差不离吧

红影 发表于 2026-6-14 22:46

马黑黑 发表于 2026-6-13 22:56
差不离吧

也许可能大概{:4_173:}

马黑黑 发表于 2026-6-16 18:13

红影 发表于 2026-6-14 22:46
也许可能大概

必须立刻马上{:4_170:}

红影 发表于 2026-6-19 21:09

马黑黑 发表于 2026-6-16 18:13
必须立刻马上

不必慢点没事{:4_196:}

马黑黑 发表于 2026-6-19 21:36

红影 发表于 2026-6-19 21:09
不必慢点没事

节奏慢了赶不上潮流

红影 发表于 2026-6-20 22:32

马黑黑 发表于 2026-6-19 21:36
节奏慢了赶不上潮流

赶那个做什么,轻松自在多好。

马黑黑 发表于 2026-6-20 23:02

红影 发表于 2026-6-20 22:32
赶那个做什么,轻松自在多好。

不能总是做老古董呀
页: [1] 2 3 4
查看完整版本: SVG内部图像自适应宽高的实现