2013-06-24 46 views
3

我有一個簡單的Canvas繪圖應用程序。 有時的了lineTo()命令產生的線較少座標與圖紙有許多邊緣:畫布 - 有時用lineTo()生成邊緣

enter image description here

我使用的是最新的Firefox,是不是因爲接觸不良或我的電腦是buisy ?有沒有解決辦法? 這裏是我的代碼:JS FIDDLE

beginPath(); 
       moveTo(this.X, this.Y); 
       lineTo(e.pageX , e.pageY); 
       strokeStyle = "rgb(0,0,0)"; 
       ctx.lineWidth=3; 
       stroke(); 
+0

您可以嘗試使用mousemove點(上圖中顯示的頂點)繪製曲線而不是直線(不確定這是否實用)。用直線表示,不管瀏覽器能夠重新渲染畫布(偶爾出現瀏覽器或操作系統忙於執行其他操作的情況),您都可以輕鬆掌握。 –

+0

是的,這是一個好主意 - 您可以使用Bezier或Catmull-Rom樣條來平滑它,但有時候會產生一些奇怪的效果。 – Pointy

+0

是的,但那麼筆畫將不會類似於我的鼠標移動,它將只是一種插值。並且要計算曲線,需要花費額外的時間 – daisy

回答

4

它的響應速度,因爲它可以。您的瀏覽器將盡可能快地發送事件,但它無法保證能夠跟蹤您移動鼠標。很多都與客戶機上的負載有關。

編輯here is a modified fiddle展示了一些你可能會使它更好一些的方法。該版本保留一個單獨的「點」隊列,每隔50毫秒繪製一個新點。這使得「mousemove」處理程序只需要記錄事件中的點座標,並且當鼠標快速移動時,繪圖代碼可以在一次畫布更新時完成一堆點。它仍然不完美。

var canvas = document.getElementById('canvas'); 
     var ctx = canvas.getContext('2d'); 
     var width = window.innerWidth; 
     var height = window.innerHeight; 
     canvas.height = height; 
     canvas.width = width; 
     canvas.addEventListener('mousedown', function(e) { 
      this.down = true; 
      points.setStart(e.pageX, e.pageY); 
     }, 0); 
     canvas.addEventListener('mouseup', function() { 
      this.down = false;   
     }, 0); 
     canvas.addEventListener('mousemove', function(e) {   
      if (this.down) { 
       points.newPoint(e.pageX, e.pageY); 
      } 
     }, 0); 

var points = function() { 
    var queue = [], qi = 0; 
    var ctx = canvas.getContext('2d'); 

    function clear() { 
     queue = []; 
     qi = 0; 
    } 

    function setStart(x, y) { 
     clear(); 
     newPoint(x, y); 
    } 

    function newPoint(x, y) { 
     queue.push([x, y]); 
    } 

    function tick() { 

     var k = 20; // adjust to limit points drawn per cycle 

     if (queue.length - qi > 1) { 
      ctx.beginPath(); 
      if (qi === 0) 
       ctx.moveTo(queue[0][0], queue[0][1]); 
      else 
       ctx.moveTo(queue[qi - 1][0], queue[qi - 1][1]); 

      for (++qi; --k >= 0 && qi < queue.length; ++qi) { 
       ctx.lineTo(queue[qi][0], queue[qi][1]); 
      } 

      ctx.strokeStyle = "rgb(0,0,0)"; 
      ctx.lineWidth = 3; 
      ctx.stroke(); 
     } 
    } 

    setInterval(tick, 50); // adjust cycle time 

    return { 
     setStart: setStart, 
     newPoint: newPoint 
    }; 
}(); 
+0

+1創造性地使用積分排隊+定時抽籤!在「k」節氣門,你有沒有繪製每個queue.length/k元素,而不僅僅是前k個元素。很好的答案! – markE

+0

@markE很好的想法是試圖避免阻止鼠標響應,所以看起來好像有一個固定的最大數量的繪圖會很好。我想也可以計算淨線長度,儘管這會涉及更多的數學。 – Pointy

2

可以使用基數樣來消除這樣的臺詞:

enter image description here

的原因是因爲@Pointy已經解釋,由於瀏覽器的速度有多快能夠響應事件(mousemove)。有一個名爲Pointer Lock API的API,它可能有助於在將來解決這個問題,因爲它更低級,但現在我們需要使用算法來平滑出現分段的線條。

除了平滑處理之外,還有細節平滑處理,點減少處理,錐度處理等可以應用於改善效果的東西。

但是在這種特殊情況下,您可以使用以下函數作爲畫布的擴展。請撥打電話:

ctx.curve(myPointArray, tension, segments); 
ctx.stroke(); 

該數組包含像[x1, y1, x2, y2, ... xn, yn那樣排列的x和y點。

tension的典型值爲0.5。 segments(默認16)是可選的。

越緊張,曲線越圓。分段是數組中每個點之間的分辨率。對於繪圖應用程序,值5可能正常工作(更少的結果點)。

爲了讓它更好地工作,您可以在繪製原始線條的單獨畫布上註冊點。在鼠標上方處理具有此功能的行並將其繪製到主畫布上,然後清除繪圖畫布。

該功能是高度優化的 - 它還會返回已處理的點,因此您可以存儲結果而不是每次重新處理。

/** 
* curve() by Ken Fyrstenberg (c) 2013 Epistemex 
* See Code Project for full source: 
* http://www.codeproject.com/Tips/562175/Draw-Smooth-Lines-on-HTML5-Canvas 
*/ 
CanvasRenderingContext2D.prototype.curve = function(pts, ts, nos) { 

    nos = (typeof numOfSegments === 'undefined') ? 16 : nos; 

    var _pts = [], res = [],  // clone array 
     x, y,      // our x,y coords 
     t1x, t2x, t1y, t2y,   // tension vectors 
     c1, c2, c3, c4,    // cardinal points 
     st, st2, st3, st23, st32, // steps 
     t, i, l = pts.length, 
     pt1, pt2, pt3, pt4; 

    _pts.push(pts[0]);   //copy 1. point and insert at beginning 
    _pts.push(pts[1]); 

    _pts = _pts.concat(pts); 

    _pts.push(pts[l - 2]); //copy last point and append 
    _pts.push(pts[l - 1]); 

    this.moveTo(pts[0], pts[1]) 

    for (i = 2; i < l; i+=2) { 

     pt1 = _pts[i]; 
     pt2 = _pts[i+1]; 
     pt3 = _pts[i+2]; 
     pt4 = _pts[i+3]; 

     // calc tension vectors 
     t1x = (pt3 - _pts[i-2]) * ts; 
     t2x = (_pts[i+4] - pt1) * ts; 

     t1y = (pt4 - _pts[i-1]) * ts; 
     t2y = (_pts[i+5] - pt2) * ts; 

     for (t = 0; t <= nos; t++) { 

      // pre-calc steps 
      st = t/nos; 
      st2 = st * st; 
      st3 = st2 * st; 
      st23 = st3 * 2; 
      st32 = st2 * 3; 

      // calc cardinals 
      c1 = st23 - st32 + 1; 
      c2 = st32 - st23; 
      c3 = st3 - 2 * st2 + st; 
      c4 = st3 - st2; 

      res.push(c1 * pt1 + c2 * pt3 + c3 * t1x + c4 * t2x); 
      res.push(c1 * pt2 + c2 * pt4 + c3 * t1y + c4 * t2y); 

     } //for t 
    } //for i 

    l = res.length; 
    for(i=0;i<l;i+=2) this.lineTo(res[i], res[i+1]); 

    return res; 

} //func ext 

參見this answer以實現基數樣條。

+0

如果我想繪製硬邊的東西會發生什麼? – daisy

+0

@daisy通常這是通過計算拐點值來處理的 - 在考慮「新」筆畫之前要忍受多少彎曲。拐點值是前一行和當前行之間的距離(在繪製電流之前)。如果超過閾值(f.ex. 50-60度),則會產生新的筆畫,讓您在不平滑所有內容的情況下進行艱難的轉動。 – K3N

+0

@daisy當您還記錄筆畫的點時,您還可以提供參數來調整筆劃後的這些閾值。 – K3N