xiaoran-circle.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <template>
  2. <view
  3. :style="{
  4. width: circleSize.width,
  5. height: circleSize.height
  6. }"
  7. class="circle_box"
  8. >
  9. <view class="canvas">
  10. <slot name="canvas"></slot>
  11. </view>
  12. <view class="slot">
  13. <slot></slot>
  14. </view>
  15. </view>
  16. </template>
  17. <script>
  18. export default {
  19. name: "iCircle",
  20. props: {
  21. percent: {
  22. // 百分比
  23. type: Number,
  24. default: 0
  25. },
  26. prefix: {
  27. // 多个圆环情况下的前缀
  28. type: String,
  29. default: ""
  30. },
  31. size: {
  32. // 图表的宽度和高度,单位 upx
  33. type: Number,
  34. default: 120
  35. },
  36. strokeWidth: {
  37. // 进度环的线宽,单位 upx
  38. type: Number,
  39. default: 10
  40. },
  41. strokeColor: {
  42. // 进度环的颜色 16进制
  43. type: String,
  44. default: "#2d8cf0"
  45. },
  46. trailWidth: {
  47. // 进度环背景的线宽,单位 upx
  48. type: Number,
  49. default: 12
  50. },
  51. trailColor: {
  52. // 进度环背景的颜色
  53. type: String,
  54. default: "#eaeef2"
  55. },
  56. BgId: {
  57. // BgId
  58. type: String,
  59. required: true
  60. },
  61. InId: {
  62. // InId
  63. type: String,
  64. required: true
  65. },
  66. dashboard: {
  67. // 仪表盘
  68. type: Boolean,
  69. default: false
  70. },
  71. start: {
  72. // 仪表盘起始角度
  73. type: Number,
  74. default: 27
  75. }
  76. },
  77. data() {
  78. return {
  79. selfPercent: 0,
  80. type: "add",
  81. T: null
  82. }
  83. },
  84. computed: {
  85. UniSize() {
  86. // 转换为upx
  87. return uni.upx2px(this.size);
  88. },
  89. circleSize() {
  90. return {
  91. width: `${this.UniSize}px`,
  92. height: `${this.UniSize}px`
  93. };
  94. },
  95. center_x() {
  96. return this.UniSize / 2;
  97. },
  98. center_y() {
  99. return this.UniSize / 2;
  100. },
  101. rad() {
  102. return Math.PI*2/100;
  103. },
  104. lineWidth() {
  105. return uni.upx2px(this.trailWidth);
  106. },
  107. BgWidth() {
  108. return uni.upx2px(this.strokeWidth);
  109. },
  110. radius() {
  111. return this.center_x - this.BgWidth - Math.abs(this.BgWidth - this.lineWidth);
  112. },
  113. startDeg() {
  114. return Math.PI * (1 - this.start/180);
  115. },
  116. countDeg() {
  117. return this.start * 2 + 180;
  118. },
  119. fillDegArr() {
  120. let start, end;
  121. if (this.dashboard) {
  122. start = this.startDeg;
  123. end = Math.PI*this.selfPercent/100*this.countDeg/180 + this.startDeg;
  124. } else {
  125. start = -Math.PI/2;
  126. end = -Math.PI/2 + this.selfPercent * this.rad;
  127. }
  128. return {start, end};
  129. },
  130. bgDegArr() {
  131. let start, end;
  132. if (this.dashboard) {
  133. start = this.startDeg;
  134. end = Math.PI*this.countDeg/180 + this.startDeg;
  135. } else {
  136. start = 0;
  137. end = Math.PI * 2;
  138. }
  139. return {start, end};
  140. }
  141. },
  142. methods: {
  143. Init() {
  144. this.backgroundCircle();
  145. this.requestAnimationFrame();
  146. },
  147. requestAnimationFrame() {
  148. if (this.type == "add") {
  149. if (this.selfPercent >= this.percent) {
  150. clearTimeout(this.T);
  151. return;
  152. }
  153. this.selfPercent++;
  154. if (this.selfPercent >= 100) {
  155. this.selfPercent = 100;
  156. }
  157. } else {
  158. if (this.selfPercent <= this.percent) {
  159. clearTimeout(this.T);
  160. return;
  161. }
  162. this.selfPercent--;
  163. if (this.selfPercent <= 0) {
  164. this.selfPercent = 0;
  165. }
  166. }
  167. this.foregroundCircle();
  168. this.T = setTimeout(this.requestAnimationFrame, 5);
  169. },
  170. backgroundCircle() {
  171. const context = uni.createCanvasContext(this.BgId);
  172. // 绘制背景圆环
  173. const rad = Math.PI*2/100;
  174. context.save();
  175. // 开始路径绘制
  176. context.beginPath();
  177. context.setLineWidth(this.lineWidth); //设置线宽
  178. context.setLineCap("round");
  179. context.setStrokeStyle(this.trailColor);
  180. //用于绘制圆弧context.arc(x坐标,y坐标,半径,起始角度,终止角度,顺时针/逆时针)
  181. // context.arc(this.center_x, this.center_y, this.radius, 0, Math.PI * 2, false);
  182. context.arc(this.center_x, this.center_y, this.radius , this.bgDegArr.start, this.bgDegArr.end, false);
  183. context.stroke();
  184. // 结束绘制路径
  185. context.closePath();
  186. // 恢复之前保存的绘图上下文。
  187. context.restore();
  188. // 绘制
  189. context.draw()
  190. },
  191. foregroundCircle(){
  192. const context = uni.createCanvasContext(this.InId);
  193. context.save();
  194. context.setStrokeStyle(this.strokeColor);
  195. context.setLineWidth(this.BgWidth); //设置线宽
  196. context.setLineCap("round");
  197. context.beginPath();
  198. context.arc(this.center_x, this.center_y, this.radius , this.fillDegArr.start, this.fillDegArr.end, false);
  199. context.stroke();
  200. context.closePath();
  201. context.restore();
  202. context.draw();
  203. }
  204. },
  205. mounted() {
  206. this.Init();
  207. },
  208. onReady() {
  209. },
  210. watch: {
  211. percent(newV, oldV) {
  212. if (newV > oldV) {
  213. this.type = "add";
  214. } else {
  215. this.type = "reduce";
  216. }
  217. this.Init();
  218. }
  219. }
  220. };
  221. </script>
  222. <style>
  223. @import url("./xiaoran-circle.css");
  224. </style>