cmd-circle.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. <template>
  2. <view class="cmd-circle">
  3. <canvas :canvas-id="cid" :style="calCircleStyle"></canvas>
  4. </view>
  5. </template>
  6. <script>
  7. /**
  8. * 进度圈组件
  9. * @description 用圈显示一个操作完成的百分比时,为用户显示该操作的当前进度和状态。
  10. * @tutorial https://ext.dcloud.net.cn/plugin?id=259
  11. * @property {String} cid 画布编号 - 默认defaultCanvas
  12. * @property {String} type 进度圈类型 - 圆圈形:circle、仪表盘:dashboard,默认圆圈形:circle
  13. * @property {Number} percent 进度圈百分比值 - 显示范围0-100 ,可能数比较大就需要自己转成百分比的值
  14. * @property {Boolean} show-info 进度圈进度状态信息 - 显示进度数值或状态图标,默认true
  15. * @property {String} font-color 进度圈文字信息颜色
  16. * @property {String} font-size 进度圈文字信息大小 - 默认:14
  17. * @property {String} status 进度圈状态 - 正常:normal、完成:success、失败:exception,默认正常:normal
  18. * @property {Number} stroke-width 进度圈线条宽度 - 建议在条线的宽度范围:1-50,与进度条显示宽度有关,默认:6
  19. * @property {String} stroke-color 进度圈的颜色 - 设置后status状态无效
  20. * @property {String} stroke-background 进度圈的底圈颜色 - 默认:#eeeeee
  21. * @property {String} stroke-shape 进度圈两端的形状 - 圆:round、方直角:square,默认圆:round
  22. * @property {Number} width 进度圈布宽度 - 默认80
  23. * @property {String} gap-degree 进度圈形缺口角度 - 可取值 0 ~ 360,仅支持类型:circle
  24. * @property {String} gap-position 进度圈形缺口位置 - 可取值'top', 'bottom', 'left', 'right',仅支持类型:circle
  25. * @example <cmd-circle id="circle1" type="circle" :percent="75"></cmd-circle>
  26. */
  27. export default {
  28. name: "cmd-circle",
  29. props: {
  30. // 画布编号 默认defaultCanvas
  31. cid: {
  32. type: String,
  33. default: "defaultCanvas"
  34. },
  35. // 圈类型默认:circle,可选 circle dashboard
  36. type: {
  37. type: String,
  38. validator: val => {
  39. return ['circle', 'dashboard'].includes(val);
  40. },
  41. default: 'circle'
  42. },
  43. // 圈进度百分比值
  44. percent: {
  45. type: Number,
  46. validator: val => {
  47. return val >= 0 && val <= 100;
  48. },
  49. default: 0
  50. },
  51. // 圈是否显示进度数值或状态图标
  52. showInfo: {
  53. type: Boolean,
  54. default: true
  55. },
  56. // 圈文字信息颜色
  57. fontColor: {
  58. type: String,
  59. default: "#595959"
  60. },
  61. // 圈文字信息大小 默认14
  62. fontSize: {
  63. type: Number,
  64. default: 14
  65. },
  66. // 圈进度状态,可选:normal success exception
  67. status: {
  68. type: String,
  69. validator: val => {
  70. return ['normal', 'success', 'exception'].includes(val);
  71. },
  72. default: 'normal'
  73. },
  74. // 圈线条宽度1-50,与width有关
  75. strokeWidth: {
  76. type: Number,
  77. default: 6
  78. },
  79. // 圈的颜色,设置后status状态无效
  80. strokeColor: {
  81. type: String,
  82. default: ''
  83. },
  84. // 圈的底圈颜色 默认:#eeeeee
  85. strokeBackground: {
  86. type: String,
  87. default: '#eeeeee'
  88. },
  89. // 圈两端的形状 可选:'round', 'square'
  90. strokeShape: {
  91. type: String,
  92. validator: val => {
  93. return ['round', 'square'].includes(val);
  94. },
  95. default: 'round'
  96. },
  97. // 圈画布宽度
  98. width: {
  99. type: Number,
  100. default: 80
  101. },
  102. // 圈缺口角度,可取值 0 ~ 360,仅支持类型:circle
  103. gapDegree: {
  104. type: Number,
  105. validator: val => {
  106. return val >= 0 && val <= 360;
  107. },
  108. default: 360
  109. },
  110. // 圈缺口开始位置,可取值'top', 'bottom', 'left', 'right',仅支持类型:circle
  111. gapPosition: {
  112. type: String,
  113. validator: val => {
  114. return ['top', 'bottom', 'left', 'right'].includes(val);
  115. },
  116. default: 'top'
  117. }
  118. },
  119. data() {
  120. return {
  121. // 画布实例
  122. ctx: {},
  123. // 圈半径
  124. width2px: ""
  125. }
  126. },
  127. computed: {
  128. // 计算设置圈样式
  129. calCircleStyle() {
  130. return `width: ${this.width}px;
  131. height: ${this.width}px;`
  132. },
  133. // 计算圈状态
  134. calStatus() {
  135. let status = {}
  136. switch (this.status) {
  137. case 'normal':
  138. status = {
  139. color: "#1890ff",
  140. value: 1
  141. };
  142. break;
  143. case 'success':
  144. status = {
  145. color: "#52c41a",
  146. value: 2
  147. };
  148. break;
  149. case 'exception':
  150. status = {
  151. color: "#f5222d",
  152. value: 3
  153. };
  154. break;
  155. }
  156. return status
  157. },
  158. // 计算圈缺口角度
  159. calGapDegree() {
  160. return this.gapDegree <= 0 ? 360 : this.gapDegree
  161. },
  162. // 计算圈缺口位置
  163. calGapPosition() {
  164. let gapPosition = 0
  165. switch (this.gapPosition) {
  166. case 'bottom':
  167. gapPosition = 90;
  168. break;
  169. case 'left':
  170. gapPosition = 180;
  171. break;
  172. case 'top':
  173. gapPosition = 270;
  174. break;
  175. case 'right':
  176. gapPosition = 360;
  177. break;
  178. }
  179. return gapPosition
  180. },
  181. },
  182. watch: {
  183. // 监听百分比值改变
  184. percent(val) {
  185. this.drawStroke(val);
  186. }
  187. },
  188. mounted() {
  189. // 创建画布实例
  190. this.ctx = uni.createCanvasContext(this.cid, this)
  191. // upx转px 圈半径大小
  192. this.width2px = uni.upx2px(this.width)
  193. // 绘制初始
  194. this.$nextTick(() => {
  195. this.drawStroke(this.percent)
  196. })
  197. },
  198. methods: {
  199. // 绘制圈
  200. drawStroke(percent) {
  201. percent = percent >= 100 ? 100 : percent < 0 ? 0 : percent
  202. // 圈条进度色
  203. let color = this.strokeColor || this.calStatus.color
  204. // 是否圈中心显示信息
  205. if (this.showInfo) {
  206. switch (this.calStatus.value) {
  207. case 1:
  208. if (percent >= 100) {
  209. // 设置打勾
  210. this.drawSuccess()
  211. percent = 100
  212. color = "#52c41a"
  213. } else {
  214. // 设置字体
  215. this.drawText(percent)
  216. }
  217. break;
  218. case 2:
  219. // 设置打勾
  220. this.drawSuccess()
  221. percent = 100
  222. color = "#52c41a"
  223. break;
  224. case 3:
  225. // 设置打叉
  226. this.drawException()
  227. percent = 0
  228. color = "#f5222d"
  229. break;
  230. default:
  231. break;
  232. }
  233. }
  234. // 缺口
  235. let gapPosition = this.calGapPosition
  236. let gapDegree = this.calGapDegree
  237. // 仪表固定
  238. if (this.type === "dashboard") {
  239. gapPosition = 135
  240. gapDegree = 270
  241. }
  242. // 圈型条宽
  243. this.ctx.setLineCap(this.strokeShape)
  244. this.ctx.setLineWidth(this.strokeWidth)
  245. // 位置原点
  246. this.ctx.translate(this.width2px, this.width2px)
  247. // 缺口方向
  248. this.ctx.rotate(gapPosition * Math.PI / 180)
  249. // 圈底
  250. this.ctx.beginPath()
  251. this.ctx.arc(0, 0, this.width2px - this.strokeWidth, 0, gapDegree * Math.PI / 180)
  252. this.ctx.setStrokeStyle(this.strokeBackground)
  253. this.ctx.stroke()
  254. // 圈进度
  255. this.ctx.beginPath()
  256. this.ctx.arc(0, 0, this.width2px - this.strokeWidth, 0, percent * gapDegree * Math.PI / 18000)
  257. this.ctx.setStrokeStyle(color)
  258. this.ctx.stroke()
  259. // 绘制
  260. this.ctx.draw()
  261. },
  262. // 绘制文字格式
  263. drawText(percent) {
  264. this.ctx.beginPath()
  265. this.ctx.setFontSize(this.fontSize)
  266. this.ctx.setFillStyle(this.fontColor)
  267. this.ctx.setTextAlign('center')
  268. this.ctx.fillText(`${percent}%`, this.width2px, this.width2px + this.fontSize / 2)
  269. this.ctx.stroke()
  270. },
  271. // 绘制成功打勾
  272. drawSuccess() {
  273. let x = this.width2px - this.fontSize / 2
  274. let y = this.width2px + this.fontSize / 2
  275. this.ctx.beginPath()
  276. this.ctx.setLineCap('round')
  277. this.ctx.setLineWidth(this.fontSize / 4)
  278. this.ctx.moveTo(this.width2px, y)
  279. this.ctx.lineTo(y, x)
  280. this.ctx.moveTo(this.width2px, y)
  281. this.ctx.lineTo(x, this.width2px)
  282. this.ctx.setStrokeStyle("#52c41a")
  283. this.ctx.stroke()
  284. },
  285. // 绘制异常打叉
  286. drawException() {
  287. let x = this.width2px - this.fontSize / 2
  288. let y = this.width2px + this.fontSize / 2
  289. this.ctx.beginPath()
  290. this.ctx.setLineCap('round')
  291. this.ctx.setLineWidth(this.fontSize / 4)
  292. this.ctx.moveTo(x, x)
  293. this.ctx.lineTo(y, y)
  294. this.ctx.moveTo(y, x)
  295. this.ctx.lineTo(x, y)
  296. this.ctx.setStrokeStyle("#f5222d")
  297. this.ctx.stroke()
  298. }
  299. }
  300. };
  301. </script>
  302. <style>
  303. .cmd-circle {
  304. display: inline-block;
  305. box-sizing: border-box;
  306. list-style: none;
  307. margin: 0;
  308. padding: 0;
  309. }
  310. </style>