svg动画中viewBox 和 preserveAspectRatio

  SVG的viewBox是一个非常强大的属性,因为是它真正地允许SVG画布无限延伸,并同时控制和精确定义SVG的可视空间。按照x、y、width和height的顺序,viewBox有4个参数需要设置。可以发现,viewBox的值并没有带单位,这是因为SVG可视空间并不是基于像素来设定的,而是一个可任意延展的空间,这样就可以适应许多不同的尺寸。为了便于理解,我们想象图1-1中的图形是画在一张方格纸中的(参见图1-2)。

图1-2:SVG viewBox
  我们可以基于这张方格纸定义一个坐标系,这张方格纸本身是自我独立的。可以随意改变这张方格纸的width、height或者任意事物。例如,如果我们将<svg> 的width和height减少一半,但是保留同样的viewBox,那么结果就会是图1-3所展示的这样。

图1-3:viewBox改变后的结果
译者注
  我们可以理解为,所有图形都基于viewBox的坐标系,并绘制在viewBox中,而viewBox会自动根据<svg>的宽高和下面要介绍的preserveAspectRatio来进行适配。
  这就是为什么SVG能成为响应式开发适配利器的其中一个原因。SVG同时也会存储超出viewBox的信息。如果我们把一个图形移动到viewBox之外,情况如图1-4所示。

图1-4:将图形溢出viewBox空间之外的结果
  如图1-4所示,只有白色的区域是用户能看到的,但实际上无论可视或是不可视区域内的绘图信息,都会被SVG元素保存起来。这个特性让SVG可被随意伸缩和裁切。对于响应式应用来说这是十分方便的,特别是用于SVG Sprite技术。
  在这个例子中还隐藏了一个需要了解的关于viewBox的知识,那就是SVG的一个属性——preserveAspectRatio。平时我们看到的许多SVG中都没有这个属性的声明,所以大多数人都不了解它。它的初始值为xMidYMid meet,这使得viewBox以均匀的比例来适应SVG容器。
  关于这个属性还有其他一些选项,其中第一个参数xMidYMid决定了SVG内的画布是否以均匀的比例来缩放以及缩放的位置,写法是以驼峰命名法构成的(注意Y是大写)。虽然初始的缩放位置默认从正中间开始,但也有一些其他对齐选项,例如xMinYMax。你也可以将其设置为none,在这种情况下,SVG的横纵比将会被忽略,整个viewBox画布会以压扁或拉伸的方式填充到SVG的可用空间中。
  第二个参数可选的值只有slice或meet。meet会按照SVG的横纵比把整个viewBox的内容全部填充到SVG中,这个功能可以类比CSS3中的background-size:contain;,图片内容会永远保持在容器边缘内部。
  slice允许viewBox超出用户的可视区,并在指定方向上填充可用区域。你可以将其类比CSS3中的background-size:cover;,图片内容会超出容器边缘进行填充。
译者注
  Meet算法——计算viewBox的横纵比width/height。
  • 如果大于1:取SVG宽为viewBox的宽,根据比例计算viewBox的高,然后根据preserveAspectRatio中的第一个参数调整viewBox的位置。• 如果小于1:取SVG高为viewBox的高,根据比例计算viewBox的宽,然后根据preserveAspectRatio中的第一个参数调整viewBox的位置。
  Slice算法:首先假设SVG的宽高固定。当选择slice后计算viewBox的横纵比width/height。
  • 如果大于1:取y轴方向为基准。整个viewBox的高设为SVG的高,然后再根据比例求出viewBox的宽,最后根据preserveAspectRatio中的x[Min|Mid|Max]调整绘制区域位置。此时Y[Min|Mid|Max] 无效。
  • 如果小于1:取x轴方向为基准。整个viewBox的宽设为SVG的宽,然后再根据比例求出绘制区域的高,最后根据preserveAspectRatio中的Y[Min|Mid|Max]调整绘制区域位置。此时x[Min|Mid|Max] 无效。
  只要preserveAspectRatio的第一个参数不为none,就能保证viewBox是等比缩放的。
  关于PreserveAspectRatio的更多知识可阅读张鑫旭的这篇文章,http://www.zhangxinxu.com/wordpress/2014/08/svg-viewport-viewbox-preserveaspectratio/。