Rings.weapp.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. import { MainColorType } from "@/context/themes/color";
  2. import { Canvas } from "@tarojs/components";
  3. import Taro from "@tarojs/taro";
  4. import { useDidShow, useReady } from "@tarojs/taro";
  5. import { useEffect, useRef, useState } from "react";
  6. import { useSelector } from "react-redux";
  7. import '../../../app.scss'
  8. export type RingCommon = {
  9. useCase: string;
  10. status: string;
  11. isFast?: boolean;
  12. radius: number;
  13. lineWidth: number;
  14. }
  15. export type CurrentDot = {
  16. color: string;
  17. lineWidth: number;
  18. borderColor: string;
  19. timestamp?: number;
  20. offset?: number;
  21. whiteIcon?: boolean;
  22. }
  23. export type RealRing = {
  24. color: string;
  25. startArc: number;
  26. durationArc: number;
  27. radius?: number;
  28. lineWidth?: number;
  29. hideBg?: boolean;
  30. }
  31. export type TargetRing = {
  32. color: string;
  33. startArc: number;
  34. durationArc: number;
  35. radius?: number;
  36. lineWidth?: number;
  37. }
  38. export type ScheduleRing = {
  39. color: string;
  40. startArc: number;
  41. durationArc: number;
  42. }
  43. export type BgRing = {
  44. color: string;
  45. }
  46. let arrowImg;
  47. let startTime;
  48. let dotScale = 0.8;
  49. let dotAlpha = 0.8;
  50. let direction = 1;
  51. let frameAnimation: any = null;
  52. let laterTime;
  53. export default function Rings(props: {
  54. common: RingCommon;
  55. currentDot?: CurrentDot;
  56. realRing?: RealRing;
  57. targetRing?: TargetRing;
  58. scheduleRing?: ScheduleRing;
  59. breathAnimation?: boolean;
  60. bgRing: BgRing;
  61. canvasId?: string;
  62. ctx?: any;
  63. setCtx?: any;
  64. canvas?: any;
  65. setCanvas?: any;
  66. dotList?: Array<CurrentDot>;
  67. stageList?: Array<RealRing>;
  68. scale?: number;
  69. showCurrentDotAnimation?: boolean;
  70. }) {
  71. const r = props.common.radius
  72. const strokeWidth = props.common.lineWidth;
  73. // const color = props.color || 'orange'
  74. const canvasRef = useRef(null);
  75. const canvasId = props.canvasId ? 'canvas_' + props.canvasId : 'progress-canvas';
  76. const dpr = Taro.getSystemInfoSync().pixelRatio; // 获取设备的像素比
  77. const radius = r; // 圆形进度条的半径
  78. const lineWidth = strokeWidth; // 圆形进度条的线宽
  79. const time = useSelector((state: any) => state.time);
  80. const user = useSelector((state: any) => state.user);
  81. const [scale, setScale] = useState(props.scale ?? 1.0)
  82. const scaleRef = useRef(1);
  83. const alphaRef = useRef(1);
  84. const directionRef = useRef(1); // 1 for scaling down, -1 for scaling up
  85. const animationFrameRef = useRef<number | null>(null);
  86. const animationDuration = 150; // 动画时长(毫秒
  87. const animateScale = (newScale) => {
  88. // const startTime = performance.now();
  89. const animate = () => {
  90. const elapsed = new Date().getTime() - startTime.getTime();
  91. const progress = Math.min(elapsed / animationDuration, 1);
  92. const currentScale = scale + (newScale - scale) * progress;
  93. setScale(currentScale);
  94. if (progress < 1) {
  95. requestAnimationFrame(animate);
  96. } else {
  97. setScale(newScale);
  98. }
  99. };
  100. requestAnimationFrame(animate);
  101. };
  102. useEffect(() => {
  103. // drawCircle()
  104. if (props.scale && props.scale != scale) {
  105. // setScale(props.scale ?? 1.0)
  106. startTime = new Date()
  107. animateScale(props.scale)
  108. }
  109. }, [props.scale])
  110. const animate2 = () => {
  111. if (!props.showCurrentDotAnimation) {
  112. cancelAnimationFrame(animationFrameRef.current as any)
  113. animationFrameRef.current = null
  114. return;
  115. }
  116. // 更新 scale 和 alpha
  117. // scaleRef.current += directionRef.current * 0.01;
  118. // alphaRef.current += directionRef.current * 0.01;
  119. dotScale += direction * 0.005;
  120. dotAlpha += direction * 0.005;
  121. // 反转方向
  122. if (dotScale <= 0.7 || dotScale >= 1) {
  123. direction *= -1;
  124. }
  125. // 绘制圆点
  126. if (props.ctx) {
  127. drawContent(props.ctx)
  128. }
  129. else {
  130. drawCircle()
  131. }
  132. // 循环动画
  133. animationFrameRef.current = requestAnimationFrame(animate2);
  134. };
  135. useEffect(() => {
  136. // if (props.showCurrentDotAnimation) {
  137. // console.log('oooooooooooooooooooooooo')
  138. // setTimeout(() => {
  139. // console.log('sssssssssddddddd')
  140. // animate2()
  141. // }, 2000)
  142. // }
  143. // else {
  144. // if (laterTime)
  145. // clearTimeout(laterTime)
  146. // if (animationFrameRef.current) {
  147. // cancelAnimationFrame(animationFrameRef.current as any)
  148. // animationFrameRef.current = null
  149. // }
  150. // dotScale = 1;
  151. // dotAlpha = 1;
  152. // }
  153. }, [
  154. props.showCurrentDotAnimation
  155. ])
  156. useReady(() => {
  157. drawCircle()
  158. })
  159. useEffect(() => {
  160. if (props.ctx) {
  161. drawContent(props.ctx)
  162. }
  163. else {
  164. drawCircle()
  165. }
  166. }, [time.status, time.scenario, user.isLogin, props.targetRing, props.currentDot, props.realRing, props.currentDot?.color, props.canvasId]);
  167. var retryCount = 0;
  168. function drawCircle() {
  169. const query = Taro.createSelectorQuery();
  170. query.select(`#${canvasId}`).fields({ node: true, size: true });
  171. query.exec((res) => {
  172. if (res[0] == null) {
  173. retryCount++;
  174. if (retryCount > 5) {
  175. return;
  176. }
  177. drawCircle()
  178. // console.log(canvasId)
  179. return;
  180. }
  181. const _canvas = res[0].node;
  182. _canvas.width = res[0].width * dpr;
  183. _canvas.height = res[0].height * dpr;
  184. const ctx = _canvas.getContext('2d');
  185. global.canvas2 = _canvas
  186. if (props.setCtx) {
  187. props.setCtx(ctx)
  188. props.setCanvas(_canvas)
  189. drawContent(ctx)
  190. }
  191. else {
  192. drawContent(ctx)
  193. }
  194. // setCanvas(_canvas)
  195. // setContext(ctx)
  196. // const ctx = Taro.createCanvasContext(canvasId);
  197. // drawContent(ctx)
  198. });
  199. }
  200. function drawContent(ctx) {
  201. if (props.canvas) {
  202. props.canvas.width = ((radius * 2 + lineWidth)) * dpr;
  203. props.canvas.height = ((radius * 2 + lineWidth)) * dpr;
  204. }
  205. const center = radius + lineWidth / 2; // 圆心坐标
  206. ctx.clearRect(0, 0, radius * 2, radius * 2); // 清除画布
  207. // 设置画布尺寸
  208. ctx.scale(dpr, dpr)
  209. ctx.translate(center, 2 * center)
  210. ctx.scale(scale, scale);
  211. ctx.translate(-center, -2 * center);
  212. // 绘制背景圆
  213. ctx.beginPath();
  214. ctx.arc(center, center, radius, 0, 2 * Math.PI);
  215. ctx.lineWidth = lineWidth;
  216. ctx.strokeStyle = props.bgRing.color;
  217. ctx.lineCap = 'round'; // 设置为圆角
  218. ctx.stroke();
  219. // 绘制schedule进度环
  220. if (props.scheduleRing) {
  221. ctx.beginPath();
  222. ctx.arc(center, center, radius, props.scheduleRing!.startArc,
  223. props.scheduleRing!.startArc + props.scheduleRing!.durationArc);
  224. ctx.lineWidth = lineWidth + 4;
  225. ctx.strokeStyle = MainColorType.bg;
  226. ctx.lineCap = 'round'; // 设置为圆角
  227. ctx.stroke();
  228. ctx.beginPath();
  229. ctx.arc(center, center, radius, props.scheduleRing!.startArc,
  230. props.scheduleRing!.startArc + props.scheduleRing!.durationArc);
  231. ctx.lineWidth = lineWidth;
  232. ctx.strokeStyle = props.scheduleRing!.color;
  233. ctx.lineCap = 'round'; // 设置为圆角
  234. ctx.stroke();
  235. }
  236. // 绘制target进度环
  237. if (props.targetRing) {
  238. // ctx.beginPath();
  239. // ctx.arc(center, center, props.targetRing!.radius ? props.targetRing!.radius : radius, props.targetRing!.startArc,
  240. // props.targetRing!.startArc + props.targetRing!.durationArc);
  241. // ctx.lineWidth = props.targetRing!.lineWidth ? props.targetRing!.lineWidth + 4 : lineWidth + 4;
  242. // ctx.strokeStyle = MainColorType.bg;
  243. // ctx.lineCap = 'round'; // 设置为圆角
  244. // ctx.stroke();
  245. ctx.beginPath();
  246. ctx.arc(center, center, props.targetRing!.radius ? props.targetRing!.radius : radius, props.targetRing!.startArc,
  247. props.targetRing!.startArc + props.targetRing!.durationArc);
  248. ctx.lineWidth = props.targetRing!.lineWidth ? props.targetRing!.lineWidth : lineWidth;
  249. ctx.strokeStyle = props.targetRing!.color;
  250. ctx.lineCap = 'round'; // 设置为圆角
  251. ctx.stroke();
  252. }
  253. //绘制real进度环
  254. if (props.realRing) {
  255. if (props.realRing.durationArc < 0.01) props.realRing.durationArc = 0.01;
  256. // if (!props.realRing.hideBg) {
  257. // ctx.beginPath();
  258. // ctx.arc(center, center, props.realRing!.radius ? props.realRing!.radius : radius, props.realRing!.startArc,
  259. // props.realRing!.startArc + props.realRing!.durationArc);
  260. // ctx.lineWidth = props.realRing!.lineWidth ? props.realRing!.lineWidth + 4 : lineWidth + 4;
  261. // ctx.strokeStyle = MainColorType.bg;
  262. // ctx.lineCap = 'round'; // 设置为圆角
  263. // ctx.stroke();
  264. // }
  265. ctx.beginPath();
  266. ctx.arc(center, center, props.realRing!.radius ? props.realRing!.radius : radius, props.realRing!.startArc,
  267. props.realRing!.startArc + props.realRing!.durationArc);
  268. ctx.lineWidth = props.realRing!.lineWidth ? props.realRing!.lineWidth : lineWidth;
  269. ctx.strokeStyle = props.realRing!.color;
  270. ctx.lineCap = 'round'; // 设置为圆角
  271. ctx.stroke();
  272. }
  273. if (props.stageList) {
  274. props.stageList.map(item => {
  275. if (item.durationArc < 0.01) item.durationArc = 0.01;
  276. ctx.beginPath();
  277. ctx.arc(center, center, radius, item.startArc,
  278. item.startArc + item.durationArc);
  279. ctx.lineWidth = lineWidth;
  280. ctx.strokeStyle = item.color;
  281. ctx.lineCap = 'round'; // 设置为圆角
  282. ctx.stroke();
  283. })
  284. }
  285. //绘制current_dot点
  286. if (props.currentDot) {
  287. var time = new Date();
  288. var seconds = time.getHours() * 3600 + time.getMinutes() * 60 + time.getSeconds();
  289. seconds += props.currentDot.offset! * 60
  290. if (seconds > 24 * 3600) {
  291. seconds -= 24 * 3600
  292. }
  293. else if (seconds < 0) {
  294. seconds += 24 * 3600
  295. }
  296. var arc = seconds / 86400 * 2 * Math.PI - Math.PI / 2.0;
  297. const radians = arc;//angle * Math.PI / 180; // 将角度转换为弧度
  298. const xPrime = center + r * Math.cos(radians);
  299. const yPrime = center + r * Math.sin(radians);
  300. ctx.beginPath();
  301. var dotLineWidth = 2
  302. if (lineWidth == 28) {
  303. dotLineWidth = 4
  304. }
  305. ctx.arc(xPrime, yPrime, lineWidth / 2.0 + dotLineWidth / 2, 0, 2 * Math.PI);
  306. ctx.lineWidth = dotLineWidth;
  307. ctx.fillStyle = props.currentDot.borderColor
  308. ctx.fill()
  309. ctx.strokeStyle = props.currentDot.borderColor//'#1C1C1C';
  310. ctx.lineCap = 'round'; // 设置为圆角
  311. ctx.stroke();
  312. if (props.currentDot.color != '#ffffffff') {
  313. ctx.beginPath();
  314. // ctx.translate(xPrime, yPrime)
  315. ctx.arc(xPrime, yPrime, lineWidth / 2 * dotScale, 0, 2 * Math.PI, false);
  316. ctx.globalAlpha = dotAlpha//alphaRef.current
  317. // ctx.scale(scaleRef.current,scaleRef.current)
  318. // ctx.translate(-xPrime, -yPrime)
  319. ctx.fillStyle = props.currentDot.color;
  320. ctx.fill();
  321. }
  322. // if (arrowImg) {
  323. // //绘制终点图标
  324. // ctx.save()
  325. // ctx.beginPath()
  326. // ctx.translate(xPrime, yPrime)
  327. // ctx.rotate(arc)
  328. // ctx.translate(-xPrime, -yPrime)
  329. // ctx.drawImage(arrowImg, xPrime - lineWidth / 2, yPrime - lineWidth / 2, lineWidth, lineWidth)
  330. // ctx.restore()
  331. // ctx.closePath()
  332. // }
  333. // else {
  334. // let tempImage = global.canvas2.createImage()
  335. // tempImage.src = require(props.currentDot.whiteIcon ? '@/assets/images/dot_arrow_white.png' : '@/assets/images/dot_arrow.png')//'./watch_line.png'//'..//assets/images/watch_line.png'
  336. // tempImage.onload = () => {
  337. // arrowImg = tempImage
  338. // ctx.save()
  339. // ctx.beginPath()
  340. // ctx.translate(xPrime, yPrime)
  341. // ctx.rotate(arc)
  342. // ctx.translate(-xPrime, -yPrime)
  343. // ctx.drawImage(arrowImg, xPrime - lineWidth / 2, yPrime - lineWidth / 2, lineWidth, lineWidth)
  344. // ctx.restore()
  345. // ctx.closePath()
  346. // }
  347. // }
  348. // ctx.beginPath();
  349. // ctx.arc(center, center, radius, arc,
  350. // arc + 0.001);
  351. // ctx.lineWidth = lineWidth+6;
  352. // ctx.strokeStyle = props.currentDot!.borderColor;
  353. // ctx.lineCap = 'round'; // 设置为圆角
  354. // ctx.stroke();
  355. // ctx.save()
  356. // ctx.beginPath();
  357. // ctx.arc(center, center, radius, arc,
  358. // arc + 0.001);
  359. // ctx.lineWidth = lineWidth;
  360. // ctx.strokeStyle = props.currentDot!.color;
  361. // ctx.lineCap = 'round'; // 设置为圆角
  362. // ctx.stroke();
  363. // ctx.restore()
  364. }
  365. if (props.dotList) {
  366. props.dotList.map(item => {
  367. var time = item.timestamp ? new Date(item.timestamp) : new Date();
  368. var seconds = time.getHours() * 3600 + time.getMinutes() * 60 + time.getSeconds();
  369. var arc = seconds / 86400 * 2 * Math.PI - Math.PI / 2.0;
  370. ctx.beginPath();
  371. ctx.arc(center, center, radius, arc,
  372. arc + 0.001);
  373. ctx.lineWidth = lineWidth + 6;
  374. ctx.strokeStyle = item.borderColor;
  375. ctx.lineCap = 'round'; // 设置为圆角
  376. ctx.stroke();
  377. ctx.save()
  378. ctx.beginPath();
  379. ctx.arc(center, center, radius, arc,
  380. arc + 0.001);
  381. ctx.lineWidth = lineWidth;
  382. ctx.strokeStyle = item.color;
  383. ctx.lineCap = 'round'; // 设置为圆角
  384. // ctx.globalAlpha = global.breathAlpha
  385. ctx.stroke();
  386. ctx.restore()
  387. })
  388. }
  389. }
  390. return <Canvas canvasId={canvasId} id={canvasId} className="canvas" type="2d" style={{ width: (radius * 2 + lineWidth), height: (radius * 2 + lineWidth), zIndex: 0 }} ref={canvasRef} />
  391. }