【问题标题】:Snap to angle freehand pen/pencil tool in Canvas在 Canvas 中对齐角度手绘笔/铅笔工具
【发布时间】:2016-03-03 23:44:58
【问题描述】:

我目前在做什么

我正在尝试在 HTML5 Canvas 中构建一个手绘铅笔工具(使用 Paper.js 作为 Canvas 包装器)。

我想做什么

我想允许用户在绘图时绘制直线(例如按下 Shift 时)。理想情况下,这条直线应该“捕捉”到 8 向捕捉“半径”。

我尝试了什么(这不是我所需要的)。

我尝试了一个非常简单的解决方案,将鼠标点捕捉到一个接近圆形的点。这在某种程度上工作得很好,但它并不完全是一个捕捉角度的工具,它更像是一个捕捉到一个不可见的网格类型的工具。

  mousedrag: function(event) {
    var snapped = {x: snap(event.point.x), y: snap(event.point.y)};

    // add "snapped" drag point
    path.add(snapped.x, snapped.y);
  }

  // convert a number to a rounded snap
  function snap(x, div) { 
    return Math.round(x/div)*div; 
  };

这是我目前正在做的交互式Sketch(按住 Shift 键捕捉到网格,释放恢复常规手绘)


谁能告诉我如何继续捕捉角度而不是网格?

注意事项:

  • 虽然我使用的是 Canvas/Paper.js,但我知道该问题的解决方案与我使用的渲染技术无关,因此任何基于 JS 的解决方案(SVG 或 Canvas、包装器或没有)应该给我一些关于如何进行的良好基础。

  • 我感觉解决方案可能涉及Math.atan() 或类似的东西,而不是我的解决方案,我捕捉到一个圆形的Math.round 点。

【问题讨论】:

    标签: javascript canvas paperjs


    【解决方案1】:

    如果您只更改代码中的一两行,get something 可能会接近您想要的:

    // ...
    
    function onMouseDrag(event) {
        // if shift is down we transform the mousepoint
        // to a "snapped point", else add the mousepoint as it is.
        if(shiftDown)
        {
            var snapPoint = new Point(snap(event.point.x), snap(event.point.y));
            myPath.lastSegment.point = snapPoint;
        }
        else 
        {
            var snapPoint = event.point;
            myPath.add(snapPoint);
        }
    }
    
    // ...
    

    您可以轻松modify this to snap the angle 代替职位。

    function onMouseDrag(event) {
        // if shift is down we transform the mousepoint
        // to a "snapped point", else add the mousepoint as it is.
        if(shiftDown)
        {
            var vector = event.point - myPath.lastSegment.previous.point;
            vector.angle = Math.round(vector.angle/angleSnap)*angleSnap;
            myPath.lastSegment.point = myPath.lastSegment.previous.point + vector;
        }
        else 
        {
            var snapPoint = event.point;
            myPath.add(snapPoint);
        }
    }
    

    【讨论】:

    • 看起来很整洁 - 在mouseDown 处理程序中添加myPath.add(event.point) 也将允许开始 绘制直线。介意我编辑您的答案以添加此内容吗?
    • @NicholasKyriakides 继续!
    【解决方案2】:

    将绘图限制为直线的基础相当简单——双关语:-)

    当用户拖动鼠标时,计算一条假想线上以所需角度延伸的最近点。

    以下是如何计算最接近鼠标的直线上的点:

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var cw=canvas.width;
    var ch=canvas.height;
    function reOffset(){
      var BB=canvas.getBoundingClientRect();
      offsetX=BB.left;
      offsetY=BB.top;        
    }
    var offsetX,offsetY;
    reOffset();
    window.onscroll=function(e){ reOffset(); }
    window.onresize=function(e){ reOffset(); }
    
    var lineStart={x:50,y:50};
    var lineEnd={x:250,y:250};
    var cr=15;
    
    draw({x:-20,y:-20},'green');
    
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    
    //////////////////////////////////
    
    function draw(pt,fill){
      ctx.clearRect(0,0,cw,ch);
      ctx.beginPath();
      ctx.moveTo(lineStart.x,lineStart.y);
      ctx.lineTo(lineEnd.x,lineEnd.y);
      ctx.strokeStyle='black';
      ctx.stroke();
      ctx.beginPath();
      ctx.arc(pt.x,pt.y,5,0,Math.PI*2);
      ctx.closePath();
      ctx.fillStyle=fill;
      ctx.fill();
    }
    
    // Find line segment point closest to source point
    // [x0,y0] to [x1,y1] define a line segment
    // [cx,cy] is source point 
    function calcClosestPtOnSegment(x0,y0,x1,y1,cx,cy,cr){
    
      // calc delta distance: source point to line start
      var dx=cx-x0;
      var dy=cy-y0;
    
      // calc delta distance: line start to end
      var dxx=x1-x0;
      var dyy=y1-y0;
    
      // Calc position on line normalized between 0.00 & 1.00
      // == dot product divided by delta line distances squared
      var t=(dx*dxx+dy*dyy)/(dxx*dxx+dyy*dyy);
    
      // calc nearest pt on line
      var x=x0+dxx*t;
      var y=y0+dyy*t;
    
      // clamp results to being on the segment
      if(t<0){x=x0;y=y0;}
      if(t>1){x=x1;y=y1;}
    
      return({ 
        x:x, y:y, 
        isColliding:((cx-x)*(cx-x)+(cy-y)*(cy-y)) < cr*cr,
        isOnSegment:(t>=0 && t<=1),
      });
    }
    
    function handleMouseMove(e){
      // tell the browser we're handling this event
      e.preventDefault();
      e.stopPropagation();
    
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
    
      var p=calcClosestPtOnSegment(
        lineStart.x,lineStart.y,lineEnd.x,lineEnd.y,
        mouseX,mouseY,cr);
      var fill=(p.isOnSegment)?'green':'red';
    
      draw(p,fill);
    
      ctx.beginPath();
      ctx.arc(mouseX,mouseY,cr,0,Math.PI*2);
      ctx.closePath();
      ctx.strokeStyle=p.isColliding?'green':'blue';
      ctx.stroke();
    }
    body{ background-color: ivory; }
    #canvas{border:1px solid red; }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <h4>Move mouse to see closest point on angled line segment.<br>Closest point is green (or red if beyond segment) </h4>
    <canvas id="canvas" width=300 height=300></canvas>

    【讨论】:

      猜你喜欢
      • 2012-04-24
      • 1970-01-01
      • 2023-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多