欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入解析HTML5使用SVG图像时的viewBox属性用法

程序员文章站 2023-08-17 21:40:52
这篇文章主要介绍了深入解析HTML5使用SVG图像时的viewBox属性用法,包括一些响应式设计相关的内容,需要的朋友可以参考下... 15-09-02...

快速了解viewbox的参数

viewbox属性是用于指定用户svg图像的坐标系统的原点以及尺寸的。所有在svg内绘制的内容都是相对于这个坐标系统完成的。因为svg画布在所有方向都是无限延长的,你甚至可以在这个坐标系统的边界之外的地方绘制图形;但是这些相对于svg视窗定位的图形,也可以由用户坐标系的位置来控制。

viewbox属性使用四个参数来指定坐标系原点的位置以及它的尺寸:x y width height。初始情况下,这个坐标系等同于初始化的视窗坐标系(由svg图像的width和height确定),而且它的原点是在(0, 0)——即svg的左上角。

通过改变x和y这两个参数的值,可以调整原点的位置。改变width和height的值,可以改变坐标系统的尺寸。只使用viewbox属性,就可以帮你扩展或裁剪svg画布。跟着示例一起阅读。

重要提示:在本文章中,我不会改变viewbox在svg视窗内的默认行为(比例和位置)。因为,根据属性的默认行为,viewbox的内容会被尽可能地完全包含在视窗内,然后放置在中心位置。不过,使用preserveaspectratio属性可以让你*地改变viewbox的尺寸和位置,但是在这篇文章中,这不是必需的技术,所以我们也不会在这里深入讲解。
使用viewbox裁剪svg,即使用viewbox属性制作art direction的svg

前阵子,我的一个客户要求把他网站的svg头像按照不同的屏幕尺寸设置成不同的大小,从而使得它只有一小部分是在小屏幕上可见的,在中等的屏幕尺寸上可以看到再大一点的部分,然后在大屏幕上可以看到完整的内容。当时我脑子里首先出现的想法就是,他的要求其实是使用viewbox属性来裁剪svg图像,然后根据不同的屏幕尺寸,显示他想要看到的图像的某部分。

通过改变svg坐标系统的尺寸和原点位置,我们可以把svg进行裁剪,并显示我们希望在视窗中显示的那部分内容。

我们来看看如何实现。

假设我们有如下这张完整的svg图像,然后我们想要把它裁剪成小屏幕的尺寸。这张图是freepik设计的可免费使用的房屋矢量图,该图片有creative commons attribution 3.0 unported协议的许可授权。为了简单起见,我们先假设图像只是要被裁剪成在中小屏幕上显示的内容,以及大屏幕上显示的完整的内容,如下所示。
深入解析HTML5使用SVG图像时的viewBox属性用法

左边的图的是我们将要使用viewbox属性裁剪的完整的图片,右边的图是我们希望在小屏幕上显示的区域。

现在,通过改变viewbox属性的值来裁剪svg。有一些需要考虑的内容,我们等下会讲到。但是首先,我们要改变坐标系统,让它匹配上面的图片中虚框矩形区域的内容。,通过调整系统的原点以及width和height的值,我们可以改变它初始的0 0 800 800参数值。

但是我们要如何获知新坐标和新尺寸呢?重点是不要经过大量重复的实验和错误。

有几种方式。因为我们已经在图形编辑器中(我的示例使用的是ai),我们可以使用编辑器的面板来获取元素的位置和尺寸。

我画这个虚线的矩形框,除了用来表示我想要在小屏幕上显示的内容,还有一个原因就是:我们可以获取这个矩形的位置和尺寸,然后把它们作为viewbox的值来使用。使用ai的变换面板(如下图),我们获取了我们需要的这些值。通过选择矩形,并点击右上角的变换链接,我们得到了如下图所示的面板,包括我们所需要的x, y, width和height值。
深入解析HTML5使用SVG图像时的viewBox属性用法

这个ai中的变换面板可以用来获取选中矩形的位置和尺寸的值

你可能注意到了,上面的值并不是整数,所以我们需要手动修改。根据上面的信息,我们把viewbox的值改成 0 200 512 512。

因为新的viewbox的宽高比和svg视窗的宽高比是一样的(都是正方形),viewbox内的内容将会扩大,并且只有被选中的区域会在视窗中显示。改变viewbox的值之后,结果如图:
深入解析HTML5使用SVG图像时的viewBox属性用法

新裁剪的svg。只有我们指定使用viewbox属性的位置在视窗中是可见的。蓝色边框表示svg的视窗。

在这一点上,有一个问题需要解决:

如果被裁剪区域(即viewbox)的宽高比 != svg视窗的宽高比?

在这种情况下,将会有明显的溢出。明显的溢出,我指的不是超出svg视窗边界的延伸,而是viewbox定义的相对于新用户坐标系统的溢出。下图作了相应的说明。

深入解析HTML5使用SVG图像时的viewBox属性用法

如果viewbox的宽高比和视窗的宽高比不同,svg中的内容会溢出用户坐标系统,结果可能会是这样。

黑色边框代表新的用户坐标系,蓝色边框是svg视窗。

上边右图中的黑色边框是viewbox定义的区域。根据viewbox在视窗内的默认行为,它会被居中并尽可能放大,来保证自身内容尽可能地包含在视窗(蓝色边框)中。

因为svg画布从概念上来说,在所有方向都是无限延伸的,你可以在用户坐标系统边界之外绘制图形,内容会直接溢出移动,如上图所示。

如果你改变了svg视窗的宽高比(svg的width和height),来让它们适应viewbox的宽高比,你就不会看到溢出了,因为viewbox的缩放是适应视窗的,和前面的示例一样。

但是,在某些情况下,你可能不能或根本不想改变svg的宽高比。比如说,如果你是使用svg sprite作为一组图像来显示页面上图片的。在大多数情况下,图像都有一个固定的宽高比——而且你并不想改变改变图像的尺寸,就为了适应它里面的某一张小图的内容。或者可能你嵌入了一个图标系统,并希望所有的图标在同一个时间都保持相同的尺寸。

为了剪掉多余的东西(比如说,sprite上的部分其它图标,在视窗中显示),你可以使用<clippath>来把多余的部分裁剪掉。裁剪路径可以是一个覆盖了整个viewbox区域的<rect>元素,然后将该元素应用到root svg。

但是,还有一点要记住:确保<rect>的x和y属性和viewbox的保持一致,除非rect将被相对定位于原来的/初始化的系统的原点,那么svg最终被裁剪的内容也是不确定的。

css code复制内容到剪贴板
  1. <svg xmlns="http://w3.org/2000/svg" viewbox="vx vy width height" clip-path="url(#clipper)" width=".." height="..">   
  2.     <!-- svg content here -->   
  3.     <clippath id="clipper">   
  4.         <rect x="vx" y="vy" width="100%" height="100%"></rect>   
  5.     </clippath>   
  6. </svg>  

当然,裁剪多余的部分也就意味着你仍然在使用不同的宽高比,还是需要解决内容两边多出的空白。如果svg是一个连续的场景,像我们前面的那个例子,那就没必要了,因为你还需要调整视窗的宽高比。如果svg是一组图标,并且你只是在不同的视窗中使用一次,这可能就不是问题了。

这里有一点重要的东西要记住,viewbox的宽高比最好还是和视窗的宽高比保持一致;另外,你需要设置固定来避免svg中任何不确定的多余的空白出现。

所以,viewbox可以用来裁剪svg,并根据需要只展示svg的某部分内容。但是它要如何应用于实例呢?

在响应式设计中的art directing svg

这部分没有什么需要补充的内容,除了实际过程的代码。所以,假设你有如上所示的svg,并且想要把它作为头像,比如说,在中小尺寸的屏幕上你只想显示裁剪下来的那部分内容,然后在大屏幕上显示完整的头像。

改变svg视窗的width和height值,我们可以使用css。但是改变viewbox的值,目前我们需要使用javascript。

并非所有的svg属性,css属性都可以等同使用;只有一组具有和css属性相同效果的属性才可以在css中设置。你可以在这个表格中查看这组可以作为css属性的svg属性的概述。在svg2中,很多属性(如x, y, cx, cy, r等等)都可以被添加到这个列表中;但是这些都是我们现在可以使用的属性。

为了展示svg的不同部分,需要基于不同媒体查询改变viewbox的值,你可以使用modernizr,查找媒体查询条件,然后在javascript中对应更改viewbox的值。示例如下:

css code复制内容到剪贴板
  1. // 获取root <svg>的引用   
  2. var svgroot = ...; // 取决于你如何嵌入并引用svg   
  3. // 定义viewbox的参数值   
  4. var vbvalue = '0 200 512 512';   
  5. // 使用modernizr的媒体查询检索来改变viewbox的值   
  6. if (modernizr.mq('(max-width: 700px)')) {   
  7.    svgroot.setattribute('viewbox', vbvalue);   
  8. }   
  9. // 其它的尺寸情况  

这是可以运行的,但是如果我们可以使用css来完成这个效果岂不更赞?

使用css的viewbox属性来裁剪svg

免责声明:在写这篇文章的时候,并没有cssviewbox属性。这只是一个用来解释为什么这个属性有用,以及我想象的它如何被使用的示例。

理想情况下,我们可以这样使用它:

css code复制内容到剪贴板
  1. <style>   
  2.   
  3. @media screen and (max-width700px) {   
  4.     svg {          
  5.         viewbox: 0 200 512 512;    
  6.     }    
  7. }   
  8.   
  9. /* etc. */  
  10.   
  11. </style>  

这些样式会被放进(或取出)svg中,然后svg将会根据视窗的尺寸调整其viewbox值。让它成为页面视窗(内联<svg>)的情况),或是通过其它任何引用svg的元素的尺寸确定的视窗(这可以给我们一些近乎相同的元素查询)。

不过,这在目前是不可能实现的,因为css中还没有viewbox属性。

前阵子,我就这个问题询问了一个svg规范的编辑,他说我可以根据实际使用情况和实例,向svgwg提建议。后来在twitter上进行了一些讨论,我才发现在几年前已经有一个相似的svgwg proposal thread。当初的提议今天仍然存在,所以我希望,能够通过一些实际使用示例,推进这个提议,并在不久的将来可以实现。如果你也想要在css中看到viewbox属性,请帮助实现这一目标,推动这个提议的转发和评论。

当使用viewbox完成svg art-direction的时候,需要记住的东西

在做我的客户的项目的时候,我花了一分钟不到的时间来按照对方的要求对头像进行art-direct。但是,这最终分出了三个独立的svg,而不是不同屏幕尺寸上的相同svg不同viewbox。

我们选择三个svg的原因是,完整svg的尺寸太大,在移动端达到了100kb以上的大小。最初的svg是200kb左右的,我可以通过优化svg来把文件压缩到接近一半的大小,但是对于移动设备来说,图片还是太大了,所以最后我们只能使用三张不同大小的图像。art-directing svg的时候,需要记住的就是:性能问题。如果你的svg太大了,不要使用viewbox来art-direct。

现在,如果你选择使用三个不同的svg图像,也有多种可能的方式可以完成——这取决于你嵌入svg的方法,也取决于你想要完成什么、不想完成什么。

使用<picture>元素来完成不同svg图像是最理想的方式。它不仅能够根据浏览器为我们提供不同的可供选择的svg,而且还不需要使用javascript,还可以让我们对不支持它的浏览器(ie8及以下)提供多种优化的降级图像。<picture>对于使用svg是非常有用的,你可以在这篇文章中阅读所有svg fallback的内容。

但是如前面所说,如果你想要有动画或交互效果的svg,<picture>不是最佳选择。就像使用<img>嵌入svg,svg不能被添加样式和动画,除非样式和动画是在<svg>文件中定义的,svg不能添加脚本(出于安全考虑),也不能有任何交互(css或js)——比如说,悬停,不会有交互效果。

所以,我总是说:svg为我们提供了很多选项,可以让我们完成几乎所有的事情;你需要做一个权衡、要主次分明、有时甚至需要作出妥协,基于此作出最佳的选择。但是对于性能,永不妥协才有利于发展!

在我们结束之前,因为我们提到了使用viewbox属性改变svg画布尺寸的问题,我们来看看另一个例子,我们可以借助这个属性来帮我们在处理svg时节省一些时间和精力。

使用viewbox扩展svg画布

正如viewbox属性可以用于缩放svg,它也可以用来扩展svg画布。

几周前我创建了一个可以生成svg圆形菜单的工具。我创建了几个例子来演示如何使用javascript让生成的菜单动起来。demo使用<object>元素嵌入到应用程序页面上。<object>的边界定义了svg视窗的边界,任何在这些边界之外的内容都是溢出,而且默认隐藏。

需要注意的是“边界之外”指的是在svg中的内容,它还是在无穷大的svg画布上的,但是超过了由视窗定义的无穷大的矩形

译者注:关于svg画布、视窗的内容可阅读w3cplus上的相关文章。
创建的菜单,svg的尺寸是恰好可以将菜单包含进去,而没有再大一些。避免了菜单周围任何多余的空白。

我给某个菜单应用了一个弹跳动画,作为菜单动画的示例。这个弹跳效果“拉长了”各菜单项,也导致了菜单项在它们弹跳的时候会单独切出来(即溢出)。
深入解析HTML5使用SVG图像时的viewBox属性用法

起初,由于svg视窗是由<object>元素定义的,所以视窗和菜单本身是一样大的,菜单项上的弹跳效果导致了这些菜单项在弹跳的时候溢出。

这些可爱的弹跳动画应用于那些使用弹跳时间函数从0开始放大到100%的项目(即该项目最初是不可见的,缩小状态),这个效果就是如果项目弹跳到超过了100%的大小,那就把它缩回到100%。这个效果会导致项目在弹跳超过svg边界的时候溢出。

下图展示了缩放菜单项在放大到超过<object>的边界(灰色边框)的时候的效果,其中,<object>用于嵌入这个缩放菜单项。
深入解析HTML5使用SVG图像时的viewBox属性用法

上方的示意图展示了菜单项放大到溢出svg视窗边界时的效果。灰色边框表示svg视窗的边框(即<object>元素)。

给<object>设置overflow: visible也不能解决问题,因为<object>和<iframe>实际上是相似的。我们需要做的是扩展<object>创建的视窗内的svg画布,使得缩放的项目有足够的“反弹”空间,而不会超过它的边界。我们可以使用viewbox属性来完成它。

为了延长svg画布,只需简单增加它的尺寸。因此,我们使用的是700 x 500px的尺寸,而不是500 x 250这个svg菜单的原始尺寸。这还会让画布在视窗中显示的高度增加100px,而视窗中的画布宽度会增加200px。我根据这些菜单项在弹跳效果需要放大的空间来确定这些值。根据您的svg以及您要完成的具体内容,这些值并不要求一致。

现在,为了确保菜单是放置在视窗的中心的,我们需要把坐标系统的位置往负方向分别移动100px(即向上和向左)。把这个移动应用到坐标系统的原点上,和把一个平移转换应用到系统中的菜单上是一样的。结果是菜单会在视窗中保持居中。
深入解析HTML5使用SVG图像时的viewBox属性用法

在该图中,蓝色边框表示svg视窗边界(即<object>元素),灰色边框表示用户坐标系统的初始尺寸。蓝色数字和箭头表示视窗中的坐标系统的扩展。

在延长用户坐标系统尺寸的同时,你也增加了画布在视窗中可见区域的面积。这样做的结果是画布的内容会显得略小——这根据你把画布放大了多少而定。但是对于菜单来说,这样的结果是可以接受的。

下面的屏幕记录显示了扩展svg画布的结果,以及在svg边界内的菜单动画效果。
深入解析HTML5使用SVG图像时的viewBox属性用法

一旦svg画布被扩展,菜单项就有足够的空间来进行缩放,在应用弹跳效果的时候也不会再因为溢出被剪切。

通过改变viewbox属性的四个参数值来延伸svg画布,这样所有问题以及菜单项被剪切的问题都可以解决。viewbox确实非常棒~~

结束语

viewbox属性非常棒,它就是一个svg的加强版工具。通过使用这个属性,在使用svg进行工作时就可以节省很多时间,无需借助图形编辑器即可快速解决svg的问题。总而言之,这对于编辑svg真的方便了很多。

我强烈建议你全面学习一下这个属性,然后让它在你的工作中发光发热。如果你想要使用它来做art-direct svg,不要忘了性能才是重点。