字玩FontPlayer开发笔记2 钢笔工具优化

字玩FontPlayer开发笔记2 钢笔工具优化

字玩FontPlayer是笔者开源的一款字体设计工具,使用Vue3 + ElementUI开发,源代码:
github: https://github.com/HiToysMaker/fontplayer
gitee: https://gitee.com/toysmaker/fontplayer

笔记

今天笔者发现原先的钢笔工具逻辑有些问题,进行了修复和优化。

笔者原先的钢笔工具,鼠标按下创建第一个锚点后,会自动生成第一个锚点对应的控制点,鼠标再次移动后,直接创建下一个锚点,而不能拖动自由选择控制点位置。只有后续锚点,才能拖动自由创建对应控制点。另外闭合路径时,没有自动延切线与第一条贝塞尔曲线进行连接,体验不是很好。原先效果如下图所示:
请添加图片描述

参考其他成熟软件的钢笔工具,做了以下两点优化:

  1. 创建第一个锚点时可以拖动创建控制点

关键代码:

// 长按鼠标
if (editAnchor.index === 0) {
  //第一个锚点
  let _anchor = _points[editAnchor.index]
  let _control = _points[editAnchor.index + 1]
  //将第一个锚点对应的控制点设置为鼠标移动位置
  _control.x = getCoord(e.offsetX)
  _control.y = getCoord(e.offsetY)
  _control.isShow = true
} else {
  // 后续锚点
  let _anchor = _points[editAnchor.index]
  let _control1 = _points[editAnchor.index - 1]
  let _control2 = _points[editAnchor.index + 1]
  //将锚点对应的后续控制点设置为鼠标移动位置
  _control2.x = getCoord(e.offsetX)
  _control2.y = getCoord(e.offsetY)
  _control2.isShow = true
  //将锚点对应的前接控制点设置为与后续控制点对称的位置
  _control1.x = _anchor.x - (getCoord(e.offsetX) - _anchor.x)
  _control1.y = _anchor.y - (getCoord(e.offsetY) - _anchor.y)
  _control1.isShow = true
}
  1. 闭合路径时如果用户不做拖动控制点操作,自动计算控制点切线,使最后一条贝塞尔与第一条贝塞尔曲线延切线连接

关键代码:

// 当鼠标移动至第一个锚点所在位置附近时,自动闭合路径
if (isNearPoint(getCoord(e.offsetX), getCoord(e.offsetY), points.value[0].x, points.value[0].y, nearD)) {
  // 将最后一个锚点位置设置为第一个锚点位置
  _anchor.x = points.value[0].x
  _anchor.y = points.value[0].y
  // 自动延切线与第一条贝塞尔曲线进行连接
  _control2.x = points.value[1].x
  _control2.y = points.value[1].y
  _control1.x = points.value[0].x - (points.value[1].x - points.value[0].x)
  _control1.y = points.value[0].y - (points.value[1].y - points.value[0].y)
  closePath = true
}

优化后,效果如下图所示:
请添加图片描述

优化后,移动鼠标监听事件的完整代码:

const onMouseMove = (e: MouseEvent) => {
  const _points = R.clone(points.value)
  if (mousedown) {
    _points[_controlIndex] = _lastControl
    // 长按鼠标
    if (editAnchor.index === 0) {
      //第一个锚点
      let _anchor = _points[editAnchor.index]
      let _control = _points[editAnchor.index + 1]
      //将第一个锚点对应的控制点设置为鼠标移动位置
      _control.x = getCoord(e.offsetX)
      _control.y = getCoord(e.offsetY)
      _control.isShow = true
    } else {
      // 后续锚点
      let _anchor = _points[editAnchor.index]
      let _control1 = _points[editAnchor.index - 1]
      let _control2 = _points[editAnchor.index + 1]
      //将锚点对应的后续控制点设置为鼠标移动位置
      _control2.x = getCoord(e.offsetX)
      _control2.y = getCoord(e.offsetY)
      _control2.isShow = true
      //将锚点对应的前接控制点设置为与后续控制点对称的位置
      _control1.x = _anchor.x - (getCoord(e.offsetX) - _anchor.x)
      _control1.y = _anchor.y - (getCoord(e.offsetY) - _anchor.y)
      _control1.isShow = true
    }
    mousemove = true
    setPoints(_points)
  }
  if (!mousedown) {
    if (!mousemove) {
      // 第一次移动鼠标
      _lastControl = Object.assign({}, _points[_points.length - 1])
      _controlIndex = _points.length - 1
      const _anchor = {
        uuid: genUUID(),
        type: 'anchor',
        x: getCoord(e.offsetX),
        y: getCoord(e.offsetY),
        origin: null,
        isShow: true,
      }
      const _control1 = {
        uuid: genUUID(),
        type: 'control',
        x: _anchor.x,
        y: _anchor.y,
        origin: _anchor.uuid,
        isShow: false,
      }
      const _control2 = {
        uuid: genUUID(),
        type: 'control',
        x: _anchor.x,
        y: _anchor.y,
        origin: _anchor.uuid,
        isShow: false,
      }
      _points.push(_control1, _anchor, _control2)
      setPoints(_points)
      mousemove = true
    } else {
      // 移动鼠标
      _controlIndex = _points.length - 4
      const _anchor = _points[_points.length - 2]
      const _control1 = _points[_points.length - 3]
      const _control2 = _points[_points.length - 1]
      _anchor.x = getCoord(e.offsetX)
      _anchor.y = getCoord(e.offsetY)
      closePath = false
      // 当鼠标移动至第一个锚点所在位置附近时,自动闭合路径
      if (isNearPoint(getCoord(e.offsetX), getCoord(e.offsetY), points.value[0].x, points.value[0].y, nearD)) {
        // 将最后一个锚点位置设置为第一个锚点位置
        _anchor.x = points.value[0].x
        _anchor.y = points.value[0].y
        // 自动延切线与第一条贝塞尔曲线进行连接
        _control2.x = points.value[1].x
        _control2.y = points.value[1].y
        _control1.x = points.value[0].x - (points.value[1].x - points.value[0].x)
        _control1.y = points.value[0].y - (points.value[1].y - points.value[0].y)
        closePath = true
      }
      setPoints(_points)
      mousemove = true
    }
  }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值