前端开发者学堂 - fedev.cn

CSS的视觉效果

发布于 大漠

记得在第五届中国区的CSS Conf大会上@张鑫旭、@袁川两位老师分享的话题都和视觉以及创意有关的话题,而且在社区中有关于这方面讨论也多,比如说@Lea Verou的《CSS Secrets》一书就有很多介绍CSS用来创建视觉效果的技巧。另外,在社区也有很多关于这方面的介绍,比如CSSArt网站ASingleDiv网站CodePen上的SingleDiv等。当然,也有同学私下找我,让我能不能抽空聊聊这方面的话题,那么今天,我们就和大家来聊聊如何使用CSS来实现一些视觉效果,在这里我更倾向于和项目中的一些需求和大家讨论这方面的话题。希望大家对这方面话题感兴趣。

项目中对视觉效果的诉求

对于Web开发人员来说,或者说对于视觉还原(称呼高级一点是UI开发者)来说,在项目中有很多效果(视觉效果)是可以纯CSS代码来实现的,比如像下图这些效果:

上图都是摘自历年来项目中的视觉稿

看到上图中各视觉元素的效果时,试问一下,你是采用纯CSS的代码来实现呢?还是切出图片运用于项目中呢?如果我来回答该问题的话,那就是用代码来实现。我一向的原则是:

能用代码实现的效果绝不采用图片

具体原因我就不说了,大家都懂的。那么在CSS中怎么来实现这些效果,或者说用纯代码来更真切的还原视觉效果,即CSS视觉效果

纯CSS绘图要素

在CSS的世界中我们有一些属性可以用来绘制图形,其中最为常见的有:

border

CSS的border可以轻易的绘制线条,比如直线,点线等:

border除了绘制线形之外,还可以绘制一些其他的图形,其中最为熟悉的就是三角形之类的:

正如上例所示,如果元素widthheight值为非0时,还可以绘制出别的图形。

box-shadow

估计在很多开发者的认知中,box-shadow只是用来给元素盒子添加阴影效果,事实上并非如此,借助box-shadow独有的特性,除了可以绘制阴影之外,还可以绘制一些图形。比如绘制一个3D形状:

但社区中有人提出,box-shadow对于Web性能有所影响特别是会减缓滚动速度,因此在绘制3D的效果时,时常会借助::before::after来处理(据说对于性能有所提高):

CSS的伪元素::before::after在Web中还可以做很多事情,如果你对这方面感兴趣的话,可以阅读《伪元素能帮助我们做些什么》一文。

除此之外,box-shadow还可以模拟出多个DOM元素,在ASingleDiv网站中提供的示例中就不缺这种效果:

利用该特性,并且配合CSS的animation就可以创建很多具有创意的Loading效果,比如:

border-radius

对于大多数Web开发者而言,习惯性的给border-radius设置一个参数,比如border-radius: 50%绘制一个圆或椭圆。但是,如果我们给border-radius设置八个参数值时,可以创建不同的形状:

这种特性用于实际案例中也具有独特的视觉效果:

@supremebeing09在他的教程《CSS Border-Radius Can Do That?》(译文)中还专门提供了一个工具,让你更好的控制border-radius的值,实现不同的效果:

具体的案例可以看看@Louise Flanagan绘制的超级马里奥的脸

渐变

CSS的渐变属性已经是非常成熟的了,其主要分为线性渐变(linear-gradient()repeating-linear-gradient())、径向渐变(radial-gradient()repeating-radial-gradient())以及圆锥渐变(conic-gradient()repeating-conic-gradient())等。有关于这方面更详细的介绍可以阅读:

使用CSS的渐变特性可以帮助我们绘制出很多图形,比如@Lea Verou创建的CSS Patterns Gallery:

如果你对这方面感兴趣的话,还可以阅读《51 CSS Background Patterns》一文中提供的各种效果。

另外,时至今日,CSS渐变在实际项目中使用非常的广泛。比如文章开头提展示的一些UI效果,大部分都有渐变的身影。比如进度条的效果,使用repeating-linear-gradient()就可以很容易的实现:

@rkchauhan提供了一个纯CSS实现的各种不同效果的进度条,共中很多就离不开渐变:

有关于渐变更多的效果还可以点击这里查看,另外,如果你对使用渐变绘制图形过程感兴趣的话,可以阅读《使用CSS渐变绘图》一文,英文原文可以点击这里查看

除此之外,CSS的渐变特性和background-sizebackground-clip等背景属性结合在一起也能绘制出一些具有创意的图形效果:

使用渐变最难的并不是他的用法,而且对颜色的掌握以及对光线角度的掌握,这方面并不是说掌握CSS就能做的。比如@Bradley Taunt提供的一个案例,使用CSS渐变绘制出一个立体的圆球形

clip-path

对于绘制图形而言,clip-path中提供的函数是最灵活的。可以很好的帮助我们绘制出不同的图形

有关于clip-path更详细的介绍,可以阅读《探索CSS Masking模块:Clipping》一文。

clip-path除了可以实现绘制一些图形之外,还可以实现一些具有创意的效果,比如《响应式网格布局》一文提到的穿叉效果:

clip-path在动效方面的创意也很丰富,比如@Travis Almand的《Animating with Clip-Path》和@Mikael Ainalem的《Creating Morphing Animations with CSS clip-path》提到的效果:

在布局方面也有独到之处,特别是和CSS的Shape相关属性结合在一起可以实现一些异形布局效果

transform

CSS的transform属性中包含了多个功能性函数,比如translate()rotate()skew()scalc()translate3d()rotate3d()scale3d()等。

目前W3C规范对这方面的描述也是非常的详细,主要的文档规范有:

CSS transform可做的事情非常地多,比如Codepen上的这些案例

同样的,用来绘制图形也是非常有用的,比如@Louise Flanagan写的这个Demo,就有很多地方使用了rotate

另外在构建3D和制作动画,CSS的transform也很有用。不过这里不花时间阐述,感兴趣的话可以阅读:

overflow

overflow在图形绘制中所起的作用大都是用来隐藏溢出的部分,即overflow: hidden使用的多。事实上,CSS的overflow也是在CSS中也是一个强大的功能模块,早前在《你所不知道的CSS Overflow Module》一文中有做过深入的探讨。

上面这几个属性在CSS绘图世界中用得最为频繁的属性,当然,CSS是非常有意思的,绘制同一个图形可能会有多种不同的姿势。另外,这些技巧只是一些基础知识,如果要真正的实现一些具有创意的效果,那还是需要打开自己的灵感。比如@袁川大大分享的《Generative Art With CSS》:

如果你感兴趣的话,也可以关注<css-doodle>,你会领略到CSS神奇的另一面,也会感叹到原来CSS离艺术的创作是这么的近:

附文:《一个制作Web图案的组件:css-doodle》。

CSS 绘制动物森林场景

最近动物森林(Animal Crossing)游戏非常的火爆(你玩吗?),但是说,用CSS来绘制一个类似动森中的一个场景,你可能会像为觉得不可思议吧。但是@Tee Diang就做到了

看上去很复杂,对吧。其实里面用到的技术都是前面和大家聊的部分。我们来看看怎么使用前面聊的技术来绘制。这里并不会完全介绍怎么绘制这样的一个游戏场景,只是会以其中的部分元素为例,目的是告诉大家,其实这也很简单。

我们来看其中几个元素的制作。先来看椰汁树的制作:

<!-- HTML -->
<div class="palm-tree joycon-tree">
    <div class="palm-trunk"></div>
    <div class="palm-fruit"></div>
    <div class="palm-leaves">
        <div class="palm-leaf">
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
        </div>
        <div class="palm-leaf">
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
        </div>
        <div class="palm-leaf">
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
        </div>
        <div class="palm-leaf">
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
            <div class="edges"></div>
        </div>
    </div>
</div>

CSS很简单,就是我们平常写的一些属性:

// CSS
.palm-tree {
    width: 100px;
    height: 200px;
    position: relative;
    transform: scaleX(-1);
}

.palm-tree::after {
    content: "";
    position: absolute;
    width: 60px;
    height: 40px;
    background-color: rgba(0, 0, 0, 0.3);
    bottom: 0;
    z-index: -1;
    border-radius: 100%;
    left: 40px;
    bottom: -10px;
}

.palm-trunk {
    position: absolute;
    width: 30px;
    height: 100px;
    background-color: #df916a;
    border-radius: 30px;
    bottom: 0;
    left: 40%;
    transform: rotate(-10deg);
    background: linear-gradient(135deg, #d67240 25%, transparent 25%),
        linear-gradient(225deg, #d67240 25%, transparent 25%) -50px 0,
        linear-gradient(315deg, #d67240 25%, transparent 25%) -50px 0,
        linear-gradient(45deg, #d67240 25%, transparent 25%);
    background-size: 50px 50px;
    background-color: #df916a;
    border: 1px solid black;
    box-shadow: inset 0px 30px 10px #7c2020;
}

.palm-fruit {
    position: absolute;
    z-index: 2;
}

.palm-fruit::before,
.palm-fruit::after {
    content: "";
    position: absolute;
    background-color: #ffea71;
    width: 30px;
    height: 30px;
    border-radius: 100%;
    border: 1px solid brown;
    box-shadow: inset 3px -3px goldenrod;
}

.palm-fruit::before {
    left: 40px;
    top: 90px;
}

.palm-fruit:after {
    top: 95px;
    left: 20px;
}

.palm-leaves {
    position: absolute;
}

.palm-leaf {
    position: absolute;
    width: 90px;
    height: 40px;
    background-color: #7abe7a;
    border-top-left-radius: 90px;
    border-top-right-radius: 90px;
    border: 1px solid black;
    border-bottom: none;
    display: flex;
    justify-content: center;
    align-items: baseline;
}

.palm-leaf:nth-child(1) {
    transform: rotate(-20deg);
    left: 30px;
    top: 50px;
    animation: rotateLeaf1 3s ease infinite;
}

.palm-leaf:nth-child(2) {
    transform: rotate(20deg);
    left: -40px;
    top: 50px;
    animation: rotateLeaf2 3s ease infinite;
}

.palm-leaf:nth-child(3) {
    transform: rotate(-30deg);
    left: -40px;
    top: 90px;
    width: 80px;
    height: 40px;
    animation: rotateLeaf3 3s ease infinite;
}

.palm-leaf:nth-child(4) {
    transform: rotate(20deg);
    left: 40px;
    top: 75px;
    width: 80px;
    height: 40px;
    border-top: none;
    animation: rotateLeaf2 3s ease infinite;
}

@keyframes rotateLeaf1 {
    50% {
        transform: rotate(-25deg);
    }
}

@keyframes rotateLeaf2 {
    50% {
        transform: rotate(25deg);
    }
}

@keyframes rotateLeaf3 {
    50% {
        transform: rotate(-35deg);
    }
}

.edges {
    flex: 1 1 0;
    height: 14px;
    width: 100%;
    margin-top: 29px;
    border-radius: 100%;
    background-color: #7abe7a;
    border-bottom: 2px solid black;
}

我们再来看另一个部分,就是人物部分:

<!-- HTML -->
<div class="isabelle">
    <div class="legs"></div>
    <div class="arm right-arm"></div>
    <div class="dress"></div>
    <div class="arm left-arm"></div>
    <div class="head-container">
        <div class="head">
            <div class="right-ear"></div>
            <div class="left-ear"></div>
            <div class="bangs"></div>
            <div class="ponytail"></div>
            <div class="face">
                <div class="eyes"></div>
                <div class="closed"></div>
                <div class="cheeks"></div>
                <div class="mouth"></div>
                <div class="nose"></div>
            </div>
        </div>
    </div>
</div>

对应的CSS:

// CSS
.isabelle {
    width: 100px;
    height: 150px;
    position: relative;
}

.isabelle:after {
    content: "";
    position: absolute;
    width: 65px;
    height: 28px;
    background-color: rgba(0, 0, 0, 0.2);
    z-index: -1;
    right: 25px;
    border-radius: 100%;
    bottom: 13px;
    transform: skew(25deg);
}

.head,
.face {
    position: absolute;
    left: calc(50% - 27px);
    width: 58px;
    height: 40px;
    background-color: #ffe89e;
    border-bottom-left-radius: 20px;
    border-bottom-right-radius: 15px;
    border-top-right-radius: 15px;
    border-top-left-radius: 10px;
    top: 50px;
    border: 1px solid #ff9292;
    background-image: radial-gradient(
        35px 30px at 70% 90%,
        white 60%,
        transparent 61%
    );
    animation: rotateHead 8s ease infinite;
}

@keyframes rotateHead {
    50% {
        transform: rotate(-10deg);
    }
}

@keyframes rotateEar {
    50% {
        z-index: -1;
        transform: scaleX(-1) rotate(10deg);
    }
}

.left-ear,
.right-ear,
.right-ear {
    position: absolute;
    background-color: #ffdc6b;
    top: -22px;
}

.left-ear,
.right-ear {
    width: 30px;
    height: 65px;
    border: 1px solid brown;
    border-radius: 84% 16% 42% 58% / 79% 9% 91% 21%;
    left: -15px;
    transform: rotate(10deg);
}

.right-ear {
    transform: scaleX(-1) rotate(25deg);
    z-index: -1 !important;
    left: 30px;
}

.bangs {
    position: absolute;
    width: 100%;
    height: 30px;
    left: 1px;
    transform: scale(0.95);
    z-index: 2;
}

.bangs:before,
.bangs:after {
    content: "";
    position: absolute;
    width: 35px;
    height: 30px;
    background-color: #ffe89e;
    border: 1px solid #ff6565;
    top: -25px;
}

.bangs:before {
    transform: scaleX(1.2) rotate(-25deg);
    z-index: 2;
    border-radius: 73% 27% 70% 50% / 88% 29% 71% 52%;
}

.bangs:after {
    transform: rotate(10deg);
    border-radius: 30% 70% 30% 70% / 30% 77% 53% 70%;
    left: 30px;
}

.ponytail {
    width: 20px;
    height: 10px;
    background-color: #ac0000;
    position: absolute;
    top: -32px;
    left: 20px;
    border-radius: 100%;
}

.ponytail:before,
.ponytail:after {
    content: "";
    position: absolute;
    background-color: #ffe89e;
    border: 1px solid brown;
}

.ponytail:before {
    width: 28px;
    height: 28px;
    border-radius: 100%;
    top: -25px;
    left: -10px;
}

.ponytail:after {
    width: 20px;
    height: 20px;
    border-radius: 100%;
    border-left: none;
    border-top: none;
    top: -17px;
    left: 10px;
}

.face {
    top: initial;
    animation: none;
}

.nose {
    position: absolute;
    width: 10px;
    height: 5px;
    background-color: black;
    border-radius: 100%;
    left: 65%;
    top: 20px;
}

.eyes {
    position: absolute;
    width: 50px;
    left: 20px;
    animation: toggleOpen 4s infinite;
}

.eyes:before,
.eyes:after {
    content: "";
    position: absolute;
    width: 12px;
    height: 12px;
    border-top: 3px solid black;
    border-top-left-radius: 100px;
    border-top-right-radius: 40px;
    top: 8px;
    background-image: radial-gradient(
        1px 2px at 80% 40%,
        white 99%,
        transparent 100%
        ),
        radial-gradient(3px 5px at 70% 35%, black 99%, transparent 100%);
    }

.eyes:after {
    width: 10px;
    transform: scaleX(-1);
    right: 14px;
}

.closed {
    position: absolute;
    opacity: 0;
    left: 45px;
    animation: toggleClose 4s infinite;
}

.closed:before,
.closed:after {
    content: "";
    position: absolute;
    width: 12px;
    height: 12px;
    border-bottom: 3px solid black;
    border-radius: 100%;
    top: 5px;
}

.closed:after {
    width: 10px;
    right: 14px;
}

.cheeks {
    position: absolute;
    width: 100%;
}

.cheeks:after {
    content: "";
    position: absolute;
    width: 6px;
    height: 2px;
    background-color: #ffd038;
    border: 1px solid orange;
    border-radius: 100%;
    left: 15px;
    top: 22px;
}

.mouth {
    position: absolute;
    width: 20px;
    height: 8px;
    transform: scale(0.7);
    background-color: maroon;
    border: 1px solid black;
    border-bottom-left-radius: 90px;
    border-bottom-right-radius: 90px;
    left: 30px;
    top: 27px;
    overflow: hidden;
}

.mouth:before {
    content: "";
    position: absolute;
    width: 9px;
    height: 4px;
    background-color: pink;
    border-radius: 100%;
    left: 5px;
    top: 5px;
}

.dress {
    position: absolute;
    width: 45px;
    height: 80px;
    background-color: #4f4363;
    top: 50px;
    left: 30px;
    background-image: radial-gradient(lavender 30%, transparent 0),
        radial-gradient(lavender 30%, transparent 0);
    background-size: 30px 30px;
    background-position: 0 0, 15px 15px;
    clip-path: polygon(20% 0%, 80% 0%, 100% 100%, 0% 100%);
    clip-path: polygon(37% 0, 65% 0, 100% 100%, 0% 100%);
    border-bottom-left-radius: 10px;
    border-bottom-right-radius: 10px;
}

.arm {
    position: absolute;
    width: 10px;
    height: 30px;
    background-image: linear-gradient(to bottom, #4f4363 30%, #ffe89e 31%);
    border-radius: 30px;
    border: 1px solid black;
}

.left-arm {
    position: absolute;
    left: 24px;
    top: 86px;
    transform: rotate(45deg);
}

.right-arm {
    position: absolute;
    left: 55px;
    top: 86px;
    transform: rotate(25deg);
}

.legs {
    width: 20px;
}

.legs:before,
.legs:after {
    content: "";
    position: absolute;
    width: 10px;
    height: 30px;
    background-color: #ffe89e;
    border-radius: 90px;
    border: 1px solid black;
    transform: rotate(-45deg);
}

.legs:before {
    top: 115px;
    left: 45px;
}

.legs:after {
    top: 110px;
    left: 60px;
}

从这两个示例的代码来看,你会发现,这里面并没有什么复杂的代码。

来自于项目中的案例

可能大家觉得上面动森中的示例难以用到实际项目中来,但我想说的是,他们的原理是一致的,正如文章最开头提到的一些示例,完全可以用纯CSS的代码来实现。接下来,我们就来看一些示例。

Ribbon的制作

Ribbon的效果在实际项目是很常见的一种效果,而且有很多不同风格的Ribbon效果。比如在一个卡片的右上角有一个Ribbon,如下图所示:

就该效果而言,有很多种方式来实现,但随着clip-path的到来,实现这样的效果可以说是非常的简单了。

我想你从上图中就看出左右两者之间的差异。特别是在要实现上图右侧的效果时,那么clip-path会变得更为灵活。我们来看一个实际效果,比如你有一个卡片:

.card::before {
    content: "";
    position: absolute;
    left: -4px;
    top: -4px;
    width: 100px;
    height: 100px;
    border-radius: 12px;
    z-index: 9;
    clip-path: polygon(0 0, 100% 0, 104% 0, 0% 104%);
    background:  #ffb202 url('data:image/png;base64,...') no-repeat 14px 14px;
    background-size: 32px 32px;
}

当然,除了clip-path方法之外,还可以采用最古老的方式,那就是使用border来实现:

.card::before {
    content: "";
    position: absolute;
    left: -4px;
    top: -4px;
    border: 50px solid #ffb202;
    border-right-color: transparent;
    border-bottom-color: transparent;
    border-radius: 13px;
}

不同的是,如果在给Ribbon添加别的元素,那这种方式就有点困难了,需要另外来处理:

可以说,有了clip-path,很多Ribbon的效果都可以非常容易的实现

Badge

最近在一个项目中有一个类似下图这样的需求:

面对这样的效果,估计很多同学第一意愿是用图标来替代这些系列号,但在我的方案中,我采用了另一种姿势,那就是使用CSS的Masking技术,简单地说,使用下图做为mask-image源:

路径 15

我们的代码可以这么写:

<!-- HTML -->
<li><span>1</span></li>

//CSS
li {
    width: 42px;
    height: 42px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
}

li span {
    width: 42px;
    height: 42px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    mask: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAnBAMAAABgRHy1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAwUExURUdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVM9DkAAAAPdFJOUwDylNS/BSoc4jZGea5aCCjbjdkAAACqSURBVCjPY2BgSDL8jwQ+GiUwAEHQfzTwKYCBgdUfXfS/KwMDE4bg/08MDPmYov8TGPSxiCow2GMRNWCQxyIqwIBF8P+HUVGqifZjDXV/rDF0H4voAYYILKILGDgxBb8A09lCDNF2oCg7un2fwYmSF9VxH/cxgMFrlJSiDhFkYK1BEjzGAAeL4YKeDEhgI1TQClmQgR2SYHsZUAEnyH3SAQzowsrCZx/AOACyl6ocYYMMjwAAAABJRU5ErkJggg==")
        no-repeat center;
    mask-size: cover;
    background-color: #ff79b7;
    font-weight: 500;
    font-size: 28px;
    line-height: 1;
    color: #fff;
    z-index: 2;
}

li::after {
    content: "";
    width: 42px;
    height: 42px;
    position: absolute;
    mask: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAnBAMAAABgRHy1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAwUExURUdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVM9DkAAAAPdFJOUwDylNS/BSoc4jZGea5aCCjbjdkAAACqSURBVCjPY2BgSDL8jwQ+GiUwAEHQfzTwKYCBgdUfXfS/KwMDE4bg/08MDPmYov8TGPSxiCow2GMRNWCQxyIqwIBF8P+HUVGqifZjDXV/rDF0H4voAYYILKILGDgxBb8A09lCDNF2oCg7un2fwYmSF9VxH/cxgMFrlJSiDhFkYK1BEjzGAAeL4YKeDEhgI1TQClmQgR2SYHsZUAEnyH3SAQzowsrCZx/AOACyl6ocYYMMjwAAAABJRU5ErkJggg==")
        no-repeat center;
    mask-size: cover;
    left: 0;
    top: 4px;
    z-index: 1;
    background-color: #1c4d8e;
}

li:nth-child(1) span {
    background-color: #ffe12e;
}

li:nth-child(1)::after {
    background-color: #a84700;
}

li:nth-child(2) span {
    background-color: #e7e7e7;
}

li:nth-child(2)::after {
    background-color: #b0b0b0;
}

li:nth-child(3) span {
    background-color: #e1ad5b;
}

li:nth-child(3)::after {
    background-color: #985800;
}

效果如下:

Starbursts

在平时开发中,可能会处理像下图这样的徽标效果:

像这样的效果,CSS处量起为也非常的容易:

绘制不规则图形

下图这个效果也是来自于实际项目中:

就上图而言,我想到的最简单的方法就是使用clip-path,不过需要对坐标点做一点计算:

我们用代码来描述上图的结果:

<!-- HTML -->
<div class="box">
    <span>CSS Clipping</span>
</div>

CSS如下:

.box {
    width: 220px;
    height: 64px;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #1c4d8e;
    font-size: 24px;
    position: relative;
}

.box::before,
.box::after {
    content: "";
    position: absolute;
}

.box::before {
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: #ff79b7;
    clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 8.12% 100%, 0% 71.87%);
    z-index: 1;
}

.box::after {
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
    z-index: 2;
    background-color: #fff;
    clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 7% 100%, 0% 69.57%);
}

.box span {
    position: relative;
    z-index: 3;
}

这个时候只需要改变容器大小,就要以得不同尺寸的结果:

.box2 {
    width: 400px;
    height: 120px;
}

而且使用transform还可以来个镜像:

.box3 {
    width: 30vw;
    height: 30vh;
    transform: scaleX(-1);
}

.box3 span {
    transform: scaleX(-1);
}

效果如下:

如果你想花过多脑细胞去做数学计算的话,还可以使用开发者工具根据原图绘制:

渐变图形

再来看下面这样的示例:

仔细分析一下,我们可以采用多背景来实现:

我们来看其中一个代码:

.card {
    width: 218px;
    min-height: 148px;
    position: relative;
    display: flex;
    flex-direction: column;
    border-radius: 12px 72px 12px 12px;
    border: 1px solid transparent;
    font-size: 24px;
    padding: 70px 18px 18px;
    margin: 10px;
    border-color: rgba(184, 112, 0, 0.28);
    box-shadow: 0 8px 12px 0 rgba(234, 165, 55, 0.3),
        inset 0 1px 3px 0 rgba(255, 255, 255, 0.5);
    color: #870000;
    background-size: 28px 28px, 49px 49px, 103px 103px, cover;
    background-position: calc(100% - 20px) calc(100% - 20px), calc(100% + 10px) calc(100% + 10px), -34px -28px, 0 0;
    background-image:
        radial-gradient(circle, rgba(255,255, 255, .2), rgba(255,255, 255, .2) 70%, rgba(255,255, 255,0) 70%),
        radial-gradient(circle, rgba(255,255, 255, .2), rgba(255,255, 255, .2) 70%, rgba(255,255, 255,0) 70%),
        radial-gradient(circle, rgba(255,255, 255, .3), rgba(255,255, 255, .3) 70%, rgba(255,255, 255,0) 70%), 
        linear-gradient(180deg, #ffe3ae 0%, #ffd34f 100%);
    background-repeat: no-repeat;
}

效果如下:

如何快速通过Sketch还原UI

其实到今天为止,随着设计软件越来越强大,我们还原UI视觉稿的事情变得越来越容易,比如说,通过Sketch就可以快速还原。我们来看一个示例,比如我们要还原这样的一个卡片背景:

正确使用软件,可以获取所有元素的信息:

接着开始撸码了。就该卡片UI效果来看,主要有两部分,顶部类似钉子部分和下面卡片主体部分。HTML结构可以像下面这样来构建:

<!-- HTML -->
<div class="card">
    <div class="card__nail"></div>
    <div class="card__content"></div>
</div>

在Sketch软件中选中对应的图层,可以获取到相应信息:

这些信息都可以为CSS所有,也可以快速复制出CSS:

具体的代码如下:

.card {
    position: relative;
}

.card__nail {
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background-image: linear-gradient(180deg, #fb9f0e 0%, #ff563e 100%);
    box-shadow: 0 2px 0 0 #d03800, 0 2px 7px 0 #b50713;
    top: -20px;
    left: 50%;
    position: absolute;
    transform: translate(-50%, 0);
}

.card__nail::before,
.card__nail::after {
    content: "";
    position: absolute;
    z-index: -1;
    width: 7px;
    height: 50px;
    border-radius: 4px;
    opacity: 0.5;
    transform: scaleX(-1) rotate(70deg);
    background-image: linear-gradient(180deg, #faa900 0%, #f27000 100%);
    transform-origin: center top;
    top: 6px;
    left: 0;
}

.card__nail::after {
    transform: rotate(70deg);
}

.card__content {
    width: 134px;
    height: 150px;
    border-radius: 8px;
    background-image: linear-gradient(to top, #fffab5 0%, #ffffff 39%),
        linear-gradient(to bottom, #fff33e, #ffed14);
    background-origin: border-box;
    background-clip: padding-box, border-box;
    box-shadow: 0 6px 0 0 #f27000;
    border: 2px solid transparent;
    position: relative;
    z-index: 2;
}

效果如下:

是不是很简单。

小结

这篇文章主要和大家一起探讨了如何使用CSS来实现视觉效果,主要围绕着如何通过代码实现UI,换句话说,就是绘制图形。在很多情况之下,针对上面的一些实例效果,更多的同学会考虑使用图片,这并不是不好的方式,只不过使用图片除了多了请求,有一定的带宽,而且也需要流量。即使抛开这一切,图片的扩展性和灵活性就是比不过代码的。

在CSS中,绘制图形主要的一些方式,就是采用borderborder-radiusclip-path和渐变等组合。如果在美学方面有一定的造诣,还能创作出一些更优秀的作品。最后,希望这篇文章对大家有所帮助。如果你在这方面有更好的建议,欢迎在下面的评论中与我们一起共享。