From d9019713c6eb94c533b00e35f01e03a6fae1a210 Mon Sep 17 00:00:00 2001 From: zhangjiming Date: Thu, 6 Mar 2025 11:31:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A7=92=E5=BA=A6=E6=A0=87?= =?UTF-8?q?=E6=B3=A8=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/measure/components/MeasureAction.tsx | 13 +++ src/pages/measure/components/graph/MarkLayer.tsx | 109 ++++++++++++++++----- .../measure/components/graph/RealtimeLayer.tsx | 1 - .../measure/components/graph/StandardLayer.tsx | 49 +++++---- src/utils/index.ts | 26 ++++- 5 files changed, 144 insertions(+), 54 deletions(-) diff --git a/src/pages/measure/components/MeasureAction.tsx b/src/pages/measure/components/MeasureAction.tsx index efeea06..e6e71e4 100644 --- a/src/pages/measure/components/MeasureAction.tsx +++ b/src/pages/measure/components/MeasureAction.tsx @@ -6,6 +6,7 @@ import { createWebSocket, sharedWsUrl } from "../../../services/socket"; import GridLayer from "./graph/GridLayer"; import StandardLayer from "./graph/StandardLayer"; import RealtimeLayer from "./graph/RealtimeLayer"; +import MarkLayer from "./graph/MarkLayer"; export default function MeasureAction() { const navigate = useNavigate(); @@ -78,6 +79,18 @@ export default function MeasureAction() { rows={7} /> +
+ +

测量步骤

diff --git a/src/pages/measure/components/graph/MarkLayer.tsx b/src/pages/measure/components/graph/MarkLayer.tsx index 28bb3d8..560e814 100644 --- a/src/pages/measure/components/graph/MarkLayer.tsx +++ b/src/pages/measure/components/graph/MarkLayer.tsx @@ -1,31 +1,88 @@ import { useCallback, useEffect, useRef } from "react"; +import { calculatePointOnCircle, findSymmetricPoint } from "../../../../utils"; + +const marks = [ + { x: 9.949007022412, y: 0.1650166186941, degree: -80 }, + { x: 25.35, y: 2.184814802617, degree: -60 }, + { x: -9.949007022412, y: 0.1650166186941, degree: -100 }, + { x: -25.35, y: 2.184814802617, degree: -120 }, +]; export default function MarkLayer(props: { - width: number; - height: number; - leftPadding: number; - rightPadding: number; - topPadding: number; - bottomPadding: number; + width: number; + height: number; + leftPadding: number; + rightPadding: number; + topPadding: number; + bottomPadding: number; + columns: number; + rows: number; }) { - const canvasRef = useRef(null); - const draw = useCallback((ctx: CanvasRenderingContext2D) => { - // 使用context对象进行绘图 - ctx.fillStyle = "skyblue"; // 设置填充颜色 - ctx.fillRect(50, 50, 150, 100); // 绘制一个矩形 - }, []); - useEffect(() => { - const canvas = canvasRef.current; - if (!canvas) return; - - const context = canvas.getContext("2d"); - if (!context) return; - draw(context); - }, [draw]); - - return ( -
- -
- ); + const xStartPx = props.leftPadding; + const xEndPx = props.width - props.rightPadding; + const xStepPx = (props.width - props.leftPadding - props.rightPadding) / props.columns; + const yStartPx = props.topPadding; + // const yEndPx = props.height - props.bottomPadding; + const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows; + + const unitPx = xStepPx / 10; + const canvasRef = useRef(null); + + const calcPoints = useCallback( + (arr: typeof marks) => { + return arr.map(p => { + const p1 = calculatePointOnCircle(p.x * unitPx, p.y * unitPx, xStepPx / 2, p.degree); + const p2 = findSymmetricPoint(p1.x, p1.y, p.x * unitPx, p.y * unitPx); + // 角度文本,向外偏移 + const p3 = calculatePointOnCircle(p.x * unitPx, p.y * unitPx, xStepPx / 2 + 10, p.degree); + return [p1, p2, p3]; + }); + }, + [unitPx, xStepPx] + ); + + const draw = useCallback( + (ctx: CanvasRenderingContext2D) => { + // 偏移原点 + const xOffset = (xEndPx - xStartPx) / 2; + const yOffset = yStepPx * 2; + ctx.translate(xStartPx + xOffset, yStartPx + yOffset); + + ctx.fillStyle = "#333333"; + ctx.textAlign = "center"; + ctx.font = "normal 14px system"; + + const lines = calcPoints(marks); + for (let idx = 0; idx < lines.length; idx++) { + const line = lines[idx]; + ctx.moveTo(line[0].x, line[0].y); + ctx.lineTo(line[1].x, line[1].y); + + ctx.save(); + ctx.translate(line[2].x, line[2].y); + ctx.rotate(((marks[idx].degree + 90) * Math.PI) / 180); + ctx.fillText(`${-marks[idx].degree}°`, 0, 0); + ctx.restore(); + } + ctx.stroke(); + // ctx.fillStyle = "skyblue"; // 设置填充颜色 + // ctx.fillRect(50, 50, 150, 100); // 绘制一个矩形 + }, + [calcPoints, xEndPx, xStartPx, yStartPx, yStepPx] + ); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const context = canvas.getContext("2d"); + if (!context) return; + draw(context); + }, [draw]); + + return ( +
+ +
+ ); } diff --git a/src/pages/measure/components/graph/RealtimeLayer.tsx b/src/pages/measure/components/graph/RealtimeLayer.tsx index 54fbd75..387c5b2 100644 --- a/src/pages/measure/components/graph/RealtimeLayer.tsx +++ b/src/pages/measure/components/graph/RealtimeLayer.tsx @@ -72,7 +72,6 @@ export default function RealtimeLayer(props: { // console.log(data.data); // setRtPoints(rtPoints.concat([data.data])); pointArr.push(data.data); - console.log(pointArr.length); const canvas = canvasRef.current; if (!canvas) return; diff --git a/src/pages/measure/components/graph/StandardLayer.tsx b/src/pages/measure/components/graph/StandardLayer.tsx index ed243f0..6c6a325 100644 --- a/src/pages/measure/components/graph/StandardLayer.tsx +++ b/src/pages/measure/components/graph/StandardLayer.tsx @@ -73,44 +73,44 @@ export default function StandardLayer(props: { // 绘制标准线 ctx.beginPath(); ctx.strokeStyle = "red"; - // ---- 右侧 + // ---- 右侧 (从上到下) let point0 = pointsRight[0]; let point1 = pointsRight[1]; ctx.moveTo(point0[0], point0[1]); let param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 300 * unitPx); - console.log(param); + // console.log(param); ctx.arc( param.center.x, param.center.y, 300 * unitPx, - param.anglesForCenter.startAngle, - param.anglesForCenter.endAngle + param.angles.startAngle, + param.angles.endAngle ); point0 = pointsRight[1]; point1 = pointsRight[2]; ctx.moveTo(point0[0], point0[1]); param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 80 * unitPx); - console.log(param); + // console.log(param); ctx.arc( param.center.x, param.center.y, 80 * unitPx, - param.anglesForCenter.startAngle, - param.anglesForCenter.endAngle + param.angles.startAngle, + param.angles.endAngle ); point0 = pointsRight[2]; point1 = pointsRight[3]; ctx.moveTo(point0[0], point0[1]); param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 13 * unitPx); - console.log(param); + // console.log(param); ctx.arc( param.center.x, param.center.y, 13 * unitPx, - param.anglesForCenter.startAngle, - param.anglesForCenter.endAngle + param.angles.startAngle, + param.angles.endAngle ); point0 = pointsRight[3]; @@ -122,15 +122,15 @@ export default function StandardLayer(props: { point1 = pointsRight[5]; ctx.moveTo(point0[0], point0[1]); param = calculateCircleAbove(point0[0], point0[1], point1[0], point1[1], 5 * unitPx); - console.log(param); - ctx.arc(param.center.x, param.center.y, 5 * unitPx, param.anglesForCenter.startAngle, param.anglesForCenter.endAngle); + // console.log(param); + ctx.arc(param.center.x, param.center.y, 5 * unitPx, param.angles.startAngle, param.angles.endAngle); point0 = pointsRight[5]; point1 = pointsRight[6]; ctx.moveTo(point0[0], point0[1]); ctx.lineTo(point1[0], point1[1]); - // ---- 左侧 + // ---- 左侧 (从下向上) point0 = pointsLeft[0]; point1 = pointsLeft[1]; ctx.moveTo(point0[0], point0[1]); @@ -140,8 +140,8 @@ export default function StandardLayer(props: { point1 = pointsLeft[2]; ctx.moveTo(point0[0], point0[1]); param = calculateCircleAbove(point0[0], point0[1], point1[0], point1[1], 5 * unitPx); - console.log(param); - ctx.arc(param.center.x, param.center.y, 5 * unitPx, param.anglesForCenter.startAngle, param.anglesForCenter.endAngle); + // console.log(param); + ctx.arc(param.center.x, param.center.y, 5 * unitPx, param.angles.startAngle, param.angles.endAngle); point0 = pointsLeft[2]; point1 = pointsLeft[3]; @@ -152,43 +152,42 @@ export default function StandardLayer(props: { point1 = pointsLeft[4]; ctx.moveTo(point0[0], point0[1]); param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 13 * unitPx); - console.log(param); + // console.log(param); ctx.arc( param.center.x, param.center.y, 13 * unitPx, - param.anglesForCenter.startAngle, - param.anglesForCenter.endAngle + param.angles.startAngle, + param.angles.endAngle ); point0 = pointsLeft[4]; point1 = pointsLeft[5]; ctx.moveTo(point0[0], point0[1]); param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 80 * unitPx); - console.log(param); + // console.log(param); ctx.arc( param.center.x, param.center.y, 80 * unitPx, - param.anglesForCenter.startAngle, - param.anglesForCenter.endAngle + param.angles.startAngle, + param.angles.endAngle ); point0 = pointsLeft[5]; point1 = pointsLeft[6]; ctx.moveTo(point0[0], point0[1]); param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 300 * unitPx); - console.log(param); + // console.log(param); ctx.arc( param.center.x, param.center.y, 300 * unitPx, - param.anglesForCenter.startAngle, - param.anglesForCenter.endAngle + param.angles.startAngle, + param.angles.endAngle ); ctx.stroke(); - // ctx.fillRect(100, 100, 150, 100); // 绘制一个矩形 }, [pointsLeft, pointsRight, unitPx, xEndPx, xStartPx, xStepPx, yEndPx, yStartPx, yStepPx] ); diff --git a/src/utils/index.ts b/src/utils/index.ts index 6c3a9b8..6d17803 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,6 +4,7 @@ export function formatRemainTime(seconds: number) { return min.padStart(2, "0") + ":" + sec.padStart(2, "0"); } +// 根据弧线的两个顶点和半径,能计算出两个圆心和相应的夹角。 export function calculateCircleInfo(x1: number, y1: number, x2: number, y2: number, radius: number) { // 计算P1到P2的中点 const xm = (x1 + x2) / 2; @@ -41,12 +42,13 @@ export function calculateCircleInfo(x1: number, y1: number, x2: number, y2: numb const endAngle2 = calculateAngle(cx2, cy2, x2, y2); return [ - { center: { x: cx1, y: cy1 }, anglesForCenter: { startAngle: startAngle1, endAngle: endAngle1 } }, - { center: { x: cx2, y: cy2 }, anglesForCenter: { startAngle: startAngle2, endAngle: endAngle2 } }, + { center: { x: cx1, y: cy1 }, angles: { startAngle: startAngle1, endAngle: endAngle1 } }, + { center: { x: cx2, y: cy2 }, angles: { startAngle: startAngle2, endAngle: endAngle2 } }, ]; } export type CenterAndAngle = ReturnType; +// 取圆心偏下的那一组圆心和夹角 export function calculateCircleBelow(x1: number, y1: number, x2: number, y2: number, radius: number) { const results = calculateCircleInfo(x1, y1, x2, y2, radius); if (results[0].center.y > results[1].center.y) { @@ -55,6 +57,7 @@ export function calculateCircleBelow(x1: number, y1: number, x2: number, y2: num return results[1]; } } +// 取圆心偏上的那一组圆心和夹角 export function calculateCircleAbove(x1: number, y1: number, x2: number, y2: number, radius: number) { const results = calculateCircleInfo(x1, y1, x2, y2, radius); if (results[0].center.y < results[1].center.y) { @@ -63,3 +66,22 @@ export function calculateCircleAbove(x1: number, y1: number, x2: number, y2: num return results[1]; } } +// 根据圆心坐标、半径和角度来计算目标点的坐标 +export function calculatePointOnCircle(cx: number, cy: number, radius: number, angleInDegrees: number) { + // 将角度从度转换为弧度 + const angleInRadians = angleInDegrees * (Math.PI / 180); + + // 计算目标点的坐标 + const x = cx + radius * Math.cos(angleInRadians); + const y = cy + radius * Math.sin(angleInRadians); + + return { x, y }; +} +// 计算一个点相对于原点的对称点坐标 +export function findSymmetricPoint(px: number, py: number, ox = 0, oy = 0) { + // 计算对称点的坐标 + const sx = 2 * ox - px; + const sy = 2 * oy - py; + + return { x: sx, y: sy }; +}