l-circularProgress.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <template>
  2. <view class="circular-container" :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }">
  3. <canvas class="circular-bg" :canvas-id="bgCanvasId" :id="bgCanvasId"
  4. :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }" v-if="isBgShow"></canvas>
  5. <canvas class="circular-progress" :canvas-id="canvasId" :id="canvasId"
  6. :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }"></canvas>
  7. <slot></slot>
  8. </view>
  9. </template>
  10. <script>
  11. export default {
  12. name: 'LCircularProgress',
  13. props: {
  14. // 宽度
  15. boxWidth: {
  16. type: Number,
  17. default: 200
  18. },
  19. //高度
  20. boxHeight: {
  21. type: Number,
  22. default: 200
  23. },
  24. //进度条线条宽度
  25. lineWidth: {
  26. type: Number,
  27. default: 4
  28. },
  29. //是否显示背景进度条
  30. isBgShow: {
  31. type: Boolean,
  32. default: true
  33. },
  34. //背景进度条颜色
  35. bgColor: {
  36. type: String,
  37. default: '#CCCCCC'
  38. },
  39. // 进度条类型 circular圆形 halfCircular 半圆
  40. type:{
  41. type:String,
  42. default:'circular'
  43. },
  44. //进度百分比
  45. percent: {
  46. type: Number,
  47. default: 0
  48. },
  49. //进度条颜色
  50. progressColor: {
  51. type: String,
  52. default: '#5677fc'
  53. },
  54. //进度条渐变颜色
  55. gradualColor: {
  56. type: String,
  57. default: ''
  58. },
  59. //是否显示进度文字
  60. fontShow: {
  61. type: Boolean,
  62. default: true
  63. },
  64. //进度字体大小
  65. fontSize: {
  66. type: Number,
  67. default: 12
  68. },
  69. //进度字体颜色
  70. fontColor: {
  71. type: String,
  72. default: '#5677fc'
  73. },
  74. },
  75. watch: {
  76. percent(val) {
  77. this.draw();
  78. }
  79. },
  80. mounted() {
  81. this.draw(true)
  82. },
  83. data() {
  84. return {
  85. canvasId: 'canvasId',
  86. bgCanvasId: 'bgCanvasId',
  87. startPercent: 0,
  88. progressContext: null,
  89. linearGradient: null,
  90. }
  91. },
  92. methods:{
  93. draw(init) {
  94. let start = this.startPercent;
  95. start = start > this.percent ? 0 : start;
  96. if (this.isBgShow && init) { //初始化需判断 防止后续手动增加再次绘制背景
  97. this.drawBgCircular();
  98. }
  99. this.drawCircular(start);
  100. },
  101. //背景
  102. drawBgCircular() {
  103. let ctx = uni.createCanvasContext(this.bgCanvasId, this);
  104. let lineWidth = Number(this.lineWidth)
  105. ctx.setLineWidth(lineWidth);
  106. ctx.setStrokeStyle(this.bgColor);
  107. //终止弧度
  108. let end = null,start = null
  109. if(this.type == 'circular'){
  110. start = -Math.PI / 2
  111. end = Math.PI - start;
  112. }else{
  113. start = -Math.PI
  114. end = 0;
  115. }
  116. this.drawArc(ctx,start,end);
  117. },
  118. //创建弧线
  119. drawArc(ctx,start,end) {
  120. ctx.setLineCap('round');
  121. ctx.beginPath();
  122. let radius = this.boxWidth / 2;
  123. let lineWidth = Number(this.lineWidth)
  124. ctx.arc(radius, radius, radius - lineWidth, start, end, false);
  125. ctx.stroke();
  126. ctx.draw();
  127. },
  128. //进度圆环
  129. drawCircular(startPercent) {
  130. let ctx = this.progressContext;
  131. let gradient = this.linearGradient;
  132. if (!ctx) {
  133. ctx = uni.createCanvasContext(this.canvasId, this);
  134. let boxWidth = Number(this.boxWidth)
  135. gradient = ctx.createLinearGradient(0, 0, boxWidth, 0);
  136. gradient.addColorStop('0', this.progressColor);
  137. if (this.gradualColor) {
  138. gradient.addColorStop('1', this.gradualColor);
  139. }
  140. this.progressContext = ctx;
  141. this.linearGradient = gradient;
  142. }
  143. let lineWidth = Number(this.lineWidth)
  144. ctx.setLineWidth(lineWidth);
  145. ctx.setStrokeStyle(gradient);
  146. let time = this.percent == 0 || this.duration < 50 ? 0 : this.duration / this.percent;
  147. if (this.percent > 0) {
  148. startPercent = this.duration < 50 ? this.percent - 1 : startPercent;
  149. startPercent++;
  150. }
  151. if (this.fontShow) {
  152. let fontSize = Number(this.fontSize)
  153. ctx.setFontSize(fontSize);
  154. ctx.setFillStyle(this.fontColor);
  155. ctx.setTextAlign('center');
  156. ctx.setTextBaseline('middle');
  157. let percent = startPercent + '%';
  158. let radius = this.boxWidth / 2;
  159. ctx.fillText(percent, radius, radius);
  160. }
  161. if (this.percent == 0 ) {
  162. ctx.draw();
  163. } else {
  164. //终止弧度
  165. let end = null,start = null
  166. if(this.type == 'circular'){
  167. start = -Math.PI / 2
  168. end = ((2 * Math.PI) / 100) * startPercent + start;
  169. }else{
  170. start = -Math.PI
  171. end = (Math.PI / 100) * startPercent + start;
  172. }
  173. let eAngle = ((2 * Math.PI) / 100) * startPercent + this.sAngle;
  174. this.drawArc(ctx,start,end);
  175. }
  176. setTimeout(() => {
  177. this.startPercent = startPercent;
  178. if (startPercent >= this.percent) {
  179. this.$emit('end', {
  180. percent: startPercent + '%'
  181. });
  182. } else {
  183. this.drawCircular(startPercent);
  184. }
  185. }, time);
  186. },
  187. }
  188. }
  189. </script>
  190. <style lang="scss" scoped>
  191. .circular-container {
  192. position: relative;
  193. .circular-progress {
  194. position: absolute;
  195. left: 0;
  196. top: 0;
  197. z-index: 999;
  198. }
  199. }
  200. </style>