吊机防碰撞一体机软件
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

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