You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
478 lines
21 KiB
478 lines
21 KiB
import React, { useEffect, useState, useRef } from 'react'
|
|
import { Col, Row } from 'antd';
|
|
import '../style.less'
|
|
|
|
function calculateIntersection (cx, cy, d, angle) {
|
|
// 将角度转换为弧度
|
|
const theta = angle * Math.PI / 180;
|
|
// 计算圆半径
|
|
const r = d / 2;
|
|
// 计算射线方向向量的 x 和 y 分量
|
|
const dx = Math.cos(theta);
|
|
const dy = Math.sin(theta);
|
|
// 射线起点坐标,可以根据实际情况设定
|
|
const x0 = cx;
|
|
const y0 = cy;
|
|
// 计算向量 OC
|
|
const OCx = cx - x0;
|
|
const OCy = cy - y0;
|
|
// 计算向量 OP 的长度
|
|
const dOP = OCx * dx + OCy * dy;
|
|
// 判断是否有交点
|
|
if (dOP > r) {
|
|
// 没有交点
|
|
return null;
|
|
}
|
|
// 计算向量 OP 在射线方向向量上的长度
|
|
const t = Math.sqrt(r * r - dOP * dOP);
|
|
// 计算交点坐标
|
|
const intersection_x = x0 + dx * (dOP - t);
|
|
const intersection_y = y0 + dy * (dOP - t);
|
|
return [intersection_x, intersection_y];
|
|
}
|
|
|
|
|
|
function Index (props) {
|
|
const xyCvs = useRef()
|
|
const xzCvs = useRef()
|
|
|
|
useEffect(() => {
|
|
const canvasArea = document.getElementById('canvasArea')
|
|
const canvasHeight = canvasArea.clientHeight - 12 * 2 - 6
|
|
const canvasWidth = canvasArea.clientWidth - 12 * 2
|
|
const mainColor = "rgb(249,179,45)"
|
|
// xy 视图
|
|
console.log(xyCvs, xyCvs.current, canvasHeight);
|
|
const xyCtx = xyCvs.current.getContext("2d");
|
|
xyCvs.current.height = canvasHeight
|
|
xyCvs.current.width = canvasWidth
|
|
const center = [canvasWidth / 2, canvasHeight / 2]
|
|
const diameter = Math.min(canvasWidth, canvasHeight)
|
|
// 画圆
|
|
xyCtx.beginPath();
|
|
xyCtx.arc(...center, diameter / 2, 0, 2 * Math.PI);
|
|
xyCtx.stroke();
|
|
// 圆心圆
|
|
xyCtx.beginPath();
|
|
xyCtx.arc(...center, diameter / 48, 0, 2 * Math.PI);
|
|
xyCtx.fillStyle = mainColor;
|
|
xyCtx.fill();
|
|
xyCtx.stroke();
|
|
// 吊臂
|
|
xyCtx.moveTo(...center);
|
|
xyCtx.lineTo(...calculateIntersection(...center, diameter, 42));
|
|
// 配重臂
|
|
xyCtx.moveTo(...center);
|
|
xyCtx.lineTo(...calculateIntersection(...center, diameter / 8, 42 + 180));
|
|
xyCtx.strokeStyle = mainColor;
|
|
xyCtx.stroke();
|
|
// 索
|
|
xyCtx.beginPath();
|
|
xyCtx.arc(...calculateIntersection(...center, 168, 42), diameter / 48, 0, 2 * Math.PI);
|
|
xyCtx.fillStyle = mainColor;
|
|
xyCtx.fill();
|
|
xyCtx.stroke();
|
|
|
|
// xz 视图
|
|
const xzCtx = xzCvs.current.getContext("2d");
|
|
xzCvs.current.height = canvasHeight
|
|
xzCvs.current.width = canvasWidth
|
|
|
|
/**
|
|
* 1、绘制矩形(主要分为:绘制填充矩形和绘制边框矩形和清除矩形区域(利用isClear标记是否绘制清除矩形,实际上就是绘制一个与画布背景色一致的矩形区域),利用isFill变量来标记)
|
|
* 主要使用canvas原生的API:FillRect(x,y,width,height),StrokeRect(x,y,width,height),ClearRect(x,y,width,height)
|
|
* 自己封装的参数:drawRect(x,y,width,height,isClear,isFill,bgColor)
|
|
* x:矩形起点的X坐标(注意:相对坐标系是以画布的左上角为原点,向右为X坐标正方向,向下为Y坐标的正方向)
|
|
* y:矩形终点的Y坐标
|
|
* width:矩形的宽度
|
|
* height:矩形的高度
|
|
* isClear:是否绘制清除画布的矩形区域,true则就是绘制一个清除画布矩形区域,false就是绘制其他两种矩形
|
|
* isFill:是否是填充,false为绘制边框,true为绘制填充
|
|
* bgColor:矩形的颜色,若为填充则为整个矩形背景色,边框则为边框色
|
|
* */
|
|
|
|
function drawRect (x, y, width, height, isClear, isFill, bgColor) {
|
|
if (isClear) { //为true表示绘制清除画布的矩形区域,那么传入的isFill, bgColor值可以为任意值
|
|
xzCtx.clearRect(x, y, width, height);
|
|
} else { //false
|
|
if (isFill) { //为true,则绘制填充矩形
|
|
xzCtx.fillStyle = bgColor;
|
|
xzCtx.fillRect(x, y, width, height);
|
|
} else { //为false,则绘制边框矩形
|
|
xzCtx.strokeStyle = bgColor;
|
|
xzCtx.strokeRect(x, y, width, height);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* 2、绘制圆弧(主要分为:绘制填充圆弧和绘制圆弧边框利用isFill变量来标记,注意:在绘制圆弧边框的时候还有一种特殊情况就是,只需要仅仅绘制弧边,不需要绘制圆弧开始起点和终点之间的连线,这个就是调用了beginPath()不需要调用closePath(),这里也使用一个isOnlyArc变量来标记true为仅仅绘制弧边
|
|
*其他的正常)
|
|
* 主要是使用的是canvas原生的API:
|
|
* arc(x,y,radius,startAngle,endAngle,anticlockwise);
|
|
* x:圆心X坐标
|
|
* y:圆心Y坐标
|
|
* startAngle:开始的弧度
|
|
* endAngle:结束的弧度
|
|
* anticlockwise:true为逆时针,false为顺时针
|
|
* 自己封装的参数:drawCircle(x,y,radius,startAngle,endAngle,anticlockwise,isFill,bgColor)
|
|
* x:圆心X坐标
|
|
* y:圆心Y坐标
|
|
* startAngle:开始的角度(通过getAngle方法将传入的角度转换成相应角度的弧度,
|
|
* 因为在原生的绘制圆弧的API它是根据弧度大小来绘制的,例如如果你想绘制一个30度的圆弧,如果直接传入30是不行的,要传入Math.PI/6
|
|
* 所以在这里个人做了一个优化,直接传入30就通过getAngle方法转换成Math.PI/6,这样就很方便的绘制自己传入的角度大小的圆弧.
|
|
* )
|
|
* endAngle:结束的角度
|
|
* 注意:如果要绘制圆形那么只需要调用该方法,传入的startAngle和endAngle是0度和360度即可.
|
|
* anticlockwise:true为逆时针,false为顺时针
|
|
* isFill:是否是填充,false为绘制边框,true为绘制填充
|
|
* bgColor:圆弧的颜色
|
|
* */
|
|
|
|
function drawArc (x, y, radius, startAngle, endAngle, anticlockwise, isOnlyArc, isFill, bgColor) {
|
|
if (isFill) { //为true绘制填充圆弧
|
|
xzCtx.fillStyle = bgColor;
|
|
xzCtx.beginPath();
|
|
xzCtx.arc(x, y, radius, getAngle(startAngle), getAngle(endAngle), anticlockwise);
|
|
xzCtx.closePath();
|
|
xzCtx.fill();
|
|
} else { //为false绘制边框圆弧
|
|
xzCtx.strokeStyle = bgColor;
|
|
xzCtx.beginPath();
|
|
xzCtx.arc(x, y, radius, getAngle(startAngle), getAngle(endAngle), anticlockwise);
|
|
if (isOnlyArc) { //绘制边框的另一种情况就是仅仅绘制弧边不需要调用closePath()
|
|
|
|
} else { //否则就是不仅绘制边框还得绘制起点和终点的连线,需要调用了closePath();
|
|
xzCtx.closePath();
|
|
}
|
|
xzCtx.stroke();
|
|
}
|
|
}
|
|
/**
|
|
* 3、绘制扇形(主要分为:绘制填充扇形和绘制扇形边框利用isFill变量来标记)
|
|
*主要是使用的是canvas原生的API:
|
|
* arc(x,y,radius,startAngle,endAngle,anticlockwise);
|
|
* x:圆心X坐标
|
|
* y:圆心Y坐标
|
|
* startAngle:开始的弧度
|
|
* endAngle:结束的弧度
|
|
* anticlockwise:true为逆时针,false为顺时针
|
|
* 自己封装参数API:drawSector(x,y,radius,startAngle,endAngle,anticlockwise,isFill,bgColor);
|
|
* x:圆心X坐标
|
|
* y:圆心Y坐标
|
|
* startAngle:开始的角度(通过getAngle方法将传入的角度转换成相应角度的弧度,
|
|
* 因为在原生的绘制圆弧的API它是根据弧度大小来绘制的,例如如果你想绘制一个30度的圆弧,如果直接传入30是不行的,要传入Math.PI/6
|
|
* 所以在这里个人做了一个优化,直接传入30就通过getAngle方法转换成Math.PI/6,这样就很方便的绘制自己传入的角度大小的圆弧.
|
|
* )
|
|
* endAngle:结束的角度
|
|
* anticlockwise:true为逆时针,false为顺时针
|
|
* isFill:是否是填充,false为绘制边框,true为绘制填充
|
|
* bgColor:扇形的颜色
|
|
* */
|
|
|
|
function drawSector (x, y, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
|
|
if (isFill) {
|
|
xzCtx.fillStyle = bgColor;
|
|
|
|
xzCtx.beginPath();
|
|
xzCtx.moveTo(x, y); //把路径移动到画布中的指定点,不创建线条,注意:绘制扇形唯一与绘制弧的区别在于,紧跟着beginPath()后面调用,首先将路径移动到圆心位置
|
|
xzCtx.arc(x, y, radius, getAngle(startAngle), getAngle(endAngle), false);
|
|
xzCtx.closePath();
|
|
xzCtx.fill();
|
|
} else {
|
|
xzCtx.strokeStyle = bgColor;
|
|
|
|
xzCtx.beginPath();
|
|
xzCtx.moveTo(x, y);
|
|
xzCtx.arc(x, y, radius, getAngle(startAngle), getAngle(endAngle), false);
|
|
xzCtx.closePath();
|
|
xzCtx.stroke();
|
|
}
|
|
}
|
|
/**
|
|
* @description 4、绘制线段(主要分为:绘制填充线段和绘制空心线段利用isFill变量来标记)
|
|
* 主要是使用的是canvas原生的API:
|
|
* lineTo(x,y):表示从某点连线到该坐标点
|
|
*moveTo(x,y):表示将路径移动到画布中的该坐标点
|
|
* x:画布中某点的X坐标
|
|
* y:画布中某点的Y坐标
|
|
* 注意:如果开始没有调用moveTo,那么第一个lineTo的功能就相当于一个moveTo
|
|
* 自己封装的API:drawLine(startX,startY,endX,endY,lineWidth,bgcolor)
|
|
*
|
|
* startX:表示线的起点的X坐标
|
|
* startY:表示起点的Y坐标
|
|
* endX:表示线的终点的X坐标
|
|
* endY:表示线的终点的Y坐标
|
|
* lineWidth:表示线段的宽度
|
|
* bgColor:线的颜色
|
|
* */
|
|
|
|
function drawLine (startX, startY, endX, endY, lineWidth, bgColor) {
|
|
xzCtx.beginPath();
|
|
xzCtx.lineWidth = lineWidth;
|
|
xzCtx.strokeStyle = bgColor;
|
|
xzCtx.moveTo(startX, startY);
|
|
xzCtx.lineTo(endX, endY);
|
|
xzCtx.stroke();
|
|
xzCtx.fill();
|
|
}
|
|
/**
|
|
* @description 5、绘制贝塞尔曲线
|
|
* drawBezierCurve
|
|
* */
|
|
|
|
//将角度转换成弧度函数,
|
|
function getAngle (arc) {
|
|
return Math.PI * (arc / 180);
|
|
}
|
|
/**
|
|
* @description 6、吊钩
|
|
* variousHooks
|
|
* x原点横坐标,
|
|
* y原点纵坐标,
|
|
* X变量值左右,
|
|
* Y变量值上下,
|
|
* bgColor颜色
|
|
* */
|
|
|
|
function variousHooks (x, y, X, Y, bgColor) {
|
|
xzCtx.beginPath();
|
|
xzCtx.strokeStyle = bgColor;
|
|
//钩子头部分
|
|
xzCtx.moveTo(x + 5 + X, y);
|
|
xzCtx.lineTo(x + 35 + X, y);
|
|
xzCtx.moveTo(x + 5 + X, y + 10);
|
|
xzCtx.lineTo(x + 35 + X, y + 10)
|
|
xzCtx.moveTo(x + 10 + X, y);
|
|
xzCtx.lineTo(x + 10 + X, y + 10);
|
|
xzCtx.moveTo(x + 30 + X, y);
|
|
xzCtx.lineTo(x + 30 + X, y + 10);
|
|
xzCtx.moveTo(x + 11 + X, y + 10);
|
|
xzCtx.lineTo(x + 11 + X, y + 10 + Y);
|
|
xzCtx.moveTo(x + 29 + X, y + 10);
|
|
xzCtx.lineTo(x + 29 + X, y + 10 + Y);
|
|
//半圆
|
|
xzCtx.moveTo(x + 20 + X, y + 10 + Y);
|
|
xzCtx.arc(x + 20 + X, y + 10 + Y, 10, 0, 180 * Math.PI / 180, false);
|
|
xzCtx.moveTo(x + 20 + X, y + 10 + Y + 5);
|
|
xzCtx.lineTo(x + 10 + X, y + 10 + Y + 15);
|
|
xzCtx.moveTo(x + 20 + X, y + 10 + Y + 5);
|
|
xzCtx.lineTo(x + 30 + X, y + 10 + Y + 15);
|
|
xzCtx.moveTo(x + 30 + X, y + 10 + Y + 15);
|
|
xzCtx.lineTo(x + 30 + X, y + 10 + Y + 35);
|
|
xzCtx.lineTo(x + 10 + X, y + 10 + Y + 35);
|
|
xzCtx.lineTo(x + 10 + X, y + 10 + Y + 35);
|
|
xzCtx.lineTo(x + 10 + X, y + 10 + Y + 15);
|
|
xzCtx.lineTo(x + 30 + X, y + 10 + Y + 15);
|
|
xzCtx.stroke();
|
|
xzCtx.fill();
|
|
}
|
|
/**
|
|
* @description 7、叉线
|
|
* wiredCables
|
|
* */
|
|
|
|
function wiredCables (x, y, lineWidth, bgColor) {
|
|
xzCtx.beginPath();
|
|
xzCtx.lineWidth = lineWidth;
|
|
xzCtx.strokeStyle = bgColor;
|
|
xzCtx.moveTo(x - 10, y + 15);
|
|
xzCtx.lineTo(x + 10, y - 15);
|
|
xzCtx.moveTo(x - 10, y - 15);
|
|
xzCtx.lineTo(x + 10, y + 15);
|
|
xzCtx.stroke();
|
|
xzCtx.fill();
|
|
}
|
|
|
|
/**
|
|
* @description 8、文字
|
|
* wiredCables
|
|
* */
|
|
|
|
function drawText (text, x, y, color, font, textAlign) {
|
|
xzCtx.font = font;
|
|
xzCtx.textAlign = textAlign;
|
|
xzCtx.fillStyle = color;
|
|
xzCtx.fillText(text, x, y);
|
|
}
|
|
|
|
function initCanvas () { //onload事件加载该方法,当HTML5页面加载的时候就会回调该方法
|
|
|
|
// 适应塔吊的高的高度
|
|
// 150 是驾驶舱底部的开始高度 180是预留的底座的高度
|
|
const autoHeight = canvasHeight - 150 - 60
|
|
const autoWidth = canvasWidth - 170 - 60
|
|
let maxHeightAutoI = 0
|
|
for (let i = 0; i * 30 < autoHeight; i++) {
|
|
/** 画矩形 */
|
|
drawRect(150, 160 + i * 30, 20, 30, false, false, mainColor);
|
|
/** 画点 */
|
|
drawArc(150, 160 + i * 30, 2, 0, 360, false, false, true, mainColor);
|
|
drawArc(170, 160 + i * 30, 2, 0, 360, false, false, true, mainColor);
|
|
/** 话叉线 */
|
|
wiredCables(160, 175 + i * 30, 1, mainColor)
|
|
maxHeightAutoI = i
|
|
}
|
|
maxHeightAutoI += 1
|
|
// 底座
|
|
drawLine(110, 160 + maxHeightAutoI * 30, 210, 160 + maxHeightAutoI * 30, 1, mainColor);
|
|
drawLine(135, 160 + maxHeightAutoI * 30, 150, 160 + (maxHeightAutoI - 2) * 30, 1, mainColor);
|
|
drawLine(185, 160 + maxHeightAutoI * 30, 170, 160 + (maxHeightAutoI - 2) * 30, 1, mainColor);
|
|
|
|
// 右臂斜线
|
|
let maxWidthAutoI = 0
|
|
for (let i = 0; i * 20 < autoWidth; i++) {
|
|
drawLine(170 + i * 20, 110, 180 + i * 20, 90, 1, mainColor);
|
|
drawLine(190 + i * 20, 110, 180 + i * 20, 90, 1, mainColor);
|
|
maxWidthAutoI = i
|
|
}
|
|
// 右臂上下线条
|
|
maxWidthAutoI += 1
|
|
drawLine(170, 110, 170 + maxWidthAutoI * 20, 110, 1, mainColor);
|
|
drawLine(180, 90, 180 + maxWidthAutoI * 20 - 20, 90, 1, mainColor);
|
|
|
|
// 驾驶舱左
|
|
drawRect(150, 115, 10, 40, false, true, mainColor);
|
|
//左小重物
|
|
drawRect(57, 113, 9, 15, false, true, mainColor);
|
|
drawRect(67, 113, 9, 15, false, true, mainColor);
|
|
drawRect(77, 113, 9, 15, false, true, mainColor);
|
|
|
|
//驾驶室
|
|
drawLine(160, 115, 170, 115, 1, mainColor);
|
|
drawLine(170, 115, 180, 135, 1, mainColor);
|
|
drawLine(180, 135, 160, 135, 1, mainColor);
|
|
drawLine(180, 135, 170, 155, 1, mainColor);
|
|
drawLine(170, 155, 160, 155, 1, mainColor);
|
|
//塔吊尖
|
|
drawLine(150, 110, 170, 110, 1, mainColor);
|
|
drawLine(150, 110, 160, 20, 1, mainColor);
|
|
drawLine(170, 110, 160, 20, 1, mainColor);
|
|
drawLine(152, 90, 168, 90, 1, mainColor);
|
|
drawLine(155, 70, 165, 70, 1, mainColor);
|
|
drawLine(158, 50, 162, 50, 1, mainColor);
|
|
|
|
drawLine(160, 45, 85, 80, 1, mainColor);
|
|
drawLine(160, 45, 88, 80, 1, mainColor);
|
|
drawLine(160, 45, 91, 80, 1, mainColor);
|
|
|
|
drawLine(160, 45, 240, 90, 1, mainColor);
|
|
drawLine(160, 45, 243, 90, 1, mainColor);
|
|
drawLine(160, 45, 246, 90, 1, mainColor);
|
|
drawLine(160, 45, 350, 90, 1, mainColor);
|
|
drawLine(160, 45, 353, 90, 1, mainColor);
|
|
drawLine(160, 45, 356, 90, 1, mainColor);
|
|
|
|
//左吊臂
|
|
drawLine(55, 110, 150, 110, 1, mainColor);
|
|
drawLine(55, 110, 55, 80, 1, mainColor);
|
|
drawLine(85, 110, 85, 80, 1, mainColor);
|
|
drawLine(115, 110, 115, 80, 1, mainColor);
|
|
drawLine(145, 110, 145, 80, 1, mainColor);
|
|
drawLine(55, 100, 145, 100, 1, mainColor);
|
|
drawLine(55, 90, 145, 90, 1, mainColor);
|
|
drawLine(55, 80, 145, 80, 1, mainColor);
|
|
|
|
//左小重物竖线
|
|
drawLine(62, 110, 62, 130, 1, mainColor);
|
|
drawLine(72, 110, 72, 130, 1, mainColor);
|
|
drawLine(82, 110, 82, 130, 1, mainColor);
|
|
|
|
//6.控制吊钩的运动 以(165,110)为原点 X~(0--180),Y~(0--240)
|
|
variousHooks(110, 110, 180, 444, mainColor)
|
|
|
|
//8 吊钩区域文字
|
|
// drawText('尾臂: ', 20, 35, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('12.5米', 65, 35, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('倾角: ', 20, 240, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('0°', 65, 240, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('更新时间: ', 75, 430, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('2018-11-12', 160, 430, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('14:57:30', 275, 430, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('长臂: ', 280, 35, "#01adec", '18px 微软雅黑', "left")
|
|
// drawText('30.26米', 325, 35, "#01adec", '18px 微软雅黑', "left")
|
|
}
|
|
initCanvas()
|
|
}, [])
|
|
|
|
return (
|
|
<div style={{ height: '100vh', padding: 8 }}>
|
|
<div style={{ height: '80%', padding: 8 }}>
|
|
<Row style={{ height: '100%' }}>
|
|
<Col span={12} id="canvasArea" style={{ paddingRight: 8, }}>
|
|
<div className='card'>
|
|
<canvas ref={xzCvs} id='xzCvs' height={120} width={120} style={{}}>
|
|
您的浏览器不支持canvas,请更换浏览器.
|
|
</canvas>
|
|
</div>
|
|
</Col>
|
|
<Col span={12} style={{ paddingLeft: 8,}}>
|
|
<div className='card'>
|
|
<canvas ref={xyCvs} id='xyCvs' height={120} width={120} style={{}}>
|
|
您的浏览器不支持canvas,请更换浏览器.
|
|
</canvas>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</div>
|
|
<div style={{ height: '20%' }}>
|
|
<Row style={{ height: '100%' }}>
|
|
{
|
|
[{
|
|
k: '高度',
|
|
v: '100m',
|
|
s: '上升',
|
|
}, {
|
|
k: '幅度',
|
|
v: '100m',
|
|
s: '上升',
|
|
}, {
|
|
k: '回转',
|
|
v: '100m',
|
|
s: '上升',
|
|
}, {
|
|
k: '重量',
|
|
v: '100m',
|
|
s: '上升',
|
|
}, {
|
|
k: '力矩',
|
|
v: '100m',
|
|
s: '上升',
|
|
}, {
|
|
k: '风速',
|
|
v: '100m',
|
|
s: '上升',
|
|
}, {
|
|
k: '倾角',
|
|
v: '100m',
|
|
s: '上升',
|
|
}, {
|
|
k: '',
|
|
v: '',
|
|
s: '',
|
|
},].map(s => {
|
|
return (
|
|
<Col span={3} style={{ padding: 8, height: '100%' }}>
|
|
<div class="card">
|
|
{
|
|
s.k ?
|
|
<>
|
|
<h2>{s.k}</h2>
|
|
<p>{s.v}</p>
|
|
<span class="status">{s.s}</span>
|
|
</>
|
|
: ''
|
|
}
|
|
</div>
|
|
</Col>
|
|
)
|
|
})
|
|
}
|
|
</Row>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Index
|