canvas繪制自定義的曲線,以橢圓為例,通俗易懂,童叟無欺


本篇文章,將講述如何通過自定義的曲線函數,使用canvas的方式進行曲線的繪制。

為了通俗易懂,將以大家熟悉的橢圓曲線為例,進行橢圓的繪制。至於其他比較復雜的曲線,用戶只需通過數學方式建立起曲線函數,然后變換成為距離函數方程,替換即可。另外:代碼還沒進行任何優化。

(注:本文只適合那種能在一個點為原點、基於原點的每個角度只能存在一個點的曲線,通俗說就是,過原點作直線,與曲線相交的交點最多兩個,而且兩交點分別位於原點兩端。)

目錄結構

1、數學分析

2、曲線方程

3、畫一個點

4、畫形狀

5、廢話

正文:

1、數學分析

首先講解下橢圓的構造,如圖所示,數學厲害的忽視這段。

======================================分割線,數學帝請忽略這一段==================================

橢圓構造圖

其中,OA,OB分別為半長軸和短長軸,通過此兩線段的距離能計算出半焦距FO的長度,再確定心O的坐標就能確認整個橢圓的范圍。

由此可知,清楚了OA,OB的距離,就能知道橢圓的形狀。

百度百科里面講到,

橢圓的標准方程有兩種,取決於焦點所在的坐標軸:

1)焦點在X軸時,標准方程為:x²/a²+y²/b²=1 (a>b>0)

2)焦點在Y軸時,標准方程為:y²/a²+x²/b²=1 (a>b>0)

其實兩個概念差不多一樣,將a 和 b在方程中的位置對調就行。不詳細講。

看好,我要變形了。→_→

假設θ為∠POF,P點距離x軸的長度為Y1的值,設為y;距離Y軸長度為X1值,設為X,P點基於O的坐標就是(x1,y1)。

tanθ = y/x

y = xtanθ

帶入橢圓方程 x²/a²+x2(tanθ)2/b²=1,解得(注:tanθ*tanθ,不記得是用tanθ2/tan2θ表示了,貌似是tan2θ,本文統一(tanθ)2表示)

x2 = a2b2 / (b2+a2(tanθ)2),帶入y = xtanθ,並進行化簡得出 x2+y2 = (a2b2(1+(tanθ)2)/ (b2+a2(tanθ)2),就是說,通過a,b,θ 能計算出OP 的距離來了。

======================================分割線,忽略了上面段的可以回來了====================================

2、曲線方程

因此,得到一個方程,返回的是OP的距離

/*
    func: 
          ellipseFunc:return the length
    args:
          a:[number] ellipse's a
          b:[number] ellipse's b
          theta:[number] how much of Math.PI
  */
  function ellipseFunc(a,b,theta) {
    // javascript Math對象下面的三角函數,傳入的theta值必須是轉換成弧度制的值,就是多少個3.141592…等等等的那個弧度制
    return Math.pow(((a*a*b*b)*(1+Math.tan(theta)*Math.tan(theta)))/(b*b+a*a*Math.tan(theta)*Math.tan(theta)),1/2);
  }

3、畫一個點

既然曲線函數完畢,下面就行畫圖了,先從畫一個點來說,網上很對關於描繪一個點的帖子,我選了ctx.fillRect(x,y,a,b),這是繪制矩形的函數,x、y為繪制矩形的起點,就是左上角,設置a=b=1,就能繪制一個長寬各為1px大小的矩陣,個人喜歡使用其他也行。

下面是繪制一個點的函數,填充風格沒有定義,在傳入_ctx時是什么fillStyle就填充什么風格。

  /*
    func: 
          drawPoint:draw a point
    args:
          _ctx:[object]the canvas's getContent("2d") variable
          point:[object] the point where to draw a dot such as {"x":200,"y":200}
          strokwidth:[number] the draw line's width and height 
  */
  function drawPoint(_ctx,point,strokwidth){
    strokwidth = strokwidth || 1;
    if(!(_ctx !== undefined && _ctx !== null)) return false;
    var x = point.x,
        y = point.y;
    _ctx.fillRect(x,y,strokwidth,strokwidth);
    return true;
  }

4、畫形狀

點的繪制講述完畢,開始畫曲線了,用for循環,畫圖吧。

  /*
    func: 
          drawShape:draw a shape
    args:
          canvasId:[string]the canvas's id
          func:[function]the shape function
          ellipse:[object] the ellipse's a and b length such as {"a":300,"b":200}
          center:[object] the draw center such as {"x":400,"y":400}
  */
  function drawShape(canvasId,func,ellipse,center){
     var _c = document.getElementById(canvasId);
    if(_c === null) return false;
    var _ctx = _c.getContext("2d");
   // 默認橢圓中心為canvas的中心
    var a = ellipse.a || 0,
        b = ellipse.b || 0,
        centerX = center.x || _c.width/2,
        centerY = center.y || _c.height/2,
        drawX,drawY,pointCX,pointCY;
    shapeGet = func;
    for(var i = 0;i <= 2*Math.PI; i+=0.0001){// 通過弧度繪圖,精確到每個0.0001弧度畫圖,可以更加精確。但是小圖的話,沒必要那么精確,浪費CPU時間。
      length = shapeGet(a,b,i);
      pointCX = length*Math.cos(i);
      pointCY = length*Math.sin(i);
      drawX = centerX + pointCX;
      drawY = centerY - pointCY;
      drawPoint(_ctx,{"x":drawX,"y":drawY},1);
    }
    return true;
  }

調用方式 

drawShape("myCanvas",ellipseFunc,{"a":300,"b":200},{"x":400,"y":400});

頁面的全部源碼如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
<body>
  <div id="cloud">
    <canvas id="myCanvas" width="800" height="800"></canvas>
  </div>

<script>
  /*
    func: 
          ellipseFunc:return the length
    args:
          a:[number] ellipse's a
          b:[number] ellipse's b
          theta:[number] how much of Math.PI
  */
  function ellipseFunc(a,b,theta) {
    // javascript Math對象下面的三角函數,傳入的theta值必須是轉換成弧度制的值,就是多少個3.141592…等等等的那個弧度制
    return Math.pow(((a*a*b*b)*(1+Math.tan(theta)*Math.tan(theta)))/(b*b+a*a*Math.tan(theta)*Math.tan(theta)),1/2);
  }

  /*
    func: 
          drawPoint:draw a point
    args:
          _ctx:[object]the canvas's getContent("2d") variable
          point:[object] the point where to draw a dot such as {"x":200,"y":200}
          strokwidth:[number] the draw line's width and height 
  */
  function drawPoint(_ctx,point,strokwidth){
    strokwidth = strokwidth || 1;
    if(!(_ctx !== undefined && _ctx !== null)) return false;
    var x = point.x,
        y = point.y;
    _ctx.fillRect(x,y,strokwidth,strokwidth);
    return true;
  }

  /*
    func: 
          drawShape:draw a shape
    args:
          canvasId:[string]the canvas's id
          func:[function]the shape function
          ellipse:[object] the ellipse's a and b length such as {"a":300,"b":200}
          center:[object] the draw center such as {"x":400,"y":400}
  */
  function drawShape(canvasId,func,ellipse,center){
     var _c = document.getElementById(canvasId);
    if(_c === null) return false;
    var _ctx = _c.getContext("2d");
   // 默認橢圓中心為canvas的中心
    var a = ellipse.a || 0,
        b = ellipse.b || 0,
        centerX = center.x || _c.width/2,
        centerY = center.y || _c.height/2,
        drawX,drawY,pointCX,pointCY;
    shapeGet = func;
    for(var i = 0;i <= 2*Math.PI; i+=0.0001){// 通過弧度繪圖,精確到每個0.0001弧度畫圖,可以更加精確,0.0001更加歡迎。但是小圖的話,沒必要那么精確,浪費CPU時間。
      length = shapeGet(a,b,i);
      pointCX = length*Math.cos(i);
      pointCY = length*Math.sin(i);
      drawX = centerX + pointCX;
      drawY = centerY - pointCY;
      drawPoint(_ctx,{"x":drawX,"y":drawY},1);
    }
    return true;
  }
  drawShape("myCanvas",ellipseFunc,{"a":300,"b":200},{"x":400,"y":400});

</script>
</body>
</html>
View Code

 

5、廢話

本文產生的原因,本來是想做詞雲的,給定詞雲的形狀,在這個形狀內填充詞語,產生了這個念頭,詞雲還沒實現,關鍵是如何才能讓填充詞語相互不覆蓋的問題。后來,選擇在github里面搜索算了,選擇了一個jQuery.awesomeCloud.plugin,但是填充效率確實壓力山大。想過去模擬他的方法,做一個出來,因此先從畫自定義曲線開始了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM