基于fabricjs实现钢笔工具

1.gif

一、弄清钢笔工具原理
1.当点击下未拖动时则为lineTo的直线
2.当点击下后并进行拖拽时使用Q二次贝塞尔曲线
3.当上个点是一个二次贝塞尔曲线时,而后的第二个点进行了拖拽则采用C三次贝塞尔曲线

二、渲染path函数(写的有点土,有优化空间可以告诉一下)

import type { IPoint } from '../../implements'

export function getPath(points: Array<IPoint>, movePoint?: IPoint) {
  let path = ''
  if (points.length) {
    const firstPoint = points[0]
    path = `M${firstPoint.x} ${firstPoint.y}`
    for (let i = 1; i < points.length; i++) {
      const point = points[i]
      const prevPoint = points[i - 1]
      // 如果上一个点是存在曲线而当前点并未曲线 则使用二次贝塞尔曲线
      if (prevPoint && prevPoint.isMove && !point.isMove) {
        path += ` Q ${prevPoint.handleOutX} ${prevPoint.handleOutY}, ${point.x} ${point.y}`
      }
      // 如果上一个点是存在曲线而当前点并曲线 则使用三次贝塞尔曲线
      else if (prevPoint && prevPoint.isMove && point.isMove) {
        path += `C ${prevPoint.handleOutX},${prevPoint.handleOutY} ${points[i].handleInX},${points[i].handleInY} ${points[i].x},${points[i].y}`
      }
      // 如果上一个不存在曲线 而当前点曲线 使用二次贝塞尔
      else if (point.isMove) {
        path += ` Q ${point.handleInX} ${point.handleInY}, ${point.x} ${point.y}`
      }
      // 直线
      else {
        path += ` L ${point.x} ${point.y}`
      }
    }
    // 跟随手指的线
    if (movePoint) {
      const last = points[points.length - 1]
      if (last.isMove) {
        path += ` Q ${last.handleOutX} ${last.handleOutY}, ${movePoint.x} ${movePoint.y}`
      } else {
        path += `L ${movePoint.x} ${movePoint.y}`
      }
    }
  }
  return path
}

计算handleOut和handleIn的点
handleOut就是手指跟随拖拽的点
而handleIn是二次贝塞尔曲线中的控制点需要计算对应的向量点,原理如下图:

微信图片_20220530093929.png

实现代码

// 获取镜像的点 p1原点 p2手的位置
export function getMirrorPoint(p1: Point, p2: Point) {
  // 获取两点之间的弧度
  const radian = getRadian(p1, p2)
  // 弧度转角度
  const degrees = radian2Angle(radian)
  // 求出距离作为半径
  const radius = getDistance(p1, p2)
  // 根据半径[radius],原点[p1.x,p1.y]坐标, 计算出角度[degrees]对应的圆上坐标点
  const x = p1.x + radius * Math.cos((degrees * Math.PI) / 180)
  const y = p1.y + radius * Math.sin((degrees * Math.PI) / 180)
  return { x, y }
}
// 获取两点之间的弧度
export function getRadian(p1: Point, p2: Point) {
  return Math.atan2(p1.y - p2.y, p1.x - p2.x)
}
// 弧度转角度
export function radian2Angle(radian: number) {
  return (180 / Math.PI) * radian
}
// 获取两点的距离
export function getDistance(p1: Point, p2: Point) {
  const dx = Math.abs(p1.x - p2.x)
  const dy = Math.abs(p1.y - p2.y)
  const radius = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
  return radius
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容