理解SVG transform坐标变换(转)

byzhangxinxufromhttp://www.zhangxinxu.com

本文地址:http://www.zhangxinxu.com/wordpress/?p=4965

一、HTML transform和SVG transform

SVG中自带transform属性,没错,是属性,例如:


普通的HTML元素没有transform属性,但是支持CSS3的transform, 好奇的小伙伴可能会疑问了,CSS3中的transform变换,跟SVG中的transform是什么关系呢?

恩,有点类似于谢霆锋和陈冠希之间的关系,有些小复杂。

OK, 先说说相似之处吧。

一些基本的变换类型是一样的,包括:位移translate, 旋转rotate, 缩放scale, 斜切skew以及直接矩阵matrix. 但只局限于2D层面的变换。SVG似乎只支持二维变换(若有不对,欢迎指正),且类似translateX,rotateX也都是不支持的。

下面就是不一样的地方了:

1.CSS3transform一般用在普通元素上,虽然也可以应用在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.具体的语法细节有差异。SVGtransform属性语法有些自带偏移。而CSStransform则更加纯粹些。

//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

结果会发现,两者位置大相径庭了:

CSStransform中的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变换一样,围绕元素的中心点变换。怎么办?

我们可以借助CSS3transform-origin修改SVG元素默认的变换中心点,然后配合CSS变换。但是,我们前面多次提到,IE浏览器的SVG元素不识别CSS中的transform. 所以,从兼容性上讲,CSS策略是不可行的。难道就没有其他办法了,有,就是这里的可选参数[ x y],通过对变换中心点的偏移修正,我们也能让SVG元素围绕自身的中心点旋转了。

所以,上面的demo,我们稍微修改下,就能让矩形围绕自己旋转了,见下:


您可以狠狠地点击这里: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度反向旋转:

结果位置回去了?才怪呢!

CSS的是又回去了,但是SVG的确是挂在月球上了。究其原因是rotate(-45)又是相对SVG左上角变换的啦!

您可以狠狠地点击这里:SVG连续旋转demo

虽然乍看上去,好像SVG的坐标系统有些怪怪的,但是,实际上,在有些需求场景下,SVG这种看似独立的偏移系统更容易实现一些功能。

比方说,我们希望某个SVG元素先以右下角为中心旋转90度,然后再以右上角为中心旋转90度,该怎么处理?

对于SVGtransform,我们直接面向需求写数值就可以了。假设我们的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中的使用有所不同,两外,SVGtransform属性值缩放是不支持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变换,想要如CSStransform-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(矩阵)”,一脉相承的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 关于css3变形 CSS3变形是一些效果的集合,比如平移、旋转、缩放和倾斜效果,每个效果都被称作为变形函数(Tra...
    hopevow阅读 6,247评论 2 13
  • Transform字面上就是变形,改变的意思。在CSS3中transform主要包括以下几种:旋转rotate、扭...
    hzrWeber阅读 21,896评论 0 19
  • 1、属性选择器:id选择器 # 通过id 来选择类名选择器 . 通过类名来选择属性选择器 ...
    Yuann阅读 1,520评论 0 7
  • 选择qi:是表达式 标签选择器 类选择器 属性选择器 继承属性: color,font,text-align,li...
    wzhiq896阅读 1,689评论 0 2
  • mybatis二级缓存 1.mybatis的一级缓存sqlsession 2.二级缓存指的同一命名空间(names...
    闫子扬阅读 225评论 0 0