主要的技术点:
1、利用Canvas的rect方法绘制三个方框,并监听鼠标移动事件;
2、利用canvasContext的quadraticCurveTo方法绘制贝塞尔曲线;
3、贝塞尔曲线的原理介绍: 掘金https://juejin.cn/post/6844903666361565191一、效果图:
二、React组件代码(注意我用的是tsx文件,如果是jsx的话把相关的类型声明删除即可)
import React from "react";
export default class BezierComponent extends React.Component {
// canvas引用
canvasRef: any;
// canvasContext
canvasContext: CanvasRenderingContext2D | undefined;
// 矩形框尺寸
optionRectSize = 20;
// 贝塞尔曲线起点
startPoint = {
x: 200,
y: 200,
}
// 贝塞尔曲线终点
endPoint = {
x: 600,
y: 200,
}
// 贝塞尔曲线控制点
controlPoint = {
x: 400,
y: 400,
}
constructor(props) {
super(props);
this.canvasRef = React.createRef();
}
/**
* 点击的位置是否在操作点上
* @param x
* @param y
* @param target
*/
isInOptionPoint = (x: number, y: number, target: { x: number, y: number }) => {
let right = target.x + this.optionRectSize;
let bottom = target.y + this.optionRectSize;
let top = target.y;
let left = target.x;
return x > left && x < right && y > top && y < bottom;
}
initCanvas = () => {
let canvas = this.canvasRef.current;
this.canvasContext = canvas.getContext('2d');
let isMoveStartPoint = false;
let isMoveEndPoint = false;
let isMoveControlPoint = false;
// 鼠标按下事件
canvas.addEventListener('mousedown', (e: MouseEvent) => {
e.preventDefault();
if (this.isInOptionPoint(e.offsetX, e.offsetY, this.startPoint)) {
isMoveStartPoint = true;
return;
}
if (this.isInOptionPoint(e.offsetX, e.offsetY, this.endPoint)) {
isMoveEndPoint = true;
return;
}
if (this.isInOptionPoint(e.offsetX, e.offsetY, this.controlPoint)) {
isMoveControlPoint = true;
return;
}
});
// 鼠标移动事件
canvas.addEventListener('mousemove', (e: MouseEvent) => {
if (isMoveStartPoint) {
this.startPoint.x = e.offsetX - this.optionRectSize / 2;
this.startPoint.y = e.offsetY - this.optionRectSize / 2;
this.drawBezier();
}
if (isMoveEndPoint) {
this.endPoint.x = e.offsetX - this.optionRectSize / 2;
this.endPoint.y = e.offsetY - this.optionRectSize / 2;
this.drawBezier();
return;
}
if (isMoveControlPoint) {
this.controlPoint.x = e.offsetX - this.optionRectSize / 2;
this.controlPoint.y = e.offsetY - this.optionRectSize / 2;
this.drawBezier();
return;
}
})
// 鼠标抬起事件
canvas.addEventListener('mouseup', (e: MouseEvent) => {
isMoveStartPoint = false;
isMoveEndPoint = false;
isMoveControlPoint = false;
})
}
drawBezier = () => {
this.canvasContext?.clearRect(0, 0, this.canvasRef.current.width, this.canvasRef.current.height);
this.canvasContext?.beginPath();
this.canvasContext?.rect(this.startPoint.x, this.startPoint.y, this.optionRectSize, this.optionRectSize);
this.canvasContext?.rect(this.endPoint.x, this.endPoint.y, this.optionRectSize, this.optionRectSize);
this.canvasContext?.rect(this.controlPoint.x, this.controlPoint.y, this.optionRectSize, this.optionRectSize);
this.canvasContext?.moveTo(this.startPoint.x, this.startPoint.y);
this.canvasContext?.quadraticCurveTo(this.controlPoint.x, this.controlPoint.y, this.endPoint.x, this.endPoint.y)
this.canvasContext?.stroke();
}
componentDidMount() {
this.initCanvas();
this.drawBezier();
}
render() {
return (<>
<canvas width={800} height={800} ref={this.canvasRef} style={{background: '#1677ff'}}>
浏览器不支持Canvas标签
</canvas>
</>
);
}
}