忙了很长一段时间,一直都没有写文章了。正好偷偷闲,记录一下之前遇到的一个需求,再就是解决的方案。
起因
被分配了一个制作公司官网的任务,众所周知,官网这种东西,就是要美观,各种花里胡哨的动效都给我整上。但是UI出图都是通过figma出静态资源,动效的效果都是由产品口头叙述。然后就有了如下的一个需求: 看这个svg,这个是UI切出来的图,产品希望里面那根黄线能够沿着这个六边形不断移动。当然不是说只需要这个,譬如说还有3个顶点需要在黄线经过的时候能够让点像呼吸一样扩张再缩小,但移动这个是困住我的点,因为我一开始没啥思路。
我心里算计着,要是UI能给我个gif就好了,然而哪有那么好的事,没办法咯,只能先动手试试。
Have a try
之前Trae不是很火吗?我就开始抛弃了svg,截了个图让Calude帮我生成一下。经过我不断对问题进行补充和修正,最终Trae给出的答案不能说完美无缺,也只能说是难堪大用。Claude给出的答案是使用canvas,自行去渲染所有的图形。虽然看上去差不多,但是每次线移动到拐角处时,就非常不丝滑。
那自己去研究一下canvas?要是真有那么多时间就好了,一两天的功夫感觉通过canvas要真做到和产品说的那样丝滑,时间不一定够。
看起来这条路走不通,那就只好再回到svg上去研究。
这svg里到底藏了些啥玩意
虽然之前有接触过svg,但是了解不够深入,其实也就知道fill、path、rect这些相对简单的内容。既然搞不懂,那就一点点注释掉看看。
这里放出svg的全部内容
<svg width="640" height="640" viewBox="0 0 640 640" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M66.8719 146.144L64.8719 142.679L66.8719 146.144ZM66.8718 493.856L64.8718 497.32L66.8718 493.856ZM344 626.144L342 622.68L344 626.144ZM296 626.144L294 629.608L296 626.144ZM573.128 146.144L575.128 142.68L573.128 146.144ZM344 13.8564L346 10.3923L344 13.8564ZM296 13.8564L294 10.3923L296 13.8564ZM342 17.3205L571.128 149.608L575.128 142.68L346 10.3923L342 17.3205ZM593.128 187.713V452.287H601.128V187.713H593.128ZM571.128 490.392L342 622.68L346 629.608L575.128 497.32L571.128 490.392ZM298 622.68L68.8718 490.392L64.8718 497.32L294 629.608L298 622.68ZM46.8719 452.287V187.713H38.8719V452.287H46.8719ZM68.8719 149.608L298 17.3205L294 10.3923L64.8719 142.679L68.8719 149.608ZM46.8719 187.713C46.8719 171.993 55.2582 157.468 68.8719 149.608L64.8719 142.679C48.783 151.968 38.8719 169.135 38.8719 187.713H46.8719ZM68.8718 490.392C55.2582 482.532 46.8719 468.007 46.8719 452.287H38.8719C38.8719 470.865 48.783 488.032 64.8718 497.32L68.8718 490.392ZM342 622.68C328.386 630.539 311.614 630.539 298 622.68L294 629.608C310.089 638.897 329.911 638.897 346 629.608L342 622.68ZM593.128 452.287C593.128 468.007 584.742 482.532 571.128 490.392L575.128 497.32C591.217 488.032 601.128 470.865 601.128 452.287H593.128ZM571.128 149.608C584.742 157.468 593.128 171.993 593.128 187.713H601.128C601.128 169.135 591.217 151.968 575.128 142.68L571.128 149.608ZM346 10.3923C329.911 1.1034 310.089 1.10339 294 10.3923L298 17.3205C311.614 9.46066 328.386 9.46066 342 17.3205L346 10.3923Z"
fill="white" />
<g clip-path="url(#paint0_angular_210_160_clip_path)" data-figma-skip-parse="true">
<g transform="matrix(0 -0.32 0.32 0 320 320)">
<foreignObject x="-1025" y="-1025" width="2050" height="2050">
<div xmlns="http://www.w3.org/1999/xhtml"
style="background:conic-gradient(from 90deg,rgba(255, 119, 51, 0) 0deg,rgba(255, 119, 51, 0) 301.154deg,rgba(255, 152, 51, 1) 360deg);height:100%;width:100%;opacity:1"></div>
</foreignObject>
</g>
</g>
<path
d="M66.8719 146.144L64.8719 142.679L66.8719 146.144ZM66.8718 493.856L64.8718 497.32L66.8718 493.856ZM344 626.144L342 622.68L344 626.144ZM296 626.144L294 629.608L296 626.144ZM573.128 146.144L575.128 142.68L573.128 146.144ZM344 13.8564L346 10.3923L344 13.8564ZM296 13.8564L294 10.3923L296 13.8564ZM342 17.3205L571.128 149.608L575.128 142.68L346 10.3923L342 17.3205ZM593.128 187.713V452.287H601.128V187.713H593.128ZM571.128 490.392L342 622.68L346 629.608L575.128 497.32L571.128 490.392ZM298 622.68L68.8718 490.392L64.8718 497.32L294 629.608L298 622.68ZM46.8719 452.287V187.713H38.8719V452.287H46.8719ZM68.8719 149.608L298 17.3205L294 10.3923L64.8719 142.679L68.8719 149.608ZM46.8719 187.713C46.8719 171.993 55.2582 157.468 68.8719 149.608L64.8719 142.679C48.783 151.968 38.8719 169.135 38.8719 187.713H46.8719ZM68.8718 490.392C55.2582 482.532 46.8719 468.007 46.8719 452.287H38.8719C38.8719 470.865 48.783 488.032 64.8718 497.32L68.8718 490.392ZM342 622.68C328.386 630.539 311.614 630.539 298 622.68L294 629.608C310.089 638.897 329.911 638.897 346 629.608L342 622.68ZM593.128 452.287C593.128 468.007 584.742 482.532 571.128 490.392L575.128 497.32C591.217 488.032 601.128 470.865 601.128 452.287H593.128ZM571.128 149.608C584.742 157.468 593.128 171.993 593.128 187.713H601.128C601.128 169.135 591.217 151.968 575.128 142.68L571.128 149.608ZM346 10.3923C329.911 1.1034 310.089 1.10339 294 10.3923L298 17.3205C311.614 9.46066 328.386 9.46066 342 17.3205L346 10.3923Z"
data-figma-gradient-fill="{"type":"GRADIENT_ANGULAR","stops":[{"color":{"r":1.0,"g":0.46666666865348816,"b":0.20000000298023224,"a":0.0},"position":0.0},{"color":{"r":1.0,"g":0.46666669845581055,"b":0.20000000298023224,"a":0.0},"position":0.83653843402862549},{"color":{"r":1.0,"g":0.59999996423721313,"b":0.20000000298023224,"a":1.0},"position":1.0}],"stopsVar":[{"color":{"r":1.0,"g":0.46666666865348816,"b":0.20000000298023224,"a":0.0},"position":0.0},{"color":{"r":1.0,"g":0.46666669845581055,"b":0.20000000298023224,"a":0.0},"position":0.83653843402862549},{"color":{"r":1.0,"g":0.59999996423721313,"b":0.20000000298023224,"a":1.0},"position":1.0}],"transform":{"m00":1.0291984956336056e-13,"m01":640.0,"m02":1.8129725151694309e-13,"m10":-640.0,"m11":-1.8129725151694309e-13,"m12":640.0},"opacity":1.0,"blendMode":"NORMAL","visible":true}" />
<g filter="url(#filter0_f_210_160)">
<path
d="M309.608 18C314.226 10 325.774 10 330.392 18L586.736 462C591.355 470 585.581 480 576.344 480H63.6565C54.4189 480 48.6454 470 53.2642 462L309.608 18Z"
stroke="#8FCFEF" stroke-width="2" />
</g>
<defs>
<clipPath id="paint0_angular_210_160_clip_path">
<path
d="M66.8719 146.144L64.8719 142.679L66.8719 146.144ZM66.8718 493.856L64.8718 497.32L66.8718 493.856ZM344 626.144L342 622.68L344 626.144ZM296 626.144L294 629.608L296 626.144ZM573.128 146.144L575.128 142.68L573.128 146.144ZM344 13.8564L346 10.3923L344 13.8564ZM296 13.8564L294 10.3923L296 13.8564ZM342 17.3205L571.128 149.608L575.128 142.68L346 10.3923L342 17.3205ZM593.128 187.713V452.287H601.128V187.713H593.128ZM571.128 490.392L342 622.68L346 629.608L575.128 497.32L571.128 490.392ZM298 622.68L68.8718 490.392L64.8718 497.32L294 629.608L298 622.68ZM46.8719 452.287V187.713H38.8719V452.287H46.8719ZM68.8719 149.608L298 17.3205L294 10.3923L64.8719 142.679L68.8719 149.608ZM46.8719 187.713C46.8719 171.993 55.2582 157.468 68.8719 149.608L64.8719 142.679C48.783 151.968 38.8719 169.135 38.8719 187.713H46.8719ZM68.8718 490.392C55.2582 482.532 46.8719 468.007 46.8719 452.287H38.8719C38.8719 470.865 48.783 488.032 64.8718 497.32L68.8718 490.392ZM342 622.68C328.386 630.539 311.614 630.539 298 622.68L294 629.608C310.089 638.897 329.911 638.897 346 629.608L342 622.68ZM593.128 452.287C593.128 468.007 584.742 482.532 571.128 490.392L575.128 497.32C591.217 488.032 601.128 470.865 601.128 452.287H593.128ZM571.128 149.608C584.742 157.468 593.128 171.993 593.128 187.713H601.128C601.128 169.135 591.217 151.968 575.128 142.68L571.128 149.608ZM346 10.3923C329.911 1.1034 310.089 1.10339 294 10.3923L298 17.3205C311.614 9.46066 328.386 9.46066 342 17.3205L346 10.3923Z" />
</clipPath>
<filter id="filter0_f_210_160" x="46.6371" y="7" width="546.726" height="478"
filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_210_160" />
</filter>
</defs>
</svg>
当注释掉了第一个g元素的内容后,发现原来图形上的黄线不见了。让我们看看MDN上关于g元素的描述。g元素会组织其下的子元素,而子元素会继承它的属性。
那再看这个g元素做了什么事,它有一个clip-path属性,而它指向了当前svg中defs里的一段。clip-path是啥?defs又是啥呢?继续翻看MDN,clip-path会使用裁剪方式创建元素的可显示区域,区域内的部分显示,区域外的则会隐藏。而defs则是为了定义一些元素,这样我们后续可以在svg里重复使用。
那我们继续找到这一段,这里它是用clipPath元素,顾名思义,还是裁剪,而这段裁剪路径的path和原本六边形的路径完全相同。那么很明显,就是希望这个g元素能够裁剪出和这个六边形一样的形状出来。
那继续回去看这段:
<g clip-path="url(#paint0_angular_210_160_clip_path)" data-figma-skip-parse="true">
<g transform="matrix(0 -0.32 0.32 0 320 320)">
<foreignObject x="-1025" y="-1025" width="2050" height="2050">
<div xmlns="http://www.w3.org/1999/xhtml"
style="background:conic-gradient(from 90deg,rgba(255, 119, 51, 0) 0deg,rgba(255, 119, 51, 0) 301.154deg,rgba(255, 152, 51, 1) 360deg);height:100%;width:100%;opacity:1"></div>
</foreignObject>
</g>
</g>
transform="matrix(0 -0.32 0.32 0 320 320)"一眼便知是做了变换,于是我尝试修改了其中变换的值,惊喜地发现,黄线的位置变动了,而且正好还是在这图形内部。我以为我找到了答案,很可惜并没有,虽然它的确能动,但是在我反复改动这个值的时候,这根黄线时而长、时而短的情况,很显然我并不知道它应该做什么路径变化。
继续往里看,是foreignObject,它允许我们插入html元素。刚刚改transform不行,那现在改foreignObject的x和y可以吧?但却其实是一样的,因为不知道路径变化。
那就看看它到底引入了啥!
<div xmlns="http://www.w3.org/1999/xhtml"
style="background:
conic-gradient(from 90deg,
rgba(255, 119, 51, 0) 0deg,
rgba(255, 119, 51, 0) 301.154deg,
rgba(255, 152, 51, 1) 360deg);
height:100%;
width:100%;
opacity:1">
</div>
嗯,它引入了一个div,然后为其赋了一些样式。重点是这个conic-gradient,它是锥形渐变,渐变的颜色围绕一个中心点旋转,而不是从中心辐射。
这么看还是有点不太清楚,很简单,我们把这个background复制出来到一个div上就知道了。
哦,原来是长这样的啊,可这有什么用呢?有大用处,因为clip-path就是为了剪裁出它的颜色,而且需要渐变色(线的末端能看出来颜色变浅)。
知道这个的话,就知道该如何去让线移动起来,只要修改这个conic-gradient中各颜色的角度就行。但这时我又傻眼了,这玩意好像不太能用css动画一点点改角度吧?
于是我尝试去问Claude,如何在css动画中更改conic-gradient中的角度,而Claude给出的答案是不需要更改这个,只需要使用transform里的rotate即可实现。
的确,这样就相当于变相实现了去动态调整conic-gradient的角度。 下面放出了一个demo,可以试试看(不要在意代码的细节,这个掘金的代码段不太好用的说=。=)
结语
还是得继续努力学习呀!