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

335 lines
7.6 KiB

  1. <template>
  2. <view class="progress_box">
  3. <div class="bg-outer"></div>
  4. <canvas class="progress progress-outer" canvas-id="cpouterline" type="2d"></canvas>
  5. <div class="bg-inner"></div>
  6. <canvas class="progress progress-inner" canvas-id="cpinnerline" type="2d"></canvas>
  7. <div class="bg-score bg-score-min">0</div>
  8. <div class="bg-score bg-score-max">100</div>
  9. <div class="progress progress-info">
  10. <div class="flex">
  11. <div class="progress-score">{{ progress }}</div>
  12. <div class="progress-unit"></div>
  13. </div>
  14. <div class="progress-desc">体检分数</div>
  15. </div>
  16. </view>
  17. </template>
  18. <script>
  19. export default {
  20. props: {
  21. progress: {
  22. type: Number,
  23. default: 0
  24. }
  25. },
  26. data() {
  27. return {
  28. outerCanvas: {},
  29. innerCanvas: {},
  30. ratio: 1,
  31. dpr: 1,
  32. }
  33. },
  34. mounted() {
  35. this.init()
  36. },
  37. methods: {
  38. init() {
  39. uni.createSelectorQuery().in(this)
  40. .select('.progress-outer')
  41. .fields({
  42. node: true,
  43. size: true
  44. })
  45. .exec(async (res) => {
  46. // Canvas 画布的实际绘制宽高
  47. const width = res[0].width
  48. const dpr = wx.getWindowInfo().pixelRatio
  49. let Ratio = width / 446
  50. this.outerCanvas = res[0]
  51. this.ratio = Ratio
  52. this.dpr = dpr
  53. uni.createSelectorQuery().in(this)
  54. .select('.progress-inner')
  55. .fields({
  56. node: true,
  57. size: true
  58. })
  59. .exec(async (res) => {
  60. this.innerCanvas = res[0]
  61. this.drawOuterLine(this.progress)
  62. this.drawInnerLine(this.progress)
  63. })
  64. })
  65. },
  66. drawOuterLine(step) {
  67. uni.createSelectorQuery().in(this)
  68. .select('.progress-outer')
  69. .fields({
  70. node: true,
  71. size: true
  72. })
  73. .exec(async (res) => {
  74. const canvas = res[0].node
  75. // Canvas 画布的实际绘制宽高
  76. const width = res[0].width
  77. const height = res[0].height
  78. //根据dpr调整
  79. const dpr = wx.getWindowInfo().pixelRatio
  80. canvas.width = width * dpr
  81. canvas.height = height * dpr
  82. let Ratio = width / 446
  83. // 渲染上下文
  84. const ctx = canvas.getContext('2d')
  85. ctx.scale(dpr, dpr)
  86. ctx.clearRect(0, 0, width, height)
  87. let r = 210 * Ratio
  88. let x = this.outerCanvas.width / 2
  89. let y = this.outerCanvas.height - this.innerCanvas.height / 2
  90. let lineWidth = 10 * Ratio;
  91. let lineHeight = 26 * Ratio;
  92. let startAngle = 0
  93. // 进度条的渐变(中心x坐标-半径-边宽,中心Y坐标,中心x坐标+半径+边宽,中心Y坐标)
  94. let gradient = ctx.createLinearGradient(x - r, y, x + r, y);
  95. gradient.addColorStop('0', '#7451DE');
  96. gradient.addColorStop('1.0', '#B1A4FF');
  97. ctx.strokeStyle = '#E5E5E5';
  98. ctx.save()
  99. for (let i = 0; i < 11; i++) {
  100. ctx.beginPath();
  101. if (i % 5) {
  102. lineWidth = 6 * Ratio;
  103. lineHeight = 15 * Ratio;
  104. } else {
  105. lineWidth = 10 * Ratio;
  106. lineHeight = 18 * Ratio;
  107. }
  108. if (step >= i * 10) {
  109. ctx.strokeStyle = gradient;
  110. } else {
  111. ctx.restore();
  112. }
  113. ctx.lineWidth = lineWidth;
  114. ctx.lineCap = 'round';
  115. let angle = startAngle - Math.PI * i / 10
  116. let x0 = x - r * Math.cos(angle)
  117. let y0 = y + r * Math.sin(angle)
  118. let x1 = x0 + lineHeight * Math.cos(angle)
  119. let y1 = y0 - lineHeight * Math.sin(angle)
  120. ctx.moveTo(x0, y0);
  121. ctx.lineTo(x1, y1);
  122. ctx.stroke();
  123. ctx.closePath();
  124. }
  125. })
  126. },
  127. drawInnerLine(step) {
  128. uni.createSelectorQuery().in(this)
  129. .select('.progress-inner')
  130. .fields({
  131. node: true,
  132. size: true
  133. })
  134. .exec(async (res) => {
  135. const canvas = res[0].node
  136. // Canvas 画布的实际绘制宽高
  137. const width = res[0].width
  138. const height = res[0].height
  139. //根据dpr调整
  140. const dpr = wx.getWindowInfo().pixelRatio
  141. canvas.width = width * dpr
  142. canvas.height = height * dpr
  143. let Ratio = width / 356
  144. // 渲染上下文
  145. const ctx = canvas.getContext('2d')
  146. ctx.scale(dpr, dpr)
  147. ctx.clearRect(0, 0, width, height)
  148. let r = 140 * Ratio
  149. let x = this.innerCanvas.width / 2
  150. let y = this.innerCanvas.height / 2
  151. let lineWidth = 5 * Ratio;
  152. let lineHeight = 28 * Ratio;
  153. // 进度条的渐变(中心x坐标-半径-边宽,中心Y坐标,中心x坐标+半径+边宽,中心Y坐标)
  154. let gradient = ctx.createLinearGradient(x - r, y, x + r, y);
  155. gradient.addColorStop('0', '#E81717');
  156. gradient.addColorStop('0.5', '#ECBD00');
  157. gradient.addColorStop('1.0', '#0DB556');
  158. ctx.lineWidth = lineWidth;
  159. ctx.strokeStyle = '#989898';
  160. ctx.save()
  161. let angle = 0
  162. let i = 0
  163. while (angle > - Math.PI) {
  164. ctx.beginPath();
  165. if (i % 2) {
  166. angle -= Math.PI / 50
  167. i++
  168. continue
  169. }
  170. if (step * Math.PI / 100 >= -angle) {
  171. ctx.strokeStyle = gradient;
  172. } else {
  173. ctx.restore();
  174. }
  175. let x0 = x - r * Math.cos(angle)
  176. let y0 = y + r * Math.sin(angle)
  177. let x1 = x0 + lineHeight * Math.cos(angle)
  178. let y1 = y0 - lineHeight * Math.sin(angle)
  179. ctx.moveTo(x0, y0);
  180. ctx.lineTo(x1, y1);
  181. ctx.stroke();
  182. ctx.closePath();
  183. angle -= Math.PI / 100
  184. i++
  185. }
  186. })
  187. },
  188. }
  189. }
  190. </script>
  191. <style lang="scss" scoped>
  192. $size: 356rpx;
  193. .progress_box {
  194. position: relative;
  195. width: 100vw;
  196. height: 402rpx;
  197. }
  198. .progress {
  199. position: absolute;
  200. left: 50%;
  201. transform: translateX(-50%);
  202. &-outer {
  203. width: 446rpx;
  204. height: 100%;
  205. }
  206. &-inner {
  207. width: $size;
  208. height: $size;
  209. bottom: 0;
  210. }
  211. &-info {
  212. position: absolute;
  213. left: 50%;
  214. bottom: calc(#{$size} / 2);
  215. transform: translate(-50%, calc(50% + 5rpx));
  216. }
  217. &-score {
  218. font-size: 48rpx;
  219. font-weight: 600;
  220. font-family: PingFang SC;
  221. color: #000000;
  222. }
  223. &-unit {
  224. font-size: 28rpx;
  225. font-weight: 600;
  226. font-family: PingFang SC;
  227. color: #000000;
  228. margin-left: 8rpx;
  229. }
  230. &-desc {
  231. font-size: 26rpx;
  232. font-weight: 400;
  233. font-family: PingFang SC;
  234. color: #989898;
  235. text-align: center;
  236. }
  237. }
  238. .bg {
  239. &-outer {
  240. position: absolute;
  241. left: 50%;
  242. transform: translateX(-50%);
  243. bottom: 0;
  244. width: $size;
  245. height: $size;
  246. border-radius: 50%;
  247. box-shadow: inset 4px 4px 16px #8D96B466,
  248. 4px 4px 16px 0 rgba(141, 150, 180, 0.3),
  249. // 2px 2px 2px 0 #8D96B466,
  250. -2px -2px 8px 0 #FFFFFF,
  251. -1px -1px 14px 0 #FFFFFF;
  252. }
  253. &-inner {
  254. position: absolute;
  255. left: 50%;
  256. transform: translateX(-50%);
  257. bottom: 28rpx;
  258. width: 300rpx;
  259. height: 300rpx;
  260. background-color: #EEF0F6;
  261. border-radius: 50%;
  262. box-shadow: 6px 6px 6px 0 #0000000F,
  263. -4px -4px 13px 0 #FFFFFF,
  264. 4px 4px 7px 0 #00000012,
  265. }
  266. &-score {
  267. color: #989898;
  268. font-size: 26rpx;
  269. font-weight: 400;
  270. position: absolute;
  271. left: 50%;
  272. top: 50%;
  273. &-min {
  274. transform: translate(-303rpx, 0);
  275. }
  276. &-max {
  277. transform: translate(263rpx, 0);
  278. }
  279. }
  280. }
  281. </style>