CSS offset-path现在也支持基本形状了
随着 CSS
的不断发展,最近在Chrome 116
中,offset-path
也支持基本形状了,也就是常见的inset
、circle
、polygon
等等,有了这些形状的支持,路径动画写起来更加方便了,一起了解一下吧
一、过去仅支持 path
简单介绍一下offset-path
的用法。offset-path
是用来实现路径动画的,所以前提是需要准备好路径。这里的路径可以在支持 SVG
的设计软件中绘制,比如Figma
:
这是我用钢笔工具随便勾勒的一条路径,先准备好放在一边。
现在来一点布局:
<div class="con path"> </div>
我们用伪元素来作为偏移路径的元素:
.con{ position: relative; width: 300px; height: 200px; background-color: #FFEFC5; border-radius: 8px; flex-shrink: 0; } .con::before{ position: absolute; content: '😁'; width: 40px; height: 40px; border-radius: 8px; display: grid; place-content: center; background-color: #3E65FF; color: #fff; z-index: 2; }
此时效果:
没什么特别的,我们现在加上offset-path
,如何添加呢?我们需要一段path
路径,刚才我们在Figma
上绘制的图形,可以直接导出SVG
。
可以得到这样一段代码:
<svg width="300" height="200" viewBox="0 0 300 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 39.567C36.479 34.0777 82.9341 33.7783 104.922 76.4954C132.407 129.892 117.317 157.339 192.766 154.844C253.126 152.848 280.072 87.1417 286 54.5383" stroke="black" stroke-width="7"/> </svg>
我们只需要将path
里面的d
属性值拿出来就行了,就像这样:
.path::before{ offset-path: path("M16 39.567C36.479 34.0777 82.9341 33.7783 104.922 76.4954C132.407 129.892 117.317 157.339 192.766 154.844C253.126 152.848 280.072 87.1417 286 54.5383") }
现在效果如下:
为啥是歪的呢?这是因为路径的起始位置就是这样,我们可以把这个 SVG
也放到 html
中,顺便改一下描边。
<div class="con path"> <svg width="300" height="200" viewBox="0 0 300 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M16 39.567C36.479 34.0777 82.9341 33.7783 104.922 76.4954C132.407 129.892 117.317 157.339 192.766 154.844C253.126 152.848 280.072 87.1417 286 54.5383" stroke="#FF336F" stroke-dasharray="2 2"/> </svg> </div>
刚好位于起点处:
现在我们给个动画,让它从起点运动到终点,只需要改变offset-distance
就可以了:
.con::before{ /* */ animation: offset 3s linear infinite; } @keyframes offset { to { offset-distance: 100%; } }
这样就得到了一个最基础的路径动画:
二、path 的局限性
前面的path
虽然灵活,但是不好维护,而且一些基本形状也必须要转成path
才行。
比如,要沿着一个圆形来运动,我们可以在Figma
中绘制一个圆:
如果我们直接复制这个SVG
会得到这样一段代码:
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="100" cy="100" r="100" fill="#FFD75A"/> </svg>
Figma
还是挺聪明的,自动识别到了这是一个圆,所以得到了circle
这个标签。但这种结构之前是无法使用的,我们需要的是path
,因此要转一下。
在Figma
中,可以用Flatten
将图形“扁平化”,也就是让这个形状失去基本形状特性,变成一个普通的路径。
这样我们就能得到带path
的SVG
代码了:
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M200 100C200 155.228 155.228 200 100 200C44.7715 200 0 155.228 0 100C0 44.7715 44.7715 0 100 0C155.228 0 200 44.7715 200 100Z" fill="#FFD75A"/> </svg>
然后用在offset-path
中:
.path::before{ offset-path: path("M200 100C200 155.228 155.228 200 100 200C44.7715 200 0 155.228 0 100C0 44.7715 44.7715 0 100 0C155.228 0 200 44.7715 200 100Z") }
虽然也能实现,但是一眼看上去,完全不知道是什么形状。
其次,path
还有一个问题,就是不支持自适应尺寸,因为里面的值都是固定的,无法动态去改变,比如我们希望这个圆能尽可能大的撑满整个容器,path
就无法实现这样的效果。
因此,为了解决这样的问题,现在也支持基本形状了
三、现在支持基本形状了
所谓基本形状,就是一种表现基础图形的 CSS
数据类型,适用于clip-path
、shape-outside
和offset-path
其实就是这几类
这里面有些大家可能已经在clip-path
中用到过了,所以这里不会详细介绍每个语法的详细用法,有兴趣可以在官网自行查看。
1. circle
首先来看圆。语法很简单
circle( <shape-radius>? [ at <position> ]? )
前面的shape-radius
是圆的半径,可以是长度单位或者百分比,还支持closest-side
和farthest-side
关键词,后面的position
表示圆心位置(默认居中)。
offset-path: circle(50px); offset-path: circle(20% at right center); offset-path: circle(closest-side); offset-path: circle(farthest-side);
其中,closest-side
表示距离边缘最近时的半径,farthest-side
表示距离边缘最远时的半径,这一点和径向渐变中是类似的:
实际使用来看看,还是上面的结构:
<div class="con circle">
给伪元素一个offset-path
:
.circle::before{ offset-path: circle(); /*默认 closest-side */ animation: offset 3s linear infinite; }
效果如下:
而且这个路径是自适应的,可以自动跟随外部容器的变化而变化,比如将这个高度改小一些:
是不是比path
实现要灵活很多呢?
2. ellipse
椭圆和圆比较类似,只是多了一个半径,就不赘述了。
ellipse( [ <shape-radius>{2} ]? [ at <position> ]? )
我们直接看代码:
<div class="con ellipse">
.ellipse::before{ offset-path: ellipse(); /*默认 closest-side closest-side */ animation: offset 3s linear infinite; }
效果如下:
3.inset
inset
表示矩形,并且支持圆角。
inset( <length-percentage>{1,4} [ round <`border-radius`> ]? )
前面有 4 个值,分别表示距离上、右、下、左的距离,如下:
并且支持圆角,这样要实现一个圆角矩形的路径动画就很方便了。
<div class="con inset"></div>
.inset::before{ offset-path: inset(20px round 16px); }
效果如下:
还有两个函数,rect()
和xywh()
也能实现矩形,只是方式不一样,这个以后再做介绍
4. polygon
这个相信大家都很熟悉了,用来绘制多边形的。
比如我们要绘制一个三角形,只需要指定三个点就行了,如下:
在offset-path
中也是如此:
<div class="con polygon"> <svg width="300" height="200" viewBox="0 0 300 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <polygon points="150,0 300,200 0,200" stroke="#FF336F" stroke-dasharray="2 2"/> </svg> </div>
.polygon::before{ offset-path: polygon( 50% 0,100% 100%, 0 100%); }
效果如下:
5.其实也还支持 url
顺便介绍一下,和基本形状同时推出的还有 url()
支持(和 clip-path
一样)。
可以直接用一段 svg
作为路径偏移,更加直观。比如在 Figma
中绘制一个五角星:
我们直接复制出SVG
放到页面上:
<div class="con url"> <svg width="300" height="200" viewBox="0 0 300 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <path id="svgPath" d="M64.8437 68.8473C65.2788 68.5288 65.7911 68.2949 66.3695 68.1793L66.4675 68.6697C66.6996 68.6233 66.9456 68.5983 67.2051..." fill="#C1A4FF" fill-opacity="0.76" stroke="#FF336F" stroke-dasharray="2 2"/> </svg> </div>
给这段 path
一个 id
为svgPath
,然后可以直接这么使用:
.url::before{ offset-path: url('#svgPath'); }
这样一来,SVG
既可以用于展示,又可以用于offset-path
了,效果如下:
以上所有 demo 可以查看以下链接:
几个基本形状就这些了,接下来看一个实际应用。
四、圆弧菜单展开效果
有了基本形状的支持,可以很方便的实现一些有意思的效果,比如这样的菜单展开
其实就是一个圆形路径动画。首先来看结构:
<button class="menu-toggle" id="menu-toggle" popovertarget="menu-items"> ➕ </button> <menu class="menu-items" id="menu-items" popover anchor="menu-toggle"> <li class="item"> <button>♥️</button> </li> <li class="item"> <button>💾</button> </li> <li class="item"> <button>🔗</button> </li> <li class="item"> <button>✉️</button> </li> <li class="item"> <button>🛒</button> </li> </menu>
这里是通过 popover
来控制打开和收起的。
我们给每个子菜单加上路径偏移 offset-path
:
.item { offset-path: circle(80px); }
效果如下:
为啥只有一个子菜单呢?这是因为所有的菜单都重叠在了一起,我们需要分散开来。
由于圆形路径动画是顺时针方向的,就像这样:
所以我们需要将 5 个子元素平均分配到半个圆弧上,如下:
用代码实现就是:
.item:nth-child(1) { offset-distance: 100%; } .item:nth-child(2) { offset-distance: 87.5%; } .item:nth-child(3) { offset-distance: 75%; } .item:nth-child(4) { offset-distance: 62.5%; } .item:nth-child(5) { offset-distance: 50%; }
给每个元素分别设置不同的offset-distance
后,就变成了这样:
最后,只要在打开菜单时设置不同的延时,如下:
.menu-items:not(:popover-open) { .item:nth-child(1) { --delay: 0s; } .item:nth-child(2) { --delay: 0.1s; } .item:nth-child(3) { --delay: 0.2s; } .item:nth-child(4) { --delay: 0.3s; } .item:nth-child(5) { --delay: 0.4s; }
就能得到我们想要的展开效果了
完整代码可以查看:offset-path: circle()
五、兼容性和总结
这个是 Chrome 116
推出的新特性,目前还不是特别好,尤其是Safari
拖了后腿,兼容性如下:
所以大规模使用还是需要等待一段时间,下面总结一下本文要点
- 酷炫的路径动画可以用
offset-path
来实现 - 之前仅支持
path()
,虽然灵活,但是不好维护,不直观,而且一些基本形状也必须要转成path
才行 path()
不支持自适应尺寸- 基本形状是一种表现基础图形的
CSS
数据类型,适用于clip-path
、shape-outside
和offset-path
,语法都是通用的 - 基本形状主要有
circle()
、ellipse()
、inset()
、polygon()
- 现在还在支持
url()
,可以直接用一段svg
作为路径偏移,更加直观
原文:链接
码云笔记 » CSS offset-path现在也支持基本形状了