这篇文章发布于 2015年10月10日,星期六,22:42,归类于 SVG相关。 阅读 130745 次, 今日 15 次 22 条评论
by zhangxinxu from https://www.zhangxinxu.com
本文地址:https://www.zhangxinxu.com/wordpress/?p=4965
一、HTML transform和SVG transform
SVG中自带transform
属性,没错,是属性,例如:
<svg width="200" height="150">
<rect x="30" y="30" width="120" height="90" transform="rotate(45)"></rect>
</svg>
普通的HTML元素没有transform
属性,但是支持CSS3的transform
, 好奇的小伙伴可能会疑问了,CSS3中的transform
变换,跟SVG中的transform
是什么关系呢?
恩,有点类似于谢霆锋和陈冠希之间的关系,有些小复杂。
OK, 先说说相似之处吧。
一些基本的变换类型是一样的,包括:位移translate
, 旋转rotate
, 缩放scale
, 斜切skew
以及直接矩阵matrix
. 但只局限于2D层面的变换。SVG似乎只支持二维变换(若有不对,欢迎指正),且类似translateX
, rotateX
也都是不支持的。
下面就是不一样的地方了:
1. CSS3 transform
一般用在普通元素上,虽然也可以应用在SVG元素上,但是IE浏览器(IE edge未测试)却不支持SVG元素;
rect {
/* IE说:你这是弄啥来? */
transform:rotate(45deg);
}
2. HTML元素的CSS3 transform和SVG的transform坐标系统大相径庭;
平常我们使用transform
其坐标是相对于当前元素而言的,默认是元素的中心点变换,我们可以通过transform-origin
属性改变变换的中心点。而SVG中的transform
的坐标变换的是相对于画布的左上角计算的,跟HTML的transform差别较大,理解上也更加麻烦。而本文就是彻底理清SVG中的transform
到底是怎么工作的。
3. 具体的语法细节有差异。SVG transform
属性语法有些自带偏移。而CSS transform
则更加纯粹些。
//zxx: 据说CSS的transform和SVG的transform属性即将合并。
二、SVG transform translate位移
我们先来看下最简单最基本的translate
位移变换,例如,我们偏移(295,115)大小的位置,HTML元素的偏移(下图左)和SVG元素的偏移(下图右)就会不一样。一个是相对自己的中心点(下图左),一个是SVG的左上角(下图右)。
虽然两者的相对位置不一样,但是,对于单纯地位移来讲,无论你相对于那个点位置,实际偏移的位置都是一样的,因此,从表现上讲,两者最终的位置看上去还是一样的。
您可以狠狠地点击这里:HTML translate和SVG translate比对demo
前面我们提到过,SVG元素也能使用CSS3的transform进行变换(非IE浏览器),但是只能支持2D层面的几个属性,例如translateX(tx)
, translateY(ty)
以及translate(tx[, ty])
. translateZ(tz)
则并不支持。
如果我们使用SVG元素自带的transform
属性进行变换,则仅支持translate(tx[ ty])
这种用法(缺省使用0
代替),当多个参数值的时候,可以使用逗号,
或者直接空格分隔,但是不能包含单位,例如下面这种写法直接翘辫子:
transform="translate(30px 12px)"
下面这种无单位写法才可以:
transform="translate(30 12)" transform="translate(30, 12)"
另外,和CSS3的transform
一样,SVG中的translate
位移也是支持多声明累加的。例如:
transform="translate(30 12) translate(30 12)"
等同于:
transform="translate(60 24)"
需要注意的是,俩个translate
中间不要混有其他的transform
变换。否则,最终的位移就不是简单的相加了。
三、SVG transform rotate旋转
上面的位移变换,我们似乎没看到明显的不一样。但是,从这里的旋转变换开始,就可以看出明显的差异了。
下面图示的是基本的45度旋转(来自css-tricks)(左HTML元素,右SVG元素):
由于SVG元素的默认是SVG左上角为中心变换的,因此,矩形旋转的幅度就有了过山车的感觉。
您可以狠狠地点击这里:HTML rotate和SVG rotate比对demo
结果会发现,两者位置大相径庭了:
CSS transform
中的rotate
语法比较直白:rotate(angle)
,就一个angle
参数,表示角度大小,不过必须要有单位,比如deg(度), turn(圈), grad(百分度 – 一种角的测量单位,定义为一个圆周角的1/400。常用于建筑或土木工程的角度测量),甚至可以使用calc()
计算,例如:calc(.25turn - 30deg)
.
但是,SVG中的属性transform
旋转就没有这么多花头,单位?哦,别逗了,毛线都没有,直接光秃秃的数值,表示角度deg
,例如:
transform="rotate(45)"
具体语法为:rotate(angle[ x y])
. 大家注意到没有,这里有个[ x y]
, []
表示这个可选参数。也就是说,SVG中的rotate
旋转比CSS的rotate
多了一个可选参数,那这个可选参数[ x y]
表示什么意思呢?
告诉你,是非常有用的东西。用来偏移transform
变换中心点的。
为什么说非常有用呢?SVG元素默认是基于左上角的,但是我们的旋转元素往往都在SVG的中间区域,此时旋转跨度太大,智商余额不足的我们就脑补不过来,此时难免希望可以像CSS的transform变换一样,围绕元素的中心点变换。怎么办?
我们可以借助CSS3 transform-origin
修改SVG元素默认的变换中心点,然后配合CSS变换。但是,我们前面多次提到,IE浏览器的SVG元素不识别CSS中的transform
. 所以,从兼容性上讲,CSS策略是不可行的。难道就没有其他办法了,有,就是这里的可选参数[ x y]
,通过对变换中心点的偏移修正,我们也能让SVG元素围绕自身的中心点旋转了。
所以,上面的demo,我们稍微修改下,就能让矩形围绕自己旋转了,见下:
<rect x="30" y="30" width="120" height="90" rx="10" ry="10" fill="#a0b3d6" transform="rotate(45, 90 75)"></rect> <!-- 90 = 30 + 120/2 --> <!-- 75 = 30 + 90/2 -->
您可以狠狠地点击这里:SVG元素也围绕自身中心点旋转demo
使用原理图表示就是下面这样(左HTML旋转,右SVG元素偏移旋转):
同样的,rotate
旋转可以多个值并存,例如下面的CSS和attribute用法:
transform: rotate(45deg) rotate(-45deg);
transform="rotate(45) rotate(-45)"
然而,需要注意的是,SVG属性的transform
声明的中心变换坐标是不能共享的。
因此,虽然transform="rotate(45, 90 75)"
是中心点旋转,但是,后面再添加其他东西,例如:rotate(-45)
则偏移值忽略,终中心点还是SVG的左上角(0,0)
位置,好惨!
例如原来的45度旋转,再加个-45度反向旋转:
<rect x="30" y="30" width="120" height="90" rx="10" ry="10" fill="#a0b3d6" transform="rotate(45, 90 75) rotate(-45)"></rect>
结果位置回去了?才怪呢!
CSS的是又回去了,但是SVG的确是挂在月球上了。究其原因是rotate(-45)
又是相对SVG左上角变换的啦!
您可以狠狠地点击这里:SVG连续旋转demo
虽然乍看上去,好像SVG的坐标系统有些怪怪的,但是,实际上,在有些需求场景下,SVG这种看似独立的偏移系统更容易实现一些功能。
比方说,我们希望某个SVG元素先以右下角为中心旋转90度,然后再以右上角为中心旋转90度,该怎么处理?
对于SVG transform
,我们直接面向需求写数值就可以了。假设我们的SVG元素的高宽是120*90, 左上角坐标是(30,30), 则,显然,右下角坐标是(150,120), 右上角坐标是(150,30),于是,我们的transform
值就很简单:
transform="rotate(90, 150 120) rotate(90 150 30)"
参见下面的示意图(示意图的坐标与上面略有出入,但不影响原理示意):
但是,如果我们使用之前容易理解的CSS3来实现,反而就复杂了,因为CSS3中的transform的变换点只能一次性指定,如果要实现不同变换点的旋转效果,只能通过translate
再次偏移,例如,实现上面的右下角再右上角90度旋转,则要这样:
transform-origin: right bottom; /* 或者 100% 100% */ transform: rotate(90deg) translate(0, -100%) /* 从右下到右上 */ rotate(90deg) translate(0, 100%);
Gif示意下就是:
显然要麻烦很多。可见,两种坐标系统没有绝对的优劣。
您可以狠狠地点击这里:右下再右上旋转90度demo
上图为两种变换的最终效果,虽然最终的效果是一样的,但是,从理解上而言,这回,是SVG的transform
反而更容易理解。还是那句话,辩证看问题,凡事无绝对。
四、SVG transform scale缩放
SVG中的缩放的语法就比较单纯了,就scale(sx[, sy])
, sx
表示横坐标缩放比例,sy
表示纵坐标缩放比例。其中sy
是可缺省的,如果缺失,表示使用和sx
一样的值,也就是等比例缩放。其中,sx
和sy
两个参数可以逗号分隔,也可以使用空格分隔。这和CSS3中的使用有所不同,两外,SVG transform
属性值缩放是不支持scaleX
, scaleY
这些鬼的。
同样的,CSS控制的transform和SVG元素属性控制的transform的坐标系统是不一样的。一个默认元素中心(下图左),一个是SVG画布左上角(下图右),于是(from css-tricks):
因此,当我们对SVG元素scale缩放时候,发现位置也有出乎我们意料,就应该知道是怎么回事了。
rotate
旋转虽然也是迥异坐标,但是其参数自带偏移参数,我们我们移个花接个木,还是可以得到我们想要的结果。但是,scale
缩放这里,就要悲惨很多了,没有自带偏移参数,于是,当我们要实现SVG元素居中缩放的效果,还需要使用translate
手动偏移。
怎么个手动偏移法呢?即使先translate其中心点位置到元素的中心坐标位置,然后缩放,然后坐标再反方向还原回去。例如,某元素中心点坐标是(95, 75)
, 垂直缩放1.5倍的效果则是:
transform="translate(95 75) scale(1, 1.5) translate(-95 -75)"
您可以狠狠地点击这里:CSS transform和SVG transform scale缩放demo
对应的CSS代码就简单多了,直接:
.scale { transform: scale(1, 1.5); }
然后最终效果都是一样的:
使用Gif原理示意如下:
五、SVG transform skew斜切
先来了解下CSS中的斜切,斜切,如果单纯切一个方向,我们可以看成把矩形变成了平行四边形,其总面积不变化。
以纯X轴变换举例,skewX(-45deg)
则下面这样子(灰色方块为原始位置):
skewX(45deg)
则下面这样子:
对于SVG的skew斜切变换,表现效果原理是一样的。但是,使用的语法却相当有意思。
在前面的一些变换中,例如位移、缩放之类是不支持translateX
, scaleX
这种CSS常见用法的,但是这里的skew
却有点让人哭笑不得:不支持skew(x[, y])
这种语法,而只能是skewX
或者skewY
.
别问我为什么?我只是大自然的搬运工,我不生产语法。
因此,没有:
transform="skew(45, 0)"
只有:
transform="skewX(45)"
同样的,由于变换中心点的差异,CSS实现的变换和SVG属性变换往往最后的位置是不一样的:
不仅如此,如果元素的中心点不是就是SVG的左上角,则skewX(α1) skewX(α2)
的最终位置和skewX(α1 + α2)
是不一样的(位移和旋转不会这样子)。
您可以狠狠地点击这里:CSS SVG transform skew斜切差异及连续斜切差异demo
正如demo所示,CSS的和SVG的位置差异很大:
SVG的连续变换和一次性变换的位置也是不一样的:
可能有人要疑问,为何连续斜切变换和一次性变换位置会不一样?其实原因很简单,因为斜切的角度和元素偏移大小并不是线性的,比方说,从70到80度和80度到90度之间的位移大小(虽然都是10度的变化区间)是显然不是一个档次的。因此,分开多次连续斜切最终的坐标偏移要比一次性偏移来得小。
最后,和缩放一样,你想要让SVG元素中心点斜切,可以先translate
偏移,在skew
变换。就不重复举例演示了。
六、其他居中变换处理
像缩放、斜切这些SVG变换,想要如CSS transform-origin:50% 50%
一样的中心点变换效果,需要事先位移,我们有没有其他办法呢?
这里有两个思路可供大家参考下。
1. 原始中心位置乃SVG左上角
拿45度旋转举例,我们可以把元素默认就放在中心点和SVG左上角重合的位置上,参见下面的gif演示:
于是,我们原来的3步曲就变成了2步曲:
translate(140 105) rotate(45) translate(-140 -105) → translate(140 105) rotate(45)
2. 通过viewBox调整
viewBox
可以用来改变SVG画布的视区,这个我之前专门撰文介绍过,是SVG学习必备被深入理解的基础知识,参见:“理解SVG的viewport,viewBox,preserveAspectRatio”一文。
我们可以把元素默认挂在左上角,然后,通过viewBox
做手脚,让元素呈现的位置并不是真正的左上角,例如应用viewBox='-140 -105 280 210'
,则变化如下示意图:
此时,我们只需要让元素旋转就可以了,无需额外的做translate
位移,见下gif:
七、结束语
本文介绍的内容实际上都还是非常基本的。实际SVG应用的时候,可能是多个变换参杂在一起,所以,如果本文介绍的几个基本变换都没搞清楚,到时候,想必是想破脑袋都不明白怎么元素跑这里了,怎么变成这样了!
本文的这些知识点虽然基本,但是相当重要的。再加上,不同的变换方法的语法细节还不一样。有的自带偏移,有的需要手动偏移等等;不同变换的前后位置不同,甚至同一变换分开连续变换和一次性变换的结果都不一样等等;都要求大家要细心耐心阅读。
本文内容和结构参考自:Transforms on SVG Elements. 但要比原文要精炼很多,同时,每一个变换都有亲自实践认证,因此,从品质上讲,可能还要略高一筹。
对了,矩阵matrix
没有细说过,这个可以参考我之前的文章:“理解CSS3 transform中的Matrix(矩阵)”,一脉相承的。
我也是初学者,出错在所难免,欢迎指正!
感谢阅读,欢迎交流!
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=4965
(本篇完)
- 小tip: margin:auto实现绝对定位元素的水平垂直居中 (0.301)
- CSS实现平行四边形布局效果 (0.301)
- 小tips: zoom和transform:scale的区别 (0.166)
- 小tips: 在canvas上实现元素图片镜像翻转动画效果 (0.166)
- 移动端双指缩放图片JS事件的实践心得 (0.150)
- IE矩阵滤镜Matrix旋转与缩放及结合transform的拓展 (0.136)
- 理解CSS3 transform中的Matrix(矩阵) (0.136)
- 小tip: IE下zoom或Matrix矩阵滤镜中心点变换实现 (0.136)
- 趁热打铁,SVG feColorMatrix滤镜gogogo! (0.120)
- 图片旋转效果的一些研究、jQuery插件及实例 (0.116)
- canvas文本绘制自动换行、字间距、竖排等实现 (RANDOM - 0.100)
张老师,SVG变换会加入GPU加速吗
不知道,应该不会,加入了纯属浪费
张老师,gif无法显示,src多了’//’。
感谢反馈,已修复。
这里提到的SVG,挺有意思的,但是我现在有个想法,就是能不能用CSS来控制这个SVG里面的PATH,比如我定义了一个PATH的ID=sss,打算通过CSS来做一个简单的鼠标经过
大概要实现的效果是
#sss:hover{transform:scale(1.5,1.5);} 要如何写呢
在scale放大图形的时候,为啥图形会往右往下便宜啊
五、SVG transform skew斜切. 这部分介绍的斜切中,skewX按照您画的坐标系怎么旋转的是Y轴啊,迷惑中……
CSS 中的斜切,以skew(deg[,deg])==y变小x变大,即skewX(deg)==X轴不变,y轴转动,当deg为正数时,y轴逆时针转动(度数变小),则x轴对应的上下两边把持中点一直在y轴上,上下动来确定平行四边形范围,那怎么确定左右两边呢,则是上下两边到X轴的距离要与原来一样,即在宽相同的情况下要保持高相同才能做到面积相等
用了svg 的transform,成功的解决了IE的问题,谢谢
张大大 这个gif是如何做的?
写这样一篇图文并茂,且无处不幽默无处不专业的长篇文章来,是要花费作者大量时间和精力的,大家阅读学习后不得不对张鑫旭博主投以赞叹支持的目光, 我们一同期待博主更多力作,佳作!
大佬,IE8下面,input获得光标后1秒,背景会随着光标上移2像素,这个是什么原因?可以用带图的背景测试效果,否则不容易发现
目前对于transform发现的最大的问题,还是浏览器对其表现响应的不一致,这位说的是IE8下,不过IE似乎并不支持transform,我发现的问题在IE11下,对于添加了transform样式的元素,其中包含了一个输入框,然后输入框获得了焦点,一般会在输入框中出现|光标,而此时如果transform样式导致元素的坐标距离变换后,会发现光标并不会随着输入框的变换而变换位置,很奇异,目前不知道如何处理,似乎经测试反馈,移动端某些浏览器(手机)上也有这个问题。
学习了,赞
张gg,很幽默,出书吧。一定支持
感谢你的详细讲解,不过我还有一个疑问:
CSS transform和SVG transform scale缩放demo(不是正文中) 中,rect 元素的 width = 120, height = 90, x = 20, y = 30。这样的话,cx = 90, cy = 70。所以我觉得应该是 translate(90, 70),而不是 translate(95, 75)。
不知道我说的有没有问题,期待交流。谢谢
这个确实应该是90,不过旭哥文中说的是举例有个中心是(95,75)的。
哈哈,之前就有遇到类似问题!干货来着!
哈哈,之前正好在折腾这个,也写了俩demo关于中心变换的:
svg:http://alvarto.github.io/blog/transformation/svg.html
canvas:http://alvarto.github.io/blog/transformation/canvas.html
相关博文:https://github.com/alvarto/blog/issues/1
好久不见博主更新文章了,太激动了!
良师益友~
张鑫旭老师,第四部分 scale 第一段有个错别字“两外” 应为 “另外”