css揭秘 - 形状(二)

407 阅读1分钟

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

切角效果

难题

目前很多开发者更倾向于使用背景图片来达到目的,比如说使用三角形盖住元素的顶角来模拟切角效果,或者使用一张或者多张已经切过角的图片来作为整个元素的背景。

解决方案

渐变可以接受一个角度(比如 45deg )作为方向,而且色标的位置信息也可以是绝对的长度值,这一点丝毫不受容器尺寸的影响。

background: #58a;
background: linear-gradient(135deg, transparent 15px, #58a 0) top left,
    linear-gradient(-135deg, transparent 15px, #58a 0) top right,
    linear-gradient(-45deg, transparent 15px, #58a 0) bottom right,
    linear-gradient(45deg, transparent 15px, #58a 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;

如果用上述代码修改背景色的时候需要修改五处,在改变切角尺寸的时候需要改变四处,可以使用SCSS来写,代码如下:

@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;
}

// 调用
@include beveled-corners(#58a, 15px, 5px);

弧形切角

内凹圆角(跟圆角方向相反)可以使用径向渐变替代上述线性渐变来实现。

background: greenyellow;
background:
    radial-gradient(circle at top left,
    transparent 15px, greenyellow 0) top left,
    radial-gradient(circle at top right,
    transparent 15px, greenyellow 0) top right,
    radial-gradient(circle at bottom right,
    transparent 15px, gold 0) bottom right,
    radial-gradient(circle at bottom left,
    transparent 15px, gold 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;

内联 SVG 与 border-image 方案

渐变方案的代码繁琐冗长,而且一旦想要修改的话,需要修改多处。因此使用 border-image ,并通过一个内联的 SVG 图像来产生切角效果。由于尺寸无关紧要( border-image 会解决缩放问题,而 SVG 可以实现与尺寸完全无关的完美缩放——这就是矢量图的好处),每个切片的尺寸都可以设置为 1,以便理解和书写。切角的尺寸是 1,直线边缘也都是 1。

border: 15px solid transparent;
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;

但是发现切角不见了,如下图2,于是使用 background-clip 避免背景色蔓延到边框区域。

background-clip: padding-box;

给 div 设置一个与背景色一致的边框色,当 border-image 生效时,边框忽略不计;当 border-image 不生效时,边框色可以提供一个比较平稳的回退措施。

梯形标签页

难题

梯形的定义比平行四边形还要广泛一些:只要一对边平行就可以,另外两条边可以是任意角度。

解决方案

在现实的三维世界中旋转一个矩形,由于透视的关系,最终看到的二维的图像就是一个梯形。可以使用 CSS 中的 3D 旋转模拟效果。

transform: perspective(.5em) rotateX(5deg);

但是变换后发现内容的文字也进行了变化,而且 对元素应用了3D变形之后,其内部的变形效应是“不可逆转”的,这一点跟 2D 的不同,因此只能将其变形应用在伪元素上。

nav>a {
    position: relative;
    display: inline-block;
    padding: 1em 3em .1em;
}
nav>a::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;
}
nav>a:nth-child(2)::before{
    transform-origin: bottom left;
}
nav>a:nth-child(3)::before{
    transform-origin: bottom right;
}

简单的饼图

基于 transform 的解决方案

HTML

<div class="pie">0%</div>
<div class="pie">20%</div>
<div class="pie">40%</div>
<div class="pie">60%</div>
<div class="pie">80%</div>

CSS

.pie {
	display: inline-block;
	position: relative;
	width: 100px;
	line-height: 100px;
	border-radius: 50%;
	background: yellowgreen;
	background-image: linear-gradient(to right, transparent 50%, salmon 0);
	color: transparent;
	text-align: center;
}

@keyframes spin {
	to { transform: rotate(.5turn); }
}
@keyframes bg {
	50% { background: salmon; }
}   

.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;
}
JS

function $$(selector, context) {
    context = context || document;
    var elements = context.querySelectorAll(selector);
    return Array.prototype.slice.call(elements);
}

$$('.pie').forEach(function (pie) {
    var p = pie.textContent;
    pie.style.animationDelay = '-' + parseFloat(p) + 's';
});

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。