理解SVG viewport,viewBox,preserveAspectRatio缩放

这篇文章发布于 2014年08月27日,星期三,18:48,归类于 SVG相关。 阅读 201964 次, 今日 21 次 24 条评论

 

万丈高楼平地起,基础很重要。

viewport

表示SVG可见区域的大小,或者可以想象成舞台大小,画布大小。

<svg width="500" height="300"></svg>

上面的SVG代码定义了一个视区,宽500单位,高300单位。

注意这里的措辞是“单位”,不是“像素”。虽然说,width/height如果是纯数字,使用的就是“像素”作为单位的。

也就是说,上面SVG的视区大小就是500px * 300px.

当然,故弄“单位”这个措辞,潜台词就是你可以使用其他类型的单位,涵盖常见CSS单位:

单位 含义
em 相对于父元素的字体大小
ex 相对于小写字母"x"的高度
px 相对于屏幕分辨率而不是视窗大小:通常为1个点或1/72英寸
in inch, 表英寸
cm centimeter, 表厘米
mm millimeter, 表毫米
pt 1/72英寸
pc 12点活字,或1/12点
% 相对于父元素。正常情况下是通过属性定义自身或其他元素

除了SVG本身,其他一些元素,例如<rect>width/height属性也可以使用上面的这些单位,也是默认单位是像素。

viewBox属性

这个是本文的重点,也是难点。

先看一个活蹦乱跳的例子,如下HTML代码:

<svg width="400" height="300" viewBox="0,0,40,30" style="border:1px solid #cd0000;">
    <rect x="10" y="5" width="20" height="15" fill="#cd0000"/>
</svg>

结果如下:

或者亲自围观demo

如果不看viewBox, 你一定会觉得诧异——SVG尺寸明明有400*300像素,而小小的<rect>大小只有其1/20,但是显示出来的却占据了半壁江山!不科学啊!

OK, 之所以小小矩形大显神威就是这里的viewBox起了推波助澜的作用。

viewBox值有4个数字:

viewBox="x, y, width, height"  // x:左上角横坐标,y:左上角纵坐标,width:宽度,height:高度

viewBox顾名思意是“视区盒子”的意思,好比在说:“SVG啊,要不你就让我铺满你吧~”

更形象的解释就是:SVG就像是我们的显示器屏幕,viewBox就是截屏工具选中的那个框框,最终的呈现就是把框框中的截屏内容再次在显示器中全屏显示!

更直观的解释:
1. 如果没有viewBox, 应该是长这样的:

<rect>大小只有整个SVG舞台的1/20.

2. viewBox="0,0,40,30"相当于在SVG上圈了下图左上角所示的一个框框:

3. 然后把这个框框,连同框框里的小矩形一起放大到整个SVG大小(如下gif):
gif演示区域面积扩大

到手里的才是自己的,您可以狠狠地点击这里:SVG viewBox属性原理分步演示demo

demo操作截图示意

preserveAspectRatio

上面的例子,SVG的宽高比正好和viewBox的宽高比是一样的,都是4:3. 显然,实际应用viewBox不可能一直跟viewport穿同一条开裆裤。此时,就需要preserveAspectRatio出马了,此属性也是应用在<svg>元素上,且作用的对象都是viewBox

先看下猪是怎么跑的:

preserveAspectRatio="xMidYMid meet"

下面我们来吃猪肉。

preserveAspectRatio属性的值为空格分隔的两个值组合而成。例如,上面的xMidYMidmeet.

第1个值表示,viewBox如何与SVG viewport对齐;第2个值表示,如何维持高宽比(如果有)。

其中,第1个值又是由两部分组成的。前半部分表示x方向对齐,后半部分表示y方向对齐。家族成员如下:

含义
xMin viewport和viewBox左边对齐
xMid viewport和viewBox x轴中心对齐
xMax viewport和viewBox右边对齐
YMin viewport和viewBox上边缘对齐。注意Y是大写。
YMid viewport和viewBox y轴中心点对齐。注意Y是大写。
YMax viewport和viewBox下边缘对齐。注意Y是大写。

x, y自由合体就可以了,如:

xMaxYMax

xMidYMid

亲爱的小伙伴,看出啥意思没?

噔噔蹬蹬,没错,就是组合的意思:“右-下”和“中-中”对齐。恭喜你此处的知识点学习顺利毕业!

preserveAspectRatio属性第2部分的值支持下面3个:

含义
meet 保持纵横比缩放viewBox适应viewport,受
slice 保持纵横比同时比例小的方向放大填满viewport,攻
none 扭曲纵横比以充分适应viewport,变态

现在急需一个活生生的例子,让大家感受下这三个值的表现。

您可以狠狠地点击这里:meet,slice,none功能演示demo

首先,看下SVG代码:

<svg width="400" height="200" viewBox="0 0 200 200" style="border:1px solid #cd0000;">
    <rect x="10" y="10" width="150" height="150" fill="#cd0000"/>
</svg>

截取SVG左边一半(200正好宽度400的一般)作为视区,里面有个150*150的红色矩形。

默认展示如下:
默认效果

如果我估计没错,默认应该是"xMidYmid meet"效果。

如果是meet效果,代码如下:

<svg width="400" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet" style="border:1px solid #cd0000;">
    <rect x="10" y="10" width="150" height="150" fill="#cd0000"/>
</svg>

截图效果如下:
meet效果

表现原理为:SVG宽400, 高200,viewBox宽200, 高200. x横轴比例是2, y纵轴比例是1. meet的作用是让viewBox等比例的同时,完全在SVG的viewport中显示。这里,最小比例是纵向的1,所以,实际上viewBox并没有任何的缩放。

我们只要对viewBox属性值做一点小小的修改(200→300),就可以感受到缩放了:

<svg width="400" height="200" viewBox="0 0 200 300" preserveAspectRatio="xMinYMin meet" style="border:1px solid #cd0000;">
    <rect x="10" y="10" width="150" height="150" fill="#cd0000"/>
</svg>

此时的显示效果为:
viewBox高度改成300后的效果

改成300后,viewBox的高度就比viewport的200高,所以,viewBox要想完全适应viewport,就要进行缩放,所以,我们可以上到上面的矩形面积变小了,就是因为缩放的结果(缩放了200/300, 差不多原来的66.7%)。

如果是slice, slice本身就有剪切的意思。代码如下:

<svg width="400" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin slice" style="border:1px solid #cd0000;">
    <rect x="10" y="10" width="150" height="150" fill="#cd0000"/>
</svg>

效果截图:
slice值下的效果

slice也是要保持viewBox的纵横比的,不过,其作用是尽量填满viewport. 同样,这里viewBox宽度200,SVG的width400. 显然,要想最大化充满,viewBox的宽度就需要扩大为原来的两倍。于是,就有了上图viewBox放大两倍后的效果截图。由于viewBox部分区域超出了viewport, 视区之外内容是不可见的,于是就出现了slice所表意的“剪切”效果。

如果是none, 则表示不关心比例,viewBox直接拉伸到最大填满viewport.

<svg width="400" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin none" style="border:1px solid #cd0000;">
    <rect x="10" y="10" width="150" height="150" fill="#cd0000"/>
</svg>

纠正,直接属性值"none"即可,例如:

<svg width="400" height="200" viewBox="0 0 200 200" preserveAspectRatio="none" style="border:1px solid #cd0000;">
    <rect x="10" y="10" width="150" height="150" fill="#cd0000"/>
</svg>

默认状态下,以及使用工具导出的SVG图形子啊设置宽高的时候都是保持比例不拉伸的,这对于图标等尺寸控制非常有用,但如果我们希望SVG图形像图片默认行为一样,宽高设置直接拉伸,就是设置这里的preserveAspectRatio="none"即可!

none时候的拉伸填充效果

原本好好的一个正方形,现在因为viewBox的拉伸,变成了一个宽高2:1的矩形了。

viewBox的对齐
千言万语不如一个可以自己动手体验的demo实在,您可以狠狠地点击这里:viewBox的对齐各个名称表现感受demo

截两张图给大家瞅瞅:
viewBox对齐demo的截图
viewBox对齐演示demo的截图

无论是meet还是slice,你是不可能在一种状态下同时看到x, y方向上的位移的。因为总会有一个方向是充满viewport的。

结束语

本文是几乎没有个人情绪,个人吐槽的一篇基础技术文章,以满足不同群体的胃口。

行文仓促,错误难免,欢迎纠错。欢迎交流。

行文参考:SVG Viewport and View Box

(本篇完)

分享到:


发表评论(目前24 条评论)

  1. canoe说道:

    挖个坟。
    大佬有个问题想跟你探讨下。假设不加viewBox又会是什么情况呢?
    可以参考下面的这个demo。
    https://codepen.io/WeiYi-Huang/pen/pomMRXe

    因为我有个需求,我需要svg内的画布撑满自适应。就是浏览器怎么拉都是100vw、100vh,用js可以很好实现,但是我想不用js实现,然后我尝试了很多方法,最后发现不加viewBox竟然奇怪的实现了这个需求,可以查看上述rect的dom大小,能否解答下原因嘛。

  2. youky说道:

    这个gif真的太到位了,一个图就讲明白了

  3. xxx说道:

    viewbox这个动画可以说很形象了,一下就搞懂了~

  4. maxmeng说道:

    最近在学D3,补补SVG基础知识

    • 小螺号滴滴的吹说道:

      俺也一样。
      张鑫旭大佬太牛逼了,真的很适合我这种菜鸟学习。

  5. Jason说道:

    牛b

  6. qian说道:

    请问下移动端布局rem下面该怎么自适应呢

  7. zhuli说道:

    厉害

  8. warlock说道:

    张哥贼溜,开启了我新世界的大门

  9. mlqin说道:

    这个属性都有哪些应用场景呢

  10. xgqfrms说道:

    this website has too much ads!

  11. llfylwg说道:

    viewBox 对于不起作用。。。。。。

  12. 练小习说道:

    参数none 是align的值,也就是他和 xMinYMin 是一个属性的值,当写了xMinYMin 的时候,none的位置就错了,不会再产生作用。他和meet并不是同一个属性的值。
    align:
    none
    xMinYMin
    xMidYMin
    xMaxYMin
    xMinYMid
    xMidYMid
    xMaxYMid
    xMinYMax
    xMidYMax
    xMaxYMax

    meetOrSlice:
    meet
    slice

    • 安妮说道:

      表示同意,我写demo的时候,把这个none值,想象成他希望同上右下左四个方向同时对齐,所以就必须改变自己的比例/形状,去迎合四个方向。

    • 时光说道:

      翻翻文档查证了一下,确实是这样的:https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute

  13. Sigma说道:

    viewBox 相当于 crop(裁剪)

    • 李冰冰说道:

      您好,请教:viewBox=”50 0 200 200″ 这种情况,x轴偏移50,为什么是向左偏移,而不是向右呢

  14. whws说道:

    <symbol>元素可以有自己的viewBox和preserveAspectRatio属性。
    但是我试了,preserveAspectRatio 属性无效,请问楼主到底有没有这个属性?

  15. gafish说道:

    preserveAspectRatio=”xMinYMin none”

    例子是不是错了,应该是这样吧:

    preserveAspectRatio=”none”

  16. zzllrr说道:

    楼主对px的介绍有误! px通常不约等于1pt或1/72in,而是与DPI有关,比如DPI=96时, 1px = 1/DPI inch ≈ 1/96 inch 可以看W3C或这个参考资料 http://hax.iteye.com/blog/374323