使用 CSS 绘制单个 Div 图形

为什么使用单个 Div?

2013 年 5 月,我参加了 CSSConf 并观看了Lea Verou 关于 border-radius 的演讲。这是一个令人大开眼界的演讲,我意识到自己对 CSS 行为的理解还不够全面。这让我回想起我在艺术学院学习的时光,当时我不断被鼓励成为自己所选媒介的大师。作为一名网页设计师,CSS 就是我的媒介,因此我挑战自己去学习尽可能多的知识,并探索和实验它的极限。

但是,为什么使用单个 div 呢?

当我学习绘画时,我的课程会进行一些颜色混合练习,我们从三种原色(红、黄、蓝)中创造出各种光谱颜色。练习的目的是学习媒介的行为,而限制则向我们展示了组合的力量。你当然可以购买绿色颜料,但你也可以用蓝色和黄色混合出绿色。限制你的可用选项会迫使你重新评估你现有的工具。

我决定开始一个CSS 绘图项目,每隔几天用 CSS 绘制一些新的东西。为了进一步挑战和探索 CSS 的能力,我给自己设定了一个限制,即在标记中只使用一个 div。与其购买绿色颜料(或添加另一个 div),我需要扩展和组合 CSS 属性来实现我的目标。

工具包

只使用一个 div 和浏览器支持的 CSS 属性,似乎工具过于有限。我发现,重要的不是你有什么工具,而是你如何看待它们。

伪元素

在 HTML 中使用一个 div,由于 CSS 伪类,我实际上可以使用三个元素。因此,使用 div、div:before 和 div:after,我们可以得到如下效果

pseudo elements

  div { background: red; }
  div:before { background: yellow; }
  div:after { background: blue; }

将这三个元素视为可以按顺序出现以及三个可叠加的图层会很有帮助。所以在我的脑海中,它通常看起来更像这样

pseudo elements as layers

形状

使用 CSS 和一个元素,我们可以得到三种基本形状类型。我们可以使用 width 和 height 属性创建正方形/矩形,使用 border-radius 创建圆形/椭圆形,使用 border 创建三角形/梯形。

css shapes

我们还可以使用 CSS 创建其他形状,但大多数东西都可以简化为这些形状的某种组合。而且这些是最容易创建和操作的。

相同形状的多个实例

使用多个 box-shadows,我们可以创建许多不同大小、颜色和模糊度的相同形状的版本。在 x 和 y 轴上进行偏移,可以让我们获得几乎无限的多个实例。

multiple box-shadows

div {
    box-shadow: 170px 0 10px yellow,
                330px 0 0 -20px blue,
                330px 5px 5px -20px black;
}

我们甚至可以为我们的 box-shadows 添加 box-shadows。注意它们声明的顺序。同样,将它们视为图层会很有帮助。

渐变

渐变可以通过暗示光源来添加阴影和深度。这使得简单的平面形状看起来更逼真。组合多个 background-images 允许我们使用许多分层的渐变来实现更复杂的阴影,甚至更多形状。

gradients

div {
    background-image: linear-gradient(to right, gray, white, gray, black);
}

div:after {
    background-image: radial-gradient(circle, yellow 50%, transparent 50%),
                      linear-gradient(to right, blue, red);
}

可视化

最困难的部分是将这些部分可视化地组合成一个可识别的图形。尽管我关注图形的技术方面,但流程的这一部分至关重要。为了帮助解决这个问题,我通常会查看主题的照片并将其可视地分解成碎片。一切都是形状,一切都是颜色。我将整体图片简化为更小的形状或颜色块,我知道(或怀疑可以)用 CSS 实现。

演示

让我们仔细看看两个图形,并分解构成较大图片的一些碎片。首先是绿色蜡笔。

蜡笔由两种主要形状组成:矩形主体和三角形绘图笔尖。

crayon shapes

为了捕捉逼真的图像,我必须保证以下几点

  • 纸包装的不同颜色
  • 包装上的印刷图案和文字
  • 显示蜡笔圆润度的阴影
  • 显示圆润度和光源的光泽度

所以首先,我使用 div 和背景颜色、从上到下的渐变以及 box-shadow 来创建蜡笔的主体,以显示一些维度

crayon body

(注意,我在这里使用黑色(a)和白色(a)的混合来代替 rgba)

div {
    background: #237449;
    background-image: linear-gradient(to bottom,
                                  transparent 62%,
                                  black(.3) 100%);
    box-shadow: 2px 2px 3px black(.3);
}

然后我添加了一个从左到右的线性渐变来创建包装。它的 alpha 值为 .6,因此之前的一些渐变会透出来。

crayon wrapper

div {
    background-image: linear-gradient(to right,
                                  transparent 12px,
                                  rgba(41,237,133,.6) 12px,
                                  rgba(41,237,133,.6) 235px,
                                  transparent 235px);
}

接下来,我使用相同的从左到右的渐变技术来创建蜡笔上的印刷条纹。

crayon printing

div {
    background-image: linear-gradient(to right,
                                  transparent 25px,
                                  black(.6) 25px,
                                  black(.6) 30px,
                                  transparent 30px,
                                  transparent 35px,
                                  black(.6) 35px,
                                  black(.6) 40px,
                                  transparent 40px,
                                  transparent 210px,
                                  black(.6) 210px,
                                  black(.6) 215px,
                                  transparent 215px,
                                  transparent 220px,
                                  black(.6) 220px,
                                  black(.6) 225px,
                                  transparent 225px);
}

对于印刷的椭圆形,径向渐变非常有效!

crayon printing

div {
    background-image: radial-gradient(ellipse at top,
                                  black(.6) 50px,
                                  transparent 54px);
}

我将其分解以演示每个部分,但请记住,background-image 实际上看起来会像这样

div {
                      // ellipse printed on wrapper
    background-image: radial-gradient(ellipse at top,
                                  black(.6) 50px,
                                  transparent 54px),
                      // printed stripes
                      linear-gradient(to right,
                                  transparent 25px,
                                  black(.6) 25px,
                                  black(.6) 30px,
                                  transparent 30px,
                                  transparent 35px,
                                  black(.6) 35px,
                                  black(.6) 40px,
                                  transparent 40px,
                                  transparent 210px,
                                  black(.6) 210px,
                                  black(.6) 215px,
                                  transparent 215px,
                                  transparent 220px,
                                  black(.6) 220px,
                                  black(.6) 225px,
                                  transparent 225px),
                      // wrapper
                      linear-gradient(to right,
                                  transparent 12px,
                                  rgba(41,237,133,.6) 12px,
                                  rgba(41,237,133,.6) 235px,
                                  transparent 235px),
                      // crayon body shading
                      linear-gradient(to bottom,
                                  transparent 62%,
                                  black(.3) 100%)
}

因此,在完成 div 之后,我继续使用 :before 伪元素来创建蜡笔的三角形笔尖。使用实线和透明边框,我创建了一个三角形并将其放置在我刚刚绘制的 div 旁边。

triangle crayon tip

div:before {
    height: 10px;
    border-right: 48px solid #237449;
    border-bottom: 13px solid transparent;
    border-top: 13px solid transparent;
}

它与蜡笔的主体相比显得有些平淡,但可以通过 :after 伪元素进行修复。使用它,我添加了一个从上到下的线性渐变来创建一个反射光泽效果,该效果跨越蜡笔的整个宽度。

crayon gloss

div:after {
    background-image: linear-gradient(to bottom,
                                    white(0) 12px,
                                    white(.2) 17px,
                                    white(.2) 19px,
                                    white(0) 24px);
}

这增加了更多维度和真实感,并有助于解决那个扁平的三角形。作为最后的润色,我在 :after 中添加了一些文本内容,并将其定位为蜡笔包装上的另一个印刷元素。

crayon color label

div:after {
    content: 'green';
    font-family: Arial, sans-serif;
    font-size: 12px;
    font-weight: bold;
    color: black(.3);
    text-align: right;
    padding-right: 47px;
    padding-top: 17px;
}

就是这样!

让我们看看另一个例子

蜡笔是使用 background-image 和渐变产生逼真效果的一个很好的例子。以下是一个展示多个 box-shadows 强大功能的例子:一个单个 div 相机。

这是相机的主体,使用 background-image 和 border-image 创建。

camera body

这是一个 gif,演示了 :before 伪元素(黑色矩形)以及使用其 box-shadows 创建的许多细节。

camera rectangle

div:before {
    background: #333;
    box-shadow: 0 0 0 2px #eee,
                -1px -1px 1px 3px #333,
                -95px 6px 0 0 #ccc,
                30px 3px 0 12px #ccc,
                -18px 37px 0 46px #ccc,

                -96px -6px 0 -6px #555,
                -96px -9px 0 -6px #ddd,

                -155px -10px 1px 3px #888,
                -165px -10px 1px 3px #999,
                -170px -10px 1px 3px #666,
                -162px -8px 0 5px #555,

                85px -4px 1px -3px #ccc,
                79px -4px 1px -3px #888,
                82px 1px 0 -4px #555;
}

类似地,这是 :after(灰色圆圈)及其几个 box-shadow 细节。

camera circle

div:after {
    background: linear-gradient(45deg, #ccc 40%, #ddd 100%);
    border-radius: 50%;
    box-shadow: 0 3px 2px #999,
                1px -2px 0 white,
                -1px -3px 2px #555,
                0 0 0 15px #c2c2c2,
                0 -2px 0 15px white,
                -2px -5px 1px 17px #666,
                0 10px 10px 15px black(.3),

                -90px -51px 1px -43px #aaa,
                -90px -50px 1px -40px #888,
                -90px -51px 0 -34px #ccc,
                -90px -50px 0 -30px #aaa,
                -90px -48px 1px -28px black(.2),

                -124px -73px 1px -48px #eee,
                -125px -72px 0 -46px #666,
                -85px -73px 1px -48px #eee,
                -86px -72px 0 -46px #666,
                42px -82px 1px -48px #eee,
                41px -81px 0 -46px #777,
                67px -73px 1px -48px #eee,
                66px -72px 0 -46px #666,

                -46px -86px 1px -45px #444,
                -44px -87px 0 -38px #333,
                -44px -86px 0 -37px #ccc,
                -44px -85px 0 -34px #999,

                14px -89px 1px -48px #eee,
                12px -84px 1px -48px #999,
                23px -85px 0 -47px #444,
                23px -87px 0 -46px #888;
}

有点疯狂,但如你所见,多个 box-shadows 可以为单个 div 绘图添加很多细节。

最大的挑战

我遇到的两个最大的障碍是三角形形状方面的限制以及渐变的自然行为。

三角形的麻烦

因为三角形是使用边框创建的,所以它限制了我对它们的处理。使用 border-image 添加渐变会应用于所有边框,而不仅仅是一侧。Box-shadows 应用于框的形状,而不是边框创建的三角形形状,因此创建多个三角形形状可能很困难。以下是一个示例,展示了它的外观

trouble with triangles

div {
    border-left: 80px solid transparent;
    border-right: 80px solid transparent;
    border-bottom: 80px solid red;
}

div:before {
    border-left: 80px solid transparent;
    border-right: 80px solid transparent;
    border-bottom: 80px solid red;
    border-image: linear-gradient(to right, red, blue);
}

div:after {
    border-left: 80px solid transparent;
    border-right: 80px solid transparent;
    border-bottom: 80px solid red;
    box-shadow: 5px 5px 5px gray;
}

分层渐变

对于渐变,它们的自然行为是填充整个背景。当将多个渐变叠加在一起时,这可能会变得有点棘手。需要花费一些额外的时间来考虑透明度、z-index 以及了解哪些内容可见以及哪些内容不可见。通过有效地使用此技术,我们的图形可以具有惊人的细节。

Tardis 是一个展示和隐藏渐变以创建详细图片的很好的例子。这是一个绘制过程中的图形,显示了一些跨越容器整个宽度的从上到下的渐变。

single div tardis in-process

使用从左到右和从右到左的渐变,我能够覆盖渐变的部分并留下一些部分暴露在外。

single div tardis in-process

生成的图形似乎有许多形状构成 Tardis 的正面,但它是经过策略性分层的线性渐变。有时你必须伪造它。

查看它们在实际中的效果

由于这个项目,出现了一件很棒的事情,那就是一个由Rafael Carício创建的非常酷且有用的 Chrome 浏览器扩展,名为CSS Gradient Inspector。它扩展了开发者工具,可以检查和切换每个元素的渐变,就像它们是图层一样。(在日常项目中也非常有用。)

我非常高兴地看到设计师和开发人员使用动画和 JavaScript 功能对这些图形进行实验和改进。访问该网站并使用您自己的 CSS 进行尝试,网址为a.singlediv.com或在GitHub上!

关于 Lynn Fisher

Lynn Fisher 是一位居住在亚利桑那州钱德勒市的艺术家和设计师。她与&yet的优秀团队合作,设计和开发界面,偶尔也会画画。她喜欢将艺术与技术相结合,并改善设计师和开发人员之间的协作方式。

更多 Lynn Fisher 的文章……

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道师和编辑。发表关于 HTML5、JavaScript 和开放网络的演讲和博客文章。Robert 坚定地相信 HTML5 和开放网络,自 1999 年以来一直从事网页前端开发工作 - 在瑞典和纽约市。他还定期在http://robertnyman.com上发表博客文章,并且喜欢旅行和结识新朋友。

更多 Robert Nyman [荣誉编辑] 的文章……


17 条评论

  1. Evgeny Stepanischev

    看看这个

    http://bolknote.ru/files/webkit-purecss-image.html

    2014 年 9 月 15 日 06:12

    1. Robert Nyman [编辑]

      看起来不错!只有 WebKit 版本吗?

      2014 年 9 月 16 日 00:11

    2. Rizqi Nizamil

      哇,太酷了!

      2014 年 9 月 16 日 02:29

    3. Joao

      x)

      2014 年 9 月 16 日 06:12

  2. njn

    非常酷!

    2014 年 9 月 15 日 20:11

  3. Felipe Moura

    很棒的作品,以及逐步展示魔法发生过程的绝佳方式!:)

    我之前写过关于 CSS 中几何图形的文章,但它比你的这些要简单得多
    http://felipenmoura.org/en/articles/creating-geometric-forms-using-divs-and-css3/

    2014 年 9 月 16 日 06:12

    1. Lynn Fisher

      太棒了!正是这样的文章帮助我开始了 CSS 绘图实验。

      感谢分享!:)

      2014 年 9 月 16 日 09:12

  4. Jose Damian

    哇!!!这真是太酷了!我不知道仅用 CSS 就可以做出如此棒的东西。好吧,我真的很想学习这样的东西。

    2014 年 9 月 16 日 07:04

    1. Lynn Fisher

      绝对可以尝试!进行实验并看看你能做出什么。对我来说,这是一次非常棒的学习体验。(而且非常有趣。)

      2014 年 9 月 16 日 09:15

  5. Rusty GB

    太棒了!!!用一个 div 做出来真是太棒了。我真的很想知道你在这些发现和学习中的路径

    2014 年 9 月 16 日 12:02

  6. Arrow Fan

    我的天哪!这太酷了。Tardis 真的很令人印象深刻。CSS 比图像占用更少的空间,当然。

    2014 年 9 月 17 日 13:14

  7. Nowai Matthew

    哇!多么棒的文章!从未想过以这种方式使用 CSS。

    2014 年 9 月 18 日 07:07

  8. Bijay

    那真是太酷了,伙计。很喜欢。

    2014 年 9 月 20 日 21:59

  9. Jay Wilson Jr.com

    Lynn,很棒的文章!

    几周前我偶然发现了你的网站,我想知道你是如何以及为什么创建这些图像的。你的网站是灵感的来源,它让我开阔了眼界,看到了纯 CSS 的可能性……

    太棒了!!

    2014 年 9 月 22 日 17:16

  10. Dimas Dono

    这篇文章很好,适合有创意的人。我以为用 CSS 创建其他图形很难,即使是抽象图形。这篇文章让我对 CSS 有了更多了解,并意识到自己可以成为一个有创意的人,并在网页设计中表达自己的想法。所以,感谢你的信息。这真是太棒了,也很有用。:-)

    2014 年 9 月 26 日 16:05

  11. Jane

    太棒了

    2014 年 9 月 27 日 00:02

  12. ArrowFanToo

    非常好。谢谢。

    2014 年 10 月 2 日 13:45

本文的评论已关闭。