使用SVG制作进度条之二

简介:

在上一节中,学习了怎么利用SVG的stroke-dasharraystroke-dashoffset来制作进度条。记得在文章末尾留了一个悬念,说这一节中,要聊聊怎么用Vue来把这个SVG的进度封装成组件。

咱们先不聊Vue怎么把这个封装成组件(我搜索了一下,有现在所这方面组件,而且做得蛮好的,接下来先学习一下)。今天接着聊上一节中的进度条怎么来实现。不过略有不同。不同点来自于网上一位朋友向我提的一个问题。问题是这样的:

用SVG做一个环形的进度条一样的东西,这个很好做(用的 stroke-dasharray),但是,我需要再做一个小圆,随着环形慢慢变成一个圆时一直和头部在一起移动(这个不懂)。这个能稍微指点一下我吗?

640?wx_fmt=png&wxfrom=5&wx_lazy=1

说实话,我也很好奇!但我想在以前的基础上添加一个<circle>,让这个新添加的circle跟着内圈做位移(旋转),应该是可以的。也正因为出于好奇,给他找了两个Demo(Demo1、Demo2) 。原本应该能满足其需求。但并不是这样,他希望不借用第三方的库。后来回来自己写了一个 Demo。简单的记录一下这个过程。

简单的分析一下

不管是水平的还是圆形的,其都具有三层,比如下图所示:

640?wx_fmt=png

  • 灰色表示进度条的底层(总共的长度)

  • 红色表示进度条的进度 (已完成长度)

  • 绿色表示起点 (跟随圆点)

在SVG中的话,对应的就是:

  • 水平进度条,灰色和红色是由<line>元素构建,绿色的由<circle>构建

  • 圆形进度条由三个<circle>构建

知道其中的原委,那就好办的得多了。咱们可以先绘制一个静态的图出来:

[xml]  view plain  copy
  1.   

<svg width="200" height="200" viewBox="0 0 200 200">    <!-- 水平进度条 -->    <line x1="10" y1="10" x2="180" y2="10"fill="none" stroke-width="12" stroke="#666" stroke-linecap="round" />    <line x1="10" y1="10" x2="180" y2="10" fill="none" stroke-width="12" stroke="#FC4D04" stroke-dasharray="170" stroke-dashoffset="90" stroke-linecap="round" id="lineInner" />    <circle cx="10" cy="10" r="3" fill="none" stroke="green" stroke-width="6" />    <!-- 圆形进度条 -->    <circle cx="100" cy="120" r="74" fill="none" stroke="#666" stroke-width="12" />    <circle cx="100" cy="120" r="74"fill="none" stroke="#FC4D04" stroke-width="12" stroke-dasharray="465" stroke-dashoffset="400" stroke-linecap="round" />   <circle cx="100" cy="120" r="74" fill="none" stroke="green" stroke-width="12" stroke-dashoffset="464.5" stroke-dasharray="465" stroke-linecap="round" />
</svg>

这个时候看到的效果如下:

从效果中,可以看出来,现在绿色的圆点都在默认的起点。并没有跟随进度条红色的部分。在SVG中并没有直接的方法或者API来让绿色点保持在红色点的终点。这个时候咱们需要借助CSS的特性来完成。这也离不开一些数学的计算。具体怎么来计算呢?先来看水平进度条。

通过前面的学习,知道使用.getTotalLength()可以知道其长度。在这个示例中,通过:

 
 

document.querySelectorAll('line')[0].getTotalLength() // => 170

可以知道整个水平进度条的总长度是170,而红色的进度是通过stroke-dashoffset值来控制的,在这个示例中是90。有了这两个值,就很好的控制绿色的值了。使用CSS的translateX()来做对应的位移,其位移的值是170 - 90,即80

[xml]  view plain  copy
  1.   

<circle cx="10" cy="10" r="3" fill="none" stroke="green" stroke-width="6" id="circle" style="transform: translateX(80px);"/>

在行内添加了transform:translateX(80px),当然你也可以在CSS样式中写。这个时候你看到的效果如下:

上面完成了水平进度条的效果,接下来看圆形进度条。对于圆形的进度条,我们同要要知道其长度,同样可以能过.getTotalLength()来获取:

 
 

document.querySelectorAll('circle')[2].getTotalLength(); // =>464.2044677734375

建议上向上取整,此时我们的周长是465。除此之外,还可以通过2π * r来计算。

 
 

Math.PI * 2 * 74// => 464.9557127312894

因为圆形进度条是一个圆,那么通过transltate()是无法实现的,这个时候,需要使用的是transform中的rotate()来旋转。既然需要旋转就需要一个角度值。那又得数学公式了。简单的回忆一下:

弧度 = 角度 * Math.PI / 180 => rad = (π / 180) * deg 角度 = 弧度 * 180 / Math.PI => deg = (rad * 180) / π

其实就是角度(deg)和弧度(rad)之间的转换。一个完整的圆的弧度是,所以2π rad = 360°1 π rad = 180°1°=π/180 rad1 rad = 180°/π(约57.29577951°)。以度数表示的角度,把数字乘以π/180便转换成弧度;以弧度表示的角度,乘以180/π便转换成度数。

角所对的弧长是半径的几倍,那么角的大小就是几弧度

平时我们常看到的各种弧度如下:

640?wx_fmt=jpeg

通过JavaScript可以来这样进行角度和弧度之间的换算:

 
 

rad = (Math.PI * deg) / 180deg = (rad * 180) / Math.PI

下图展示了常见的角度和弧度之间的换算:

640?wx_fmt=jpeg

回到我们的示例当中来。通过.getTotalLength()可以获取圆角的长度约为465,对应红色圆的stroke-dashoffset值为400。那么就能获取到红色进度的弧长为65。前面说过,角所对的弧长是半径的几倍,那么角的大小就是几弧度:

 
 

65 / 74 = .87rad

另外也可以将.87rad换成deg。根据前面的计算公式,可以计算出来:

 
 

65 / 74 * 180 / Math.PI = 50.32737389662636

大约50deg。那么在circle元素中添加:

transform:rotate(.8783783783783784rad); transform-origin: 100px 120px;

这个时候看到的效果如下:

特别声明:SVG的坐标系统和Web中的坐标系统是不一样的,所以需要对元素做坐标变换,也就有了transform-origin: 100px 120px的设置。

添加动画效果

SVG中改变stroke-dashoffset的值,可以实现线条自画的效果。这并不是什么新东西,也不是复杂的东西。在我们今天的示例当中的关键点是怎么当绿色的点跟着移动。

通过前面的了解,在水平进度条中,需要给translateX( )一个值,而这个值是:

[sql]  view plain  copy
  1.   

.getTotalLength() - stroke-dashoffset

对于圆形的稍为复杂一点,其要给rotate()传一个值:

(.getTotalLength() - stroke-dashoffset) / r; // => 弧度值

或者:

[sql]  view plain  copy
  1.   

(.getTotalLength() - stroke-dashoffset) / r * 180 / Math.PI; =>角度值

既然知道其中原理,我们就来换成Vue的环境。把红色的stroke-dashoffset绑定一个数据,然后使用input[type="range"]来动态修改stroke-dashoffset的值。并且动态修改绿色圆点的位置或者旋转值。比如:

[xml]  view plain  copy
  1.   

<div id="app">    <svg width="200" height="200" viewBox="0 0 200 200">        <line x1="10" y1="10" x2="180" y2="10" fill="none" stroke-width="12" stroke="#666" stroke-linecap="round" />        <line x1="10" y1="10" x2="180" y2="10" fill="none" stroke-width="12" stroke="#FC4D04" stroke-dasharray="170" :stroke-dashoffset="dashOffsetLine" stroke-linecap="round" id="lineInner" />        <circle cx="10" cy="10" r="3" fill="none" stroke="green" stroke-width="6" id="circle" style="transform: translateX(80px);" id="circle"/>        <circle cx="100" cy="120" r="74" fill="none" stroke="#666" stroke-width="12" />        <circle cx="100" cy="120" r="74" fill="none" stroke="#FC4D04" stroke-width="12" stroke-dasharray="465" :stroke-dashoffset="dashOffsetCircle" stroke-linecap="round" />        <circle cx="100" cy="120"r="74" fill="none" stroke="green" stroke-width="12" stroke-dashoffset="464.5" stroke-dasharray="465" stroke-linecap="round" id="circleInner" style="transform:rotate(.8783783783783784rad); transform-origin: 100px 120px;"/>    </svg>    <div class="action">        <div class="line">            <label for="">line: stroke-dashoffset = "{{dashOffsetLine}}"</label>            <input type="range" name="points" min="0" value="{{dashOffsetLine}}" v-model="dashOffsetLine" max="170" step="1" @input="moveLine">        </div>        <div class="circle">            <label>circle:stroke-dashoffset = "{{dashOffsetCircle}}"</label>            <input type="range" name="points" min="0" value="{{dashOffsetCircle}}" v-model="dashOffsetCircle" max="465" step="1" @input="moveCircle">        </div>    </div>
</div>

对应的Vue代码:

[javascript]  view plain  copy
  1.   

let app = new Vue({    el: '#app',    data () {        
       return {            dashOffsetCircle: 400,            dashOffsetLine: 90,        }    },    methods: {        moveLine: function (e){            let circleLine = document.getElementById('circle')            
           let lineInnerLen = document.getElementById('lineInner').getTotalLength()            circleLine.style.transform="translateX(" + (lineInnerLen - e.target.value) + "px)"        },        moveCircle: function(e) {            
           let circleInner = document.getElementById('circleInner')            
           let circleInnerLen = Math.ceil(document.getElementById('circleInner').getTotalLength())            
           let arcLen = circleInnerLen - this.dashOffsetCircle            
           let rad = arcLen / 74            circleInner.style.transform="rotate(" + rad + "rad)"        }    } })

Vue代码写得比较拙逼,路过的大婶请多多指点。这个时候,你拖动input,改变其值时,就可以看到对应的效果。

640?wx_fmt=png

事实上除了使用line元素和circle元素之外,还可以使用path元素来实现类似的效果。感兴趣的同学,不仿使用path来做一个。如果做了,记得在下面的评论中与一起分享您的成果。

总结

这篇文章主要在上一节的基础上添加了一个网友想要的效果。就是在进度条上有一个圆点,能跟随进度条一起移动。其实现原理并不复杂。对于水平进度条,通过translateX()来改变其位移的位置,对于圆形进度条,则通过旋转来改变其位置。不管是哪一种,都需要动态的计算出他们的值,然后修改对应元素的样式。很多时候CSS和SVG的结合能让我们做出一些意想不到的效果。如果你感兴趣的话,不仿一试。


文章涉及到图片和代码,如果展示不全给您带来不好的阅读体验,欢迎点击文章底部的 阅读全文。如果您觉得小站的内容对您的工作或学习有所帮助,欢迎关注此公众号。

本文作者:W3cplus_
本文发布时间:2018年01月27日
本文来自云栖社区合作伙伴 CSDN,了解相关信息可以关注csdn.net网站。
目录
相关文章
|
8月前
|
数据采集 机器学习/深度学习 编解码
视频生成框架EasyAnimate正式开源!
EasyAnimate是人工智能平台PAI自主研发的DiT-based视频生成框架,它提供了完整的高清长视频生成解决方案,包括视频数据预处理、VAE训练、DiT训练、模型推理和模型评测等。可以使用EasyAnimate进行任意风格视频模型的训练和推理,还可以在预训练模型的基础上,通过少量图片的LoRA微调来改变生成视频的风格。
|
5月前
|
应用服务中间件 nginx 索引
7-16| *8 directory index of "E:\wwwroot/" is forbidden, client: 127.0.0.1, server: 192.168.1.240, re
7-16| *8 directory index of "E:\wwwroot/" is forbidden, client: 127.0.0.1, server: 192.168.1.240, re
|
4月前
|
编解码 数据可视化
基于transform的scale属性,实现数据可视化大屏自适应缩放,保持比例不变,轻松应对不同分辨率
基于transform的scale属性,实现数据可视化大屏自适应缩放,保持比例不变,轻松应对不同分辨率
296 0
|
前端开发
把 SVG 图标对齐到文本,以告别字体图标(Font Icons)的时代
本文讲的是把 SVG 图标对齐到文本,以告别字体图标(Font Icons)的时代,在字体图标盛行的时代,推行 SVG 图标可谓是 Web 社区中的一次重要契机。毕竟,使用 SVG 图标系统能更好地遵循图形的访问性标准,并渲染出更高质量的图像。
4228 0
|
缓存 Java
java通过反射获取加了某个注解的所有的类
java通过反射获取加了某个注解的所有的类
|
Linux 应用服务中间件 Perl
|
JSON 移动开发 JavaScript
基于js开发的一个h5页面可视化布局器
前段时间笔者一直忙于数据可视化方面的工作,比如如何实现拖拽式生成可视化大屏,如何定制可视化图表交互和数据导入方案等,这块需求在B端企业中应用非常大,所以非常有探索价值。 本篇文章并非和数据可视化相关,而是通过抽象技术底层,将其应用于H5页面可视化搭建上,通过技术的手段实现拖拽式生成H5页面。这块也有非常多的应用场景,比如我们需要开发一个移动端网站,一个H5营销页面,H5活动页面等,如果有这样的傻瓜式拖拽的工具生成H5页面,将会极大的提高我们的工作效率。 接下来笔者将对h5页面可视化编辑器做详细的演示
2873 0
基于js开发的一个h5页面可视化布局器
|
Oracle 安全 Java
2022 最新版 JDK 17 下载与安装 步骤演示 (图示版)
证书我们这边可以借助辅助工具appuploader Appuploader可以辅助在Windows、linux或mac系统直接申请iOS证书p12,及上传ipa到App Store,最方便在Windows开发上架没有苹果Mac电脑的开发者!配合本教程使用,可以快速掌握如何真机测试及上架!
2022 最新版 JDK 17 下载与安装 步骤演示 (图示版)
|
新零售 Web App开发 监控
《企业大数据实践路线》之企业大数据的现状与痛点
大数据与云计算的关系就像一枚硬币的正反面一样密不可分,没有云计算就没有大数据。
1942 0
|
9月前
|
弹性计算 安全 关系型数据库
rds网络配置
阿里云RDS网络配置涉及网络类型(经典网络或VPC)、子网、安全组规则、内网白名单和SSL加密。确保ECS与RDS在同一VPC内,配置相同可用区或对等连接,调整安全组允许ECS访问RDS端口,将ECS内网IP加入RDS白名单,并启用SSL增强安全。配置步骤包括选择网络、设置白名单和更新安全组规则。根据业务需求,还需考虑其他如存储、读写分离和监控设置。
249 6

热门文章

最新文章