ring_progress.tsx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { Canvas } from "@tarojs/components";
  2. import Taro, { useReady } from "@tarojs/taro";
  3. import { useEffect, useRef, useState } from "react";
  4. export default function RingProgress(props: {
  5. canvasId?: string;
  6. bgRing: any;
  7. target?: any;
  8. real?: any;
  9. scale?: number;
  10. radius: number;
  11. width?: number;
  12. height?: number;
  13. extra?: any;
  14. count?: number;
  15. shareBg?: any;
  16. shareCover?: any;
  17. isCompleted?: boolean;
  18. }) {
  19. const canvasId = props.canvasId ?? new Date().getTime() + ''
  20. const info = Taro.getWindowInfo ? Taro.getWindowInfo() : Taro.getSystemInfoSync()
  21. const dpr = info.pixelRatio; // 获取设备的像素比
  22. const [ctx, setCtx] = useState<any>(null)
  23. const [canvas, setCanvas] = useState<any>(null)
  24. const canvasRef = useRef(null);
  25. const canvasWidth = props.width ?? 450
  26. const canvasHeight = props.height ?? 360
  27. const scale = props.scale ?? 1
  28. useEffect(() => {
  29. if (ctx) {
  30. drawContent(ctx)
  31. }
  32. else {
  33. initCanvas()
  34. }
  35. }, [props.canvasId, props.count])
  36. useReady(() => {
  37. initCanvas()
  38. })
  39. var retryCount = 0;
  40. function initCanvas() {
  41. const query = Taro.createSelectorQuery();
  42. query.select(`#${canvasId}`).fields({ node: true, size: true });
  43. query.exec((res) => {
  44. if (res[0] == null) {
  45. retryCount++;
  46. if (retryCount > 5) {
  47. return;
  48. }
  49. initCanvas()
  50. return
  51. }
  52. const _canvas = res[0].node;
  53. _canvas.width = res[0].width * dpr;
  54. _canvas.height = res[0].height * dpr;
  55. const ctx = _canvas.getContext('2d');
  56. ctx.scale(dpr, dpr)
  57. ctx.translate(0, 0)
  58. ctx.scale(scale, scale);
  59. ctx.translate(0, 0);
  60. drawContent(ctx)
  61. setCtx(ctx)
  62. setCanvas(_canvas)
  63. })
  64. }
  65. function drawContent(ctx) {
  66. ctx.clearRect(0, 0, canvasWidth, canvasHeight); // 清除画布
  67. // 设置画布尺寸
  68. if (props.shareBg) {
  69. const grd = ctx.createLinearGradient(0, 0, 0, canvasHeight);
  70. grd.addColorStop(0, props.shareBg[0])
  71. grd.addColorStop(1, props.shareBg[1])
  72. ctx.fillStyle = grd;
  73. ctx.fillRect(0, 0, canvasWidth, canvasHeight)
  74. }
  75. ctx.beginPath();
  76. ctx.arc(canvasWidth / 2.0, canvasHeight / 2.0, props.radius, 0, 2 * Math.PI);
  77. ctx.lineWidth = props.bgRing.width;
  78. ctx.strokeStyle = props.bgRing.color;
  79. ctx.lineCap = 'round'; // 设置为圆角
  80. ctx.stroke();
  81. if (props.target) {
  82. ctx.beginPath();
  83. ctx.arc(canvasWidth / 2.0, canvasHeight / 2.0, props.radius, props.target.start, props.target.start + Math.max(props.target.duration, 0.01));
  84. ctx.lineWidth = props.target.width;
  85. ctx.strokeStyle = props.target.color;
  86. ctx.lineCap = 'round'; // 设置为圆角
  87. ctx.stroke();
  88. }
  89. if (props.real) {
  90. ctx.beginPath();
  91. ctx.arc(canvasWidth / 2.0, canvasHeight / 2.0, props.radius, props.real.start, props.real.start + Math.max(props.real.duration, 0.01));
  92. ctx.lineWidth = props.real.width;
  93. ctx.strokeStyle = props.real.color;
  94. ctx.lineCap = 'round'; // 设置为圆角
  95. ctx.stroke();
  96. }
  97. // ctx.beginPath()
  98. // ctx.arc(100, 100, 30, 0, 2 * Math.PI, false);
  99. // ctx.clip();
  100. // ctx.beginPath();
  101. // ctx.arc(100, 100, 30, 0, 2 * Math.PI, true);
  102. // ctx.fill();
  103. // 设置混合模式为擦除
  104. // ctx.globalCompositeOperation = 'destination-out'
  105. // // 绘制透明环
  106. // ctx.beginPath()
  107. // ctx.arc(100, 100, 50, 0, 2 * Math.PI)
  108. // ctx.lineWidth = 10
  109. // ctx.stroke()
  110. // // 重置混合模式
  111. // ctx.globalCompositeOperation = 'source-over'
  112. if (!props.isCompleted) {
  113. var time = new Date();
  114. var seconds = time.getHours() * 3600 + time.getMinutes() * 60 + time.getSeconds();
  115. // seconds += props.currentDot.offset! * 60
  116. if (seconds > 24 * 3600) {
  117. seconds -= 24 * 3600
  118. }
  119. else if (seconds < 0) {
  120. seconds += 24 * 3600
  121. }
  122. var arc = seconds / 86400 * 2 * Math.PI - Math.PI / 2.0;
  123. const radians = arc;//angle * Math.PI / 180; // 将角度转换为弧度
  124. const xPrime = canvasWidth / 2.0 + props.radius * Math.cos(radians);
  125. const yPrime = canvasHeight / 2.0 + props.radius * Math.sin(radians);
  126. ctx.globalCompositeOperation = 'destination-out'
  127. ctx.beginPath();
  128. var dotLineWidth = 4
  129. // if (lineWidth == 28) {
  130. // dotLineWidth = 4
  131. // }
  132. ctx.arc(xPrime, yPrime, props.target.width / 2.0, 0, 2 * Math.PI);
  133. ctx.lineWidth = dotLineWidth;
  134. ctx.fillStyle = 'transparent'
  135. ctx.fill()
  136. ctx.strokeStyle = '#1C1C1C';
  137. ctx.lineCap = 'round'; // 设置为圆角
  138. ctx.stroke();
  139. ctx.globalCompositeOperation = 'source-over'
  140. }
  141. if (props.extra) {
  142. const { header, value, footer, color } = props.extra
  143. if (header) {
  144. ctx.font = `bold ${12}px sans-serif`
  145. ctx.fillStyle = '#000'
  146. ctx.textAlign = 'center'
  147. ctx.fillText(header, 225, 110);
  148. }
  149. if (value) {
  150. ctx.font = `bold ${36}px sans-serif`
  151. ctx.fillStyle = '#000'
  152. ctx.textAlign = 'center'
  153. ctx.fillText(value, 225, 164);
  154. }
  155. if (footer) {
  156. ctx.font = `bold ${15}px sans-serif`
  157. ctx.fillStyle = color
  158. ctx.textAlign = 'center'
  159. ctx.fillText(footer, 225, 195);
  160. }
  161. }
  162. if (props.isCompleted) {
  163. if (canvas) {
  164. const img1 = canvas.createImage(); // 创建图像对象
  165. img1.src = global.checkImg
  166. img1.onload = () => {
  167. ctx.drawImage(img1, 162, 30, 200, 170);
  168. ctx.stroke();
  169. if (props.shareCover && canvas) {
  170. save()
  171. }
  172. }
  173. }
  174. }
  175. else {
  176. if (props.shareCover && canvas) {
  177. save()
  178. }
  179. }
  180. }
  181. function save() {
  182. Taro.canvasToTempFilePath({
  183. canvas: canvas,
  184. success: (res) => {
  185. console.log('图片保存成功:', res.tempFilePath);
  186. props.shareCover(res.tempFilePath)
  187. },
  188. fail: (err) => {
  189. console.error('转为图片失败:', err);
  190. }
  191. });
  192. }
  193. return <Canvas canvasId={canvasId} id={canvasId} className="canvas" type="2d"
  194. style={{ width: canvasWidth, height: canvasHeight, zIndex: 0 }}
  195. ref={canvasRef}
  196. />
  197. }