2013-06-24 2 views
3

나는 간단한 캔버스 드로잉 응용 프로그램을 가지고 있습니다. ,캔버스 - 때때로 lineTo()가있는 획이 모서리를 생성합니다.

enter image description here

내가 최신 파이어 폭스를 사용하고 연결이 불량하기 때문에 그것을 또는 내 컴퓨터가 buisy입니다 다음에 lineTo() 명령이 적은 좌표의 라인을 생산하고 도면이 많은 가장자리가 때때로? 해결 방법이 있습니까? 그것은 빨리 그것을 할 수있는 응답 것 JS FIDDLE

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

mousemove 포인트 (위의 스크린 샷에 표시된 정점)를 사용하여 실험 해 볼 수 있습니다. 그러나 직선보다는 곡선을 그리는 것이 좋습니다. 직선을 사용하면 브라우저가 빨리 캔버스를 다시 렌더링 할 수 있습니다 (브라우저 또는 OS가 다른 작업을 수행하는 경우 가끔 발생하는 딸꾹질). –

+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"스로틀에서 첫 번째 k 요소 대신 모든 queue.length/k 요소를 그려야합니다. 좋은 대답! – markE

+0

@markE 한계에 대한 생각은 마우스 반응을 막지 않으려 고 시도하는 것이 었으므로 고정 된 최대 드로잉 양을 갖는 것이 좋을 것 같았습니다. 나는 그것이 좀 더 많은 수학을 포함 할지라도, 그물 선 길이를 계산할 수 있다고 생각합니다. – Pointy

2

이 같은 라인을 부드럽게 추기경 스플라인을 사용할 수 있습니다 @Pointy 이미 설명한 바와 같이

enter image description here

원인으로 인해 브라우저가 이벤트에 응답 할 수 있습니다 얼마나 빨리이다 (mousemove). 더 낮은 수준이기 때문에 나중에 이것을 해결하는 데 도움이되는 Pointer Lock API이라는 API가 있습니다. 그러나 지금은 알고리즘을 사용하여 세그먼트 화 된 라인을 부드럽게 처리해야합니다.

스무딩 외에도 세부 스무딩, 포인트 감소, 테이퍼 및 결과를 향상시키는 데 적용 할 수있는 다른 사항이 있습니다.

하지만이 특별한 경우 캔버스의 확장 기능으로 다음과 같은 기능을 사용할 수 있습니다. 그냥 전화 :

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

배열은 x와 y 포인트 [x1, y1, x2, y2, ... xn, yn 같은 명령이 포함되어 있습니다.

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

@ 데이지 일반적으로 이것은 무릎 값을 계산하여 처리됩니다 - "새로운"스트로크를 고려하기 전에 얼마나 많이 구부릴 것입니까? 무릎 값은 이전 라인과 현재 라인 사이의 각도입니다 (현재가 그려지기 전입니다). 임계 값 (f.ex. 50-60도) 이상이면 새로운 획이 생성되어 모든 것을 부드럽게하지 않고 힘차게 돌아가도록 할 수 있습니다. – K3N

+0

@daisy 스트로크에 대한 포인트를 기록 할 때 스트로크 후 이러한 임계 값을 조정하는 매개 변수를 제공 할 수도 있습니다. – K3N

관련 문제