CSS 揭秘 - 读书笔记(其二)

发表于: 2016-08-28分类于:CSS

自适应的椭圆

提到圆或者椭圆,很自然地会想到 border-radius 属性。但很多人对于 border-radius 的了解都是不够的。border-radius 的完整写法如下:

border-radius: x x x x / x x x x;

其中 x 都表示长度,可以是具体数值也可以是百分比。但是这个长度究竟描述的是什么呢?看下图,就明白了:

这里我使用了如下代码给容器设置了圆角:

border-radius: 80px / 40px;

图中有一个椭圆完全贴合圆角,这个椭圆正是用来说明代码中斜杠前后的值得意义的,斜杠前的值表示椭圆的横轴半径,斜线后的值表示椭圆的纵轴半径。

想到这个椭圆,border-radius 的属性值也就很清楚了。对于 border-radius: x x x x / x x x x; 斜线前后四个值分别表示从左上角顺时针的四个角落椭圆的横纵轴半径。

这个前后的 4 个值可以适当简写,简写的方法和 margin 一样:

  • 如果只提供三个值:第四个值和第二个相同
  • 如果只提供两个值:第三个和第一个相同,第四个和第二个相同
  • 如果只提供一个:四个值都相同

另外,如果只写了斜杠前的一个值,斜杠后的值会和斜杠前的值保持一致。也就是说前后的共八个值都相同。

半椭圆

绘制半椭圆,比如这样的:

想象那个椭圆,左上角和左下角的两个椭圆共同作用得到了上面这幅图,而且他们的横轴半径为容器的宽度,而纵轴半径为容器高度的一半。这样就很容易写出以下代码了:

width: 300px;
height: 200px;
border-radius: 100% 0 0 100% / 50% 0 0 50%;
border: 1px solid #333;

当值写为百分比的时候,参照的是容器的宽度和高度。

四分之一椭圆

到这里,画椭圆已经是小菜一碟了,四分之一椭圆,椭圆的横纵半径为容器的宽度和高度,写法如下:

border-radius: 100% 0 0 0;

平行四边形

使用 transform 中的 skew 可以很容易地得到平行四边形。但是如果希望得到下面这样的样式该怎么做呢?注意其内容并没有倾斜。

HELLO

是的,使用一个元素搞定,这个时候可以使用到伪元素,对伪元素进行形变,然后将伪元素作为背景,这样内容就不会受到影响了。同样的,可以利用此方案得到菱形背景。

菱形图片

哈哈,想想如何实现下面这样的效果:

这里使用了首先将容器进行了旋转,然后将内容再进行了反向的旋转,这个时候容器的高度和宽度已经变成了对角线的长度了。对于正方形来说,对角线的长度为边长的 1.414 (根号 2 )倍,因此需要将旋转后的图片放大 1.42 倍(宁可大一点也不能小一点,铺不满整个容器)。

另外使用 CSS4 中的新属性可以简单实现:

-webkit-clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%);

切角效果

常常看到这样的设计,在容器的某个角上存在一个三角形的切角。这个可以使用线性渐变来高度,渐变从被切去的角落开始,到切角的边保持为透明,之后是背景色。

实现代码:

width: 300px;
height: 200px;
background-image: linear-gradient(-45deg, transparent 20px, #6893da 0);

但是如果有多个切角呢?使用多个渐变可以搞定,这个时候会出现一个问题,渐变会覆盖掉对方的切角,为了不至于此,可以指定一下各自的 background-size ,然后设置一下 background-position

为了进行说明,这里使用了两种颜色。但是如果有四个切角呢,代码将会更加复杂,好在在未来(CSS4)将推出一个叫做 corner 的属性,实现类似效果只需要两行代码:

border-radius: 0 0 15px;
corner-shape: bevel;

梯形标签页

在网站的便签页上,或者某些浏览器的标签页上都能看到梯形。在 CSS 中画梯形虽然不易,但却也是可行的。

使用 3D 形变就可以很实现梯形,效果如下:

HELLO

这里用到了 perspective 这个 3D 变换的属性,另外为了不让容器中得内容发生 3D 变换,使用了同上面的平行四边形的方案,对作为背景的伪元素进行了变换。

需要着重说明的代码是下面两行:

transform-origin:bottom;
transform: scaleY(2) perspective(50px) rotateX(45deg);

transform-origin 用于将形变的中心设置为底边的中点,这样确保使用 rotateX 进行旋转,以及设置了视距属性 perspective 的时候,形变后的图形不会超出容器。否则将会是这样的效果(这里我给容器加了一个边框):

HELLO

另外 transform 中 scaleY 和 perspective 和 rotate 的顺序很重要,这里表示先进行缩放,然后在在 50px 的视距上进行旋转。transform 属性中值的顺序是很关键的,就像高中或初中学的先平移后伸缩,或者先伸缩后平移,最后的效果是完全不同的。

由于要进行 rotateX(45deg) 操作,这会导致元素看起来变矮了,为了让它看起来还是那么高,这里使用 scaleY(2) 将元素在旋转之前变高了一些。

简单饼图

书中介绍了基于 transform 和 SVG 的解决方案,由于我不懂 SVG 这里只记录了 transform 的方案。

简单饼图,就像下面这样:

我需要使用一块绿色或者灰色的半圆来围绕圆心旋转,以遮挡住部分区域,实现不同比率的饼图。半圆很好得到,这里可以使用伪元素来作为遮罩,使用下面的 CSS 代码便可实现效果:

.pie{
  width: 150px;
  height: 150px;
  background: yellowgreen;
  border-radius: 50%;
  background-image: linear-gradient(90deg, transparent 50%, #665 0);
}
.pie::after{
  content: '';
  display: block;
  height: 100%;
  width: 50%;
  margin-left: 50%;
  background-color: inherit;
  border-radius: 0 100% 100% 0% / 0 50% 50% 0;
  transform-origin: left;
}

将伪元素的右上角和右下角设为圆角,得到了一个半圆,然后将其覆盖在圆盘的右半边,并将 transform-origin 设置为 left,将形变中心设为圆心位置,这样就可以使用 rotate 来旋转半圆,以遮挡不同角度的扇形了。

这里是使用绿色来遮挡灰色,但是当灰色的区域超出 50% 的时候,就有问题了,超出 50% 以后我们需要将遮罩变为灰色。并立刻将遮罩回归到 0 度,然后继续旋转。因此在一个周期内,遮罩的颜色在 50% 的时候切换了一次,遮罩进行了两次从 0 至 180度的旋转。

这样就可以写出两个动画了:

@keyframes spin{
  to {
    transform: rotate(.5turn);
  }
}
@keyframes bg{
  50%{
    background: #655;
  }
}
/* 将其运用在遮罩上 */
.pie::after{
    /* ... */
    animation: spin 50s linear infinite,
        bg 100s step-end infinite;
}

这里颜色使用了 step-end 作为 timing-function 的效果就是时间进行到 50s 的时候遮罩的颜色立刻变为了灰色。关于 step-end 和 step-start 以及 steps 这三个时间函数更多的细节,可以参考这篇博客

为了让它能够灵活地表示不同比率的静态图而不是动画,需要借助于 animation-play-state:paused 来停止动画。再利用 animation-delay 设置为负值的一个特性:

animation-delay 设置为负数的时候,动画会立刻开始播放,且从 animation-delay 的绝对值处开始播放,就好像动画在过去已经播放了指定时间一样。

因此,可以将动画暂停,并给其设置不同的 animation-delay 值,这样就能得到不同角度的比率图了。

.pie::after{
    /* ... */
    animation: spin 5s linear forwards,
        bg 20s step-end;
    animation-delay: inherit;
}

因为没有办法给伪元素添加类和内联样式,这里对 animation-delay 使用了继承,这样就可以直接在元素上设置 animation-delay 了。

设置 animation-delay-40s

设置 animation-delay-60s