CSS Secret——Shapes

灵活的椭圆

这里要注意,border-radius是个简写,它可以拆成4个部分,8个值。

border-top-left-radius
border-top-right-radius
border-bottom-right-radius
border-bottom-left-radius

可以简写到一个里面,规则和padding什么的一样。

border-radius: 50% / 50px 20px;

比如这个,就相当于:

border-bottom-left-radius: 50% 20px;
border-bottom-right-radius: 50% 50px;
border-top-left-radius: 50% 50px;
border-top-right-radius: 50% 20px;

那半个椭圆就是:

border-radius: 50% / 100% 100% 0 0;

4分之一个椭圆:

border-radius: 100% 0 0 0;

平行四边形

这里准备采用的办法是使用transform来水平拉伸矩形成为平行四边形,如果直接这样做会同时把里面的内容拉伸,解决办法就是使用一个伪元素。(如果你想使用两个元素的话。。。当我啥都没说)

#Parallelograms {
  position: relative;
  padding:10px;
  margin:10px;
}
#Parallelograms::before {
  content: ''; /* To generate the box */
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: -1;
  background: #58a;
  transform: skew(-45deg);
}

这里的伪元素相对生成元素使用绝对定位,并完全规定了所有位置将伪元素拉满了生成元素。然后把它扯歪。这样就不会影响到元素内的内容了。
这种做法不止适用于菱形,还适用于所有想要变形但不影响内容的操作。还可以用来在IE8中产生多重背景,之前的外方内圆也可以用这个来实现。或者使用这个来创建使用opacity属性只透明背景色。或者多重边框也可以。

菱形图片

之前一般使用图片处理软件来实现,但是这样显然不够灵活。

使用变形

<div class="picture">
    <img src="img/adamcatlace.jpg" alt="..." />
</div>
.picture {
  margin:50px;
  width: 200px;
  transform: rotate(45deg);
  overflow: hidden;
}
.picture > img {
  max-width: 100%;
  transform: rotate(-45deg) scale(1.42);
}

这里之所以使用scale来放大img而不直接使用width,是因为在浏览器不支持transform时显示还是正常的,否则图片会被直接放大,现在这样只有支持transform的浏览器会对图片进行旋转和放大。
这个方法并不优雅,使用了新的HTML元素。如果图片不是正方的。。。那就悲剧了。。

clip-path

这是从SVG那边借鉴来的特性直接应用在元素上,把元素剪为指定的形状。
在这里使用的是多边形polygon()函数,这个函数通过规定多边形端点在元素原本矩形形状中的位置来创建。
我们之前要创建的形状这里就是polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
这里使用这个还有一个很棒的好处,这个属性是直接支持CSS动画的,只要函数相同(同是polygon),顶点数也相同,就可以应用动画。

#picture-clip-path {
  -webkit-clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  clip-path: polygon(50% 0, 100% 50%,50% 100%, 0 50%);
  transition: 1s;
}
#picture-clip-path:hover {
  clip-path: polygon(0 0, 100% 0,100% 100%, 0 100%);
  -webkit-clip-path: polygon(0 0, 100% 0,100% 100%, 0 100%);
}

切角

就是元素有一个或多个角是不全的,这个以前多是由图片或hack(添加一个绝对定位元素)实现的。显然应该有更好的方法。

gradient

这个只使用有方向的linear gradient就可以解决。

#corner-cut1{
  width:100px;
  height:100px;
  background: #58a;
  background:
          linear-gradient(135deg,  transparent 15px, #58a 0)  top left,
          linear-gradient(-135deg, transparent 15px, #655 0)  top right,
          linear-gradient(-45deg, transparent 15px, #58a 0)  bottom right,
          linear-gradient(45deg, transparent 15px, #655 0)  bottom left;
  background-size: 50% 50%;
  background-repeat: no-repeat;
}

4个角,对应4个背景,每个背景占1/4,每个背景都是一个相应方向的linear gradient。要注意,linear gradient这个东西是默认接管整个元素背景的,如果不禁止重复的话,会造成最后一个覆盖所有。
这个如果使用SCSS的mixin会方便一点:

@mixin beveled-corners($bg, $tl:0, $tr:$tl, $br:$tl, $bl:$tr) {
  background: $bg;
  background:
          linear-gradient(135deg, transparent $tl, $bg 0) top left,
          linear-gradient(225deg, transparent $tr, $bg 0) top right,
          linear-gradient(-45deg, transparent $br, $bg 0) bottom right,
          linear-gradient(45deg, transparent $bl, $bg 0) bottom left;
  background-size: 50% 50%;
  background-repeat: no-repeat;
}

#corner-cut1{
  width:100px;
  height:100px;
  @include beveled-corners(#58a, 15px, 5px);
}

这里使用了默认值,传1-5个参数都可以。

弯曲的切角

这里的实现和上面的思想是一样的,换成radial-gradient即可:

#corner-cut2{
  width:100px;
  height:100px;
  background: #58a;
  background: radial-gradient(circle at top left, transparent 15px, #58a 0) top left,
          radial-gradient(circle at top right, transparent 15px, #58a 0) top right,
          radial-gradient(circle at bottom right, transparent 15px, #58a 0) bottom right,
          radial-gradient(circle at bottom left, transparent 15px, #58a 0) bottom left;
  background-size: 50% 50%;
  background-repeat: no-repeat;

}

SVG加border-image

使用上面的办法有几个问题,首先就是代码太复杂,要改颜色和切角的大小要改的太多了;还有就是这种方式并不能支持动画。
我们还可以采用外的两个方法来克服这个缺点,这个方法需要写内联的SVG代码。

#corner-cut3{
  border: 20px solid #58a;
  border-image: 1 url('data:image/svg+xml,\
  <svg xmlns="http://www.w3.org/2000/svg" width="3" height="3" fill="%2358a">\
    <polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\
  </svg>');
  background: #58a;
  background-clip: padding-box;
}

这里要控制切角的大小,只需改变border的宽度,这个宽度是可以应用CSS动画的哦。而且背景与边框的样式不再关联了,背景想是啥是啥。这里给border设置了一个颜色作为fallback。

clip-path

以上两种方法的局限是只能使用很有限样式的边框,如果我们想要图片是背景而且要切角呢?
就用之前的办法咯~

#corner-cut4{
  background-image: url("../img/adamcatlace.jpg");
  width:100px;
  height:100px;
  -webkit-clip-path: polygon(20px 0, calc(100% - 20px) 0, 100% 20px, 100% calc(100% - 20px), calc(100% - 20px) 100%, 20px 100%, 0 calc(100% - 20px), 0 20px );
  clip-path: polygon(20px 0, calc(100% - 20px) 0, 100% 20px, 100% calc(100% - 20px), calc(100% - 20px) 100%, 20px 100%, 0 calc(100% - 20px), 0 20px );

}

将来

在Level 4的CSS背景与边框中,新引入了corner-shape属性,将来就方便咯。

梯形

梯形的形状广泛的被用于标签页的形状。
这里利用transform 3D,使用近大远小的效果创建两边倾斜的元素。

.trapezoid  {
  position: relative;
  display: inline-block;
  padding: .3em 1em 0;
  margin-right:-15px;
  //background: rgba(255,255,255,0.5);
}
.trapezoid::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  background: #ccc;
  background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,0));
  border: 1px solid rgba(0,0,0,.4);
  border-bottom: none;
  border-radius: .5em .5em 0 0;
  box-shadow: 0 .15em white inset;
  transform: perspective(.5em) rotateX(5deg);
  transform-origin: bottom;

}

相较于以前前后加两个伪元素使用border来模拟,这个方法更加灵活,想给整个梯形加个边框加个圆角什么的非常容易,原来的方法你试试。
通过改变transform-origin,可以改变梯形两边倾斜的角度,或者只让一边倾斜。

简单的饼图

使用transform

我们想只用一个元素,最多再加一个伪元素来实现这个。
我们首先需要一个圆:

.pie {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background: yellowgreen;
}

我们把它分为两半,左边的一半颜色是背景,右边半的颜色用来表示百分比:

background-image:  linear-gradient(to right, transparent 50%, #655 0);

此时这个元素的样子就像是50%的饼图。
接下来就轮到伪元素上场了,在0-50%时,我们使用一个与背景色相同的半圆伪元素,利用transform围绕圆的中心旋转遮住元素右边的一部分,使用未遮住的部分来表示百分比。在50%-100%时,使用一个与百分比背景颜色相同的半圆伪元素,遮住部分左边背景色部分,并与原元素右边一起组成大于50%的百分比。

.pie::before {
  content: '';
  display: block;
  margin-left: 50%;
  height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background: #655;
  transform-origin: left;
  transform: rotate(.1turn);
}

好了,这样的话我们有了一个可以使用transform:rotate和伪元素背景来控制的饼图。这显然是不灵活的。
不过想要直接通过这样的方式来控制CSS显然是不现实的:

<div class="pie">20%</div> 
<div class="pie">60%</div>

为了实现这样的灵活性,我们首先做一个铺垫,使用CSS3动画循环整个0-100%的过程:

@keyframes spin { to { transform: rotate(.5turn); } }
@keyframes bg { 50% { background: #655; } }
.pie {
  position: relative;
  width: 100px;
  line-height: 100px;
  border-radius: 50%;
  background: yellowgreen;
  background-image:  linear-gradient(to right, transparent 50%, #655 0);
  color: transparent;
  text-align: center;
}

.pie::before {
  content: '';
  display: block;
  margin-left: 50%;
  height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background-color: inherit;
  transform-origin: left;
  animation: spin 3s linear infinite,
          bg 6s step-end infinite;
}

这里的动画在6秒钟的过程中,前3秒伪元素背景就是背景的颜色,转180度到50%;
在第3秒时,伪元素背景变为百分比的颜色并回到初始位置,与刚才结束时一样表示50%,但是在这个瞬间是有闪烁的,不过没关系,我们最后并不真的使用这个动画效果本身。
后三秒伪元素转180度,结束在100%的位置。
好了,那么这个动画到底有什么作用呢?
我们使用一个我们不太好想到的特性,将动画的animation-delay设为一个负值,这个属性本来是控制动画延迟开始时间的,当它是负值时,规范规定它和将这个值设为0一样,动画会马上开始,但是动画会自动的从这个负值的绝对值处开始,也就是这个值前面的那些动画被跳过。想想这个设定挺符合逻辑的。
再使用animation-play-state: paused使动画一开始就停住。

animation: spin 50s linear infinite,            
    bg 100s step-end infinite; 
animation-play-state: paused; 
animation-delay: inherit; 

这样我们就可以直接使用内联样式来控制饼图了(记得把伪元素的animation-delay设置为继承):

<div class="pie" style="animation-delay: -20s"></div> <div class="pie" style="animation-delay: -60s"></div>

如果你一定要用:

<div class="pie">20%</div> 
<div class="pie">60%</div>

再使用JS将这个值读出来赋给style就好。
这个思想适用于很多地方,比如你想要个渐变的颜色,你又懒得自己调颜色,那你可以使用这种办法,一个从一种颜色到另一种的动画。

.pie {
  position: relative;
  width: 100px;
  line-height: 100px;
  border-radius: 50%;
  background: yellowgreen;
  background-image:  linear-gradient(to right, transparent 50%, #655 0);
  color: transparent;
  text-align: center;
}
@keyframes spin {
  to {
    transform: rotate(.5turn);
  }
}
@keyframes bg {
  50% {
    background: #655;
  }
}
.pie::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 50%;
  height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background-color: inherit;
  transform-origin: left;
  animation: spin 50s linear infinite,
        bg 100s step-end infinite;
  animation-play-state: paused;
  animation-delay: inherit;
}

SVG

利用一个圆,加一个dash形状的粗边框来实现,dash的粗边框可以控制dash的长度和空的长度,设置为只有一个dash,并通过控制这个dash的宽度来表示百分比。

<svg viewBox="0 0 32 32"> 
    <circle r="16" cx="16" cy="16" /> 
</svg>
.piesvg svg {
  width: 100px;
  height: 100px;
  transform: rotate(-90deg);
  background: yellowgreen;
  border-radius: 50%;
}
.piesvg circle {
  fill: yellowgreen;
  stroke: #655;
  stroke-width: 32;
  stroke-dasharray: 38 100; /* for 38% */
}

我们可以直接使用js来创建svg

.piesvg svg {
  width: 100px;
  height: 100px;
  transform: rotate(-90deg);
  background: yellowgreen;
  border-radius: 50%;
}
.piesvg circle {
  fill: yellowgreen;
  stroke: #655;
  stroke-width: 32;
}
$$('.piesvg').forEach(function(pie) {
    var p = parseFloat(pie.textContent);
    var NS = "http://www.w3.org/2000/svg";
    var svg = document.createElementNS(NS, "svg");
    var circle = document.createElementNS(NS, "circle");
    var title = document.createElementNS(NS, "title");
    circle.setAttribute("r", 16);
    circle.setAttribute("cx", 16);
    circle.setAttribute("cy", 16);
    circle.setAttribute("stroke-dasharray", p + " 100");
    svg.setAttribute("viewBox", "0 0 32 32");
    title.textContent = pie.textContent; pie.textContent = '';
    svg.appendChild(title); 
    svg.appendChild(circle); 
    pie.appendChild(svg); 
});
<div class="piesvg">25%</div>
<div class="piesvg">35%</div>

如果你想使用动画的话,直接对这个属性动画就好了:stroke-dasharray。

    原文作者:exialym
    原文地址: https://www.jianshu.com/p/e7fa44a6cc17
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞