n階貝塞爾曲線(bezier)javascript 實現解析

最近學習canvas,看到bezier曲線,因此補充了下知識,另外相關的數學定律都忘光了~node

須要瞭解的

前期須要瞭解相關的知識,能夠看下維基百科git

繪製

canvas自己只提供了二次和三次的繪製函數,若是更高階級的怎麼辦呢~要對起進行降階拆分。數組

網上有個很牛掰的案例 bezier curveasync

咱們來看下這個案例的js,這個demo並無像咱們的方程式寫的那樣來進行計算,可是它用了遞歸的操做,遞歸調用draw方法,來實現層層的繪製函數

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) {
    // 從第二個元素開始,第二個點,這時候index爲1,計算獲得p點,index爲1的時候,p點爲bezier開始點到第一個控制點的插值
    // 第三個元素的時候,第三個點,index爲2,計算獲得p點,index爲2的時候,p點爲第一個控制點向第二個控制點移動的插值
    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) {
      // 當達到第二個控制點的時候,獲取從開始點到第一個控制點的p,進行line
      ctx2.beginPath()
      ctx2.moveTo(previous.x, previous.y)
      ctx2.lineTo(p.x, p.y)
      ctx2.stroke()
      ctx2.closePath()
    }
    // 繪製當前的插值點
    ctx2.beginPath()
    ctx2.arc(p.x, p.y, 3, 0, Math.PI * 2, true)
    ctx2.fill()
    ctx2.closePath()
    // 將座標點push到新的座標數組中
    ary.push(p)
    return p
  })

  if(ary.length > 1) {
    // 將插值做爲新的開始點和控制點進行繪製,就這樣遞歸下去
    draw(per, ary, color + 1)
  } else {
    // 若是插值的數組只有1個值,繪製的就是bezier曲線上的點,從起點一點一點連到結束點
    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)

總結

讓我本身寫還真寫不出來。。。對array.reduce的用法真的爐火純青了學習

相關文章
相關標籤/搜索