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

287 lines
7.2 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. console.log('watch progress', val)
  39. this.$nextTick(() => {
  40. this.drawProgress(val)
  41. })
  42. },
  43. },
  44. },
  45. methods: {
  46. drawProgressbg() {
  47. uni.createSelectorQuery().in(this)
  48. .select('.progress_bg')
  49. .fields({
  50. node: true,
  51. size: true
  52. })
  53. .exec(async (res) => {
  54. console.log('progress_bg', 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 / 244
  64. // 渲染上下文
  65. const ctx = canvas.getContext('2d')
  66. ctx.scale(dpr, dpr)
  67. ctx.clearRect(0, 0, width, height)
  68. let w = 12 * 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 = 102 * 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 / 244
  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 = 90 * 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, 6);
  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. const canvas = res[0].node
  149. // Canvas 画布的实际绘制宽高
  150. const width = res[0].width
  151. const height = res[0].height
  152. //根据dpr调整
  153. const dpr = wx.getWindowInfo().pixelRatio
  154. canvas.width = width * dpr
  155. canvas.height = height * dpr
  156. let Ratio = width / 244
  157. // 渲染上下文
  158. const ctx = canvas.getContext('2d')
  159. ctx.scale(dpr, dpr)
  160. ctx.clearRect(0, 0, width, height)
  161. // 进度条的渐变(中心x坐标-半径-边宽,中心Y坐标,中心x坐标+半径+边宽,中心Y坐标)
  162. let gradient = ctx.createLinearGradient(0, 0, 130, 0);
  163. // 参数step 为绘制的百分比
  164. let end = (step / 100) * (END - START) + START; // 最后的角度
  165. gradient.addColorStop('0', '#7451DE');
  166. gradient.addColorStop('1.0', '#B1A4FF');
  167. let w = 12 * Ratio
  168. ctx.lineWidth = w; // 设置圆环的宽度
  169. ctx.strokeStyle = gradient; // 设置圆环的颜色
  170. ctx.lineCap = 'round'; // 设置圆环端点的形状
  171. ctx.shadowOffsetX = 8;
  172. ctx.shadowOffsetY = 8;
  173. ctx.shadowBlur = 32;
  174. ctx.shadowColor = "rgba(64, 0, 255, 0.3)";
  175. ctx.beginPath();
  176. let x = width / 2
  177. let y = height / 2
  178. let r = 102 * Ratio - w / 2
  179. ctx.arc(x, y, r, START + Math.PI / 50, end - Math.PI / 50, false);
  180. ctx.stroke();
  181. })
  182. },
  183. }
  184. };
  185. </script>
  186. <style lang="scss" scoped>
  187. $size: 244rpx;
  188. $innerSize: 130rpx;
  189. .progress_box {
  190. position: relative;
  191. width: $size;
  192. height: $size;
  193. background: #F6F6F9CC;
  194. border-radius: 50%;
  195. box-shadow: 0 0 6.7rpx 1rpx #D3D2E599;
  196. }
  197. .progress_bg,
  198. .progress_bar,
  199. .progress_line,
  200. .progress_font {
  201. position: absolute;
  202. width: $size;
  203. height: $size;
  204. }
  205. .bg-inner {
  206. position: absolute;
  207. left: 50%;
  208. top: 50%;
  209. transform: translate(-50%, -50%);
  210. width: $innerSize;
  211. height: $innerSize;
  212. border-radius: 50%;
  213. background-image: linear-gradient(to bottom, #FFFFFF, #E9E7F4);
  214. box-shadow: 0 8rpx 8rpx 0 #D3D2E5;
  215. }
  216. .progress {
  217. &-info {
  218. position: absolute;
  219. left: 50%;
  220. top: 50%;
  221. transform: translate(-50%, -50%);
  222. }
  223. &-score {
  224. font-size: 52rpx;
  225. font-weight: 600;
  226. font-family: PingFang SC;
  227. color: transparent;
  228. background-image: linear-gradient(to right, #4B348F, #845CFA);
  229. background-clip: text;
  230. display: inline-block;
  231. }
  232. &-unit {
  233. font-size: 24rpx;
  234. font-weight: 600;
  235. font-family: PingFang SC;
  236. color: #000000;
  237. margin-left: 8rpx;
  238. }
  239. &-desc {
  240. font-size: 16rpx;
  241. font-weight: 400;
  242. font-family: PingFang SC;
  243. color: #989898;
  244. text-align: center;
  245. }
  246. }
  247. </style>