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
}