普兆健康管家前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
7.5 KiB

  1. <template>
  2. <view class="progress_box">
  3. <canvas class="progress_bg" :canvas-id="`cpbg${code}`" type="2d"></canvas>
  4. <canvas class="progress_line" :canvas-id="`cpline${code}`" type="2d"></canvas>
  5. <canvas class="progress_bar" :canvas-id="`cpbar${code}`" type="2d"></canvas>
  6. <div class="bg-inner"></div>
  7. <div class="progress-info">
  8. <div class="flex">
  9. <div class="progress-score">{{ progress }}</div>
  10. <div class="progress-unit"></div>
  11. </div>
  12. <div class="progress-desc">健康综合分数</div>
  13. </div>
  14. </view>
  15. </template>
  16. <script>
  17. const START = - Math.PI * 3 / 2 + Math.PI * 1 / 6
  18. const END = Math.PI * 2 / 6
  19. export default {
  20. props: {
  21. progress: {
  22. type: Number,
  23. default: 0
  24. }
  25. },
  26. data() {
  27. return {
  28. code: Math.floor(Math.random() * 100).toString(),
  29. retry: 20,
  30. }
  31. },
  32. mounted() {
  33. this.drawProgressbg();
  34. this.drawLine()
  35. },
  36. watch: {
  37. progress: {
  38. handler(val) {
  39. this.$nextTick(() => {
  40. this.drawProgress(val)
  41. })
  42. },
  43. immediate: true,
  44. },
  45. },
  46. methods: {
  47. drawProgressbg() {
  48. uni.createSelectorQuery().in(this)
  49. .select('.progress_bg')
  50. .fields({
  51. node: true,
  52. size: true
  53. })
  54. .exec(async (res) => {
  55. const canvas = res[0].node
  56. // Canvas 画布的实际绘制宽高
  57. const width = res[0].width
  58. const height = res[0].height
  59. //根据dpr调整
  60. const dpr = wx.getWindowInfo().pixelRatio
  61. canvas.width = width * dpr
  62. canvas.height = height * dpr
  63. let Ratio = width / 366
  64. // 渲染上下文
  65. const ctx = canvas.getContext('2d')
  66. ctx.scale(dpr, dpr)
  67. ctx.clearRect(0, 0, width, height)
  68. let w = 20 * Ratio
  69. ctx.lineWidth = w; // 设置圆环的宽度
  70. ctx.strokeStyle = '#E1E0E6'; // 设置圆环的颜色
  71. ctx.lineCap = 'round'; // 设置圆环端点的形状
  72. ctx.beginPath(); //开始一个新的路径
  73. let x = width / 2
  74. let y = height / 2
  75. let r = 159 * Ratio - w / 2
  76. ctx.arc(x, y, r, START + Math.PI / 50, END - Math.PI / 50, false);
  77. //设置一个原点(110,110),半径为100的圆的路径到当前路径
  78. ctx.stroke(); //对当前路径进行描边
  79. })
  80. },
  81. // 画刻度
  82. drawLine() {
  83. uni.createSelectorQuery().in(this)
  84. .select('.progress_line')
  85. .fields({
  86. node: true,
  87. size: true
  88. })
  89. .exec(async (res) => {
  90. const canvas = res[0].node
  91. // Canvas 画布的实际绘制宽高
  92. const width = res[0].width
  93. const height = res[0].height
  94. //根据dpr调整
  95. const dpr = wx.getWindowInfo().pixelRatio
  96. canvas.width = width * dpr
  97. canvas.height = height * dpr
  98. let Ratio = width / 366
  99. // 渲染上下文
  100. const ctx = canvas.getContext('2d')
  101. ctx.scale(dpr, dpr)
  102. ctx.clearRect(0, 0, width, height)
  103. let x = width / 2
  104. let y = height / 2
  105. let r = 139 * Ratio
  106. let lineWidth = 3 * Ratio;
  107. let lineHeight = 8 * Ratio;
  108. let startAngle = Math.PI / 2
  109. let textArr = ['', '0', '差', '20', '较差', '40', '中等', '60', '良好', '80', '优秀', '100']
  110. for (let i = 0; i < 12; i++) {
  111. ctx.beginPath();
  112. ctx.lineWidth = lineWidth;
  113. ctx.strokeStyle = '#E1E0E6';
  114. let angle = startAngle - Math.PI * i / 6
  115. let x0 = x - r * Math.cos(angle)
  116. let y0 = y + r * Math.sin(angle)
  117. let x1 = x0 + lineHeight * Math.cos(angle)
  118. let y1 = y0 - lineHeight * Math.sin(angle)
  119. ctx.moveTo(x0, y0);
  120. ctx.lineTo(x1, y1);
  121. ctx.stroke();
  122. ctx.closePath();
  123. ctx.translate(x1, y1)
  124. ctx.rotate(Math.PI * 3 / 2 - angle);
  125. let text = textArr[i]
  126. ctx.font = "normal normal normal 7px normal";
  127. ctx.fillStyle = "#D0CFD3";
  128. // 计算文本的宽度和高度
  129. const metrics = ctx.measureText(text);
  130. const textWidth = metrics.width;
  131. ctx.fillText(text, - textWidth / 2 - lineWidth / 2, 8);
  132. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  133. }
  134. })
  135. },
  136. drawProgress(step) {
  137. console.log('drawProgress', step)
  138. if (!step) {
  139. return
  140. }
  141. uni.createSelectorQuery().in(this)
  142. .select('.progress_bar')
  143. .fields({
  144. node: true,
  145. size: true
  146. })
  147. .exec(async (res) => {
  148. console.log('progress_bar', res)
  149. if (!res?.[0]?.node) {
  150. if (this.retry) {
  151. this.retry -= 1
  152. console.log('retry', this.retry)
  153. setTimeout(() => {
  154. this.drawProgress(step)
  155. }, 200)
  156. }
  157. return
  158. }
  159. const canvas = res[0].node
  160. // Canvas 画布的实际绘制宽高
  161. const width = res[0].width
  162. const height = res[0].height
  163. //根据dpr调整
  164. const dpr = wx.getWindowInfo().pixelRatio
  165. canvas.width = width * dpr
  166. canvas.height = height * dpr
  167. let Ratio = width / 366
  168. // 渲染上下文
  169. const ctx = canvas.getContext('2d')
  170. ctx.scale(dpr, dpr)
  171. ctx.clearRect(0, 0, width, height)
  172. // 进度条的渐变(中心x坐标-半径-边宽,中心Y坐标,中心x坐标+半径+边宽,中心Y坐标)
  173. let gradient = ctx.createLinearGradient(0, 0, 130, 0);
  174. // 参数step 为绘制的百分比
  175. let end = (step / 100) * (END - START) + START; // 最后的角度
  176. gradient.addColorStop('0', '#7451DE');
  177. gradient.addColorStop('1.0', '#B1A4FF');
  178. let w = 20 * Ratio
  179. ctx.lineWidth = w; // 设置圆环的宽度
  180. ctx.strokeStyle = gradient; // 设置圆环的颜色
  181. ctx.lineCap = 'round'; // 设置圆环端点的形状
  182. ctx.shadowOffsetX = 2 * Ratio;
  183. ctx.shadowOffsetY = 2 * Ratio;
  184. ctx.shadowBlur = 8 * Ratio;
  185. ctx.shadowColor = "rgba(64, 0, 255, 0.3)";
  186. ctx.beginPath();
  187. let x = width / 2
  188. let y = height / 2
  189. let r = 159 * Ratio - w / 2
  190. ctx.arc(x, y, r, START + Math.PI / 50, end - Math.PI / 50, false);
  191. ctx.stroke();
  192. })
  193. },
  194. }
  195. };
  196. </script>
  197. <style lang="scss" scoped>
  198. $size: 366rpx;
  199. $innerSize: 202rpx;
  200. .progress_box {
  201. position: relative;
  202. width: $size;
  203. height: $size;
  204. border-radius: 50%;
  205. box-shadow: 0 0 6.7px 1px #D3D2E599;
  206. }
  207. .progress_bg,
  208. .progress_bar,
  209. .progress_line,
  210. .progress_font {
  211. position: absolute;
  212. width: $size;
  213. height: $size;
  214. }
  215. .bg-inner {
  216. position: absolute;
  217. left: 50%;
  218. top: 50%;
  219. transform: translate(-50%, -50%);
  220. width: $innerSize;
  221. height: $innerSize;
  222. border-radius: 50%;
  223. background-image: linear-gradient(to bottom, #FFFFFF, #E9E7F4);
  224. box-shadow: 0 8rpx 8rpx 0 #D3D2E5;
  225. }
  226. .progress {
  227. &-info {
  228. position: absolute;
  229. left: 50%;
  230. top: 50%;
  231. transform: translate(-50%, -50%);
  232. }
  233. &-score {
  234. font-size: 56rpx;
  235. font-weight: 600;
  236. font-family: PingFang SC;
  237. color: transparent;
  238. background-image: linear-gradient(to right, #4B348F, #845CFA);
  239. background-clip: text;
  240. display: inline-block;
  241. }
  242. &-unit {
  243. font-size: 28rpx;
  244. font-weight: 600;
  245. font-family: PingFang SC;
  246. color: #000000;
  247. margin-left: 8rpx;
  248. }
  249. &-desc {
  250. font-size: 16rpx;
  251. font-weight: 400;
  252. font-family: PingFang SC;
  253. color: #989898;
  254. text-align: center;
  255. }
  256. }
  257. </style>