经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » HTML/CSS » HTML5 » 查看文章
canvas实现有递增动画的环形进度条的实现方法
来源:jb51  时间:2019/7/11 8:37:15  对本文有异议

老规矩,直接看图!

效果如下:

高清大图!

 码农多年,老眼昏花,动图看不清?!那就看静态截图!!!

不同分值效果如下:

 看完了卖家秀,我们来看产品的制作过程吧!

canvas绘制圆环1、vue中,<template lang="pug">里的代码如下:

canvas#baseCanvas是底部的灰色圆环

canvas#myCanvas是上边的彩色圆环

需要用css样式帮助我们把彩色圆环盖到灰色圆环上边。

2、css样式:

 3、js-canvas的样式绘制代码

这段代码也很简单,看canvas的api即可

3-1、vue组件中,script标签顶部定义需要用的变量

3-2、vue的methos对象中,定义方法三个:

drawBaseCanvas:用来绘制底部灰色圆环。由于灰色圆环没有动画效果,所以一开始就绘制一个完整的灰色圆环即可。drawClrCanvas:用来绘制上边的彩色圆环。clearCanvas:用来清空画布。这是彩色圆环动画需要。因为我们圆环动画效果的核心就是,每隔一段时间就把彩色圆环清空一下,然后把结束角度值增大、重画,这样连续起来就是动画。

以下是三个方法的代码:

上边三个方法里边的代码,几乎都是对canvas API的应用,看教程即可。

只有draoClrCanvas方法中,canvas圆形的绘制时,arc的参数里关于开始值、结束值的设置。

开始值决定了圆环的起始绘制位置,结束值决定了结束的位置(我好像说了一句废话,但是冥思苦想后的思想描述文字,不想删掉哈哈哈)

这个结束值的计算,对于我来说还是比较麻烦的。

count变量为什么要这么计算,我也忘了我是怎么鼓捣出来的了。

this.grade是100以内的正整数,表示分值。被定义在data中,默认是0分。

所以一开始彩色圆环就看不见,因为起始点和结束点都是0点。

如果更改grade的值,从0-100,canvas彩色圆环的值也就会更改。

这样,只要我们逐渐修改grade的值,重新绘制,彩色圆环就会逐渐递增,实现动画效果。

圆环动画效果

由于我这里需求特殊,需要用户每次翻到canvas所在swiper时,才会触发动画(后来更麻烦一点需要柱状图和canvas部分有个入场效果后,动画才开始。效果就是上图中最长的那张gif动画那样)。

所以我得借助swiper才能实现。在swiper切换的回调函数中,从0开始不停递增grade分数,并重新触发彩色圆环的绘制,进而实现动画效果。

vue中我用的swiper是'vue-awesome-swiper'。她的用法我在其他文章中写过步骤。

swiper在vue-data中的配置里,有一个on对象。在on对象中的slideChange函数,就是每次翻页swiper时会触发的回调函数。

这里我说一下几个比较特殊的点:

(1)vm:是我早就在vue的script中存储的变量,初始化为null,然后在mounted中,将其赋值为vue实例对象。

初始化数据、绘制灰色圆环

通过这种方法,我在vue实例对象 - data - swiper - 回调函数中去拿vue实例对象 - data中的grade和gradeTarget属性值,并对其进行修改。

ps:我也不知道这么做是不是很傻的一种做法,当时做到这里时是我遇到的一个难题,不知道怎么在swiper的on回调中获取vue实例。于是就有了这么曲线救国的方法。如果看官有更好的解决方案,希望可以给我提供一个新的思路,感激不尽哦亲

(2)(this.activeIndex == 2 && vm.isStar) || (this.activeIndex == 1 && !vm.isStar)

这里是因为业务,才这么判断,可以忽略。

this在swiperChange函数中指向swiper对象。this.activeIndex是swiper实例的属性,用官方的话说“返回当前活动块(激活块)的索引。”可以理解他指的是当前翻到的是哪一页,就是当前你所看的swiper-slide的下标。

我因为用户的身份,会判断性的决定当前canvas所在swiper前一页是否展示。 如果不展示就根本不会绘制前一页,那么相应的当前页的swiper的下标就会变成(index-1)。

总而言之,当满足条件、用户翻到canvas所在swiper页面后,我就要触发if里边的圆环绘制逻辑。否则就走到else里初始化数据页面的状态、清除定时器暂停动画、并把彩色圆环清空

(3)vm.aniShow

在我上篇《纯css绘制柱状图》里边说了,柱状图的动画要跟canvas的动画一起说。因为他们的动画实现需要配合swiper的切换。说的就是这里的代码:

vue - data - aniShow属性变为true时,div.row就会添加ani这个class类名:

同样,aniShow为true,progress的高度就会附上自己的目标值,也就是这个progress的实际高度经过百分制转化后被赋予给了style属性的height。

此时,因为progress的transition监听了height变化,就开始有了高度渐增的柱状图递增动画了。

而ani类名下,progress的transition-delay实现了其高度错开递增效果。

可能只看文字描述很晦涩,再看一眼效果:

 (4)彩色圆环绘制代码部分

gradeTarget是实际分值,是最终要绘制到的结果。

grade从0开始,自增到gradeTarget的大小。

这里我没有直接++vm.grade,我也不知道自己当时咋想的。

if判断,如果grade递增到了目标值gradeTarget或者大于目标值,就停止递增,并让grade=gradeTarget。属于临界值的判断。在运动功能中,又算碰撞检测。

反之,不到目标的话,就清除上一次绘制的canvas画布,在grade递增变化后重新绘制新的彩色圆环。

(5)所有这些放到setTimeout中,暂停500毫秒再执行,是为了等柱图和环图入场后,在开始绘制圆环的递增效果。

其实上边代码都是很简单的逻辑处理,看官们读一遍代码应该就差不离了。

新想法:

这个效果是我很久以前做的,今天在整理制作方法的时候,我想到自己代码的一种优化方案:

其实没必要在定时器里重新调用彩色圆环绘制方法。我们直接改的是this.grade属性,监听这个属性的改变就好了其实。这样此属性在定时器中被修改,圆环方法就会自动执行。

这还是一个想法,还需要我的实践。

中间文字的递增效果:

因为grade是每次递增的分数,所以利用vue的双向数据绑定,直接把grade当作分数值绑定到对应dom视图处即可。

最后,圆环和上边柱状图的动画结合,就是animation控制一下动画延迟即可。很简单的。

index.vue源码:

(注,源码稍作整理,单独提取。为了完整性也为了保护其他业务代码,部分变量名做了修改,可能会和之前截图中略微不同)

  1. <template lang='pug'>
  2. .indexs#Indexs.app-bg
  3. transition(name="fade")
  4. swiper#swiperBox(:options="swiperOption" ref="mySwiper")
  5. swiper-slide.swiper-slide1
  6. .container
  7. .up
  8. swiper-slide.swiper-slide2(v-if="isShow")
  9. .my-shark
  10. .up
  11. swiper-slide.swiper-slide3
  12. .container
  13. .data-cont
  14. .data.data01
  15. .data01-charts
  16. .row(v-for='item,index in Data' :key="index" :class='aniShow ? "ani":""')
  17. .data-txt {{item.grade > 0 ? item.grade : '无数据'}}
  18. .progress(:class='item.grade == 0 ? "nodata" : ""' :style="'height: ' + (aniShow ? (item.grade >= 100 ? (100 * 1.5) / 100 : item.grade == 0 ? 0.04 : item.grade * 1.5 / 100) : 0) +'rem'")
  19. span.pg-data
  20. .week {{item.week}}
  21. .data.data02
  22. .data02-charts
  23. .canvas-box
  24. //- baseCanvas
  25. canvas#baseCanvas.my-canvas(ref="baseCanvas" width="174" height="174")
  26. //- canvas
  27. canvas#myCanvas.my-canvas.clr-canvas(ref="myCanvas" width="174" height="174")
  28. .canvas-data #[span.num {{grade}}]分
  29. </template>
  30. <script>
  31. var vm = null,
  32. timer1 = null,
  33. /* canvas基础值 */
  34. c = null, //document.getElementById("myCanvas");
  35. ctx = null, //canvas-2d画布
  36. x = 161 / 2 + 1, //圆心坐标
  37. r = (161 - 10) / 2; //半径大小
  38.  
  39. /* swiper组件 */
  40. import { swiper, swiperSlide } from "vue-awesome-swiper";
  41. import { getData } from "../io/getData";
  42.  
  43. export default {
  44. name: "Indexs",
  45. components: {
  46. swiper,
  47. swiperSlide
  48. },
  49. data() {
  50. return {
  51. grade: 0, //圆环图分数
  52. gradeTarget: 78.54, //实际得分数,可ajax请求数据后修改
  53. isShow: true,//是否展示第二页swiper
  54. aniShow: false,//是否开启柱图动画
  55. Data:[{
  56. week: "第一周",
  57. grade: 0
  58. },
  59. {
  60. week: "第二周",
  61. grade: 30
  62. },
  63. {
  64. week: "第三周",
  65. grade: 99.99
  66. },
  67. {
  68. week: "第四周",
  69. grade: 76.98
  70. },
  71. {
  72. week: "第五周",
  73. grade: 100
  74. }],
  75. swiperOption: {
  76. //swiper参数
  77. notNextTick: true,
  78. direction: "vertical",
  79. grabCursor: true,
  80. setWrapperSize: true,
  81. autoHeight: true,
  82. slidesPerView: 1,
  83. mousewheel: false,
  84. mousewheelControl: false,
  85. height: window.innerHeight, // 高度设置,占满设备高度
  86. resistanceRatio: 0,
  87. observeParents: true,
  88. initialSlide: 2 - 1, //设置初始化时,swiper的默认展示页面,从零开始
  89. on: {
  90. slideChange() {
  91. if (
  92. (this.activeIndex == 2 && vm.isShow) ||
  93. (this.activeIndex == 1 && !vm.isShow)
  94. ) {
  95. console.log(this.activeIndex, vm.isShow, "绘制动画");
  96. setTimeout(function() {
  97. // 配合展示柱状图动画
  98. vm.aniShow = true;
  99. // 定时器不断触发绘制彩色圆环,实现圆环动画效果
  100. timer1 = setInterval(function() {
  101. // 中间分数文案更改
  102. var num = vm.grade;
  103. num++;
  104. if (num >= vm.gradeTarget) {
  105. vm.grade = vm.gradeTarget;
  106. clearInterval(timer1);
  107. } else {
  108. vm.grade = num;
  109. }
  110. vm.clearCanvas();
  111. vm.drawClrCanvas();
  112. }, 1000 / 60);
  113. }, 500);
  114. } else {
  115. // 翻页后,初始化数据页面的状态、清除定时器暂停动画、并把彩色圆环清空
  116. console.log("其他页");
  117. clearInterval(timer1);
  118. vm.grade = 0;
  119. vm.aniShow = false;
  120. vm.clearCanvas();
  121. }
  122. }
  123. }
  124. }
  125. };
  126. },
  127. computed: {},
  128. mounted() {
  129. // 初始化数据、绘制灰色圆环
  130. vm = this;
  131. c = this.$refs.myCanvas;
  132. ctx = c.getContext("2d");
  133. this.drawBaseCanvas();
  134. },
  135. methods: {
  136. drawBaseCanvas() {
  137. // canvas绘制
  138. /* 基础值 */
  139. var c = this.$refs.baseCanvas, //document.getElementById("myCanvas");
  140. // debugger;
  141. ctx = c.getContext("2d"),
  142. o = x,
  143. randius = r;
  144. /* 默认灰色圆圈 */
  145. ctx.strokeStyle = "#eee";
  146. ctx.lineWidth = 10;
  147. ctx.beginPath();
  148. ctx.arc(o, o, randius, 0, 2 * Math.PI);
  149. ctx.stroke();
  150. },
  151. clearCanvas() {
  152. // 清除画布
  153. ctx.clearRect(0, 0, 200, 200);
  154. },
  155. drawClrCanvas() {
  156. var gradient = ctx.createLinearGradient(75, 50, 5, 90);
  157. gradient.addColorStop("0", "#C88EFF");
  158. gradient.addColorStop("1.0", "#7E5CFF");
  159. ctx.strokeStyle = gradient; // 用渐变进行填充
  160. ctx.lineWidth = 10;
  161. ctx.lineCap = "round";
  162. ctx.shadowColor = "rgba(191,142,255, 0.36)";
  163. ctx.shadowBlur = 8;
  164. ctx.shadowOffsetY = 8;
  165. ctx.beginPath();
  166. var count = this.grade / (100 / 2) + 1;
  167. ctx.arc(x, x, r, Math.PI, Math.PI * count, false);
  168. ctx.stroke();
  169. }
  170. }
  171. };
  172. </script>
  173. <style lang='scss'>
  174. // 柱图
  175. .row {
  176. position: relative;
  177. z-index: 1;
  178. width: 0.61rem;
  179. margin-bottom: -0.28 - 0.08 - 0.38rem;
  180. text-align: center;
  181. }
  182.  
  183. .data-txt {
  184. font-size: 0.2rem;
  185. line-height: 0.2rem;
  186. margin-bottom: 0.09rem;
  187. }
  188.  
  189. .progress {
  190. height: 0rem;
  191. transition: height 0.5s ease-in-out;
  192. }
  193.  
  194. .ani {
  195. @for $i from 1 to 6 {
  196. &:nth-of-type(#{$i}) {
  197. .progress {
  198. transition-delay: #{$i * 0.15}s;
  199. }
  200. }
  201. }
  202. // &:nth-of-type(1) {
  203. // .progress {
  204. // transition-delay: .4s;
  205. // }
  206. // }
  207.  
  208. // &:nth-of-type(2) {
  209. // .progress {
  210. // transition-delay: .8s;
  211. // }
  212. // }
  213.  
  214. // &:nth-of-type(3) {
  215. // .progress {
  216. // transition-delay: 1s;
  217. // }
  218. // }
  219.  
  220. // &:nth-of-type(4) {
  221. // .progress {
  222. // transition-delay: 1.4s;
  223. // }
  224. // }
  225.  
  226. // &:nth-of-type(5) {
  227. // .progress {
  228. // transition-delay: 1.8s;
  229. // }
  230. // }
  231. }
  232.  
  233. .pg-data {
  234. display: block;
  235. width: 0.12rem;
  236. height: 100%;
  237. margin: 0 auto;
  238. background: linear-gradient(0deg, #c88eff 0%, #7e5cff 100%);
  239. box-shadow: 0 -0.04rem 0.14rem 0 rgba(129, 93, 255, 0.4);
  240. border-radius: 0.05rem 0.05rem 0 0;
  241. }
  242.  
  243. // 0分展示规则
  244. .nodata {
  245. .pg-data {
  246. border-radius: 0;
  247. background: #e7e7e7;
  248. box-shadow: none;
  249. }
  250. }
  251.  
  252. .week {
  253. font-size: 0.2rem;
  254. line-height: 0.2rem;
  255. margin-top: 0.08rem;
  256. color: #666;
  257. }
  258. // 环图 - data02数据部分
  259. .data02-charts {
  260. margin-top: 0.32rem;
  261. height: 1.61rem;
  262. }
  263.  
  264. .canvas-box {
  265. position: relative;
  266. float: left;
  267. width: 1.61rem;
  268. height: 1.61rem;
  269. margin-left: 0.92rem;
  270. }
  271.  
  272. .my-canvas {
  273. width: 1.61rem;
  274. height: 1.61rem;
  275. }
  276. .clr-canvas {
  277. position: absolute;
  278. top: 0;
  279. left: 0;
  280. }
  281.  
  282. .canvas-data {
  283. position: absolute;
  284. top: 0.56rem;
  285. left: 0;
  286. right: 0;
  287. margin: auto;
  288. margin-left: -0.1rem;
  289. text-align: center;
  290. font-size: 0.24rem;
  291.  
  292. .num {
  293. font-size: 0.32rem;
  294. font-weight: 600;
  295. }
  296. }
  297. </style>

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号