svg基础变形: transform 之 rotate
<style>.ma p, .ma pre, .ma svg { margin: 8px 0; }
.rred { color: red; }
.bblue { color: blue; }
.zs { color: green; }
.ma pre { padding: 16px; background: #efefef; font: normal 16px monospace; white-space: pre-wrap; word-wrap: break-word; tab-size: 4; line-height:1.5em; }
.ma svg { border: 1px solid gray; }
.flex { display: flex; gap: 8px; align-items: center; }
</style>
<div class="ma">
<h2>svg基础变形: transform 之 rotate</h2>
<p>svg变形中的旋转 rotate,和CSS原理一致,但是表达方式大不相同。svg的transform属性中,rotate需要两个参数,角度和基点坐标,表示为 transform="rotate(deg, x y)",其中,deg是角度,x和y分别是旋转的XY坐标值,例如,rotate(45, 100 100) 表示,旋转45度,旋转基点为基于svg画布的坐标点{100,100}。CSS中,旋转基点另外使用 transform-origin 设定。</p>
<p>旋转基点通常总是根据需要设定,我们做帖的系列音频播放器中,很多用旋转的东东做按钮,需要将旋转基点设为按钮的中心,我们就以旋转对象的中心为基点,设计下例代码:橄榄色矩形、海军蓝矩形和红色矩形的位置、宽高一模一样,橄榄色矩形以自身中心为基点旋转四十五度,红色矩形也旋转四十五度但不设置旋转基点(默认值0 0):</p>
<pre>
<svg width="200" height="160" style="border: 1px solid gray;">
<rect <span class="bblue">x="15" y="15" width="70" height="70"</span> fill="olive" />
<rect <span class="bblue">x="15" y="15" width="70" height="70"</span> <span class="rred">transform="rotate(45,50 50)"</span> fill="navy" />
</svg>
</pre>
<p>下面是效果,可以看出,红色矩形旋转后移位了,原因是它绕着基于svg画布的 0 0 即左上角坐标点旋转。</p>
<svg width="200" height="160" style="border: 1px solid gray;">
<rect x="15" y="15" width="70" height="70" fill="olive" />
<rect x="15" y="15" width="70" height="70" transform="rotate(45,50 50)" fill="navy" />
<rect id="redRect" x="15" y="15" width="70" height="70" transform="rotate(45)" fill="red" />
</svg>
<p class="rred">
<input id="rr" type="checkbox" value="45, 50 50" />
<label for="rr">设置红色矩形旋转基点为矩形中心{50,50}</label>
</p>
<p>可以勾选上面的复选框,尝试将红色矩形的旋转基点设为矩形的中心,看看发生什么;取消勾选按钮,则恢复初始旋转基点{0,0}。</p>
<p>【附】rotate 基点缺失时如何让元素旋转后停在原位</p>
<p>下面是代码和效果(圆点即circle元素作参照用):</p>
<pre>
<svg width="200" height="200" style="border: 1px solid gray;">
<rect
x="20"
y="20"
width="80"
height="80"
<span class="rred">transform="translate(60 60) rotate(45) translate(-60 -60)"</span>
fill="olive"
/>
<circle cx="60" cy="60" r="4" fill="red" />
</svg>
</pre>
<svg width="200" height="200" style="border: 1px solid gray;">
<rect
x="20"
y="20"
width="80"
height="80"
transform="translate(60 60) rotate(45) translate(-60 -60)"
fill="olive"
/>
<circle cx="60" cy="60" r="4" fill="red" />
</svg>
<p>红色文本代码是关键所在:矩形的圆心在svg画布的 {60,60} 处,我们先用 translate(60 60) 将矩形位移,旋转之后,再用 translate(-60 -60) 反方向回来。实现原理和我们许久以前介绍过的此类CSS的妙法一样。</p>
<p>最后小结一下:svg的transform属性,rotate旋转默认以画布的{0,0}即左上角为旋转基点,若需要设置旋转基点,使用 (角度, x坐标 y坐标) 的方式进行,xy坐标参照svg画布坐标体系。当需要设置元素自身的中心为旋转基点时,所取的xy坐标值是元素中心相对于svg画布的坐标值。</p>
</div>
<script>
rr.onchange = () => {
let val = 'rotate(' + (rr.checked ? rr.value : '45') + ')';
redRect.setAttribute('transform', val);
};
</script>
前面一个好理解,直接放上角度和图形中心,后面一个挺难理解的,用transform="translate(60 60) rotate(45)"去试了试,转到了红点点的正下方,想移回来还挺难,这样连续移动、旋转、再移动。倒是能回来了。很奇怪。 红影 发表于 2023-11-17 23:21
前面一个好理解,直接放上角度和图形中心,后面一个挺难理解的,用transform="translate(60 60) rotate(45) ...
我没说清楚,也就是
transform="translate(60 60) rotate(45) "的情况下,旋转后的图像在红点正下方,
所以觉得后面的平移不应该是translate(-60 -60),而是translate的x不动只移y的一个负值,实际却不是这样。没弄明白怎么回事。 红影 发表于 2023-11-17 23:21
前面一个好理解,直接放上角度和图形中心,后面一个挺难理解的,用transform="translate(60 60) rotate(45) ...
这个其实好理解的:相当于转换了坐标系 本帖最后由 马黑黑 于 2023-11-18 08:16 编辑
红影 发表于 2023-11-17 23:31
我没说清楚,也就是
transform="translate(60 60) rotate(45) "的情况下,旋转后的图像在红点正下方,
...
transform 的 translate 和 rotate 配套使用所产生的的效果是很奇妙的,会出乎我们的意料之外。但有原理在里面,只是较难描述,记住这个就好:位移加旋转会导致运动对象围绕基点(svg中这个基点在{0,0}处)转圈。形变组合有顺序,它们依次执行,但需要基点的形变属性(比如rotate)始终都围绕着基点进行。
你设计的 translate(60 60) rotate(45),它就是形变组合,位移+旋转,这是要围绕基点转圈的,它的左上角坐标位{60,60},就是那个顶角(它翻转了,左上角变为顶角)。这个时候,想要它回到原位的中心点,看着好像是竖直上移60像素就成,其实不然,这个 translate(0 -60)要加入前面的形变组合,变成第三个形变属性,始终要遵循 translate+rotate 的运行规则,即总是以原始基点{0,0}为绕圈中心,所以,第三个形变的位移必须是 {-60 -60},矩形才会在原地旋转 45 度。
transform形变,形变基点是个重要因素。默认情况下:
CSS中,运动对象以自己的中心为形变基点。CSS可以通过设置 transform-origin 来改变形变基点。
svg中,运动对象以svg画布的左上角{0,0}为形变基点。张三就原地转个45度,转完后他不在原地了,因为他是绕着svg左上角旋转,变成了转了一个弧度。
svg可以通过设置,让形变基点也是绕着运动对象的中心进行,如果需要,以后我们也会介绍这个知识点。 马黑黑 发表于 2023-11-17 23:53
这个其实好理解的:相当于转换了坐标系
“相当于转换了坐标系”
一言惊醒梦中人!{:5_116:}
当我只取前两步,得到原坐标系下的结果,我仍然顺着这个思路去想它的位移,当然不对。除非全部做完后,不受旋转影响的情况下,另外完成位移,那已经是第三种方式了,变得更复杂。
第一种,最聪明,旋转时加入旋转角度和物体原点,通过内部设置,直接完成了绕原地旋转的过程。
第二种,应该算是把第一种的原理打开来显示的过程,那就是通过坐标平移到原点后再旋转,同时再反向平移,达到目的。
第三种,属于硬做的法子,即使不平移,旋转后,通过空间关系,仍能将矩形移动到原来的原地位置,这种很难找到规律,还是第一种的使用最好。感谢svg设计者的聪明。 马黑黑 发表于 2023-11-18 08:14
transform 的 translate 和 rotate 配套使用所产生的的效果是很奇妙的,会出乎我们的意料之外。但有原理 ...
你说得对,第三个形变属性仍然在前面的形变组合中,并不是单单自身的形变。实际单单的形变也不是竖直上移60像素了,而是20加上那个正方形的交叉斜边,大概是56.56多。
即使开始不平移,直接基于svg画布的 0 0 旋转,通过原图形所在位置等空间关系,表面上仍然能弄回到原点,只是不能在旋转的后面加平移,这样仍是组合,跟单纯移动不一样。 还好这个只是坐标平移。坐标系的变化包含平移和旋转,如果是坐标系自身的平移加旋转,再去找到新坐标系下的点,就太复杂了{:4_173:} 这个终于弄懂了,谢谢黑黑{:4_187:} 红影 发表于 2023-11-18 09:15
还好这个只是坐标平移。坐标系的变化包含平移和旋转,如果是坐标系自身的平移加旋转,再去找到新坐标系下的 ...
那是的 红影 发表于 2023-11-18 09:12
你说得对,第三个形变属性仍然在前面的形变组合中,并不是单单自身的形变。实际单单的形变也不是竖直上移 ...
理解正确 红影 发表于 2023-11-18 08:55
“相当于转换了坐标系”
一言惊醒梦中人!
也可以理解为和ps加图层那样的原理:transform形变,相当于给svg加一个图层,这个图层拥有自己的坐标系统。
不过这只是相当,但是,形变之后,形变“图层”的虚拟坐标系统确实存在。 红影 发表于 2023-11-18 09:16
这个终于弄懂了,谢谢黑黑
svg形变比CSS的抽象得多,会令人困惑。 马黑黑 发表于 2023-11-18 10:45
那是的
我记得那个计算公式还是比较复杂的,当然在cad系统里不算个事。 马黑黑 发表于 2023-11-18 10:45
理解正确
我去画了一下,直观去看觉得挺清楚。 红影 发表于 2023-11-18 12:42
我去画了一下,直观去看觉得挺清楚。
是的,不过理解起来还需要自己体会 红影 发表于 2023-11-18 12:40
我记得那个计算公式还是比较复杂的,当然在cad系统里不算个事。
应该不太简单。cad已经封装好了所有可能出现的算法。 马黑黑 发表于 2023-11-18 08:39
transform形变,形变基点是个重要因素。默认情况下:
CSS中,运动对象以自己的中心为形变基点。CSS可以 ...
“svg中,运动对象以svg画布的左上角{0,0}为形变基点。张三就原地转个45度,转完后他不在原地了,因为他是绕着svg左上角旋转,变成了转了一个弧度。”
问题是绕着左上角为几点转了45度后,距离左上角不再是15了,rect的定位是15 15,这才是费解的地方。 马黑黑 发表于 2023-11-18 10:48
也可以理解为和ps加图层那样的原理:transform形变,相当于给svg加一个图层,这个图层拥有自己的坐标系统 ...
是的,和图层不完全相同。虽然这个说法更便于理解。