【css】如何实现环形进度条
最近团队的童鞋接到了一个有关环形进度条的需求,想要还原一个native的沿环轨迹渐变进度条的效果,看到这个效果的时候,笔者陷入了沉思。。
环形进度条的效果,最先想到的就是使用CSS利用两个半圆的hack来模拟实现的:
<div class='circle-container'>
<div class="circle-item">
<div class="circle-left-wrap">
<div class="left"></div>
</div>
<div class="circle-right-wrap">
<div class="right" style="transform: rotate(70deg)"></div>
</div>
<div class='mask'></div>
</div>
</div>
<div class='circle-container'>
<div class="circle-item">
<div class="circle-left-wrap">
<div class="left" style="transform: rotate(70deg)"></div>
</div>
<div class="circle-right-wrap">
<div class="right" style="transform: rotate(180deg)"></div>
</div>
<div class='mask'></div>
</div>
</div>
<style>
.circle-container{
position: relative;
float: left;
width: 120px;
height: 120px;
}
.circle-item{
position: absolute;
left: 10px;
top: 10px;
width: 100px;
height: 100px;
border-radius: 50%;
background-color: #59d;
}
.circle-left-wrap, .circle-right-wrap{
position: absolute;
left: 0;
top: 0;
width: 50px;
height: 100px;
overflow: hidden;
}
.circle-right-wrap{
left: 50px;
}
.left, .right {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
border-radius: 50%;
background: #ddd;
}
.left{
clip: rect(0, 50px, auto, 0);
}
.right{
clip: rect(0, auto, auto, 50px);
left: -50px;
}
.mask{
position: absolute;
top: 5px;
left: 5px;
width: 90px;
height: 90px;
border-radius: 50%;
background-color: #fff;
}
</style>
代码如上所示,实现起来并不复杂,主要是利用遮挡关系和clip属性,具体效果如下:
其中值得一提的是,虽然这种实现很巧妙,但是:
1、使用上两个半圆各控制一般的进度,需要js中间做一次转换,操作起来并不算太方便;
2、实现上由于本身的限制,虽然可以实现带渐变的进度条(只需要简单修改最外层容器背景色,效果见下图),但是由于本身遮挡关系的实现机制,并不能实现“纯透明”的无进度部分;
3、而且css的实现上似乎并不支持沿环形的渐变;
.circle-item{
position: absolute;
left: 10px;
top: 10px;
width: 100px;
height: 100px;
border-radius: 50%;
background: linear-gradient(to top left, #f63, yellow);
}
基于以上原因,笔者尝试了一些其他的实现方式,先进入笔者视野的就是svg,因为工作中其实用得并不算多,也正常趁此次机会实践一下:
<style>
svg{
-webkit-mask-image: linear-gradient(to top left, rgba(0,0,0,0), rgba(0,0,0,1));
}
svg:last-of-type{
position: absolute;
top:;
left:;
-webkit-mask-image: linear-gradient(to top left, rgba(0,0,0,1), rgba(0,0,0,0));
}
</style>
<div class='container'>
<i class="circle"></i>
<svg width="440" height="440" viewbox="0 0 440 440">
<circle cx="220" cy="220" r="195" stroke-width="50" stroke="yellow" fill="none" transform="matrix(0,-1,1,0,0,440)" stroke-dasharray="700 1069"></circle>
</svg>
<svg width="440" height="440" viewbox="0 0 440 440">
<circle cx="220" cy="220" r="195" stroke-width="50" stroke="#f63" fill="none" transform="matrix(0,-1,1,0,0,440)" stroke-dasharray="700 1069"></circle>
</svg>
</div>
由于没有采用之前的“遮挡hack”的方式实现,所以实现能完全没有css实现的不能实现部分透明的缺陷,而且进度条的控制可以使用stroke-array很方便的控制。看上去好像是最好的实现呢?确实每种方案都有优劣,svg也不是全能:
1、由于svg渐变仍然是作用于标签主体的,似乎并不能作用于stroke(目前笔者并没有查到相关的资料,如果错误,烦请留言),那么环的渐变要如何实现呢?笔者使用了mask-image,然后通过叠加两个同样进度的circle标签,改变它们alpha通道上的渐变防线,实现的和之前CSS一样的渐变效果;
2、svg本身实践上也有一点小问题,当然这是其本身机制所致,并不算是实现的缺陷:
1)svg 的witdh和height与viewbox第三、第四个参数存在着对应关系(这就和我们之前提到的缩放的关系是一样的,请特别注意);
2)circle的cx、cy实际上是计算边框的,也即是如果圆范围为440*440,那么中点就在(220,220);
3)circle的半径r在计算时指的是从圆心到边框中心的距离,以上述代码为例,边框为50的circle,它的半径应该是220 - 50 / 2 = 195;
4)由于circle的起点并不是通常的图形的最上部而是最右侧(也就是3点钟方向),所以还需要做一点的transform;
当然,撇开一些svg的使用特性,svg的这套方案还是蛮不错的,不过由于mask-image和svg本身的兼容性上有一些问题,使用的时候还是需要谨慎对待。
既然说到了svg,那就不得不提提canvas的实现了,笔者虽然自己也简单了实现了一下,不过看到codepen上面一个很酷炫的实例。。就不放上自己的献丑了:
当然这个例子并不是一个环形进度条的例子(从原名就能看出来XD),还有很多多余的粒子效果,不过从实现上来讲,canvas的实现确实非常酷炫。
看似问题好像都要解决了,但是我们似乎忘了什么?最开始的需求是要实现沿圆环的旋转方向渐变。。但是以上的两种方式都是纯线性渐变和圆环本身是没关系的。。有没有办法实现呢?答案当然是有的,先撇开canvas可以自己订制画笔画出的颜色不谈,回到渐变上,我们之前一直使用线性渐变,但是css中还有另一种渐变——锥形渐变,但是它目前还在草案阶段,实现的厂商也并不多(笔者仅用canary的实验属性中见过),那么就不能实现了么?答案当然也是否定的,想想之前的clip,再想想渐变,是不是有什么神奇的事情就要发生呢!?
是不是觉得很神奇,让我们来看看具体如何实现的:
<style>
.wheel, .colors, .color {
content: "";
position: absolute;
border-radius: 50%;
width: 9.5em;
height: 9.5em;
} .wheel {
display: block;
z-index:;
box-shadow: inset 0 16px 32px 14px rgba(0, 0, 0, 0.7);
overflow: hidden;
} .colors {
list-style: none;
position: relative;
-webkit-filter: blur(10px);
transform: rotate(170deg) scaleX(-1);
} .color {
clip: rect(0px, 9.5em, 9.5em, 4.75em);
}
.color:after {
content: "";
position: absolute;
border-radius: 50%;
left: calc(50% - 4.75em);
top: calc(50% - 4.75em);
width: 9.5em;
height: 9.5em;
clip: rect(0px, 4.75em, 9.5em, 0px);
} .color:nth-child(1):after {
background-color: #9ED110;
z-index:;
transform: rotate(30deg);
} .color:nth-child(2):after {
background-color: #50B517;
z-index:;
transform: rotate(60deg);
} .color:nth-child(3):after {
background-color: #179067;
z-index:;
transform: rotate(90deg);
} .color:nth-child(4):after {
background-color: #476EAF;
z-index:;
transform: rotate(120deg);
} .color:nth-child(5):after {
background-color: #9f49ac;
z-index:;
transform: rotate(150deg);
} .color:nth-child(6):after {
background-color: #CC42A2;
z-index:;
transform: rotate(180deg);
} .color:nth-child(7):after {
background-color: #FF3BA7;
z-index:;
transform: rotate(180deg);
} .color:nth-child(8):after {
background-color: #FF5800;
z-index:;
transform: rotate(210deg);
} .color:nth-child(9):after {
background-color: #FF8100;
z-index:;
transform: rotate(240deg);
} .color:nth-child(10):after {
background-color: #FEAC00;
z-index:;
transform: rotate(270deg);
} .color:nth-child(11):after {
background-color: #FFCC00;
z-index:;
transform: rotate(300deg);
} .color:nth-child(12):after {
background-color: #EDE604;
z-index:;
transform: rotate(330deg);
} .color:nth-child(n+7) {
transform: rotate(180deg);
} .left-wrapper,.right-wrapper{
position: absolute;
left:;
top:;
width: 50%;
height: 100%;
overflow: hidden;
}
.left-circle,.right-circle{
position: absolute;
top:;
left:;
width: 9.5em;
height: 100%;
border-radius: 50%;
background: #ddd;
}
.left-circle{
clip: rect(0, 4.75em, auto, 0);
}
.right-circle{
clip: rect(0, auto, auto, 4.75em);
left: -4.75em;
}
.right-wrapper{
left: 50%;
}
.wheel-mask{
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 90%;
border-radius: 50%;
background-color: #fff;
}
</style>
<div class="wheel">
<ul class="colors">
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
<li class="color"></li>
</ul>
<div class='left-wrapper'>
<div class="left-circle" style="transform:rotate(70deg)"></div>
</div>
<div class='right-wrapper'>
<div class="right-circle" style="transform:rotate(180deg)"></div>
</div>
<div class="wheel-mask"></div>
</div>
其实进度条的实现和之前并没有什么差别,最大的就是在于背景的看着像锥形渐变的一块区域的实现,这其实是csstrick上的一个有关实现锥形渐变的小技巧,原理是:
1、利用clip迭代出各种纯色的三角形区域;
2、利用filter将纯色区域模糊化,达到类似渐变的效果;
其实最开始就是上图的效果,然后添加一个filter:blur(10px),模糊边界,看起来就变成了这样:
剩下的事情也就水到渠成了。
到最后,我们来简单总结一下用到的技术:
首先是CSS的相关遮挡原理,当然这个原理离不开一个。。一个已经在标准里被废弃的属性——clip,满屏的部分支持令人觉得倍感尴尬,不过我们用到的clip的功能主要也就是裁剪,所以在短时间内并不会有太大的影响。
那么简单的过一下api:
clip:rect(top, right, bottom, left);
如左图所示,虚线的矩形便是实际裁剪的矩形的区域,右图则是锥形色块的实现原理:首先通过父容器将显示区域裁成只有右边一半,再在子元素里用clip把左边的元素也裁掉,在没有旋转的情况下,就变成了什么都看不到,然后通过rotate和li标签间的遮挡关系来最终实现锥形的色块。
最后的最后还有个小插曲,以上的代码如果在.wheel中简单的改动一下:
.wheel {
display: block;
z-index:;
box-shadow: inset 0 16px 32px 14px rgba(0, 0, 0, 0.7);
margin: 250px 0 0 250px;
overflow: hidden;
}
其实只是添加了一段margin,但是在某些android手机(实验发现有问题的手机是s6 edge,系统版本6.0)上却呈现了奇怪的效果(表现为模糊的色块不能完全显示或者直接不能显示)。笔者开始觉得是合成层的问题,强制开启了硬件加速之后确实好了,但是后来发现,去掉那一行margin也能恢复正常,但是加上就会导致整个着色区域在一个以屏幕左上角为顶点有一定宽高的矩形范围内,如图:
多番查找之后仍没有找到解决方案,(也许css filter在堪忧的性能确实使用得比较少也是原因之一吧XD),暂时就先留在本文中,后续有了解决方案再更新吧。
2018.01.03补充:
最近看到了一个纯CSS实现的一个饼图,虽然不是不是完全的环形的,但是感觉也挺有意思的,研究下来发现了一个一直不太关注的知识点:
border-radius
相信很多同学对这个属性并不陌生,期初笔者也是这么想的,直到遇到了要用一个矩形的容器画一个半圆的时候:
脑中如果浮现的code是border-radius: 0 50% 50% 0的同学,你得到的将是下面
再让我们来看看border-radius的语法是怎样的:
border-radius: <length-percentage>{1,4} [ / <length-percentage>{1,4} ]?
where <length-percentage> = <length> | <percentage>
可以看到,其实border-radius的完整参数应该是有两组的,而我们平常只写了一组,前面那一组代表的是水平半径,后面则代表垂直半径,当后面的参数省略的时候,将使用前面一组的copy作为缺省值,可能大家心里都不太清楚何谓水平和垂直半径,其实这个border-radius的原理是画一个半径为参数值的“椭圆”,当我们作用在正方形的容器上时,垂直和水平半径是一样的,所以通常就忽略了,而当容器为一个矩形的时候,我们这需要考虑其每个顶点的椭圆的半径了,比如如下的三个例子:
左起第一个图:正方形容器宽高均为80px,当设置border-radius:50%时,等价于
border-radius: 40px 40px 40px 40px/40px 40px 40px 40px;
相当于画了4个长轴长、短轴长也就是水平半径和垂直半径相同的四个“椭圆”;
第二个图:在一个高80px宽40px的容器中,当设置border-radius: 50%时,等价于
border-radius: 20px 20px 20px 20px/40px 40px 40px 40px;
效果就相当于画了4个长轴长(垂直半径)为40px,短轴长(水平半径)为20px的椭圆;
第三个图:在一个高80px宽40px的容器中,当设置border-radius: 0 100% 100% 0/0 50% 50% 0时,等价于(注:此处的图片有误,实际上应该用下面的属性画出来的是只有右边的半圆)
border-radius: 0 40px 40px 0/0 40px 40px 0;
效果就相当于画了2个水平垂直半径相同的圆,如虚线的示意,于是就得到了我们想要的用矩形画一个半圆的效果。
其中有关相对单位的转换笔者就不再赘述了,最后再放上那个饼图的实际效果。
【css】如何实现环形进度条的更多相关文章
- CSS制作环形进度条
参考来源 <Radial progress indicator using CSS>,该文核心是用纯CSS来做一个环形的进度条.纯css的意思就是连百分比这种数字,都是css生成的.文章作 ...
- 【CSS】环形进度条
效果图 原理剖析 1.先完成这样一个半圆(这个很简单吧) 2.overflow: hidden; 3.在中间定位一个白色的圆形做遮挡 4.完成另一半 5.使用animate配合时间完成衔接 源码 &l ...
- 环形进度条的实现方法总结和动态时钟绘制(CSS3、SVG、Canvas)
缘由: 在某一个游戏公司的笔试中,最后一道大题是,“用CSS3实现根据动态显示时间和环形进度[效果如下图所示],且每个圆环的颜色不一样,不需要考虑IE6~8的兼容性”.当时第一想法是用SVG,因为SV ...
- Canvas实现环形进度条
Canvas实现环形进度条 直接上代码: <canvas width="200" height="200" >60%</canvas> ...
- 基于svg的环形进度条
其实需求是这么一个基于日期的环形进度条,开始用css3写了一下感觉太麻烦了,于是抽了点时间用svg画了一个. 不多说 上代码: css: <style> circle { -webkit- ...
- 图解CSS3制作圆环形进度条的实例教程
圆环形进度条制作的基本思想还是画出基本的弧线图形,然后CSS3中我们可以控制其旋转来串联基本图形,制造出部分消失的效果,下面就来带大家学习图解CSS3制作圆环形进度条的实例教程 首先,当有人说你能不能 ...
- iOS带动画的环形进度条(进度条和数字同步)
本篇写的是实现环形进度条,并带动画效果,要实现这些,仅能通过自己画一个 方法直接看代码 为了方便多次调用,用继承UIView的方式 .m文件 #import <UIKit/UIKit.h> ...
- iOS 开发技巧-制作环形进度条
有几篇博客写到了怎么实现环形进度条,大多是使用Core Graph来实现,实现比较麻烦且效率略低,只是一个小小的进度条而已,我们当然是用最简单而且效率高的方式来实现. 先看一下这篇博客,博客地址:ht ...
- iOS一分钟学会环形进度条
有几篇博客写到了怎么实现环形进度条,大多是使用Core Graph来实现,实现比较麻烦且效率略低,只是一个小小的进度条而已,我们当然是用最简单而且效率高的方式来实现.先看一下这篇博客,博客地址:htt ...
随机推荐
- 一个动态链接的问题,dlsym后符号调用主函数的符号报告无法找到
先看看状况(小心头疼) client.c 编译得到 client:在 client 的 main 中用 dlopen( "./liba.so", RTLD_LAZY|RTLD_GL ...
- PHP移植
1. 首先交叉编译zlib. CC=arm-linux-gcc ./configure --sahred --prefix=/usr/local/arm/3.4.1/arm-linux make&am ...
- SQL2008安装时,“provider: 命名管道提供程序, error: 40 - 无法打开到 SQL Server 的连接) (.Net SqlClient Data Provider)” 错误的解决方案
错误提示: 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器.请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接. (provide ...
- OUTPUT 子句
除了修改数据以外,一般不会希望修改语句后再做其他事情.也就是说,一般不会希望修改语句能够返回任何输出.然而,在有些场合下,能够从修改过的行中返回数据,这个功能可能也有一定的用处. 例如,考虑UPDAT ...
- Java 中 泛型的限定
泛型 一般 出如今集合中,迭代器中 也会出现! 泛型 是为了 提高代码的 安全性. 泛型 确保数据类型的唯一性. 在我们经常使用的容器中. 越是 单一 约优点理啊! ...
- my-small.cnf my-medium.cnf my-large.cnf my-huge.cnf
my-small.cnf my-medium.cnf my-large.cnf my-huge.cnf 是 MySQL 默认的几个配置文件.针对不同配置的服务器可以使用不同的配置文件,将你需要的那一个 ...
- diy数据库(二)--网络通信类
一.首先,我们先实现OSS层的ossSocket类.供数据库client和数据库引擎进行通信 友情提示:相应上面的类图的头文件和源码附在了本文的最以下. int _fd ;//socket的文件描写叙 ...
- Navicat 提示Cannot create oci environment 解决方案
一直在使用 Navicat ,这是一个数据库客户端软件,能连接多种不同类型的数据库,给我们的日常的工作带来了不少的便捷.当Navicat 就莫名其妙的不能连接 oracle 数据库了.总是提示如下错误 ...
- VisualSVN Server的配置和使用
VisualSVN Server的配置与使用 本版本为VisualSVN Server 2.7.3版本-不同的版本可能在设置有不同的差异,但都大同小异 1.1启动界面 安装好 VisualSVN Se ...
- UVA315 Network —— 割点
题目链接:https://vjudge.net/problem/UVA-315 A Telephone Line Company (TLC) is establishing a new telepho ...