头图

CSS 绘制一只思否猫

欢迎关注我的公众号:前端侦探

练习 CSS 有一个比较有趣的方式,就是发挥想象,绘制各式各样的图案,比如来绘制一只思否猫?

思否猫,SegmentFault 思否的吉祥物,是一只独一无二、特立独行、热爱自由的(>^ω^<)独角猫,也是社区的首席摸鱼官

image.png

你也可以提前查看完整代码 CSS sifou cat (runjs.work)

其实还是有点小复杂的,不要慌,根据结构,可以拆分成眼睛眉毛耳朵鼻子嘴巴这几个部分,一起来看看如何实现的吧,有超级多的 CSS 绘制小技巧~

一、头

首先是最外层头的形状,是一个不太规则的椭圆,可以用border-radius实现,直接手写可能会有点不太准确,可以借助这个圆角生成工具进行微调

https://9elements.github.io/fancy-border-radius

image.png

假设 HTML是这样的

<cat></cat>

那么,脸的形状可以这样来实现

cat{
  width: 400px;
  height: 320px;
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  border: 3px solid #222224;
  background: #fef2dc;
}

效果如下

image.png

然后脸上还有两个圆形的花纹,通过两个径向渐变绘制

对径向渐变不太熟悉的可以参考张鑫旭老师的这篇文章:10个demo示例学会CSS3 radial-gradient径向渐变
cat{
  /**/
  background: radial-gradient(circle at 20px 14px, #fff 6px, transparent 7px), 
    radial-gradient(circle at 12px 26px, #fff 3px, transparent 4px) 
    #222224 no-repeat;
}

效果如下

image.png

哈哈,是不是有点像小孩头发?

二、眼睛

接着来绘制眼睛,可以单独用一个标签,然后用两个伪元素绘制

<cat>
  <!-- 眼睛 -->
    <eye></eye>
</cat>

眼睛比较简单,一个圆形加一个边框,边框可以用border或者box-shadow生成,然后通过绝对定位定位就行了

eye::before,
eye::after{
  position: absolute;
  content: '';
  width: 50px;
  height: 50px;
  background: #222224;
  box-shadow: 0 0 0 10px #967c51;
  border-radius: 50%;
}
eye::before{
  left: 60px;
  top: 100px;
}
eye::after{
  right: 110px;
  top: 110px;
}

效果如下

image-20221017231137989

现在有点傻乎乎的,需要加点高光,和前面一样,用径向渐变绘制两个白色的圆就行了

eye::before,
eye::after{
  /**/
  background: radial-gradient(circle at 20px 14px, #fff 6px, transparent 7px), 
    radial-gradient(circle at 12px 26px, #fff 3px, transparent 4px) 
    #222224 no-repeat;
}

效果如下

image-20221017231347297

看,有了高光立刻就不一样了,炯炯有神

三、眉毛

然后给眼睛上加点眉毛,左右两侧的眉毛是三根白色的弧线,弧线可以用border-radius加上border来绘制,只保留一个方向上的border,原理是这样的

image-20221017232713868

由于一个元素只能绘制一条这样的弧线,加上伪元素也只能绘制三条,所以这里用两个标签

<cat>
  <!-- 眼睛 -->
  <eye></eye>
  <!-- 眉毛 -->
  <eyebrow-l></eyebrow-l>
  <eyebrow-r></eyebrow-r>
</cat>

然后借助伪元素,适当旋转调整

eyebrow-l,
eyebrow-r{
  position: absolute;
  width: 60px;
  height: 10px;
  border-radius: 100%;
  border: 2px solid #fff;
  border-width: 2px 0 0 0;
}
eyebrow-l{
  left: 80px;
  top: 60px;
  transform: rotate(30deg);
}
eyebrow-r{
  right: 130px;
  top: 60px;
  transform: rotate(-35deg);
}
eyebrow-l::before,
eyebrow-l::after,
eyebrow-r::before,
eyebrow-r::after{
  content: '';
  position: absolute;
  width: 50px;
  height: 10px;
  right: 0;
  border-radius: 100%;
  border: 2px solid #fff;
  border-width: 2px 0 0 0;
  transform-origin: right center;
}
eyebrow-l::before{
  top: -10px;
  transform: rotate(20deg);
}
eyebrow-l::after{
  top: 10px;
  transform: rotate(-15deg);
}
eyebrow-r::before,
eyebrow-r::after{
  left: 0;
  right: auto;
  transform-origin: left center;
}
eyebrow-r::before{
  top: -10px;
  transform: rotate(-20deg);
}
eyebrow-r::after{
  top: 10px;
  transform: rotate(15deg);
}

效果如下

image-20221017233441643

现在有点可爱了吧~

四、耳朵

现在加上耳朵,这个相对比较容易,首先通过border-radius生成工具调整为一个弧形三角的形状,如下

image-20221017233817984

HTML 中需要添加一个标签

<cat>
  <!-- 眼睛 -->
  <eye></eye>
  <!-- 眉毛 -->
  <eyebrow-l></eyebrow-l>
  <eyebrow-r></eyebrow-r>
  <!-- 耳朵 -->
  <ear></ear>
</cat>

耳朵的轮廓可以用不同的border绘制,实现如下

ear::before{
  position: absolute;
  content: '';
  width: 120px;
  height: 200px;
  left: -20px;
  top: -50px;
  border-radius: 79% 21% 0% 100% / 100% 56% 44% 0% ;
  background-color: #9e5439;
  transform: rotate(-36deg);
  border: 10px solid #222224;
  border-width: 5px 30px 0 10px;
  z-index: -1;
}

效果如下

image-20221018000700562

现在耳朵里只有一种颜色,没有层次感,可以通过内阴影生成

ear::before{
  /**/
  box-shadow: inset -40px -10px 0 #b68679, 
    inset -110px -80px 0 #b06648;
}

这样就得到了层次分明的耳朵

image-20221018000629610

右耳朵也是同样的方法,可以用::after来生成,代码就不重复了,效果如下

image-20221018000828131

有了耳朵,这样是不是就有点像一只猫了?

五、鼻子

接下来是鼻子,鼻子可以看成是一个半椭圆和一个圆三角,HTML如下,只需一个标签就行了

<cat>
  <!-- 眼睛 -->
  <eye></eye>
  <!-- 眉毛 -->
  <eyebrow-l></eyebrow-l>
  <eyebrow-r></eyebrow-r>
  <!-- 耳朵 -->
  <ear></ear>
  <!-- 鼻子 -->
  <nose></nose>
</cat>

半椭圆可以通过border-radius实现,注意鼻子上的高光,圆角三角可以用一个圆角矩形旋转45deg然后配合横向缩放得到,圆角三角绘制过程如下

image-20221018191243612

具体实现如下

nose{
  position: absolute;
  left: 140px;
  top: 140px;
  transform: rotate(5deg);
  filter: drop-shadow(0 2px #b68679);
  width: 46px;
  height: 22px;
  border-radius: 50% 50% 50% 50% / 80% 80% 20% 20%;
  background: radial-gradient( 8px 4px at 60% 50%, rgba(255, 255, 255, 0.5) 100%, transparent 0) #fcbaa1 no-repeat;
  z-index: 1;
}
nose::after{
  content: '';
  position: absolute;
  width: 24px;
  height: 24px;
  border-radius: 5px;
  background: linear-gradient(135deg, transparent 50%, #fcbaa1 0);
  transform: scale(0.8,1) rotate(45deg);
  left: 10px;
  bottom: -6px;
  z-index: -1;
}

这样就得到了小巧玲珑的鼻子~

image-20221018191528009

六、嘴巴

嘴巴其实就是几条弧线,和前面画眉毛的方式类似,通过用border-radius加上border来绘制,由于弯曲程度比较大,可以保留两个方向上的border,绘制过程如下

image-20221018192550133

所以只需要一个标签,然后用两个伪元素画出嘴巴的两条弧线

<cat>
  <!-- 眼睛 -->
  <eye></eye>
  <!-- 眉毛 -->
  <eyebrow-l></eyebrow-l>
  <eyebrow-r></eyebrow-r>
  <!-- 耳朵 -->
  <ear></ear>
  <!-- 鼻子 -->
  <nose></nose>
  <!-- 嘴巴 -->
  <mouth></mouth>
</cat>

具体实现如下,注意一下旋转中心轴

mouth{
  position: absolute;
  left: 159px;
  top: 170px;
  width: 2px;
  height: 30px;
  border: 3px solid #222224;
  border-width:  0 0 0 2px;
  border-radius: 100%;
}
mouth::before{
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 60px;
  height: 20px;
  border-radius: 0 0 0 20px;
  border: 2px solid #222224;
  border-width:  0 0 2px 2px;
  transform: translate(-100%) rotate(-20deg);
  transform-origin: right bottom;
}
mouth::after{
  content: '';
  position: absolute;
  right: 3px;
  bottom: 0;
  width: 80px;
  height: 30px;
  border-radius: 0 0 30px 0;
  border: 2px solid #222224;
  border-width:  0 2px 2px 0;
  transform: translate(100%) rotate(25deg);
  transform-origin: left bottom;
}

嗯,一只可爱的小猫咪就出现了

image-20221018193001092

七、角

最后,加上思否猫独特的标识,独角。

这部分算是这里面最复杂的一块了,整体外形是一个圆锥形,同样可以借助border-radius生成工具调整出来

image-20221018193644969

然后左侧的高光可以用内阴影模拟,螺纹形状可以用径向渐变绘制,绘制过程如下

image-20221018195559692

角的下面还有三个彩色的小标记(暂时还不知道叫什么😂)(官方介绍叫代码缩影>代码光波🤣)

image-20221018203443604由于形状相同,只需绘制一个,其他两个可以直接用box-shadow生成,向两个方向投射

image-20221018195533723

同样只需要一个标签

<cat>
  <!-- 眼睛 -->
  <eye></eye>
  <!-- 眉毛 -->
  <eyebrow-l></eyebrow-l>
  <eyebrow-r></eyebrow-r>
  <!-- 耳朵 -->
  <ear></ear>
  <!-- 鼻子 -->
  <nose></nose>
  <!-- 嘴巴 -->
  <mouth></mouth>
  <!-- 角 -->
  <horn></horn>
</cat>

具体实现如下

horn{
  position: absolute;
  width: 40px;
  height: 120px;
  border-radius: 52% 48% 50% 50% / 93% 93% 7% 7% ;
  background: #808087;
  box-shadow: inset 10px -14px 0 -10px #808087, inset 24px -14px 0 -14px #b6b8be;
  border: 3px solid #808087;
  left: 165px;
  top: -110px;
  transform-origin: center bottom;
  transform: rotate(-20deg);
}
horn::before{
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 52% 48% 50% 50% / 93% 93% 7% 7% ;
  background: repeating-radial-gradient( circle at 36px 0px,#b6b8be 30px 34px, transparent 35px 61px),
  repeating-radial-gradient( circle at 36px 0px,#808087 34px 38px, transparent 39px 65px);
}
horn::after{
  content: '';
  position: absolute;
  bottom: -25px;
  transform: rotate(20deg);
  width: 12px;
  height: 4px;
  border-radius: 4px;
  background-color: #f6f090;
  box-shadow: 18px 0 0 #d56564, -18px 0 0 #a2a9a6;
}

完成!

image-20221018205027362

完整代码可以查看 CSS sifou cat (runjs.work)

八、总结一下

哦对了,本文绘制的效果并不是完全和原图一致,很多地方做了改动,主要是原图偏手绘风格,很多线条都比较随意,尤其是头上的角,思索半天,简化成了本文的效果,下方是官方原图

37322ce8ak1f2bdccc1654ed87f0dc04

不过,从整体神情上还是有八九分相似的😁下面总结一下绘制的几个小技巧

  1. 圆形、不太规则的椭圆都可以考虑用border-radius,可视化生成工具可以很方便地完成
  2. 弧线可以用border-radius配合局部border生成
  3. 圆形高光背景可以用radial-gradient绘制
  4. 侧边高光还可以用内阴影来实现
  5. 圆角三角可以通过旋转圆角矩形得到
  6. 相同形状可以用box-shadow生成多份
  7. 各种绘制技巧灵活使用

当然实际工作中,我是不推荐绘制这么复杂的图形的,主要是了解 CSS 绘制的相关技巧,碰到一些简单的图标、自适应尺寸、颜色可变的图形就绰绰有余了。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤

欢迎关注我的公众号:前端侦探
本文参与了1024程序员节,欢迎正在阅读的你也加入。

前端侦探
致力于有趣的前端探索~
13k 声望
12.7k 粉丝
0 条评论
推荐阅读
2022年我写了哪些CSS文章?
欢迎关注我的公众号:前端侦探2022 全年共计发布了 41 篇关于 CSS 的原创文章。为了方便查阅学,花了一点时间分类整理了一下,按照时间倒序排序如下CSS 如何根据背景色自动切换黑白文字?CSS 如何实现羽化效果?...

XboxYan11阅读 525

封面图
从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木60阅读 5.9k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.1k评论 12

封面图
面向猫猫编程,SegmentFault 思否 1024 程序员节惊喜彩蛋攻略来啦!
在 SegmentFault 思否 1024 程序员节系列活动 中,思否小姐姐为大家预留了一个悬念,还没有公布第三站:惊喜彩蛋「喵了个喵」的具体玩法,思否小姐姐也收到了不少咨询活动玩法的私信,惊喜彩蛋哪里找?趣味游戏怎...

SegmentFault思否159阅读 4k评论 154

从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木39阅读 7k评论 6

还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan34阅读 2.2k评论 2

封面图
从零搭建 Node.js 企业级 Web 服务器(二):校验
校验就是对输入条件的约束,避免无效的输入引起异常。Web 系统的用户输入主要为编辑与提交各类表单,一方面校验要做在编辑表单字段与提交的时候,另一方面接收表单的接口也要做足校验行为,通过前后端共同控制输...

乌柏木32阅读 6k评论 9

13k 声望
12.7k 粉丝
宣传栏