普兆健康管家前端代码仓库
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.

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