Analysis of n-order Bezier curve with JavaScript

Time:2021-6-20

Recently, I learned canvas and saw Bezier curve, so I added some knowledge. In addition, I forgot all the related mathematical laws~

What we need to know

Early need to understand the relevant knowledge, you can see Wikipedia

draw

Canvas itself only provides quadratic and cubic drawing functions. What should we do if we are of a higher class.

There is a very good case on the Internetbezier curve

Let’s take a look at the JS of this case. This demo does not calculate like our equation, but it uses recursive operation, recursive calldrawMethod to achieve layer by layer rendering

var input = document.getElementsByTagName('input')[0]
var span = document.getElementsByTagName('span')[0]
var div = document.getElementsByTagName('div')[0]
var ctx1 = document.getElementsByTagName('canvas')[0].getContext('2d')
var ctx2 = document.getElementsByTagName('canvas')[1].getContext('2d')
var ctx3 = document.getElementsByTagName('canvas')[2].getContext('2d')

var points = [], colors = [], running = true, steps = 200, interval = 16, num

ctx1.font = '16px consolas'
ctx1.fillStyle = ctx1.strokeStyle = 'hsl(0, 0%, 50%)'
ctx1.lineWidth = ctx2.lineWidth = 2
ctx3.strokeStyle = 'hsl(0, 90%, 70%)'

function count() {
  num = parseInt(input.value)
  span.innerHTML = num
}

function toggle() {
  input.disabled = running = !running
}

function draw(per, arr, color) {
  var ary = []
  var node

  ctx2.strokeStyle = ctx2.fillStyle = colors[color]

  node = arr.reduce(function(previous, current, index) {
    //Starting from the second element, the second point, when the index is 1, the P point is calculated. When the index is 1, the P point is the interpolation from the Bezier start point to the first control point
    //When the third element is used, the third point, index 2, is calculated to get point P. when index 2, point P is the interpolation of the first control point moving to the second control point
    var p = {x: arr[index - 1].x + (arr[index].x - arr[index - 1].x) * per, y: arr[index - 1].y + (arr[index].y - arr[index - 1].y) * per}

    if(index > 1) {
      //When the second control point is reached, P from the start point to the first control point is obtained and the line is executed
      ctx2.beginPath()
      ctx2.moveTo(previous.x, previous.y)
      ctx2.lineTo(p.x, p.y)
      ctx2.stroke()
      ctx2.closePath()
    }
    //Draws the current interpolation point
    ctx2.beginPath()
    ctx2.arc(p.x, p.y, 3, 0, Math.PI * 2, true)
    ctx2.fill()
    ctx2.closePath()
    //Push coordinate points into a new coordinate array
    ary.push(p)
    return p
  })

  if(ary.length > 1) {
    //The interpolation is used as a new start point and control point for drawing, so recursion goes on
    draw(per, ary, color + 1)
  } else {
    //If the interpolation array has only one value, the points on the Bezier curve are drawn from the starting point to the end point
    ctx3.lineTo(node.x, node.y)
    ctx3.stroke()
  }
}

var drawAsync = eval(Wind.compile("async", function () {
  toggle()
  ctx3.beginPath()
  ctx3.moveTo(points[0].x, points[0].y)
  for(var i = 0; i <= steps; i++) {
    draw(i / steps, points, 0)
    $await(Wind.Async.sleep(interval))
    ctx2.clearRect(0, 0, 800, 600)
  }
  ctx3.closePath()
  points = []
  toggle()
}))

div.addEventListener('click', function(e) {
  if(running) {
    return
  }

  var point = {x: e.pageX - div.offsetLeft, y: e.pageY - div.offsetTop}

  if(points.length == 0) {
    ctx1.clearRect(0, 0, 800, 600)
    ctx2.clearRect(0, 0, 800, 600)
    ctx3.clearRect(0, 0, 800, 600)
  } else {
    ctx1.beginPath()
    ctx1.moveTo(point.x, point.y)
    ctx1.lineTo(points[points.length - 1].x, points[points.length - 1].y)
    ctx1.stroke()
    ctx1.closePath()
  }

  ctx1.beginPath()
  ctx1.fillText('[' + point.x + ', ' + point.y + ']', 15, 25 * (points.length + 1))
  ctx1.arc(point.x, point.y, 4, 0, Math.PI * 2, true)
  ctx1.fill()
  ctx1.closePath()

  points.push(point)

  if(points.length == num) {
    drawAsync().start()
  }
}, false)

input.addEventListener('change', count, false)

window.addEventListener('load', function() {
  for(var i = 0; i < parseInt(input.max); i++) {
    colors[i] = 'hsl(' + 60*(i + 1) + ', 60%, 60%)'
  }
  count()
  toggle()
}, false)

summary

I can’t write it myself… yesarray.reduceThe usage of is really perfect

Recommended Today

A brief introduction to CGI programming in Ruby

Ruby is a general language, not just a language used for web development, but ruby is the most common in web applications and web tools. With ruby, you can not only write your own SMTP server, FTP program, or Ruby web server, but also use ruby for CGI programming. Next, let’s take a moment to […]