IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    让路径动画更好用!CSS offset-path现在也支持基本形状了

    XboxYan发表于 2024-06-13 11:01:34
    love 0
    欢迎关注我的公众号:前端侦探

    大家有使用过offset-path吗?没用过不要紧,相信大家都见过这种酷炫的路径动画,这种就可以用offset-path来实现

    Kapture 2024-05-11 at 14.53.05

    demo 来源:https://codepen.io/ahmadbassamemran/pen/bXByBv

    随着 CSS的不断发展,最近在Chrome 116中,offset-path也支持基本形状了,也就是常见的inset、circle、polygon等等,有了这些形状的支持,路径动画写起来更加方便了,一起了解一下吧

    一、过去仅支持 path

    先简单介绍一下offset-path的用法。offset-path是用来实现路径动画的,所以前提是需要准备好路径。这里的路径可以在支持 SVG的设计软件中绘制,比如Figma

    image-20240511150146184

    这是我用钢笔工具随便勾勒的一条路径,先准备好放在一边。

    现在来一点布局

    <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;
    }

    现在效果如下

    image-20240511151720832

    没什么特别的,我们现在加上offset-path,如何添加呢?我们需要一段path路径,刚才我们在Figma上绘制的图形,可以直接导出SVG

    image-20240511162034634

    可以得到这样一段代码

    <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")
    }

    现在效果如下

    image-20240511162309671

    为啥是歪的呢?这是因为路径的起始位置就是这样,我们可以把这个 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>

    刚好位于起点处

    image-20240511170815021

    现在我们给个动画,让它从起点运动到终点,只需要改变offset-distance就行了

    .con::before{
      /* */
      animation: offset 3s linear infinite;
    }
    @keyframes offset {
      to {
        offset-distance: 100%;
      }
    }

    这样就得到了一个最基础的路径动画了

    Kapture 2024-05-11 at 17.11.22

    是不是非常简单呢?

    二、path 的局限性

    前面的path虽然灵活,但是不好维护,而且一些基本形状也必须要转成path才行。

    比如,要沿着一个圆形来运动,我们可以在Figma中绘制一个圆

    image-20240511172311876

    如果我们直接复制这个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将图形“扁平化”,也就是让这个形状失去基本形状特性,变成一个普通的路径

    image-20240511172550760

    这样我们就能得到带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就无法实现这样的效果

    image-20240511173005059

    因此,为了解决这样的问题,现在也支持基本形状了

    三、现在支持基本形状了

    所谓基本形状,就是一种表现基础图形的 CSS 数据类型,适用于clip-path、shape-outside和offset-path

    https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape

    其实就是这几类

    1. 圆 circle()
    2. 椭圆 ellipse()
    3. 矩形 inset()、rect()、xywh()
    4. 多边形 polygon()

    这里面有些大家可能已经在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表示距离边缘最远时的半径,这一点和径向渐变中是类似的

    image-20240511190348067

    实际使用来看看,还是上面的结构

    <div class="con circle">

    给伪元素一个offset-path

    .circle::before{
      offset-path: circle(); /*默认 closest-side */
      animation: offset 3s linear infinite;
    }

    效果如下

    Kapture 2024-05-11 at 19.12.33

    而且这个路径是自适应的,可以自动跟随外部容器的变化而变化,比如将这个高度改小一些

    Kapture 2024-05-11 at 19.14.30

    是不是比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;
    }

    效果如下

    Kapture 2024-05-11 at 19.23.51

    3.inset

    inset表示矩形,并且支持圆角

    inset( <length-percentage>{1,4} [ round <`border-radius`> ]? )

    前面有 4 个值,分别表示距离上、右、下、左的距离,如下

    image-20240511193510098

    并且支持圆角,这样要实现一个圆角矩形的路径动画就很方便了

    <div class="con inset"></div>
    .inset::before{
      offset-path: inset(20px round 16px);
    }

    效果如下

    Kapture 2024-05-11 at 19.37.20

    还有两个函数,rect()和xywh()也能实现矩形,只是方式不一样,这个以后再做介绍

    4. polygon

    这个相信大家都很熟悉了,用来绘制多边形的

    比如我们要绘制一个三角形,只需要指定三个点就行了,如下

    image-20240511194601032

    在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%);
    }

    效果如下

    Kapture 2024-05-11 at 19.51.54

    5.其实也还支持 url

    顺便介绍一下,和基本形状同时推出的还有 url()支持(和 clip-path 一样)。

    可以直接用一段 svg作为路径偏移,更加直观。比如在 Figma 中绘制一个五角星

    image-20240512162152367

    我们直接复制出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了,效果如下

    Kapture 2024-05-12 at 16.20.08

    以上所有demo可以查看以下链接

    • CSS offset-path (codepen.io)')

    几个基本形状就这些了,接下来看一个实际应用

    四、圆弧菜单展开效果

    有了基本形状的支持,可以很方便的实现一些有意思的效果,比如这样的菜单展开

    Kapture 2024-05-12 at 14.58.38

    其实就是一个圆形路径动画。首先来看结构

    <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 来控制打开和收起的,不清楚popover的可以参考之前这篇文章:原生 popover 终于来了!

    我们给每个子菜单加上路径偏移 offset-path

    .item {
      offset-path: circle(80px);
    }

    效果如下

    image-20240512151946922

    为啥只有一个子菜单呢?这是因为所有的菜单都重叠在了一起,我们需要分散开来。

    由于圆形路径动画是顺时针方向的,就像这样

    Kapture 2024-05-11 at 19.12.33

    所以我们需要将 5 个子元素平均分配到半个圆弧上,如下

    image-20240512152548634

    用代码实现就是

    .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后,就变成了这样

    image-20240512153231160

    最后,只要在打开菜单时设置不同的延时,如下

    .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;
    }

    就能得到我们想要的展开效果了

    Kapture 2024-05-12 at 14.58.38

    完整代码可以查看:Radial Menu Popover remix using offset-path: circle() (codepen.io) (codepen.io)')

    五、兼容性和总结

    这个是 Chrome 116推出的新特性,目前还不是特别好,尤其是Safari拖了后腿,兼容性如下

    image-20240512162449759

    所以大规模使用还是需要等待一段时间,下面总结一下本文要点

    1. 酷炫的路径动画可以用offset-path来实现
    2. 之前仅支持 path(),虽然灵活,但是不好维护,不直观,而且一些基本形状也必须要转成path才行
    3. path()不支持自适应尺寸
    4. 基本形状是一种表现基础图形的 CSS 数据类型,适用于clip-path、shape-outside和offset-path,语法都是通用的
    5. 基本形状主要有 circle()、ellipse()、inset()、polygon()
    6. 现在还在支持 url(),可以直接用一段 svg作为路径偏移,更加直观

    关注我,学习更多有趣的前端交互小技巧。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发 ❤❤❤



沪ICP备19023445号-2号
友情链接