马黑黑 发表于 2024-9-16 11:57

svg子元素绕自身中心点运动的实现方法

<style>
#artbox { position: relative; }
#artbox > p { position: relative; margin: 10px 0; font: normal 18px / 26px sans-serif; }
#artbox pre { margin: 20px; background: #eee; padding: 12px; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; tab-size: 4; white-space: pre-wrap; word-wrap: break-word; display: none; }
</style>

<div id="artbox">
        <p>svg坐标系以svg画布左上角{0,0}为中心,且svg画布内的所有元素也以此为中心。这意味着,如果svg内的元素做transform形变或运动,例如伸缩或旋转,它不会在原地进行,而是伸缩或旋转的同时还环绕svg画布左上角做一定距离的圆周运动。试从以下例子加以领会,点击矩形它就会做旋转运动(随后的例子都是通过点击子元素触发动画):</p>

<svg width="200" height="200" viewBox="0 0 200 200" style="border:1px solid gray">
        <rect x="70" y="80" width="60" height="40" fill="olive">
                <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="5s" begin="click" repeatCount="indefinite"/>
        </rect>
</svg>

        <p><button class="btn" type="button" value="1">点击查看代码</button></p>
        <div><pre></pre></div>

        <p>如何能让svg里的各种元素能像CSS所实现的那样,都是以元素自身的中心点作为运动中心?</p>
        <h2>方法一:给出元素在svg画布中元素中心点坐标</h2>
        <p>下面的代码,在前例基础上修改了animateTransform动画中的from和to属性,加入了矩形自己的中心点相对于svg画布的坐标值:</p>

<svg width="200" height="200" viewBox="0 0 200 200" style="border:1px solid gray">
        <rect x="70" y="80" width="60" height="40" fill="orange">
                <animateTransform attributeName="transform" type="rotate" from="0 100 100" to="360 100 100" dur="5s" begin="click" repeatCount="indefinite"/>
        </rect>
</svg>

        <p><button class="btn" type="button" value="1">点击查看代码</button></p>
        <div><pre></pre></div>

        <p>from和to属性值都是3个数值,第一个是要旋转的角度,第二、三个是矩形中心点在svg画布中的坐标。例中矩形中心点恰好和svg画布的中心点重合,如果不是呢?试看:</p>

<svg width="200" height="200" viewBox="0 0 200 200" style="border:1px solid gray">
        <rect x="50" y="140" width="60" height="40" fill="olive">
                <animateTransform attributeName="transform" type="rotate" from="0 80 160" to="360 80 160" dur="5s" begin="click" repeatCount="indefinite"/>
        </rect>
</svg>

        <p><button class="btn" type="button" value="1">点击查看代码</button></p>
        <div><pre></pre></div>

        <p>从上例可以看出,只要给出元素中心点在svg画布上的坐标值,元素就能在原地旋转(或做其他transform形变),不论其身居何处。</p>
        <h2>方法二:使用viewBox视窗坐标系+简单处理元素位置</h2>
        <p>svg画布是一个特殊的画布,理论上它是无限大的,我们通过svg标签的width和height属性设定了它的物理宽高尺寸,这样svg画布就和我们现实生活中的画布一样了。但是,svg还可以设定一个viewBox属性,即视窗,用来设置画布中的可视区域,属性值四个,前两个是视窗坐标起始值,后两个是宽高。前面的示例,viewBox与物理画布重合,而以下例子,我们将 viewBox 的坐标系往左、上各移动100,这样viewBox视窗的中心恰好处在svg物理画布的左上角,此时,矩形的x设为左移到自身宽度的一半、y值上移高度的一半,最终元素在viewBox视窗中呈现出来的就在画布(实际上是viewBox视窗)的中心并能在原地旋转:</p>

<svg width="200" height="200" viewBox="-100 -100 200 200" style="border:1px solid gray">
        <rect x="-30" y="-20" width="60" height="40" fill="orange">
                <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="5s" begin="click" repeatCount="indefinite"/>
        </rect>
</svg>

        <p><button class="btn" type="button" value="1">点击查看代码</button></p>
        <div><pre></pre></div>

        <h2>方法三:借助CSS通过transform-box和transform-origin改变元素运动中心</h2>
        <p>svg基于XML,XML和HTML同宗,故而,svg能够接受CSS设置,甚至自身的一些特有属性也可以使用CSS方法设置。我们可以通过CSS的style属性设置svg子元素的transform*属性以实现我们的需求,其中:transform-box用于定义元素的布局框,值为fill-box时指以自身的边框为包装盒边框;transform-origin大家应该熟悉,用来定义运动中心,值为center时等同于{50%,50%},表示运动原点在中心。二者结合起来使用,svg里的元素就和HTML元素一样绕自己的中心点做运动,不论它们在画布中的什么位置:</p>

<svg width="200" height="200" viewBox="0 0 200 200" style="border:1px solid gray">
        <rect x="130" y="30" width="60" height="40" fill="olive" style="transform-box: fill-box; transform-origin: center;">
                <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="5s" begin="click" repeatCount="indefinite"/>
        </rect>
        <rect x="30" y="130" width="60" height="40" fill="orange" style="transform-box: fill-box; transform-origin: center;">
                <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="5s" begin="click" repeatCount="indefinite"/>
        </rect>
</svg>

        <p><button class="btn" type="button" value="1">点击查看代码</button></p>
        <div><pre></pre></div>

</div>

<script>
var btns = artbox.querySelectorAll('button'), pres = artbox.querySelectorAll('pre'), svgs = artbox.querySelectorAll('svg');
pres.forEach((pre,key) => pres.innerText = svgs.outerHTML);
btns.forEach((btn,key) => btn.onclick = () => {
        pres.style.display = 'inline-block';
        btn.disabled = true;
});
</script>

起个网名好难 发表于 2024-9-16 12:12

https://p0.meituan.net/avatar/41afb54714c0c4e438b303eaa8f63b0068355.jpg

红影 发表于 2024-9-16 12:38

方法二不太好理解,那个视框移动到原来的左上角了,边线也跟着上去了啊。
方法二去想空间关系还挺麻烦,例如x,在-100情况下取-30,也就是在新的边框下移动70,然后宽度60,正好让横向中心位于原来的左上角。

红影 发表于 2024-9-16 12:41

方法三最方便,不用去计算位置,使用CSS的style属性设置svg子元素的中心即可,最省力{:4_173:}

红影 发表于 2024-9-16 12:41

感谢黑黑的详细讲解,学习了{:4_187:}

马黑黑 发表于 2024-9-16 12:58

红影 发表于 2024-9-16 12:38
方法二不太好理解,那个视框移动到原来的左上角了,边线也跟着上去了啊。
方法二去想空间关系还挺麻烦,例 ...

viewBox不真正移动,它只是改变了用户坐标系而已。

{-100, -100} 的坐标起始值,作用于用户坐标,原先的 0 就是100。这就是viewBox和物理画布的逻辑关系

马黑黑 发表于 2024-9-16 13:00

红影 发表于 2024-9-16 12:41
方法三最方便,不用去计算位置,使用CSS的style属性设置svg子元素的中心即可,最省力

每一种方法都有各自的用途。比如我们要制作一系列的子元素,通过viewBox坐标系将所有子元素都定位于视窗中心,这种情形,用方法二最省事。

小辣椒 发表于 2024-9-16 14:43

起个网名好难 发表于 2024-9-16 12:12


https://file.uhsea.com/2409/c7359c0095b0404959b98868f16efca978.jpg



小辣椒 发表于 2024-9-16 14:45

新的知识点,小辣椒学习了{:4_187:}

梦江南 发表于 2024-9-16 14:54

内行看门道,外行看热闹。老师辛苦了!

马黑黑 发表于 2024-9-16 15:01

梦江南 发表于 2024-9-16 14:54
内行看门道,外行看热闹。老师辛苦了!

{:4_190:}

马黑黑 发表于 2024-9-16 15:02

小辣椒 发表于 2024-9-16 14:45
新的知识点,小辣椒学习了

小辣椒节日好

起个网名好难 发表于 2024-9-16 15:03

小辣椒 发表于 2024-9-16 14:43


嗯, 这个好,我喜欢。{:5_106:}

小辣椒 发表于 2024-9-16 15:09

马黑黑 发表于 2024-9-16 15:02
小辣椒节日好

黑黑中秋节快乐!{:4_187:}

小辣椒 发表于 2024-9-16 15:09

起个网名好难 发表于 2024-9-16 15:03
嗯, 这个好,我喜欢。

{:4_205:}

你的图图改的

起个网名好难 发表于 2024-9-16 15:39

小辣椒 发表于 2024-9-16 15:09
你的图图改的

哦,还以为是有这么个对着干的图{:5_106:}

小辣椒 发表于 2024-9-16 16:13

起个网名好难 发表于 2024-9-16 15:39
哦,还以为是有这么个对着干的图

{:4_205:}

也是好玩的

马黑黑 发表于 2024-9-16 18:34

小辣椒 发表于 2024-9-16 15:09
黑黑中秋节快乐!

同乐

红影 发表于 2024-9-16 19:29

马黑黑 发表于 2024-9-16 12:58
viewBox不真正移动,它只是改变了用户坐标系而已。

{-100, -100} 的坐标起始值,作用于用户坐标,原先 ...

是的,这个是相对的,-100作为0,原先的0就成了100了。

红影 发表于 2024-9-16 19:31

马黑黑 发表于 2024-9-16 13:00
每一种方法都有各自的用途。比如我们要制作一系列的子元素,通过viewBox坐标系将所有子元素都定位于视窗 ...

是的,这样方法三就需要一个个设置,更繁琐了。
页: [1] 2 3
查看完整版本: svg子元素绕自身中心点运动的实现方法