import Taro from '@tarojs/taro'; import { Canvas, View, Image } from '@tarojs/components'; import { useEffect, useRef, useState } from 'react'; import React from 'react'; import './Dial.scss' import { useSelector } from 'react-redux'; const Component = (props) => { const [scenario] = useState(useSelector((state: any) => state.scenario)) const [currentContext, setCurrentContext] = useState(null) const canvasWidth: number = 334; const canvasHeight: number = 334; const dpr = Taro.getSystemInfoSync().pixelRatio; // 获取设备的像素比 const canvasRef = useRef(null); const watchLineWidth = 300 const canvasId = 'canvasDialId'; var canStartDrag = false; var canEndDrag = false; var canRingDrag = false; var current = { start: global.startTime, end: global.endTime } var startCount = parseInt((current.start.split(':') as any)[0]) * 60 + parseInt((current.start.split(':') as any)[1]); var endCount = parseInt((current.end.split(':') as any)[0]) * 60 + parseInt((current.end.split(':') as any)[1]); var startAngle = (startCount / 1440) * 2 * Math.PI - Math.PI / 2; var endAngle = (endCount / 1440) * 2 * Math.PI - Math.PI / 2; endAngle = endAngle > startAngle ? endAngle : endAngle + 2 * Math.PI; var lastAngle; var lastDuration = 0; var strStart = current.start; var strEnd = current.end; var needDrawFastRing = false; if (scenario.name == 'FAST_SLEEP' && scenario.step == 'sleep') { needDrawFastRing = true; } useEffect(() => { const query = Taro.createSelectorQuery(); query.select(`.${canvasId}`).fields({ node: true, size: true }); query.exec((res) => { if (res[0] == null) return; const _canvas = res[0].node; _canvas.width = res[0].width * dpr; _canvas.height = res[0].height * dpr; global.canvas = _canvas const ctx = _canvas.getContext('2d'); setCurrentContext(ctx) // 设置画布尺寸 ctx.scale(dpr, dpr); drawCanvas(ctx); }); }, []); const drawCanvas = (ctx: any) => { const centerX = canvasWidth / 2; const centerY = canvasHeight / 2; const radius = 140;//Math.min(centerX, centerY) - 38; ctx.clearRect(0, 0, canvasWidth, canvasHeight); //画断食环 if (needDrawFastRing) { ctx.beginPath(); ctx.arc(centerX, centerY, radius + 16 + 8, global.fast_start_angle, global.fast_end_angle); ctx.strokeStyle = 'rgba(170, 255, 0, 0.3)'; ctx.lineWidth = 2; ctx.stroke(); } // 绘制圆环 ctx.beginPath(); ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI); ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.lineWidth = 38; ctx.stroke(); //绘制起终点圆弧 ctx.beginPath(); ctx.arc(centerX, centerY, radius, startAngle, endAngle); ctx.strokeStyle = canRingDrag ? "#3e3e3e" : "#1c1c1c"; ctx.lineWidth = 38; ctx.lineCap = "round"; ctx.stroke(); //画刻度线 ctx.save() ctx.beginPath() ctx.moveTo(centerX, centerY) ctx.arc( centerX, centerY, watchLineWidth / 2.0, startAngle, endAngle ) ctx.clip() if (global.canvas_watch_line) { ctx.drawImage(global.canvas_watch_line, centerX - watchLineWidth / 2.0, centerY - watchLineWidth / 2.0, watchLineWidth, watchLineWidth) } else { let tempImage = global.canvas.createImage() tempImage.src = require('@/assets/images/watch_line.png')//'./watch_line.png'//'..//assets/images/watch_line.png' tempImage.onload = () => { global.canvas_watch_line = tempImage drawCanvas(ctx) } } ctx.restore() ctx.closePath() if (scenario.step == 'fast') { global.fast_start_angle = startAngle; global.fast_end_angle = endAngle; } const pointX = centerX + Math.cos(startAngle) * radius; const pointY = centerY + Math.sin(startAngle) * radius; //绘制起点圆点 ctx.beginPath(); ctx.arc(pointX, pointY, 17, 0, 2 * Math.PI); ctx.fillStyle = (canRingDrag || canStartDrag) ? "#3e3e3e" : "#1c1c1c"; ctx.fill(); //绘制起点图标 // ctx.save() // ctx.beginPath() // ctx.translate(pointX, pointY) // ctx.rotate(startAngle) // ctx.translate(-pointX, -pointY) if (global.canvas_start_icon) { ctx.drawImage(global.canvas_start_icon, pointX - 20, pointY - 20, 40, 40) } else { let tempImage = global.canvas.createImage() tempImage.src = require('@/assets/images/wakeup-white.png')//'./watch_line.png'//'..//assets/images/watch_line.png' tempImage.onload = () => { global.canvas_start_icon = tempImage drawCanvas(ctx) } } // ctx.restore() // ctx.closePath() // 绘制终点圆点 const pointX2 = centerX + Math.cos(endAngle) * radius; const pointY2 = centerY + Math.sin(endAngle) * radius; ctx.beginPath(); ctx.arc(pointX2, pointY2, 17, 0, 2 * Math.PI); ctx.fillStyle = (canRingDrag || canEndDrag) ? "#3e3e3e" : "#1c1c1c"; ctx.fill(); //绘制终点图标 if (global.canvas_end_icon) { ctx.drawImage(global.canvas_end_icon, pointX2 - 20, pointY2 - 20, 40, 40) } else { let tempImage = global.canvas.createImage() tempImage.src = require('@/assets/images/watch_end_white.png')//'./watch_line.png'//'..//assets/images/watch_line.png' tempImage.onload = () => { global.canvas_end_icon = tempImage drawCanvas(ctx) } } }; global.updateDial = (start, end) => { var startCount = parseInt((start.split(':') as any)[0]) * 60 + parseInt((start.split(':') as any)[1]); var endCount = parseInt((end.split(':') as any)[0]) * 60 + parseInt((end.split(':') as any)[1]); startAngle = (startCount / 1440) * 2 * Math.PI - Math.PI / 2; endAngle = (endCount / 1440) * 2 * Math.PI - Math.PI / 2; endAngle = endAngle > startAngle ? endAngle : endAngle + 2 * Math.PI; drawCanvas(currentContext); } function twoPointDistance(p1, p2) { let dep = Math.sqrt(Math.pow((p1.x - p2.x), 2) + Math.pow((p1.y - p2.y), 2)); return dep; } function limitAngle(start, end) { var angle1 = start < 0 ? start + 2 * Math.PI : start; var angle2 = end < 0 ? end + 2 * Math.PI : end; if (angle2 < angle1) { angle2 = angle2 + 2 * Math.PI; } var leftAngle = angle2 - angle1; if (leftAngle > Math.PI * 23 / 12) { return 1; } else if (leftAngle < Math.PI / 12) { return 2; } return 0; } function durationAngle(end, start) { var angle1 = start < 0 ? start + 2 * Math.PI : start; var angle2 = end < 0 ? end + 2 * Math.PI : end; if (angle2 < angle1) { angle2 = angle2 + 2 * Math.PI; } return angle2 - angle1; } const handleTouchMove = (e: any) => { const ctx = currentContext;//canvasRef.current.getContext('2d'); if (!canStartDrag && !canEndDrag && !canRingDrag) { drawCanvas(ctx); return; } const { x, y } = e.touches[0]; let angle = Math.atan2(y - canvasWidth / 2.0, x - canvasWidth / 2.0) if (canStartDrag) { if (Math.abs(durationAngle(endAngle, angle) - lastDuration) > Math.PI) { //禁止跨越 return; } var result = limitAngle(angle, endAngle); switch (result) { case 0: startAngle = angle; break; case 1: startAngle = endAngle - 23 * Math.PI / 12; break; case 2: startAngle = endAngle - Math.PI / 12; break; } } if (canEndDrag) { if (Math.abs(durationAngle(angle, startAngle) - lastDuration) > Math.PI) { //禁止跨越 return; } var result = limitAngle(startAngle, angle); switch (result) { case 0: endAngle = angle; break; case 1: endAngle = startAngle + 23 * Math.PI / 12; break; case 2: endAngle = startAngle + Math.PI / 12; break; } } if (canRingDrag) { let delta = angle - lastAngle; startAngle += delta; endAngle += delta; lastAngle = angle; } startAngle = normalizeAngle(startAngle); endAngle = normalizeAngle(endAngle); angleToTime(); drawCanvas(ctx); lastDuration = durationAngle(endAngle, startAngle); }; function angleToTime() { var oldStart = parseInt(strStart.split(':')[0] as any) * 60 + parseInt(strStart.split(':')[1] as any); var oldEnd = parseInt(strEnd.split(':')[0] as any) * 60 + parseInt(strEnd.split(':')[1] as any); if (oldEnd < oldStart) oldEnd += 1440; var oldDuration = oldEnd - oldStart; var startCount = (startAngle + Math.PI / 2) / (2 * Math.PI) * 1440; var endCount = (endAngle + Math.PI / 2) / (2 * Math.PI) * 1440; startCount = startCount > 0 ? startCount : startCount + 1440; endCount = endCount > 0 ? endCount : endCount + 1440; startCount = roundToNearestFive(startCount); endCount = roundToNearestFive(endCount); var startHour = Math.floor(startCount / 60); var startMinute = Math.floor(startCount % 60); var endHour = Math.floor(endCount / 60); var endMinute = Math.floor(endCount % 60); startHour = startHour < 24 ? startHour : startHour - 24; endHour = endHour < 24 ? endHour : endHour - 24; var start = (startHour < 10 ? '0' + startHour : startHour) + ':' + (startMinute < 10 ? '0' + startMinute : startMinute); var end = (endHour < 10 ? '0' + endHour : endHour) + ':' + (endMinute < 10 ? '0' + endMinute : endMinute); if (canStartDrag) { strStart = start; global.updateDuration(start, strEnd); } else if (canEndDrag) { strEnd = end; global.updateDuration(strStart, end); } else if (canRingDrag) { var newCount = startHour * 60 + startMinute var newEnd = oldDuration + newCount const resultHour = Math.floor(newEnd / 60) % 24; // 对 24 取模以确保结果在 0-23 之间 const resultMinute = newEnd % 60; strStart = start strEnd = `${String(resultHour).padStart(2, '0')}:${String(resultMinute).padStart(2, '0')}`; global.updateDuration(start, strEnd); } } function roundToNearestFive(minutes: number): number { const remainder = minutes % 5; if (remainder === 0) { return minutes; } else if (remainder < 3) { return minutes - remainder; } else { return minutes + (5 - remainder); } } const handleClick = (e) => { const scaleX = 1//dpr//canvasRect.width / canvasRect.width; const scaleY = 1//dpr//canvasRect.height / canvasRect.height; const canvasX = (e.touches[0].x - 0) * scaleX; const canvasY = (e.touches[0].y - 0) * scaleY; // 判断点击位置是否在圆点范围内 const centerX = canvasWidth / 2; const centerY = canvasWidth / 2; const radius = Math.min(centerX, centerY) - 10; const pointX = centerX + Math.cos(startAngle) * radius; const pointY = centerY + Math.sin(startAngle) * radius; const distance = Math.sqrt(Math.pow(canvasX - pointX, 2) + Math.pow(canvasY - pointY, 2)); if (distance <= 40) { canStartDrag = true; } else { canStartDrag = false; } const pointX2 = centerX + Math.cos(endAngle) * radius; const pointY2 = centerY + Math.sin(endAngle) * radius; const distance2 = Math.sqrt(Math.pow(canvasX - pointX2, 2) + Math.pow(canvasY - pointY2, 2)); if (distance2 <= 40) { canEndDrag = true; } else { canEndDrag = false; } if (canStartDrag && canEndDrag) { if (distance < distance2) { canEndDrag = false } else { canStartDrag = false } } lastDuration = durationAngle(endAngle, startAngle); if (canStartDrag || canEndDrag) { drawCanvas(currentContext); canRingDrag = false; if (canStartDrag || canEndDrag){ var type = canStartDrag?1:2 global.startDuration(type) } return; } const distance3 = twoPointDistance({ x: canvasX, y: canvasY }, { x: centerX, y: centerY }); let angle = Math.atan2(canvasY - canvasWidth / 2.0, canvasX - canvasWidth / 2.0) if (distance3 > 120 && distance3 < 190 && isCenterAngle(startAngle, angle, endAngle)) { canRingDrag = true; } lastAngle = angle; startAngle = normalizeAngle(startAngle); endAngle = normalizeAngle(endAngle); drawCanvas(currentContext); if (canStartDrag || canEndDrag || canRingDrag){ var type = canStartDrag?1:canEndDrag?2:3 global.startDuration(type) } else { global.endDuration() } }; const normalizeAngle = (angle) => { if (angle < 0) { angle = angle + 2 * Math.PI; normalizeAngle(angle); } if (angle > 2 * Math.PI) { angle = angle - 2 * Math.PI; normalizeAngle(angle); } return angle; } const isCenterAngle = (start, center, end) => { start = normalizeAngle(start); center = normalizeAngle(center); end = normalizeAngle(end); if (start > end) { end += 2 * Math.PI; } if (end > 2 * Math.PI && center < start) { center += 2 * Math.PI; } return start < center && center < end; } const handleTouchEnd = (e) => { canStartDrag = false; canEndDrag = false; canRingDrag = false; global.endDuration() handleTouchMove(e) } return } export default React.memo(Component);