CSS
CSS小黄鸭动画
有道无术,术尚可求。有术无道,止于术!
-
+
首页
CSS小黄鸭动画
做为前端工程师,最大的快乐之一就是可以用 CSS 画出各种有趣的效果。 画的过程中确实很开心,这也是我当时选择做前端的很大一部分原因。 今天我们就一起来画下这个可爱的 Button 吧!纯 CSS,没用到图片和 JS 呦~ 首先我们需要一些前置知识: ## **border-radius** border-radius 大家用的比较多了。 比如一个这样的 div: ![img](https://pic4.zhimg.com/80/v2-2aacf13b269532d0273c1fd8eeedd1d7_720w.webp) ```css #box { width: 100px; height: 100px; background: #f4cf47; border: solid 3px #000; } ``` 可以分别设置四个圆角的半径: ```css #box { border-radius: 20px 30px 40px 50px; } ``` ![img](https://pic3.zhimg.com/80/v2-5f404fcdc389b975937a03cb57a5688e_720w.webp) 从效果上也可以看出来分别是左上、右上、左下、右下的圆角。 其实还可以设置椭圆角,椭圆和圆的区别是圆的半径都是一样的,而椭圆则是有长轴和短轴,可以不一样。 可以分别设置长半轴和短半轴的长度,用 / 隔开: ```css #box { border-radius: 20px 30px 40px 50px / 20px 30px 40px 50px; } ``` 当然,上面这个长短半轴相等了,也就是圆角了。 比如这样设置: ```css #box { border-radius: 20px 30px 40px 50px / 50px 40px 30px 20px; } ``` 效果是这样的: ![img](https://pic3.zhimg.com/80/v2-45ce79cff5ae323cb6bf426bb180da56_720w.webp) 分开看每个角: 左上角的横半轴是 20px,竖半轴是 50px,所以是这样的: ![img](https://pic1.zhimg.com/80/v2-73a2229d3fbe95a60b44802046eb5ef4_720w.webp) 左下角横半轴是 40px,竖半轴是 30px,所以是这样的: ![img](https://pic3.zhimg.com/80/v2-03f54f7a628d0c8b3ad04a20cf2ae00e_720w.webp) 通过调整四个角的横竖半轴长度,就可以实现很多形状。 比如三只小鸟的睡觉时的形状: ![img](https://pic4.zhimg.com/80/v2-aea2320c11b6ab4be328c545bb93f09b_720w.webp) 或者醒的时候的形状: ![img](https://pic2.zhimg.com/80/v2-c409e1e7f8648e10afe2e83148456181_720w.webp) 还有鸟嘴和眼睛的形状: ![img](https://pic1.zhimg.com/80/v2-a61851c6cdd2e37f1a80badcc7bddd08_720w.webp) 当然,圆角能画的形状终究还是受限制的,更复杂的形状需要用别的方式来画,比如 clip-path: ## **clip-path** 前面说过,整个按钮都没有用到图片,那按钮的这个背景: ![img](https://pic3.zhimg.com/80/v2-950696ba0085fdadd85b46977e645492_720w.webp) 还有这几根鸟毛: ![img](https://pic3.zhimg.com/80/v2-affd66d55005a549161427288a5739aa_720w.webp) 怎么画呢? border-radius? border-radius 再怎么调也只是各种椭圆,没法画这种复杂形状。 想实现这些没有规律的复杂形状就要用到 cli-path 了。 比如这样一个 div: ![img](https://pic1.zhimg.com/80/v2-71fc696899938e4b0bd1364f2288eb94_720w.webp) ```css #box { width: 130px; height: 38px; background: blue; } ``` 加上这样一个 clip-path: ```css #box { clip-path : path('M13.77,37.35L.25,16.6c-.87-1.33,.69-2.91,2-2.02l12.67,8.59c.81,.55,1.91,.14,2.18-.81l2.62-9.33c.39-1.4,2.34-1.42,2.76-.02l3.6,11.99c.33,1.11,1.74,1.4,2.47,.52L49.38,.52c.87-1.04,2.53-.42,2.53,.95V23.7c0,1.13,1.2,1.83,2.16,1.26l12.75-7.51c.85-.5,1.94,0,2.13,.98l1.5,7.6c.2,1.03,1.37,1.51,2.22,.92l17.74-12.3c1.09-.75,2.52,.25,2.21,1.55l-2.44,10.2c-.26,1.09,.74,2.06,1.8,1.75l30.8-9.04c1.37-.4,2.42,1.26,1.49,2.36l-9.07,10.66c-.83,.98-.1,2.49,1.17,2.42l12.12-.68c1.6-.09,2.12,2.15,.65,2.8l-2.73,1.21c-.18,.08-.38,.12-.58,.12H14.97c-.48,0-.93-.25-1.2-.65Z') ; } ``` 就会变成这种形状: ![img](https://pic3.zhimg.com/80/v2-c702e70933fa812c426d7a4487f1216a_720w.webp) 或者这样一个 div: ![img](https://pic4.zhimg.com/80/v2-2d77497a0870b56d0269b7ebd07b081b_720w.webp) ```css #box { width: 12px; height: 12px; background: #000; } ``` 加上这样的 clip-path: ```css #box { clip-path : path('M10.23,3.32c-3.54,.63-5.72,2.51-7.02,4.23-.33-1.58-.34-3.54,.93-5.12,.52-.65,.41-1.59-.24-2.11C3.24-.19,2.29-.08,1.77,.57c-3.82,4.77-.31,11.11-.13,11.42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0-.01-.02,2.49,.04,2.52,0,.1-.14,1.54-4.82,6.59-5.71,.82-.14,1.37-.92,1.22-1.74s-.94-1.36-1.75-1.21Z') ; } ``` 就会变成这种形状: ![img](https://pic1.zhimg.com/80/v2-105da14ad8fc4d49e891703b36b87c18_720w.webp) 背景和鸟毛不就出来了么~ 这个样式还是很容易理解的,就像剪纸一样,把一块区域按照某个路径进行裁剪(svg 中也有这个)。 当然,不只可以写 path,还支持别的形状: ```css clip-path: circle(40%); clip-path: ellipse(130px 140px at 10% 20%); clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); clip-path: path('M 0 200 L 0,75 A 5,5 0,0,1 150,75 L 200 200 z'); ``` 但是,这个形状怎么来呢? 手写么? 这肯定不靠谱。 其实是可以用 illustrator 等矢量图设计软件来画,导出为 svg 的。 比如 illustrator,用钢笔在左上角画一个形状: ![img](https://pic1.zhimg.com/80/v2-53fefc4c7fd022686c5507ebd03b796c_720w.webp) 选中它,然后点击“对象 > 复合路径 > 建立”: ![img](https://pic4.zhimg.com/80/v2-bf13a20dd121aa44906facd7d7b0370f_720w.webp) 再点击导出,这时候导出的就是带 path 的 svg。 有两点需要注意: - 要从左上角开始画,因为 svg 会把位移保存下来。 - 没有复合路径那一步,导出的可能是 polygon、circle 这种形状,而不是 path ![img](https://pic3.zhimg.com/80/v2-fdea99f1665d279de5c40953d3b7d966_720w.webp) 把路径复制到 cli-path 的样式处,就可以看到裁切后的效果了: ![img](https://pic1.zhimg.com/80/v2-996785b4d7e8c1996d694b11457842fc_720w.webp) 这样,涉及到的各种形状我们就都能画了。 再看一眼这个 Button: ![动图封面](https://pic2.zhimg.com/v2-95967e9e8ef01b8895c1d1d711f3f001_b.jpg) 是不是就有思路了呢~ 接下来我们动手画一下吧。 ## **画 Button** 先写出 html 的结构: ```html <a href="#" class="button button--bird"> <div class="button__wrapper"> <span class="button__text">ENTRY</span> </div> </a> ``` 比一般的 button 多了一级,这是因为我们要通过每一级的 ::before 和 ::after 加一些伪元素。 ![img](https://pic1.zhimg.com/80/v2-d120e2853d44e50d3d7df32d640dc6e4_720w.webp) 设置 .button 的样式: ```css .button { display: flex; justify-content: center; align-items: center; box-sizing: border-box; width: 280px; height: 80px; text-decoration: none; border: solid 3px #000; border-radius: 40px; background: #f4cf47; } ``` 通过 flex 布局,让子元素居中。设置 width、height,背景颜色、圆角等。 现在效果是这样的: ![img](https://pic2.zhimg.com/80/v2-3e90042cb0691b6fc72253a71e4a0731_720w.webp) 其中的背景颜色可能会变,更好的方式是抽出一个变量来维护: ```css .button--bird { --main_color: #f4cf47; } .button { background : var(--main_color); } ``` 然后设置子元素的样式,也就是 .button__warpper: ```css .button__wrapper{ display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; } ``` 它同样要设置子元素居中,然后宽高都是 100%。 它的子元素就是 span 文本标签了,也就是 .button__text: ```css .button--bird { --base_color: #000; } .button__text{ font-size: 32px; letter-spacing: 4px; color: var(--base_color); } ``` 指定字体大小、文字间距和颜色。颜色是可能会变的,所以也抽成变量。 现在的效果是这样的: ![img](https://pic2.zhimg.com/80/v2-452b5583c04a20b44b6edab4afe440ad_720w.webp) 然后我们加上一些伪元素做装饰: 先给最外层元素加: ```css .button::before{ content: ''; position: absolute; right: 20px; margin: auto 0; width: 24px; height: 24px; background: var(--base_color); clip-path: path('M24,12.02c0-1.09-.75-1.71-.81-1.77L11.17,.45c-.91-.74-2.21-.56-2.91,.42-.69,.97-.52,2.37,.39,3.11l7.12,5.81-13.7-.02h0C.93,9.77,0,10.76,0,11.99c0,1.23,.93,2.22,2.07,2.22l13.7,.02-7.13,5.78c-.91,.74-1.09,2.13-.4,3.11,.41,.58,1.03,.88,1.65,.88,.44,0,.88-.15,1.25-.45l12.04-9.76c.07-.06,.82-.67,.82-1.77Z'); } ``` 指定宽高和位置,还有背景颜色,再就是裁切的形状(这里用 clip-path 裁剪的是一个箭头的形状) 伪元素用了绝对定位,那元素就要相对定位: ```text .button { position: relative; } ``` 也就是让伪元素相对它来偏移。 效果就是这样的: ![img](https://pic3.zhimg.com/80/v2-8054aaf6ff5483a713234aabdaa09a86_720w.webp) 然后再往里一层,给 .button__wrapper 加 ::before 和 ::after 伪元素: ```css .button--bird { --sub_color1: #f4e19c; } .button--bird .button__wrapper::before, .button--bird .button__wrapper::after{ content: ''; position: absolute; bottom: 0; width: 130px; height: 38px; background: var(--sub_color1); clip-path: path('M13.77,37.35L.25,16.6c-.87-1.33,.69-2.91,2-2.02l12.67,8.59c.81,.55,1.91,.14,2.18-.81l2.62-9.33c.39-1.4,2.34-1.42,2.76-.02l3.6,11.99c.33,1.11,1.74,1.4,2.47,.52L49.38,.52c.87-1.04,2.53-.42,2.53,.95V23.7c0,1.13,1.2,1.83,2.16,1.26l12.75-7.51c.85-.5,1.94,0,2.13,.98l1.5,7.6c.2,1.03,1.37,1.51,2.22,.92l17.74-12.3c1.09-.75,2.52,.25,2.21,1.55l-2.44,10.2c-.26,1.09,.74,2.06,1.8,1.75l30.8-9.04c1.37-.4,2.42,1.26,1.49,2.36l-9.07,10.66c-.83,.98-.1,2.49,1.17,2.42l12.12-.68c1.6-.09,2.12,2.15,.65,2.8l-2.73,1.21c-.18,.08-.38,.12-.58,.12H14.97c-.48,0-.93-.25-1.2-.65Z'); } .button--bird .button__wrapper::before{ left: 0; } .button--bird .button__wrapper::after{ right: 0; transform : rotateY(180deg) ; } ``` 分别在前后加上一个伪元素,设置宽高和背景色,裁切的形状。然后分别设置不同的位移,右边要反过来,所以是 rotateY(180deg)。 背景色也可能变,所以抽出一个变量。 这里伪元素用到了 position:absolute,同样要在元素上加上 position: relative; ```css .button__wrapper { position: relative; } ``` 效果就是这样的: ![img](https://pic3.zhimg.com/80/v2-c61576aca574f34ad098ca29dbd71612_720w.webp) 然后我们先加一些 hover 的效果吧,毕竟也算一个完整的 Button 了。 hover 的时候,让字体间距变大,两个草丛背景分别左右移动,箭头往右移动: ```css .button:hover .button__text{ letter-spacing : 6px ; } .button:hover .button__wrapper::before{ transform : translateX(-12px) ; } .button:hover .button__wrapper::after{ transform : rotateY(180deg) translateX(-12px) ; } .button:hover::before{ right : 14px ; } ``` 效果是这样的: ![动图封面](https://pic3.zhimg.com/v2-491c4d5544372261d8823fba4f52d2aa_b.jpg) 过渡有点太生硬了,设置下 transition: ```css .button__text { transition: all .3s ease; } .button::before { transition: all .2s ease; } .button__wrapper::before, .button__wrapper::after { transition: all .5s ease; } ``` 文字 3s,箭头 2s,草丛 5s,都是匀速的过渡。 ![动图封面](https://pic2.zhimg.com/v2-b8826c8481a81b17bc13b8d2e6de18d9_b.jpg) 这样就自然多了。 接下来就是最有意思的部分了:画三只小鸟。 在按钮的 div 的下方,再加一点 div: ```html <a href="#" class="button button--bird"> <div class="button__wrapper"> <span class="button__text">ENTRY</span> </div> <div class="birdBox"> <div class="bird"> <div class="bird__face"></div> </div> <div class="bird"> <div class="bird__face"></div> </div> <div class="bird"> <div class="bird__face"></div> </div> </div> </a> ``` 一个 birdBox 元素包含着 3 个 bird 的子元素,它还有一层子元素。 设置这些层当然也是为了利用每一层的 ::before、::after 来画一些东西: ![img](https://pic2.zhimg.com/80/v2-a665c86a60f7e3a9e745905eac797e19_720w.webp) 先设置 .birdBox 的样式: ```css .birdBox { position: absolute ; top: -54px ; display: flex ; justify-content: space-between ; align-items: flex-end ; width: 180px ; height: 56px ; } ``` 设置宽高和绝对定位的位置,大概是在这里: ![img](https://pic1.zhimg.com/80/v2-95de65cbff958bd2e4621670011bad38_720w.webp) 并且设置内容水平平分剩余空间,竖直居中。 然后设置子元素也就是每只小鸟的样式: ```css .bird{ width: 56px ; height: 36px ; box-sizing: border-box ; border: solid 3px #000 ; background: var(--main_color) ; } ``` ![img](https://pic4.zhimg.com/80/v2-9af4e1489ff1447e9daf220b47fa444b_720w.webp) 设置圆角: ```css .button__bird { --border_radius1: 60px 60px 40px 40px / 48px 48px 30px 30px ; } .bird { border-radius: var(--border_radius1) ; } ``` 这里也作为变量抽出来。 分别设置 4 个角的横竖半轴长度,调整得到这样的形状: ![img](https://pic4.zhimg.com/80/v2-b2de4054b9bf841cc11ec80259c0a187_720w.webp) 然后通过 ::before 伪元素画上鸟毛: ```css .bird { position: relative; } .bird::before{ content: '' ; position: absolute ; top: -12px ; left: 22px ; width: 12px ; height: 12px ; background : #000 ; clip-path : path('M10.23,3.32c-3.54,.63-5.72,2.51-7.02,4.23-.33-1.58-.34-3.54,.93-5.12,.52-.65,.41-1.59-.24-2.11C3.24-.19,2.29-.08,1.77,.57c-3.82,4.77-.31,11.11-.13,11.42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0-.01-.02,2.49,.04,2.52,0,.1-.14,1.54-4.82,6.59-5.71,.82-.14,1.37-.92,1.22-1.74s-.94-1.36-1.75-1.21Z') ; } ``` 这种不规则形状当然也是通过 clip-path 裁剪出来的。 ![img](https://pic1.zhimg.com/80/v2-b2439665c5e660933ef9c0ff45ca9660_720w.webp) 然后再画上嘴: 在子元素 .bird__face 上画: ```css .button--bird{ --sub_color2: #ff8108 ; } .bird__face{ position: absolute ; top: 15px ; width: 12px ; height: 6px ; background: var(--sub_color2) ; border-radius: 50% 50% 50% 50% / 78% 78% 22% 22% ; } ``` 设置宽高和定位,背景颜色和圆角,就可以得到鸟嘴: ![img](https://pic1.zhimg.com/80/v2-9f35ecf4278243efa50877f0029cf6bc_720w.webp) 要居中的话可以在它的父元素设置 display: flex: ```css .bird { display: flex; justify-content: center; } ``` ![img](https://pic3.zhimg.com/80/v2-4305c827f412f4ec08502b7b0b098ce6_720w.webp) 然后再通过 ::before 和 ::after 伪元素来画两只眼睛: ```css .bird__face::before, .bird__face::after{ content: '' ; position: absolute ; top: -4px ; width: 8px ; height: 2px ; border-radius: 4px ; background: #000 ; } .bird__face::before{ left: -5px ; } .bird__face::after{ right: -5px ; } ``` 设置定位、宽高、位置、圆角、背景颜色即可: ![img](https://pic1.zhimg.com/80/v2-c5af5b0103167792cb1572cae12f178c_720w.webp) 至此,睡着的小鸡就画完了! 核心就是就是元素和伪元素通过定位 + flex 来布局,然后通过 border-radius 和 clip-path 设置形状。 接下来画睡醒以后的小鸡: 只有前两只会醒,所以给它俩单独加个 wakeup 的 class: ![img](https://pic2.zhimg.com/80/v2-f22a2975635e2a166ec913d7ea2542c1_720w.webp) ```html <div class="birdBox"> <div class="bird wakeup"> <div class="bird__face"></div> </div> <div class="bird wakeup"> <div class="bird__face"></div> </div> <div class="bird"> <div class="bird__face"></div> </div> </div> ``` 当 hover 的时候,wakeup 的小鸡会执行伸展身体的动画: ![动图封面](https://pic2.zhimg.com/v2-be5bf5fbdd99ddb0e0f4adba7d3ab541_b.jpg) 这个动画里变的是什么呢? 明显是 height 变了,我们设置下: ```css .button:hover .wakeup{ animation: wakeup .2s ease ; } @keyframes wakeup{ 0% { height : 32px; } 100%{ height : 56px ; } } ``` ![动图封面](https://pic3.zhimg.com/v2-a6d71b7ea5019def05efe423ac9eef36_b.jpg) 这样有两个问题,一个是动画没有停住,一执行完就回去了,这个可以设置 animation-fill-mode 来解决: ```css .button:hover .wakeup{ animation : wakeup .2s ease ; animation-fill-mode : forwards ; } ``` 设置 animation-fill-mode 为 forwards 就是停留在最后一帧的意思: ![动图封面](https://pic3.zhimg.com/v2-2e7c6298dd550725beb08d67cb6b04da_b.jpg) 还有一个问题就是鸟高度变大后,圆角也得重新设置下,不然形状很奇怪: ```css .button--bird { --border_radius2: 70px 70px 40px 40px / 48px 48px 30px 30px ; --border_radius3: 40px 40px 40px 40px / 48px 48px 30px 30px ; } @keyframes wakeup{ 0% { height: 32px; border-radius: var(--border_radius2) ; } 100%{ height: 56px ; border-radius: var(--border_radius3) ; } } ``` 这样就好多了: ![动图封面](https://pic4.zhimg.com/v2-052e2ddf1aca6a5ce7d646be213608f3_b.jpg) 再就是醒来以后眼睛也得睁开,还要眨眼,这个怎么做呢? 看下分解动作就明白了: ```css .button:hover .wakeup .bird__face::before, .button:hover .wakeup .bird__face::after{ width: 6px; height: 6px; } ``` 设置宽高都为 6px,这样就是睁开眼睛的效果: ![动图封面](https://pic4.zhimg.com/v2-a79361598414b1280cfb1846e1baf92f_b.jpg) 瞬间变精神小伙。 这样是之前闭着眼睛的效果: ```css .button:hover .wakeup .bird__face::before, .button:hover .wakeup .bird__face::after{ width: 8px; height: 2px; } ``` ![动图封面](https://pic3.zhimg.com/v2-73e43d26c80995634d6217ce87064542_b.jpg) 连起来设置几个关键帧,那就是眨眼动画了: ```css @keyframes eye { 0%{ top : -6px ; width : 6px ; height : 6px ; } 30%{ top : -6px ; width : 6px ; height : 6px ; } 32%{ top : -4px ; width : 8px ; height : 2px ; } 34%{ top : -6px ; width : 6px ; height : 6px ; } 70%{ top : -6px ; width : 6px ; height : 6px ; } 72%{ top : -4px ; width : 8px ; height : 2px ; } 74%{ top : -6px ; width : 6px ; height : 6px ; } 76%{ top : -4px ; width : 8px ; height : 2px ; } 78%{ top : -6px ; width : 6px ; height : 6px ; } 100%{ top : -6px ; width : 6px ; height : 6px ; } } ``` 当然睁眼以后高度变高,top 也得调整下。 然后应用这个动画: ```css .button:hover .wakeup .bird__face::before, .button:hover .wakeup .bird__face::after{ animation : eye 5s linear infinite ; } ``` 5s 内匀速执行动画,无限次执行。 试一下: ![动图封面](https://pic4.zhimg.com/v2-9726acf7558a3620bb9727ab8b979563_b.jpg) 瞬间就有灵气了。 都一样的动作显得有点呆,我们让第二只鸟往右边看一下。 也就是这样的动画: ```css @keyframes eye_2 { 0%{ top : -6px ; width : 6px ; height : 6px ; } 10%{ transform : translateX(0); } 12%{ transform : translateX(3px); } 20%{ top : -6px ; width : 6px ; height : 6px ; } 22%{ top : -4px ; width : 8px ; height : 2px ; } 24%{ top : -6px ; width : 6px ; height : 6px ; } 25%{ transform : translateX(3px); } 27%{ transform : translateX(0); } 74%{ top : -6px ; width : 6px ; height : 6px ; transform : translateX(0); } 76%{ top : -4px ; width : 8px ; height : 2px ; transform : translateX(3px); } 78%{ top : -6px ; width : 6px ; height : 6px ; } 80%{ top : -4px ; width : 8px ; height : 2px ; } 82%{ top : -6px ; width : 6px ; height : 6px ; } 85%{ transform : translateX(3px); } 87%{ transform : translateX(0); } 100%{ top : -6px ; width : 6px ; height : 6px ; transform : translateX(0); } } ``` 就是多了一个 translateX 的位移。 给第二支鸟应用这个动画: ```css .button:hover .wakeup:nth-child(2) .bird__face::before, .button:hover .wakeup:nth-child(2) .bird__face::after{ animation : eye_2 5s linear infinite ; } ``` 再来试下: ![动图封面](https://pic1.zhimg.com/v2-e448f6f8628e9aab99bc9a6e8c404084_b.jpg) 更可爱了一点! 最后,还要做一个睡着的动画,睡着的时候随着呼吸,身体也是有起伏的,这个动画也是改 height 和 border-radius: ```css .button__bird { --border_radius1 : 60px 60px 40px 40px / 48px 48px 30px 30px ; --border_radius2 : 70px 70px 40px 40px / 48px 48px 30px 30px ; } @keyframes sleep{ 0% { height: 36px ; border-radius: var(--border_radius1) ; } 100%{ height: 32px ; border-radius: var(--border_radius2) ; } } .bird { animation: sleep 1s ease infinite alternate; } ``` 先试一下: ![动图封面](https://pic4.zhimg.com/v2-48d76d296048522b2b9bf3554d80aeff_b.jpg) 确实有随着呼吸身体起伏的感觉了! 但不知道同学们有没有发现这个动画的不同之处? ![img](https://pic1.zhimg.com/80/v2-c99e980293dfa7d28ad72a972d31cccc_720w.webp) 我们多设置了个 alternate,这个是 animation-direction,动画方向的意思。 如果不设置是这样的效果: ![动图封面](https://pic3.zhimg.com/v2-539e507ca736fb6dfe211b6f31b72086_b.jpg) 每次都从低到高来运动。 但应该是低到高、高到低、低到高这样的循环往复的运动。 animation-direction: alternate 的效果就是正向、反向、正向、反向这样循环。 当然它也有别的取值: - normal:正向: - reverse:反向 - alternate:正向、反向、正向、反向... - alternate-reverse:反向、正向、反向、正向... 至此,这个可爱的 Button 就完成了,我们整体感受一下: ![动图封面](https://pic3.zhimg.com/v2-b52ec5924e39573f546ba484625ba7a6_b.jpg) 确实很可爱! 全部代码如下: ```html <!DOCTYPE html> <html lang="en"> <head> <title></title> </head> <body> <a href="#" class="button button--bird"> <div class="button__wrapper"> <span class="button__text">ENTRY</span> </div> <div class="birdBox"> <div class="bird wakeup"> <div class="bird__face"></div> </div> <div class="bird wakeup"> <div class="bird__face"></div> </div> <div class="bird"> <div class="bird__face"></div> </div> </div> </a> <style> body { padding: 100px; } .button--bird{ --main_color: #f4cf47; --sub_color1: #f4e19c; --sub_color2: #ff8108; --base_color: #000; --border_radius1: 60px 60px 40px 40px / 48px 48px 30px 30px; --border_radius2: 70px 70px 40px 40px / 48px 48px 30px 30px; --border_radius3: 40px 40px 40px 40px / 48px 48px 30px 30px; } .button { display: flex; justify-content: center; align-items: center; box-sizing: border-box; width: 280px; height: 80px; text-decoration: none; border: solid 3px #000; border-radius: 40px; background: var(--main_color); position: relative; } .button__wrapper{ display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; border-radius: 40px; overflow: hidden; position: relative; } .button__text{ position: relative; font-size: 32px; letter-spacing: 4px; color: var(--base_color); transition: all .3s ease; } .button::before{ content: ''; position: absolute; right: 20px; margin: auto 0; width: 24px; height: 24px; background: var(--base_color); clip-path: path('M24,12.02c0-1.09-.75-1.71-.81-1.77L11.17,.45c-.91-.74-2.21-.56-2.91,.42-.69,.97-.52,2.37,.39,3.11l7.12,5.81-13.7-.02h0C.93,9.77,0,10.76,0,11.99c0,1.23,.93,2.22,2.07,2.22l13.7,.02-7.13,5.78c-.91,.74-1.09,2.13-.4,3.11,.41,.58,1.03,.88,1.65,.88,.44,0,.88-.15,1.25-.45l12.04-9.76c.07-.06,.82-.67,.82-1.77Z'); transition: all ease .2s; } .button--bird .button__wrapper::before, .button--bird .button__wrapper::after{ content: ''; position: absolute; bottom: 0; width: 130px; height: 38px; background: var(--sub_color1); transition: all .5s ease; clip-path: path('M13.77,37.35L.25,16.6c-.87-1.33,.69-2.91,2-2.02l12.67,8.59c.81,.55,1.91,.14,2.18-.81l2.62-9.33c.39-1.4,2.34-1.42,2.76-.02l3.6,11.99c.33,1.11,1.74,1.4,2.47,.52L49.38,.52c.87-1.04,2.53-.42,2.53,.95V23.7c0,1.13,1.2,1.83,2.16,1.26l12.75-7.51c.85-.5,1.94,0,2.13,.98l1.5,7.6c.2,1.03,1.37,1.51,2.22,.92l17.74-12.3c1.09-.75,2.52,.25,2.21,1.55l-2.44,10.2c-.26,1.09,.74,2.06,1.8,1.75l30.8-9.04c1.37-.4,2.42,1.26,1.49,2.36l-9.07,10.66c-.83,.98-.1,2.49,1.17,2.42l12.12-.68c1.6-.09,2.12,2.15,.65,2.8l-2.73,1.21c-.18,.08-.38,.12-.58,.12H14.97c-.48,0-.93-.25-1.2-.65Z'); } .button--bird .button__wrapper::before{ left: 0; } .button--bird .button__wrapper::after{ right: 0; transform: rotateY(180deg); } .button:hover .button__wrapper::before{ transform: translateX(-12px); } .button:hover .button__wrapper::after{ transform: rotateY(180deg) translateX(-12px); } .button:hover .button__text{ letter-spacing: 6px; } .button:hover::before{ right: 14px; } .birdBox{ position: absolute; top: -54px; display: flex; justify-content:space-between; align-items: flex-end; width: 180px; height: 56px; } .bird{ position: relative; width: 56px; height: 36px; box-sizing: border-box; border: solid 3px #000; background: var(--main_color); border-radius: var(--border_radius1); animation: sleep 1s ease infinite alternate; display: flex; justify-content: center; } .bird__face{ position: absolute; top: 15px; width: 12px; height: 6px; background: var(--sub_color2); border-radius: 50% 50% 50% 50% / 78% 78% 22% 22%; transition: .2s; } .bird__face::before, .bird__face::after{ content: ''; position: absolute; top: -4px; width: 8px; height: 2px; border-radius: 4px; background: #000; } .bird__face::before{ left: -5px; } .bird__face::after{ right: -5px; } .bird::before{ content: ''; position: absolute; top: -12px; left: 22px; width: 12px; height: 12px; background: #000; clip-path: path('M10.23,3.32c-3.54,.63-5.72,2.51-7.02,4.23-.33-1.58-.34-3.54,.93-5.12,.52-.65,.41-1.59-.24-2.11C3.24-.19,2.29-.08,1.77,.57c-3.82,4.77-.31,11.11-.13,11.42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0-.01-.02,2.49,.04,2.52,0,.1-.14,1.54-4.82,6.59-5.71,.82-.14,1.37-.92,1.22-1.74s-.94-1.36-1.75-1.21Z'); } .button:hover .wakeup{ animation: wakeup .2s ease; animation-fill-mode: forwards; } .button:hover .wakeup .bird__face{ top: 20px; } .button:hover .wakeup .bird__face::before, .button:hover .wakeup .bird__face::after{ animation: eye 5s linear infinite; } .button:hover .wakeup:nth-child(2) .bird__face::before, .button:hover .wakeup:nth-child(2) .bird__face::after{ animation: eye_2 5s linear infinite; } @keyframes wakeup{ 0% { height: 32px; border-radius: var(--border_radius2); } 100%{ height: 56px; border-radius: var(--border_radius3); } } @keyframes eye { 0%{ top: -6px; width: 6px; height: 6px; } 30%{ top: -6px; width: 6px; height: 6px; } 32%{ top: -4px; width: 8px; height: 2px; } 34%{ top: -6px; width: 6px; height: 6px; } 70%{ top: -6px; width: 6px; height: 6px; } 72%{ top: -4px; width: 8px; height: 2px; } 74%{ top: -6px; width: 6px; height: 6px; } 76%{ top: -4px; width: 8px; height: 2px; } 78%{ top: -6px; width: 6px; height: 6px; } 100%{ top: -6px; width: 6px; height: 6px; } } @keyframes eye_2 { 0%{ top: -6px; width: 6px; height: 6px; } 10%{ transform: translateX(0); } 12%{ transform: translateX(3px); } 20%{ top: -6px; width: 6px; height: 6px; } 22%{ top: -4px; width: 8px; height: 2px; } 24%{ top: -6px; width: 6px; height: 6px; } 25%{ transform: translateX(3px); } 27%{ transform: translateX(0); } 74%{ top: -6px; width: 6px; height: 6px; transform: translateX(0); } 76%{ top: -4px; width: 8px; height: 2px; transform: translateX(3px); } 78%{ top: -6px; width: 6px; height: 6px; } 80%{ top: -4px; width: 8px; height: 2px; } 82%{ top: -6px; width: 6px; height: 6px; } 85%{ transform: translateX(3px); } 87%{ transform: translateX(0); } 100%{ top: -6px; width: 6px; height: 6px; transform: translateX(0); } } @keyframes sleep{ 0% { height: 36px; border-radius: var(--border_radius1); } 100%{ height: 32px; border-radius: var(--border_radius2); } } </style> </body> </html> ``` ## **总结** 我们通过纯 CSS 实现了一个可爱的 Button,核心就是用元素 + 伪元素通过 flex + 定位来布局,然后通过 border-radius + clip-path 设置形状,通过 animation + @keyframes 来做复杂的动画,通过 transition 设置过渡效果。 border-radius 可以设置 4 个角的横竖半轴的长度,从而做出很多形状,更复杂的不规则形状可以通过 clip-path 来裁剪出来。 其中要注意的是可以通过设置 animnation-fill-mode: forwards 让动画停在最后一帧,设置 animation-direction: alternate 可以正反交替执行动画。再就是最好通过变量把会变化的样式值提取出来,这样方便配置。 用 CSS 实现一些有趣的效果确实感觉很好,这也是前端工程师专属的快乐。
Leo
2022年11月21日 14:22
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码