qs-circle-percent.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <template>
  2. <view class="circle-container">
  3. <canvas :canvas-id="canvasId" :style="{ width: size + 'px', height: size + 'px' }"></canvas>
  4. <view class="circle-text">
  5. <view class="percent">{{ percent }}<text class="mini-text">%</text></view>
  6. <view class="tips">充电枪正在<text class="tips-text">充电中</text></view>
  7. </view>
  8. </view>
  9. </template>
  10. <script>
  11. export default {
  12. name: "qs-circle-percent",
  13. props: {
  14. canvasId: {
  15. type: String,
  16. default: "11", // 进度颜色
  17. },
  18. percent: {
  19. type: Number,
  20. default: 0, // 当前进度百分比
  21. },
  22. size: {
  23. type: Number,
  24. default: 200, // 圆尺寸
  25. },
  26. lineWidth: {
  27. type: Number,
  28. default: 5, // 线宽
  29. },
  30. color: {
  31. type: [String, Array, Object],
  32. default: () => "#ff7b00", // 进度颜色 - 支持单色、渐变色数组或渐变配置对象
  33. },
  34. backgroundColor: {
  35. type: String,
  36. default: "#f2f2f2", // 背景圆颜色
  37. },
  38. angle: {
  39. type: Number,
  40. default: 360, // 显示的圆弧角度(360=全圆, 180=半圆, 120=三分之一圆)
  41. },
  42. // 渐变配置参数
  43. gradientConfig: {
  44. type: Object,
  45. default: () => ({
  46. type: 'linear', // 'linear' 或 'radial'
  47. colorStops: [], // 渐变色停止点,如 [{offset: 0, color: '#ff7b00'}, {offset: 1, color: '#ffaa00'}]
  48. angle: 0 // 线性渐变角度(度)
  49. })
  50. },
  51. },
  52. data() {
  53. return {
  54. ctx: null,
  55. };
  56. },
  57. mounted() {
  58. this.$nextTick(() => {
  59. this.initCanvas();
  60. })
  61. },
  62. watch: {
  63. percent() {
  64. this.drawCircle();
  65. },
  66. angle() {
  67. this.drawCircle();
  68. },
  69. },
  70. methods: {
  71. initCanvas() {
  72. const ctx = uni.createCanvasContext(this.canvasId, this);
  73. this.ctx = ctx;
  74. this.drawCircle();
  75. },
  76. drawCircle() {
  77. const { ctx, percent, size, lineWidth, color, backgroundColor, angle } = this;
  78. const radius = (size - lineWidth) / 2;
  79. const center = size / 2;
  80. ctx.clearRect(0, 0, size, size);
  81. // 计算起始角度和结束角度
  82. const startAngle = (Math.PI / 2) + (Math.PI * (180 - angle) / 360);
  83. const endAngle = (Math.PI * 2 + Math.PI / 2) - (Math.PI * (180 - angle) / 360);
  84. // 背景弧
  85. ctx.beginPath();
  86. ctx.arc(center, center, radius, startAngle, endAngle, false);
  87. ctx.setStrokeStyle(backgroundColor);
  88. ctx.setLineWidth(lineWidth);
  89. ctx.setLineCap("round");
  90. ctx.stroke();
  91. // 进度弧
  92. const progressAngle = startAngle + ((endAngle - startAngle) * percent / 100);
  93. ctx.beginPath();
  94. ctx.arc(center, center, radius, startAngle, progressAngle, false);
  95. // 根据 color 类型设置颜色
  96. if (typeof color === 'string') {
  97. // 单色
  98. ctx.setStrokeStyle(color);
  99. } else if (Array.isArray(color)) {
  100. // 渐变色数组
  101. if (color.length >= 2) {
  102. const gradient = this.createGradient(ctx, center, radius, startAngle, progressAngle, color);
  103. ctx.setStrokeStyle(gradient);
  104. } else {
  105. ctx.setStrokeStyle(color[0] || "#ff7b00");
  106. }
  107. } else if (typeof color === 'object' && color.colorStops && color.colorStops.length > 0) {
  108. // 渐变配置对象
  109. const gradient = this.createGradientFromConfig(ctx, center, radius, startAngle, progressAngle, color);
  110. ctx.setStrokeStyle(gradient);
  111. } else {
  112. ctx.setStrokeStyle("#ff7b00");
  113. }
  114. ctx.setLineCap("round");
  115. ctx.setLineWidth(lineWidth);
  116. ctx.stroke();
  117. ctx.draw();
  118. },
  119. // 创建基于颜色数组的渐变
  120. createGradient(ctx, center, radius, startAngle, progressAngle, colorArray) {
  121. // 使用中心点创建径向渐变,更适合圆形
  122. const gradient = ctx.createLinearGradient(
  123. center - radius * Math.cos(startAngle),
  124. center - radius * Math.sin(startAngle),
  125. center + radius * Math.cos(progressAngle),
  126. center + radius * Math.sin(progressAngle)
  127. );
  128. const step = 1 / (colorArray.length - 1);
  129. colorArray.forEach((color, index) => {
  130. gradient.addColorStop(Math.min(step * index, 1), color);
  131. });
  132. return gradient;
  133. },
  134. // 根据配置对象创建渐变
  135. createGradientFromConfig(ctx, center, radius, startAngle, endAngle, config) {
  136. let gradient;
  137. if (config.type === 'radial') {
  138. // 径向渐变
  139. gradient = ctx.createRadialGradient(
  140. center,
  141. center,
  142. 0,
  143. center,
  144. center,
  145. radius
  146. );
  147. } else {
  148. // 线性渐变 - 根据角度计算起点和终点
  149. const angleInRadians = (config.angle || 0) * Math.PI / 180;
  150. const halfRadius = radius * 0.7; // 调整半径以适配弧形
  151. const startX = center - halfRadius * Math.cos(angleInRadians);
  152. const startY = center - halfRadius * Math.sin(angleInRadians);
  153. const endX = center + halfRadius * Math.cos(angleInRadians);
  154. const endY = center + halfRadius * Math.sin(angleInRadians);
  155. gradient = ctx.createLinearGradient(startX, startY, endX, endY);
  156. }
  157. // 添加颜色停止点
  158. config.colorStops.forEach(stop => {
  159. gradient.addColorStop(stop.offset, stop.color);
  160. });
  161. return gradient;
  162. }
  163. },
  164. };
  165. </script>
  166. <style lang="scss" scoped>
  167. .circle-container {
  168. display: flex;
  169. flex-direction: column;
  170. align-items: center;
  171. justify-content: center;
  172. position: relative;
  173. }
  174. .circle-text {
  175. position: absolute;
  176. text-align: center;
  177. font-size: 18rpx;
  178. font-weight: bold;
  179. color: #333;
  180. top: 50rpx;
  181. .percent {
  182. font-weight: 800;
  183. font-size: 80rpx;
  184. color: #2B303A;
  185. .mini-text {
  186. font-weight: normal;
  187. font-size: 28rpx;
  188. }
  189. }
  190. .tips {
  191. font-weight: 800;
  192. font-size: 24rpx;
  193. .tips-text {
  194. color: #3EB6F8;
  195. }
  196. }
  197. }
  198. </style>