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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 选择qi:是表达式 标签选择器 类选择器 属性选择器 继承属性: color,font,text-align,li...
    wzhiq896阅读 1,722评论 0 2
  • 选择qi:是表达式 标签选择器 类选择器 属性选择器 继承属性: color,font,text-align,li...
    love2013阅读 2,298评论 0 11
  • 1、属性选择器:id选择器 # 通过id 来选择类名选择器 . 通过类名来选择属性选择器 ...
    Yuann阅读 1,606评论 0 7
  • 6:30 加班起床闹铃响,又躺十分钟 6:40 起身,洗漱 7:08 跟醒来的姐姐告别,出门,买早饭 7:21 坐...
    Min_Xu阅读 150评论 0 1
  • 一步,两步,三步…… 原以为这一生就平平稳稳的走过 我很满足,我不喜风暴 独爱这平静 梦里,四周是一片荒芜,苍凉…...
    甘棠绿兮阅读 215评论 1 3