飞道的博客

圆环进度条 两种实现方式

2525人阅读  评论(0)

先看图效果图:

这边UI的 设计图 长这样

 

 一个圆环的进度展示,这个圆环上开始位置 是齐口的,终点圆口,并且有一个圆;

列举了两种实现方式:

  • 第一种 纯的CSS实现;原理是 叠加旋转 而成。缺点在某些机型上面应为遮罩没有对齐(uniapp 半个像素不显示的问题,其他平台没有这问题),会出现白边(没有遮挡好的情况)
  •  第二种 createCanvasContext 直接画弧线;动态计算原点位置 叠加 显示

第一种实现方式 

理论上:纯css  两个半圆在相应的取值范围内 旋转,旋转的时候被父对象 遮罩剩余的部分

第一步: 两个半圆在旋转,旋转时候被父对象 clip 掉 剩余部分,两个红色底就是两个半圆的父元素,红色第里面的两个深色块就是 将要旋转的 元素;


   
  1. <view class= "box0-parent">
  2. <view class= "box0" :style= "box0Style">
  3. </view>
  4. </view>
  5. <view class= "box1-parent">
  6. <view class= "box1" :style= "box1Style">
  7. </view>
  8. </view>
  9. .box0-parent {
  10. position: absolute;
  11. top: 580rpx;
  12. width: 284rpx;
  13. height: 284rpx;
  14. background-color: #dd2124;
  15. clip: rect( 0px, 142rpx, 284rpx, 0px);
  16. // border-radius: 50%;
  17. .box0 {
  18. position: absolute;
  19. top: 0;
  20. width: 284rpx;
  21. height: 284rpx;
  22. background-color: #2C405A;
  23. clip: rect( 0px, 142rpx, 284rpx, 0px);
  24. border-radius: 50%;
  25. }
  26. }
  27. .box1-parent {
  28. position: absolute;
  29. top: 580rpx;
  30. width: 284rpx;
  31. height: 284rpx;
  32. background-color: #c34c24;
  33. clip: rect( 0px, 284rpx, 284rpx, 142rpx);
  34. // border-radius: 50%;
  35. .box1 {
  36. position: absolute;
  37. top: 0;
  38. width: 284rpx;
  39. height: 284rpx;
  40. background-color: #5a4600;
  41. clip: rect( 0px, 284rpx, 284rpx, 142rpx);
  42. border-radius: 50%;
  43. // transform: rotate(60deg);
  44. }
  45. }

 第二步: 调整下参数; 再调下颜色


   
  1. <template>
  2. <view class="process">
  3. <view class="box0-parent">
  4. <view class="box0" :style="box0Style">
  5. </view>
  6. </view>
  7. <view class="box1-parent">
  8. <view class="box1" :style="box1Style">
  9. </view>
  10. </view>
  11. <view class="input-range">
  12. <text> {{percent}}% </text> <text style="margin-left: 100rpx;">{{percent*3.6}} </text>
  13. <slider @changing="onChange" value="0" max="100" min="0" v-model="percent" activeColor="#FFCC33"
  14. backgroundColor= "#000000" block-color= "#8A6DE9"> </slider>
  15. </view>
  16. </view>
  17. </template>
  18. <script>
  19. export default {
  20. data( ) {
  21. return {
  22. percent: 30,
  23. }
  24. },
  25. computed: {
  26. box0Style( ) {
  27. if ( this. percent >= 50) {
  28. return `transform: rotate(${180 + this.percent*3.6}deg)`
  29. } else {
  30. return ``
  31. }
  32. },
  33. box1Style( ) {
  34. if ( this. percent <= 50) {
  35. return `transform: rotate(${this.percent*3.6}deg)`
  36. } else {
  37. return `display: none;`
  38. }
  39. }
  40. },
  41. methods: {
  42. onChange( value) {
  43. // console.log(value.detail.value)
  44. this. percent = value. detail. value
  45. }
  46. }
  47. }
  48. </script>
  49. <style lang="scss" scoped>
  50. .process {
  51. width: 100%;
  52. display: flex;
  53. flex-direction: column;
  54. justify-content: center;
  55. align-items: center;
  56. .input-range {
  57. position: absolute;
  58. top: 980rpx;
  59. width: 300rpx;
  60. }
  61. .box0-parent {
  62. position: absolute;
  63. top: 580rpx;
  64. width: 284rpx;
  65. height: 284rpx;
  66. background-color: #dd2124;
  67. clip: rect( 0px, 142rpx, 284rpx, 0px);
  68. // border-radius: 50%;
  69. .box0 {
  70. position: absolute;
  71. top: 0;
  72. width: 284rpx;
  73. height: 284rpx;
  74. // background-color: #2C405A;
  75. background-color: #2168F9;
  76. clip: rect( 0px, 142rpx, 284rpx, 0px);
  77. border-radius: 50%;
  78. }
  79. }
  80. .box1-parent {
  81. position: absolute;
  82. top: 580rpx;
  83. width: 284rpx;
  84. height: 284rpx;
  85. // background-color: #c34c24;
  86. background-color: #dd2124;
  87. // background-color: #2168F9;
  88. clip: rect( 0px, 284rpx, 284rpx, 142rpx);
  89. // border-radius: 50%;
  90. .box1 {
  91. position: absolute;
  92. top: 0;
  93. width: 284rpx;
  94. height: 284rpx;
  95. // background-color: #5a4600;
  96. background-color: #2168F9;
  97. clip: rect( 0px, 284rpx, 284rpx, 142rpx);
  98. border-radius: 50%;
  99. // transform: rotate( 60deg);
  100. }
  101. }
  102. }
  103. </style>

然后加上蓝色底色,圆环换上白色;真理下就能得到这么的 圆环了;

至于 白色圆点  直接绝对定位于半圆, 随着半圆移动即可;每个半圆都有一个

第二种实现方式

使用的是 canvas 中的 arc  (画弧线来实现的)

uni.createCanvasContexthttps://uniapp.dcloud.io/api/canvas/CanvasContext.html


   
  1. <canvas class="canvas-content" :canvas-id="canvasId" :id="canvasId">
  2. </canvas>
  3. methods: {
  4. onChange(value) {
  5. // console.log(value.detail.value)
  6. this.percent = value.detail.value
  7. this.drawCircleByProgress()
  8. },
  9. drawCircleByProgress(){
  10. // 表示进度的两端为圆形 目前没有找到只设置一段的方式
  11. this.canvasContent.setLineCap( 'round'); //圆形
  12. this.canvasContent.setLineCap( 'square'); //方形
  13. // 设置线条的宽度和颜色
  14. this.canvasContent.setLineWidth(uni.upx2px( 30));
  15. this.canvasContent.setStrokeStyle( '#ff0000');
  16. let endAngle = (( 2 * Math.PI) / 100) * this.percent + this.startAngle;
  17. this.canvasContent.beginPath();
  18. // 半径为整个canvas宽度的一半
  19. let radius = uni.upx2px( 284) / 2;
  20. this.canvasContent.arc(radius, radius, radius - uni.upx2px( 30), this.startAngle, endAngle, false);
  21. this.canvasContent.stroke();
  22. this.canvasContent.draw();
  23. }
  24. }
  25. .canvas-content{
  26. width: 284rpx;
  27. height: 284rpx;
  28. background-color: #059cdd;
  29. }

这种方式对于圆环中的 画一个白色球需要通过 弧度计算圆上一点的位置


   
  1. drawCircleByProgress() {
  2. // 表示进度的两端为圆形 目前没有找到只设置一段的方式
  3. this.canvasContent.setLineCap( 'round'); //圆形
  4. this.canvasContent.setLineCap( 'square'); //方形
  5. let width = uni.upx2px( 30)
  6. // 设置线条的宽度和颜色
  7. this.canvasContent.setLineWidth(width);
  8. this.canvasContent.setStrokeStyle( '#ff0000');
  9. // 画圆环
  10. let endAngle = (( 2 * Math.PI) / 100) * this.percent + this.startAngle;
  11. this.canvasContent.beginPath();
  12. // 半径为整个canvas宽度的一半
  13. let radius = uni.upx2px( 284) / 2;
  14. this.canvasContent.arc(radius, radius, radius - width, this.startAngle, endAngle, false);
  15. this.canvasContent.stroke();
  16. //原点要在 圆弧前面一点点的位置 所有这个加了 0.1;
  17. let p0x = radius + Math.cos(endAngle + .1) * (radius - width)
  18. let p0y = radius + Math.sin(endAngle + .1) * (radius - width)
  19. // 画圆球
  20. this.canvasContent.beginPath()
  21. this.canvasContent.arc(p0x, p0y, width / 2, 0, 2 * Math.PI)
  22. this.canvasContent.setFillStyle( '#F0AD4E');
  23. this.canvasContent.fill()
  24. // 画白色圆球
  25. this.canvasContent.beginPath()
  26. this.canvasContent.arc(p0x, p0y, width * 0.4, 0, 2 * Math.PI)
  27. this.canvasContent.setFillStyle( '#FFFFFF');
  28. this.canvasContent.fill()
  29. this.canvasContent.draw();
  30. }

真理下就能得到下面的例子了

完整代码:


  
  1. <template>
  2. <view class="process">
  3. <canvas class="canvas-content" :canvas-id="canvasId" :id="canvasId">
  4. </canvas>
  5. <view class="box0-parent">
  6. <view class="box0" :style="box0Style">
  7. </view>
  8. </view>
  9. <view class="box1-parent">
  10. <view class="box1" :style="box1Style">
  11. </view>
  12. </view>
  13. <view class="input-range">
  14. <text> {{percent}}% </text> <text style="margin-left: 100rpx;">{{percent*3.6}} </text>
  15. <slider @changing="onChange" value="0" max="100" min="0" v-model="percent" activeColor="#FFCC33"
  16. backgroundColor= "#000000" block-color= "#8A6DE9"> </slider>
  17. </view>
  18. </view>
  19. </template>
  20. <script>
  21. export default {
  22. data( ) {
  23. return {
  24. percent: 30,
  25. canvasId: 'canvasId',
  26. canvasContent: null,
  27. startAngle: - Math. PI / 2, //canvas画圆的起始角度,默认为3点钟方向即90度 方向,定位位到12位置 0度
  28. }
  29. },
  30. computed: {
  31. box0Style( ) {
  32. if ( this. percent >= 50) {
  33. return `transform: rotate(${180 + this.percent*3.6}deg)`
  34. } else {
  35. return ``
  36. }
  37. },
  38. box1Style( ) {
  39. if ( this. percent <= 50) {
  40. return `transform: rotate(${this.percent*3.6}deg)`
  41. } else {
  42. return `display: none;`
  43. }
  44. }
  45. },
  46. onShow( ) {
  47. this. canvasContent = uni. createCanvasContext( this. canvasId, this)
  48. this. drawCircleByProgress()
  49. },
  50. methods: {
  51. onChange( value) {
  52. // console.log(value.detail.value)
  53. this. percent = value. detail. value
  54. this. drawCircleByProgress()
  55. },
  56. drawCircleByProgress( ) {
  57. // 表示进度的两端为圆形 目前没有找到只设置一段的方式
  58. this. canvasContent. setLineCap( 'round'); //圆形
  59. this. canvasContent. setLineCap( 'square'); //方形
  60. let width = uni. upx2px( 30)
  61. // 设置线条的宽度和颜色
  62. this. canvasContent. setLineWidth(width);
  63. this. canvasContent. setStrokeStyle( '#ff0000');
  64. let endAngle = (( 2 * Math. PI) / 100) * this. percent + this. startAngle;
  65. this. canvasContent. beginPath();
  66. // 半径为整个canvas宽度的一半
  67. let radius = uni. upx2px( 284) / 2;
  68. this. canvasContent. arc(radius, radius, radius - width, this. startAngle, endAngle, false);
  69. this. canvasContent. stroke();
  70. //原点要在 圆弧前面一点点的位置 所有这个加了 0.1;
  71. let p0x = radius + Math. cos(endAngle + .1) * (radius - width)
  72. let p0y = radius + Math. sin(endAngle + .1) * (radius - width)
  73. this. canvasContent. beginPath()
  74. this. canvasContent. arc(p0x, p0y, width / 2, 0, 2 * Math. PI)
  75. this. canvasContent. setFillStyle( '#F0AD4E');
  76. this. canvasContent. fill()
  77. this. canvasContent. beginPath()
  78. this. canvasContent. arc(p0x, p0y, width * 0.4, 0, 2 * Math. PI)
  79. this. canvasContent. setFillStyle( '#FFFFFF');
  80. this. canvasContent. fill()
  81. this. canvasContent. draw();
  82. }
  83. }
  84. }
  85. </script>
  86. <style lang="scss" scoped>
  87. .process {
  88. width: 100%;
  89. display: flex;
  90. flex-direction: column;
  91. justify-content: center;
  92. align-items: center;
  93. .canvas-content {
  94. width: 284rpx;
  95. height: 284rpx;
  96. background-color: #059cdd;
  97. }
  98. .input-range {
  99. position: absolute;
  100. top: 750rpx;
  101. width: 300rpx;
  102. }
  103. .box0-parent {
  104. position: absolute;
  105. top: 380rpx;
  106. width: 284rpx;
  107. height: 284rpx;
  108. background-color: #dd2124;
  109. clip: rect( 0px, 142rpx, 284rpx, 0px);
  110. // border-radius: 50%;
  111. .box0 {
  112. position: absolute;
  113. top: 0;
  114. width: 284rpx;
  115. height: 284rpx;
  116. // background-color: #2C405A;
  117. background-color: #2168F9;
  118. clip: rect( 0px, 142rpx, 284rpx, 0px);
  119. border-radius: 50%;
  120. }
  121. }
  122. .box1-parent {
  123. position: absolute;
  124. top: 380rpx;
  125. width: 284rpx;
  126. height: 284rpx;
  127. // background-color: #c34c24;
  128. background-color: #dd2124;
  129. // background-color: #2168F9;
  130. clip: rect( 0px, 284rpx, 284rpx, 142rpx);
  131. // border-radius: 50%;
  132. .box1 {
  133. position: absolute;
  134. top: 0;
  135. width: 284rpx;
  136. height: 284rpx;
  137. // background-color: #5a4600;
  138. background-color: #2168F9;
  139. clip: rect( 0px, 284rpx, 284rpx, 142rpx);
  140. border-radius: 50%;
  141. // transform: rotate( 60deg);
  142. }
  143. }
  144. }
  145. </style>


转载:https://blog.csdn.net/nicepainkiller/article/details/125327485
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场