用React实现基于Canvas的二阶贝塞尔曲线

主要的技术点:

        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>
      </>
    );
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值