如果你问我关于前端开发听到最多的问题是什么,我会说那是“如何提高我的 CSS 技能?”。这个问题通常出现在我分享自己制作的 CSS 插图之后。这是我非常喜欢在 CodePen 上做的事情。
对许多人来说,CSS 就像一头无法驯服的神话野兽。Chris 的这条推文让我忍俊不禁,因为它虽然具有讽刺意味,但也道出了很多真相。也就是说,如果我告诉你,你只需掌握几个属性和技巧就能创造任何你想要的图形,你会怎么想?事实是,你确实离目标如此之近。
我一直想写一篇这样的文章,但这是一个很难涵盖的主题,因为可能性和技巧太多了,通常不止一种方法可以实现相同的结果。CSS 插图也是如此。没有正确或错误的方法。我们都在使用相同的画布。只是有许多不同的工具可以将像素绘制到页面上。
虽然 CSS 插图没有“一刀切”的方法,但我可以提供一些技巧,希望能帮助你在你的创作之旅中。
时间和练习
CSS 插图需要大量的时间和练习。你希望插图越精确,越复杂,花费的时间就越长。耗时的部分通常不是决定使用哪些属性以及如何使用,而是调整使其看起来正确。准备好充分熟悉浏览器开发者工具中的样式检查器!如果你还没有尝试过VisBug,我建议你试试。
两位优秀的 CSS 艺术家是 Ben Evans 和 Diana Smith。他们最近都谈到了 CSS 插图的耗时问题。

我发布了一张关于杯子的模因图片,Ben 的回复完美地总结了这一切
当我第一次看到这条推文时,我曾想过用 CSS 来创作它,但后来觉得我的回复可能需要一个月的时间。
这需要时间!
>CSS 插图 pic.twitter.com/vqpQLKTte5
— Jhey 🛠 (@jh3yy) 2020 年 5 月 10 日
描图是完全可以接受的
我们通常会对想要绘制的插图有一个想法。毕竟,本文不是关于设计的;而是关于获取图像并使用 DOM 和 CSS 呈现它。我敢肯定,这种技术从远古时代就存在了。但是,这是我过去几个月一直在分享的技术。
- 找到或创建你想绘制插图的图像。
- 使用
<img>
标签将其拉入 HTML 中。 - 将其放置在插图下方的位置。
- 降低图像的不透明度,使其仍然可见但不至于过于抢眼。
- 使用 DOM 对其进行描图。
令我惊讶的是,这种技术并非众所周知。但它对于创建精确的 CSS 插图来说是无价的。
在此处查看此技巧的实际应用
这是一个创建该 CSS @eggheadio 😎 的延时视频。
— Jhey 🛠 (@jh3yy) 2020 年 5 月 1 日
使用 clip-path 调整阴影后 🛠️
💻 https://#/XhDRspwwFg 通过 @CodePen #webdev #coding #CSS #animation #webdesign #design #creative #100DaysOfCode #HTML #Timelapse https://#/ZQ1hyzcoSA pic.twitter.com/iPf7ksYCGX
并在此处尝试
注意响应式设计
如果从本文中要带走两个技巧,那就是上面的“描图”和接下来的这个。
有一些非常棒的 CSS 插图示例。但其中一些不幸的是,在小屏幕上没有样式——甚至无法查看。我们生活在一个科技的第一印象非常重要的时代。以使用 CSS 绘制的键盘为例。有人偶然发现了你的作品,在他们的智能手机上打开它,却只看到了插图的一半或一小部分。他们可能错过了演示中最酷的部分!
我的技巧是:利用视口单位进行插图,并创建自己的缩放单位。
对于大小和位置,你可以选择使用缩放单位或百分比。当需要使用盒阴影时,这尤其有用,因为该属性接受视口单位但不接受百分比。
考虑我上面创建的 CSS egghead.io logo。我找到了想要使用的图像,并使用 img 标签将其放入 DOM 中。
<image src='egghead.png'/>
img {
height: 50vmin;
left: 50%;
opacity: 0.25;
position: fixed;
top: 50%;
transform: translate(-50%, -50%);
}
高度 50vmin 是 CSS 插图所需的尺寸。降低的不透明度使我们能够在进行的过程中清楚地“描绘”插图。
然后,我们创建缩放单位。
/**
* image dimensions are 742 x 769
* width is 742
* height is 769
* my desired size is 50vmin
*/
:root {
--size: 50;
--unit: calc((var(--size) / 769) * 1vmin);
}
有了图像尺寸,我们就可以创建一个与图像一起缩放的统一单位。我们知道高度是最大的单位,因此我们将其用作创建分数单位的基础。
我们会得到类似这样的结果
--unit: 0.06501950585vmin;
这看起来有点别扭,但相信我,没关系。我们可以使用它来使用calc()
调整 CSS 插图容器的大小。
.egg {
height: calc(769 * var(--unit));
position: relative;
width: calc(742 * var(--unit));
z-index: 2;
}
如果我们使用百分比或新的--unit
自定义属性来设置 CSS 插图容器内元素的样式,我们将获得响应式的 CSS 插图……而且这只需要使用 CSS 变量进行几行数学运算!
调整此演示的大小,您会看到所有内容始终保持比例,使用 50vmin 作为大小限制。
量两次,切一次
另一个技巧是测量事物。如果你正在处理物理对象,甚至可以拿一把卷尺!
这可能看起来有点奇怪,但我测量了这个场景。这是我客厅里的电视组合柜。这些测量值相当于厘米。我用它们根据电视的实际高度获得了一个响应式单位。我们可以为这个数字——以及所有其他数字——命名一个易于记住其用途的名称,这要归功于自定义属性。
:root {
--light-switch: 15;
--light-switch-border: 10;
--light-switch-top: 15;
--light-switch-bottom: 25;
--tv-bezel: 15;
--tv-unit-bezel: 4;
--desired-height: 25vmin;
--one-cm: calc(var(--desired-height) / var(--tv-height));
--tv-width: 158.1;
--tv-height: 89.4;
--unit-height: 42;
--unit-width: 180;
--unit-top: 78.7;
--tv-bottom: 114.3;
--scaled-tv-width: calc(var(--tv-width) * var(--one-cm));
--scaled-tv-height: calc(var(--tv-height) * var(--one-cm));
--scaled-unit-width: calc(var(--unit-width) * var(--one-cm));
--scaled-unit-height: calc(var(--unit-height) * var(--one-cm));
}
一旦我们计算出一个变量,我们就可以在任何地方使用它。我知道我的电视宽 158.1 厘米,高 89.4 厘米。我查了说明书。但在我的 CSS 插图中,它将始终缩放至 25vmin。
对所有内容使用绝对定位
这个技巧可以节省你一些按键次数。大多数情况下,你都会希望对元素进行绝对定位。为自己省点事,将此规则放在某个地方。
/* Your class name may vary */
.css-illustration *,
.css-illustration *:after,
.css-illustration *:before,
.css-illustration:after,
.css-illustration:before {
box-sizing: border-box;
position: absolute;
}
你的键盘会感谢你的!
定位是 CSS 中一个棘手的概念。你可以在 CSS 年鉴中阅读更多关于它的信息,以了解如何使用它。
或者,在这个小小的定位游乐场中玩一玩
坚持一种方法
这是迄今为止最难做到的事情。你如何处理 CSS 图像?你甚至从哪里开始?你应该从最外层开始,然后向内工作吗?这种方法不太好。
很有可能你会尝试一些方法,然后找到更好的方法。你肯定会来回尝试几次,但练习得越多,你就会越擅长发现模式并开发出最适合你的方法。
我倾向于将我的方法与你如何创建矢量图像联系起来,其中插图由图层组成。如果需要,可以将其分解并在纸上进行草图。但是,从底部开始,然后向上工作。这通常意味着先绘制较大的形状,然后再绘制精细的细节。当需要移动元素时,你始终可以调整堆叠索引。
保持样式的良好结构
这将我们引向结构。尝试避免为插图使用扁平的 DOM 结构。保持原子性使移动插图的各个部分变得更容易。它还将使显示和隐藏插图的各个部分甚至稍后对其进行动画处理变得容易得多。考虑 CSS Snorlax 演示。手臂、脚、头部等都是单独的元素。这使得对手臂进行动画处理比我尝试将它们放在一起更容易,因为我可以简单地将动画应用于.snorlax__arm-left
类。
这是我创建演示的时间推移截图
尝试将我们昨晚构建的 CSS Snorlax 的时间推移放在一起😅
— Jhey 🛠 (@jh3yy) 2020年4月28日
回看很有趣!
💻 https://#/vbVYmFUN5V 通过 @CodePen#webdev #coding #HTML #CSS #webdesign #100DaysOfCode #creative #design #animation pic.twitter.com/0mJtLPRQfP
处理不规则形状
这里有一篇关于使用 CSS 创建形状的非常好的文章,就在 CSS-Tricks 上。但是,对于更“不规则”的形状,例如长曲线甚至外曲线,该怎么办?在这些情况下,我们需要跳出框框思考。诸如overflow
、border-radius
和clip-path
之类的属性是强大的助手。
考虑这个 CSS 吉利蛋演示。切换复选框。
这是创建曲线形状的关键!我们有一个比主体大得多的元素,并应用了border-radius
。然后我们将overflow: hidden
应用于主体以切掉该部分。
我们如何创建外曲线?这个有点棘手。但我喜欢使用的一个技巧是带有粗边框的透明元素。然后应用border-radius
并根据需要剪切多余的部分。
如果你点击切换按钮,它会显示我们用来穿过该角的元素。另一个技巧可能是覆盖一个与背景颜色匹配的圆圈。这很好,直到我们需要更改背景颜色。如果你有一个变量或类似的东西来表示该颜色,那也没问题。但是,这可能会使维护变得稍微困难一些。
clip-path
是你的朋友
你可能已经注意到上一个演示中的一些有趣的 CSS 属性,包括 clip-path。如果你想创建复杂的 CSS 形状,你很可能需要clip-path
。当隐藏父级框溢出不起作用时,它对于切断元素的某些部分特别方便。
这是我前段时间构建的一个小演示,展示了不同的clip-path
可能性。
还有一个演示,它借鉴了“CSS 的形状”文章中的想法,并使用clip-path
重新创建。
border-radius
是你的另一个朋友
你将需要border-radius
来创建曲线。一个不常见的技巧是使用“双”语法。这允许你为每个角创建水平和垂直半径。
使用此演示来真正欣赏border-radius
的强大功能。我提倡在整个过程中使用百分比,以便保持响应性。
阴影技巧
你已经拥有了所有形状,所有内容都已很好地布局,并且所有正确的颜色都已到位……但某些内容看起来仍然不合适。很有可能是缺少阴影。
阴影增加了深度并营造出逼真的感觉。考虑一下 Gal Shir 插图的再创作。Gal 擅长使用阴影和渐变来创建美丽的插图。我认为重新创作它并包含一个切换阴影的开关会很有趣,以便查看它会产生多大的差异。
阴影效果通常通过box-shadow
和background-image
组合创建。
这些属性的关键在于我们可以将它们以逗号分隔列表的形式堆叠。例如,演示中的坩埚有一系列用于跨主体使用的渐变。
.cauldron {
background:
radial-gradient(25% 25% at 25% 55%, var(--rim-color), transparent),
radial-gradient(100% 100% at -2% 50%, transparent, transparent 92%, var(--cauldron-color)),
radial-gradient(100% 100% at -5% 50%, transparent, transparent 80%, var(--darkness)),
linear-gradient(310deg, var(--inner-rim-color) 25%, transparent), var(--cauldron-color);
}
请注意,这里使用的是radial-gradient()
和linear-gradient()
,并且不总是使用完美的圆形数值。同样,这些数字都没问题。事实上,你将花费大量时间在样式检查器中调整和修改内容。
使用box-shadow
通常也是如此。但是,对于后者,我们还可以使用 inset 值来创建棘手的边框和额外的深度。
.cauldron__opening {
box-shadow:
0 0px calc(var(--size) * 0.05px) calc(var(--size) * 0.005px) var(--rim-color) inset,
0 calc(var(--size) * 0.025px) 0 calc(var(--size) * 0.025px) var(--inner-rim-color) inset,
0 10px 20px 0px var(--darkness), 0 10px 20px -10px var(--inner-rim-color);
}
当然,在某些时候,使用filter: drop-shadow()
来获得你想要的效果会更有意义。
Lynn Fisher 的a.singlediv.com 是这些属性实际应用的绝佳示例。在该网站上四处浏览并检查一些插图,以了解如何在插图中使用box-shadow
和background-image
的好方法。
box-shadow
非常强大,你可以用它来创建整个插图。我曾经开玩笑说要创建一个美元的 CSS 插图。
在 CSS 中?😅#webdev #CSS #animation #webdesign #coding #100DaysOfCode #HTML https://#/VmyeySsK83
— Jhey 🛠 (@jh3yy) 2020年4月22日
我使用了一个生成器来使用单个 div 创建插图。但 Alvaro Montoro 更进一步 并编写了一个使用 box-shadow 代替的生成器。
预处理器非常有用
虽然它们不是必需的,但使用预处理器可以帮助你保持代码整洁。例如,Pug 使编写 HTML 变得更快,尤其是在使用循环处理一堆重复元素时。从那里,我们可以对 CSS 自定义属性进行范围限定,这样我们只需要定义一次样式,然后在需要时覆盖它们。
这是另一个演示 DRY 结构的示例。花朵使用相同的标记构建,但每个花朵都有自己的索引类,用于应用范围限定的 CSS 属性。
第一朵花具有这些属性
.flower--1 {
--hue: 190;
--x: 0;
--y: 0;
--size: 125;
--r: 0;
}
它是第一朵,因此所有其他花朵都基于它。注意第二朵花是如何向右和向上稍微偏移的。所有这些只需要为相同的自定义属性分配不同的值即可。
.flower--2 {
--hue: 320;
--x: 140;
--y: -75;
--size: 75;
--r: 40;
}
动画响应式 CSS 利夫在最新的 CodePen Spark 中出现!✨
— Jhey 🛠 (@jh3yy) 2020年5月19日
对于那些不了解动物之森的人来说,利夫是一个热衷于园艺的树懒,他会拜访你的岛屿🌻
这是一个时间推移!📹
💻 https://#/tkHX4nWXp7 来自 @CodePen pic.twitter.com/naJIrsSlYM
就是这样了!
继续前进,使用这些技巧,想出自己的方法,分享它们,并分享你的 CSS 大作!还有,如果你有自己的建议,也请分享!这绝对是那种需要大量尝试和错误才能学到的东西——对我有用的方法可能与对你有效的方法看起来不同,我们可以从这些不同的方法中学习
哇!非常感谢你的建议,Jhey!
嘿,Sai!
没问题。希望你发现它有用 :)
感谢你揭开了 CSS 插图的神秘面纱!我将使用这些技术在周末制作一个妙蛙鸭 :)
一点也不客气,David!听起来很棒。
我考虑过画妙蛙鸭。如果你画了,请分享!我很想看看你的作品。
这真是太棒了
谢谢,Saharsh!我很高兴你喜欢它。
我只是喜欢 https://a.singlediv.com/,所以我尝试用 css 重现一些图形插图。
很棒的文章,谢谢!
它们很棒,Davor!
是的,该网站上有一些很棒的作品。
谢谢!我很高兴你喜欢它。
这篇文章读起来真的很棒!你的延时摄影太棒了,你让 CSS 艺术看起来像骑自行车不用手一样(在这种情况下,不用鼠标画画)。我想这肯定也需要一些精心编排吧!感谢你的这篇文章,很棒的作品 :)
我的意思是,我有点理解,但同时,嗯,不?
我想我的主要问题是“用进废退”的问题。如果 CSS 要完全取代 SVG 的功能,我都没问题,但是如果情况并非如此,我们能否只使用 SVG。它为了成为浏览器规范的一部分已经经历了太多困难,我不希望看到它因为人们找到了利用 CSS 的巧妙方法来做 SVG 已经做得更好的事情而消失。
感觉就像我们只是勉强回到了一个工具弥补 Flash 留下的空白的地方。
我喜欢一些好的技巧,让程序实现超出最初设计的功能,但不可否认的是,如果其他方法更容易实现相同的功能,我就会看到更少的意义。
PS 这项工作令人惊叹,并决心实现它们的一些东西。几年前的 CSS 伦敦地铁地图是我第一次看到这种趋势,当时我就像“哇,但是为什么?”
</希望稍微合理一点的抱怨结束>
我惊呆了!感谢这篇文章。我现在要练习了。
这篇文章真的很棒,但是,从如此复杂的程度来看,我想知道 SVG 是否更合适。供大家参考。
大致翻译