| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- 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<any>(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 <View style={{ width: 334, height: 334, borderRadius: 167, backgroundColor: '#000', position: 'relative' }} ref={props.ref}>
- <Canvas canvasId={canvasId} id={canvasId} className={canvasId} type="2d"
- style={{ width: 334, height: 334, zIndex: 0 }}
- onTouchMove={handleTouchMove}
- onTouchEnd={handleTouchEnd}
- onTouchStart={handleClick}
- ref={canvasRef}
- />
- <View className='inner'>
- <Image src={require('@/assets/images/watch-inner.png')} style={{ width: 226, height: 226 }} />
- </View>
- </View>
- }
- export default React.memo(Component);
|