canvas实现平滑曲线,并且使曲线穿过每个点

二次贝塞尔曲线;

 const canvas = document.getElementById('myCanvas')
      const ctx = canvas.getContext('2d')

      // 给定的点数组
      const points = [
        { x: 50, y: 100 },
        { x: 100, y: 200 },
        { x: 150, y: 50 },
        { x: 200, y: 250 },
        { x: 250, y: 150 }
      ]

      // 设置起始点
      ctx.beginPath()
      ctx.moveTo(points[0].x, points[0].y)

      // 使用贝塞尔曲线穿过每一个点
      for (let i = 1; i < points.length; i++) {
        const cpx = (points[i].x + points[i - 1].x) / 2 // 控制点的 x 坐标
        const cpy = (points[i].y + points[i - 1].y) / 2 // 控制点的 y 坐标

        ctx.quadraticCurveTo(points[i - 1].x, points[i - 1].y, cpx, cpy)
      }

      // 绘制曲线
      ctx.lineWidth = 2
      ctx.strokeStyle = 'blue'
      ctx.stroke()

      for (let i = 0; i < points.length - 1; i++) {
        ctx.beginPath()
        // 绘制小圆
        ctx.strokeStyle = 'red'
        ctx.arc(points[i].x, points[i].y, 5, 0, Math.PI * 2, false)
        ctx.stroke() // 开始画

        // 绘制大圆
        // 绘制大圆
        ctx.beginPath()
        ctx.strokeStyle = 'red' // 半透明蓝色背景
        ctx.arc(points[i].x, points[i].y, 20, 0, Math.PI * 2, false)
        ctx.stroke() // 开始画
      }
      ctx.closePath() // 关闭路径

效果如下
在这里插入图片描述
可以看到效果是很平滑,但无法穿过对应的每一个点;

三次贝塞尔曲线

const canvas = document.getElementById('myCanvas')
      const ctx = canvas.getContext('2d')

      // 给定的点数组
      const points = [
        { x: 50, y: 100 },
        { x: 100, y: 200 },
        { x: 150, y: 50 },
        { x: 200, y: 250 },
        { x: 250, y: 150 }
      ]

      // 设置起始点
      ctx.beginPath()
      ctx.moveTo(points[0].x, points[0].y)

      // 绘制曲线
      for (let i = 1; i < points.length - 1; i++) {
        const p0 = i === 1 ? points[0] : points[i - 1]
        const p1 = points[i]
        const p2 = points[i + 1]
        const p3 = i === points.length - 2 ? points[points.length - 1] : points[i + 2]

        const cp1x = p1.x + (p2.x - p0.x) / 5
        const cp1y = p1.y + (p2.y - p0.y) / 5
        const cp2x = p2.x - (p3.x - p1.x) / 5
        const cp2y = p2.y - (p3.y - p1.y) / 5

        ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y)
      }
      ctx.stroke() // 开始画
      // 绘制给定的点
      ctx.fillStyle = 'blue'
      for (let i = 0; i < points.length; i++) {
        ctx.beginPath()
        ctx.arc(points[i].x, points[i].y, 4, 0, 2 * Math.PI)
        ctx.fill()
      }

效果如下
在这里插入图片描述
可以看到无法穿过第一个点,其余都很完美;
所以我们对曲线绘制的逻辑进行修改,第一个点和第二个点之间,而其他点之间使用三次贝塞尔曲线。这样的修改可以确保曲线平滑,并且所有点都在曲线上;

 const canvas = document.getElementById('myCanvas')
      const ctx = canvas.getContext('2d')

      // 给定的点数组
      const points = [
        { x: 50, y: 100 },
        { x: 100, y: 200 },
        { x: 150, y: 50 },
        { x: 200, y: 250 },
        { x: 250, y: 150 }
      ]

      // 设置起始点
      ctx.beginPath()
      ctx.moveTo(points[0].x, points[0].y)
      const p0 = points[0]
      const p1 = points[1]
      const p2 = points[2]

      const cp1x = p0.x + (p1.x - p0.x) / 2
      const cp1y = p0.y + (p1.y - p0.y) / 2
      ctx.quadraticCurveTo(cp1x, cp1y, p1.x, p1.y)
      // 绘制曲线
      for (let i = 1; i < points.length - 1; i++) {
        const p0 = i === 1 ? points[0] : points[i - 1]
        const p1 = points[i]
        const p2 = points[i + 1]
        const p3 = i === points.length - 2 ? points[points.length - 1] : points[i + 2]

        const cp1x = p1.x + (p2.x - p0.x) / 5
        const cp1y = p1.y + (p2.y - p0.y) / 5
        const cp2x = p2.x - (p3.x - p1.x) / 5
        const cp2y = p2.y - (p3.y - p1.y) / 5

        ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y)
      }

      ctx.stroke() // 开始画
      // 绘制给定的点
      ctx.fillStyle = 'blue'
      for (let i = 0; i < points.length; i++) {
        ctx.beginPath()
        ctx.arc(points[i].x, points[i].y, 4, 0, 2 * Math.PI)
        ctx.fill()
      }
      ctx.closePath() // 关闭路径

在这里插入图片描述
其实也尝试过不同的算法

function getCtrlPoint(ps: any[], i: number, a = 0.1, b = 0.3) {
  var distX = ps[i].x - ps[i - 1].x
  var distY = ps[i].y - ps[i - 1].y
  var distance = Math.sqrt(distX * distX + distY * distY)
  var distCompX = distX / distance
  var distCompY = distY / distance

  var pAx = ps[i - 1].x + distX * distCompX * a
  var pAy = ps[i - 1].y + distY * distCompY * a
  var pBx = ps[i].x - distX * distCompX * b
  var pBy = ps[i].y - distY * distCompY * b
  return {
    pA: { x: Math.round(pAx), y: Math.round(pAy) },
    pB: { x: Math.round(pBx), y: Math.round(pBy) },
  }
}

但输出的曲线都很奇怪;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值