如何创建纯 CSS 插图并使其动起来 - 第一部分
基础知识
更高级的概念
我一直对设计、插画和配色方案非常感兴趣,但过去几年我一直专注于成为一名更优秀的前端开发者,几乎没有时间练习我的创意技能。当我第一次接触到 CSS 图像时,我迫不及待地想尝试一下。终于,我可以一边写代码,一边玩转各种形状和颜色,探索和发展我的创造力了!
但首先,什么是纯 CSS 图像?
纯 CSS 图片是指使用 HTML 和 CSS 构建的插图。它不使用图片文件导入,也不使用在插图软件中导出图片生成的代码。换句话说,它是完全在代码编辑器中手动编码的图片,仅使用 HTML 和 CSS。
原理很简单:使用 HTML,您可以创建任意数量的 div,每个 div 代表最终图像组件的基本形状。使用 CSS 属性(例如渐变、变换、边框半径、阴影等),您可以变换这些基本形状,并将它们排列成美观的插图。
为什么使用 CSS 图像?
但你可能会问,CSS 图像的意义何在?毕竟,CSS 的主要作用是为网页设计样式,而且还有更灵活高效的工具可以为网页设计制作高质量、轻量级的图形(比如 SVG)。事实上,使用 CSS 进行插图绘制不仅存在一些设计限制,而且结果可能过于复杂、耗时,并面临一些严重的跨浏览器/设备问题。
所以,CSS 图像肯定有其适用之处。然而,创作 CSS 插图是提高 CSS 水平的好方法,还能学习新的工具和概念,例如动画、预处理器或一些鲜为人知的 CSS 属性。自从我开始编写 CSS 图像代码以来,我的 CSS 技能突飞猛进,而且我对一些以前不太了解的概念也更加熟悉了,比如各种各样的 CSS 选择器、3D 变换和关键帧动画。
它适合谁?
CSS 插图非常适合:
- 希望利用自己的设计技能学习或增强对 html/css 的信心的插画师或设计师
- 希望发挥创造力并培养良好设计眼光的前端开发人员
- 任何想在加强 CSS 技能的同时获得一些乐趣的人
- 任何希望与社区建立联系、激励他人并受到启发的人
- 或者任何愿意接受挑战的人
在本系列中,我们将学习如何创建三个 CSS 插图,涵盖从简单到复杂的各种元素。我们将学习 CSS 动画的基础知识以及如何使用它们来制作插图动画。在此过程中,我们将了解哪些概念、工具和技巧可以帮助我们加快工作流程。
第 1 部分:通过 CSS 笑脸学习基础知识和工作流程技巧
第 2 部分:通过 CSS 宝丽来介绍 CSS 动画
第 3 部分:通过 CSS 灯塔场景介绍更高级的技术
虽然本教程系列不需要任何高级的 Web 开发知识,但我假设您至少熟悉 HTML 和 CSS。
在我们开始之前有几点建议:
- 如果你还没有CodePen账户,那就赶紧注册一个吧。这会省去你设置项目的麻烦,尤其是因为我们会用到 CSS 预处理器和模板语言。
- 使用 Chrome 或 Firefox,因为其他浏览器已被证明存在 CSS 插图错误。
基础知识
好了,说得够多了,让我们开始制作我们的第一个 CSS 图像!
这是一个圆圈:
<div class="circle"></div>
html,
body {
height: 100%;
width: 100%;
overflow: hidden;
padding: 0;
margin:0;
}
body {
background: #FEEE9D;
}
.circle {
position: absolute;
width: 300px;
height: 300px;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
border-radius: 50%;
background-color: #FBD671;
}
到目前为止没有什么令人印象深刻的。
我们要添加一些元素,把这个圆圈变成笑脸。这个圆圈就是笑脸的头部,我们可以添加眼睛和嘴巴:
<div class="head">
<div class="face">
<div class="mouth"></div>
<div class="eye-group">
<div class="eye eye-left"></div>
<div class="eye eye-right"></div>
</div>
</div>
</div>
html,
body {
height: 100%;
width: 100%;
overflow: hidden;
padding: 0;
margin: 0;
}
body {
background: #FEEE9D;
}
* {
position: absolute;
}
.head {
width: 300px;
height: 300px;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
border-radius: 50%;
background-color: #FBD671;
}
.face {
width: 150px;
height: 170px;
left: 75px;
top: 75px;
}
.mouth {
width: 100%;
height: 75px;
bottom: 0;
left: 0;
background-color: #20184E;
border-radius: 10px 10px 150px 150px;
border: 5px solid #20184E;
}
.eye-group {
width: 150px;
height: 50px;
top: 10px;
left: 0;
}
.eye {
background-color: #20184E;
width: 30px;
height: 50px;
border-radius: 50%;
}
.eye-left {
left: 15px;
}
.eye-right {
right: 15px;
}
好的,让我们看看这里发生了什么。
主容器
我们有一个主容器,在本例中是.head
元素,它包含所有其他 HTML 元素。正如你所期望的,它将我们的插图居中,并作为其他所有内容定位的参考。
绝对定位
所有元素都具有该position: absolute
属性。借助 top/right/bottom/left 属性,可以精确地将它们相对于父元素进行定位。需要注意的是,绝对定位会将元素从页面的自然流中移除。这意味着具有该属性的元素的位置不会影响其他元素的位置,也不会受到其他元素位置的影响。默认position: absolute
为所有元素分配该属性将确保它们彼此独立。更多关于定位的信息,请点击此处。
* {
position:absolute;
}
嵌套
按照这个逻辑,我们使用嵌套将元素组合在一起。观察.face
元素。它不是图像的实际组成部分,而仅用于将眼睛和嘴巴组合在一起。.mouth
和.eye-group
的位置(和大小,如果我们使用%而不是px)都将引用.face
。例如,如果我们决定稍后重新定位头部的脸部,我们可以立即完成,而不必调整.mouth
和的属性.eyes
。这是基本的CSS逻辑,如果您有UI开发经验,这对您来说并不陌生。根据经验,嵌套可以提供更清晰的结构,因为它将属于一起的元素分组,并使插图更易于操作。
堆叠
使用绝对定位使我们能够更好地控制元素的堆叠顺序。堆叠顺序自然遵循 HTML 元素的流动。第一个对象将位于堆叠的底部(最远),而最后一个对象将位于顶部(最靠近您)。使用 z-index 属性,我们可以更改堆叠中每个元素的位置,并使它们按所需的方式重叠,前提是这些元素属于同一个堆叠上下文。z-index 的值越高,它在堆叠中的位置就越高。
堆叠上下文有点棘手,它并不总是按照你预期的方式运行。你可以在这里阅读更多关于触发创建新堆叠上下文的信息。
仅限课程
我们仅使用 CSS 类来定位和设置 HTML 元素的样式,因为我们不想受到特殊性问题的困扰。
呼,已经说了这么多了。但这些对于 CSS 插图,乃至整个 CSS 来说,都是非常重要的概念。一旦你熟悉了定位和堆叠,你的 CSS 技能就会大幅提升。
现在我们已经对如何定位元素有了基本的了解,让我们来稍微美化一下笑脸。我想添加一些细节,比如舌头、瞳孔和阴影。
阴影很简单,但却增添了美感:
.head {
width: 100%;
height: 100%;
background-color: #FBD671;
border-radius: 50%;
box-shadow:inset -10px -10px 0px #EFBB42;
}
舌头是一个简单的粉色圆圈,但我们只想显示它的一部分,以营造它在嘴里的错觉。为此,我们将嵌套.tongue
并.mouth
应用overflow: hidden
属性到.mouth.
<div class="head">
<div class="face">
<div class="mouth">
<div class="tongue"></div>
</div>
<div class="eye-group">
<div class="eye eye-left"></div>
<div class="eye eye-right"></div>
</div>
</div>
</div>
.mouth {
width: 100%;
height: 75px;
background-color: #20184E;
left: 0;
bottom: 0;
border-radius: 10px 10px 150px 150px;
border: 5px solid #20184E;
overflow:hidden;
}
.tongue {
width: 100px;
height: 80px;
left: 25px;
top: 30px;
background-color: #F15962;
border-radius: 50%;
}
我们可以在.eye
元素内部添加带有简单椭圆形的学生。
<div class="container">
<div class="head">
<div class="face">
<div class="mouth">
<div class="tongue"></div>
</div>
<div class="eye-group">
<div class="eye eye-left">
<div class="pupil"></div>
</div>
<div class="eye eye-right">
<div class="pupil"></div>
</div>
</div>
</div>
</div>
</div>
.pupil {
width: 10px;
height: 15px;
top: 5px;
background-color: #FBD671;
border-radius: 50%;
}
看起来已经好多了。
更高级的概念
现在我们已经构建了第一个 CSS 图片,让我们回顾一下,看看如何改进代码。虽然这些步骤对于构建 CSS 图片来说并非必需,但我发现它们极大地改善了我的工作流程,并帮助我总体上提高了 CSS 的水平。
CSS预处理器
如果您熟悉CSS 预处理器,您就会知道有更好的方法来实现这一点。我不会深入探讨如何设置预处理器,因为这超出了本文的讨论范围。我强烈建议您使用Codepen,它内置了所有预处理器。您只需在 CSS 设置中选择所需的预处理器即可。
我个人喜欢使用SASS/SCSS,但您可以随意使用任何可用的选项。
预处理器最著名的特性是能够使用变量。预处理器变量的工作方式与 JavaScript 变量类似。您只需声明并赋值一次,就可以在整个代码中重复使用它们。这是一个非常棒的工具,因为它避免了重复,并赋予了您更大的灵活性。让我们在 CSS 中实现一些变量。变量的一个好用途是定义颜色。
SCSS 变量始终以 $ 符号开头:
$black: #20184E;
您可以随意命名它,但它必须以 $ 开头并分配有效的 CSS 属性。
然后你可以像这样使用它:
background-color: $black;
你已经看到了它有多方便。如果我们之后决定要更改这个特定的颜色,只需在一个地方修改,它就会反映在代码中所有使用该变量的地方。此外,对于颜色来说,记住你创建的变量的名称比记住 RGB 或十六进制值要容易得多。
我把新的颜色变量添加到代码顶部,以便我们轻松找到它们。在全局范围内声明变量(而不是在选择器中声明)可以确保我们可以在任何地方使用它们。
$black: #20184E;
$head-color: #FBD671;
$background: #FEEE9D;
$tongue-color: #F15962;
现在我们可以改变我们的 css 属性,使它们指向变量而不是硬编码的十六进制值。
例如:
.mouth {
width: 100%;
height: 75px;
bottom: 0;
background-color: $black;
border: 5px solid $black;
border-radius: 10px 10px 150px 150px;
overflow: hidden;
}
CSS 现在有了原生变量,这几乎让预处理器变量变得不再必要。然而,预处理器的功能远不止变量,在很多其他方面仍然非常有用。
SCSS 还附带许多辅助函数,包括颜色函数。这些函数非常强大,可以让你非常轻松地操作颜色。
我们来看一下头部的内嵌阴影。目前我们使用的是硬编码的十六进制值,我不得不手动查找。我们将使用 darken 函数来生成这个值:
.head {
border-radius: 50%;
width: 100%;
height: 100%;
background-color: $head-color;
box-shadow: inset -10px -10px 0px darken($head-color, 20%);
}
基本上,该函数将$head-color
其作为参数,并将黑暗程度增加 20%。
我喜欢使用颜色功能,因为它们不仅消除了借助某些设计软件来生成颜色的需要,而且还有助于为您的插图创建更和谐的调色板。
SASS 的另一个很棒的功能是嵌套。嵌套允许你将选择器嵌套在父选择器中,以便创建快捷方式。它为 CSS 创造了更好的层次结构和可读性,并避免了反复使用选择器。
例如
.pink {
background-color: pink;
.purple {
background-color: purple;
}
}
将被编译为:
.pink {
background-color: pink;
}
.pink .purple {
background-color: purple;
}
SASS 还提供了 & 选择器,它基本上指的是父选择器。所以
.pink {
background-color: pink;
&.purple{
background-color: purple;
}
}
将被编译为:
.pink {
background-color: pink;
}
.pink.purple {
background-color: purple;
}
使用 & 选择器不会.purple
像上例一样定位到带有 class 的子元素。它会定位到带有 class .pink
AND的父元素.purple
。
注意不要嵌套过多,因为它会使你的代码过于复杂,可读性降低,这与我们的预期背道而驰。通常情况下,我倾向于最多 3 层嵌套。
考虑到这一点,让我们在代码中实现嵌套。例如:
.eye-group {
top: 10px;
left: 0;
width: 150px;
height: 50px;
}
.eye {
background-color: $black;
width: 30px;
height: 50px;
border-radius: 100%;
}
.eye-left {
left: 15px;
}
.eye-right {
right: 15px;
}
变为:
.eye-group {
top:10px;
left:0;
width: 150px;
height: 50px;
.eye {
background-color: $black;
width: 30px;
height: 50px;
border-radius: 100%;
&.eye-left {
left:15px;
}
&.eye-right {
right:15px;
}
}
}
预处理器是构建 CSS 图像的利器,能够显著加快您的工作流程。它们还提供更多高级选项,例如函数、循环、混合宏等。这些功能在更复杂的插图中非常有用,我们将在本系列的下一篇中看到。
:before 和 :after
到目前为止,我们为笑脸中的每个形状都创建了一个 div。虽然这完全没问题,但还有一种方法可以减少 HTML 元素的数量。这就是伪选择器的作用:before
所在:after
。
这些伪选择器会自动随您创建的任何 HTML 元素一起提供。因此,对于 HTML 中的任何元素,您都会获得两个额外的容器来添加内容。虽然它们最初并不存在于 DOM 中,但您可以使用 content 属性来定位它们,如下所示:
.some-class:before, .some-class:after {
content: "";
}
此属性可用于插入文本或图像等内容。在 CSS 插图中,我们不需要这些,但仍需要使用选择器,可以使用空引号来实现。虽然这看起来像是一个不必要的步骤,但此属性可以确保这些选择器可用,因此请确保它存在。避免忘记它并避免重复的一个好方法是将其默认分配给所有:after
和元素::before
*:before, *:after {
position: absolute;
content: '';
}
正如您所期望的,:before
其:after
大小和定位取决于其父级。
让我们看看如何在代码中使用这些。考虑.mouth
元素。它有一个子元素:.tongue
元素。我们将.tongue
用:after
伪选择器替换选择器:
.mouth {
width: 100%;
height: 75px;
bottom: 0;
background-color: $black;
border: 5px solid $black;
border-radius: 10px 10px 150px 150px;
overflow: hidden;
&:after {
background-color: $tongue-color;
width: 100px;
height: 80px;
border-radius: 50%;
left:25px;
top:30px;
}
}
可以应用相同的逻辑来替换.pupil
元素:
.eye-group {
top: 10px;
width: 150px;
height: 50px;
.eye {
background-color: $black;
width: 30px;
height: 50px;
border-radius: 100%;
border: 5px solid $black;
&:after {
width: 10px;
height: 15px;
top: 5px;
background-color: #FBD671;
border-radius: 50%;
}
&.eye-left {
left: 15px;
}
&.eye-right {
right: 15px;
}
}
}
现在我们可以去掉html 中的.tongue
和div:.pupil
<div class="head">
<div class="face">
<div class="mouth"></div>
<div class="eye-group">
<div class="eye eye-left"></div>
<div class="eye eye-right"></div>
</div>
</div>
</div>
虽然这并非绝对必要,但我发现这种做法有助于保持代码简洁,并避免 HTML 内容过于拥挤。再次强调,仅在合理的情况下才使用伪选择器。(例如:创建与其父容器在视觉上相关的元素)。
HTML模板语言
改进代码的另一个方法是使用 HTML 模板语言。我喜欢使用Pug,它也内置于 Codepen 编辑器中。
它的工作原理如下:Pug 使用标签名称来表示完整的 HTML 元素。例如,在 Pug 中,<div></div>
会被直接翻译为div
。它使用缩进来表示嵌套,从而重建 HTML 的树形结构。
在 CSS 图像中,建议仅使用 div 元素,并为其分配一个类,以便轻松定位它们。在 Pug 中使用类表示 div 的方法如下:
正常html:
<div class="container"></div>
```
Pug:
```html
div.container
```
Or, because `div` is the default tag name, it can be omitted:
```html
.container
```
I find this syntax to be much cleaner, and it allows me to focus on the class. Since I already know that all my elements are divs, I don't need any useless syntax polluting my code.
Let's convert our HTML to Pug:
```html
.head
.face
.mouth
.eye-group
.eye.eye-left
.eye.eye-right
```
Much cleaner.
Similarly to CSS preprocessors, there are many things you can do with Pug as it is powered by JavaScript: mixins, includes etc. When it comes to CSS images, one of the most powerful features is loops. We'll see a bit later how to use them.
Here's the final project in CodePen. You can see that none of our latest changes have affected the illustration at all.
Right, we’ve learned a lot already! How to create various shapes and assemble them into a cohesive illustration, how to use hacks to emulate more complex shapes, how to use preprocessors, templating languages and pseudo-selectors to improve our workflow and clean up our code. In the second part of this series, we'll practice these new concepts and build a CSS Polaroid. Then, we'll learn how to animate it.
[Part 2: Intro to CSS animations with a CSS Polaroid](https://dev.to/agathacco/how-to-create-pure-css-illustrations-and-animate-them---part-2-1ao4)