Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/html/78.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 平滑画布线和光标起点_Javascript_Html_Canvas - Fatal编程技术网

Javascript 平滑画布线和光标起点

Javascript 平滑画布线和光标起点,javascript,html,canvas,Javascript,Html,Canvas,我正在尝试创建一个canvas web应用程序,但存在两个主要问题。我想让钢笔工具画得更流畅。其次,每次我清除草图并再次开始绘制时,该线从指向光标/鼠标的不同点开始 以下是我的绘图工具的javascript: var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); var radius = 10; var dragging = false; canvas.width = wi

我正在尝试创建一个canvas web应用程序,但存在两个主要问题。我想让钢笔工具画得更流畅。其次,每次我清除草图并再次开始绘制时,该线从指向光标/鼠标的不同点开始

以下是我的绘图工具的javascript:

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var radius = 10;
var dragging = false;

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

context.lineWidth = radius*2;

var putPoint = function(e){
if(dragging){
context.lineTo(e.clientX, e.clientY);
context.stroke();
context.beginPath();
context.arc(e.clientX, e.clientY, radius, 0, Math.PI*2);
context.fill();
context.beginPath();
context.moveTo(e.clientX, e.clientY);
}}

var engage = function(){
dragging = true;
}

var disengage = function(){
dragging = false;
}

canvas.addEventListener('mousedown', engage);
canvas.addEventListener('mousemove', putPoint);
canvas.addEventListener('mouseup', disengage);
这就是我清理草图的方式:

// JavaScript Document

  // bind event handler to clear button
  document.getElementById('clear').addEventListener('click', function() {
    context.clearRect(0, 0, canvas.width, canvas.height);
  }, false);
可以在以下位置查看实时预览:

谢谢你的帮助

我想让钢笔工具画得更流畅

使用可以使用二次曲线而不是直线:

ctx.quadraticCurveTo(cpx, cpy, x, y);

示例:

首先是线路起点错误的问题。您忘记完成创建的路径。您有
beginPath
moveTo
,但您将其挂起。当鼠标按钮打开时,您需要调用一次
stroke

平滑。

线条平滑是一件非常复杂的事情,许多专业绘图应用程序都使用各种解决方案来解决这个问题。似乎并没有一种商定的方法。最大的问题是。。如何平滑线条但不破坏所需线条?你是如何快速完成的呢

在这里,我提出了一个两阶段的过程

降低线路复杂性

第一步,降低线路复杂度。采样鼠标会让位于许多点。所以我需要减少分数,但不要丢失任何细节

我使用算法。它速度快,在降低线条的复杂度(点数)方面做得很好。下面你可以找到我的算法实现。这不是最好的,因为它可以做一些优化。您很可能会在其他语言中找到它,并将其移植到javascript

它使用递归函数来降低基于线段长度和角度的复杂性。其核心是两条线段的点积,这是一种快速确定两条线段之间角度的方法。有关更多详细信息,请参阅上面提供的链接

// Line simplification based on
// the Ramer–Douglas–Peucker algorithm
// referance https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
// points: are and array of arrays consisting of [[x,y],[x,y],...,[x,y]]
// length: is in pixels and is the square of the actual distance.
// returns array of points of the same form as the input argument points.
var simplifyLineRDP = function(points, length) {
    var simplify = function(start, end) { // recursive simplifies points from start to end
        var maxDist, index, i, xx , yy, dx, dy, ddx, ddy, p1, p2, p, t, dist, dist1;
        p1 = points[start];
        p2 = points[end];   
        xx = p1[0];
        yy = p1[1];
        ddx = p2[0] - xx;
        ddy = p2[1] - yy;
        dist1 = (ddx * ddx + ddy * ddy);
        maxDist = length;
        for (var i = start + 1; i < end; i++) {
            p = points[i];
            if (ddx !== 0 || ddy !== 0) {
               // dot product
                t = ((p[0] - xx) * ddx + (p[1] - yy) * ddy) / dist1;
                if (t > 1) {
                    dx = p[0] - p2[0];
                    dy = p[1] - p2[1];
                } else 
                if (t > 0) {
                    dx = p[0] - (xx + ddx * t);
                    dy = p[1] - (yy + ddy * t);
                } else {
                    dx = p[0] - xx;
                    dy = p[1] - yy;
                }
            }else{
                dx = p[0] - xx;
                dy = p[1] - yy;
            }
            dist = dx * dx + dy * dy 
            if (dist > maxDist) {
                index = i;
                maxDist = dist;
            }
        }

        if (maxDist > length) { // continue simplification while maxDist > length
            if (index - start > 1){
                simplify(start, index);
            }
            newLine.push(points[index]);
            if (end - index > 1){
                simplify(index, end);
            }
        }
    }    
    var end = points.length - 1;
    var newLine = [points[0]];
    simplify(0, end);
    newLine.push(points[end]);
    return newLine;
}
// This is my own smoothing method The blindman`s smoother
// It creates a set of bezier control points either 2nd order or third order 
// bezier curves.
// points: list of points [[x,y],[x,y],...,[x,y]]
// cornerThres: when to smooth corners and represents the angle between to lines. 
//     When the angle is smaller than the cornerThres then smooth.
// match: if true then the control points will be balanced.
// Function will make a copy of the points
// returns [[x,y],[x,y,bx,by],[x,y,b1x,b1y,b2x,b2y],.....] with x and y line points
// bx,by control points for 2nd order bezier and b1x,b1y,b2x,b2y the control
// points for 3rd order bezier. These are mixed as needed. Test the length of
// each point array to work out which bezier if any to use.

var smoothLine = function(points,cornerThres,match){  // adds bezier control points at points if lines have angle less than thres
    var  p1, p2, p3, dist1, dist2, x, y, endP, len, angle, i, newPoints, aLen, closed, bal, cont1, nx1, nx2, ny1, ny2, np;
    function dot(x, y, xx, yy) {  // get do product
        // dist1,dist2,nx1,nx2,ny1,ny2 are the length and  normals and used outside function
        // normalise both vectors
        dist1 = Math.sqrt(x * x + y * y); // get length
        if (dist1  > 0) {  // normalise
            nx1 = x / dist1 ;
            ny1 = y / dist1 ;
        }else {
            nx1 = 1;  // need to have something so this will do as good as anything
            ny1 = 0;
        }
        dist2  = Math.sqrt(xx * xx + yy * yy);
        if (dist2  > 0) {
            nx2 = xx / dist2;
            ny2 = yy / dist2;
        }else {
            nx2 = 1;
            ny2 = 0;
        }
       return Math.acos(nx1 * nx2 + ny1 * ny2 ); // dot product
    }
    newPoints = []; // array for new points
    aLen = points.length;
    if(aLen <= 2){  // nothing to if line too short
        for(i = 0; i < aLen; i ++){  // ensure that the points are copied          
            newPoints.push([points[i][0],points[i][1]]);
        }
        return newPoints;
    }
    p1 = points[0];
    endP =points[aLen-1];
    i = 0;  // start from second poitn if line not closed
    closed = false;
    len = Math.hypot(p1[0]- endP[0], p1[1]-endP[1]);
    if(len < Math.SQRT2){  // end points are the same. Join them in coordinate space
        endP =  p1;
        i = 0;             // start from first point if line closed
        p1 = points[aLen-2];
        closed = true;
    }       
    newPoints.push([points[i][0],points[i][1]])
    for(; i < aLen-1; i++){
        p2 = points[i];
        p3 = points[i + 1];
        angle = Math.abs(dot(p2[0] - p1[0], p2[1] - p1[1], p3[0] - p2[0], p3[1] - p2[1]));
        if(dist1 !== 0){  // dist1 and dist2 come from dot function
            if( angle < cornerThres*3.14){ // bend it if angle between lines is small
                  if(match){
                      dist1 = Math.min(dist1,dist2);
                      dist2 = dist1;
                  }
                  // use the two normalized vectors along the lines to create the tangent vector
                  x = (nx1 + nx2) / 2;  
                  y = (ny1 + ny2) / 2;
                  len = Math.sqrt(x * x + y * y);  // normalise the tangent
                  if(len === 0){
                      newPoints.push([p2[0],p2[1]]);                                  
                  }else{
                      x /= len;
                      y /= len;
                      if(newPoints.length > 0){
                          var np = newPoints[newPoints.length-1];
                          np.push(p2[0]-x*dist1*0.25);
                          np.push(p2[1]-y*dist1*0.25);
                      }
                      newPoints.push([  // create the new point with the new bezier control points.
                            p2[0],
                            p2[1],
                            p2[0]+x*dist2*0.25,
                            p2[1]+y*dist2*0.25
                      ]);
                  }
            }else{
                newPoints.push([p2[0],p2[1]]);            
            }
        }
        p1 = p2;
    }  
    if(closed){ // if closed then copy first point to last.
        p1 = [];
        for(i = 0; i < newPoints[0].length; i++){
            p1.push(newPoints[0][i]);
        }
        newPoints.push(p1);
    }else{
        newPoints.push([points[points.length-1][0],points[points.length-1][1]]);      
    }
    return newPoints;    
}
var drawSmoothedLine = function(line){
    var i,p;
    ctx.beginPath()
    ctx.moveTo(line[0][0],line[0][1])
    for(i = 0; i < line.length-1; i++){
       p = line[i];
       p1 = line[i+1]
       if(p.length === 2){ // linear 
            ctx.lineTo(p[0],p[1])
       }else
       if(p.length === 4){ // bezier 2nd order
           ctx.quadraticCurveTo(p[2],p[3],p1[0],p1[1]);
       }else{              // bezier 3rd order
           ctx.bezierCurveTo(p[2],p[3],p[4],p[5],p1[0],p1[1]);
       }
    }
    if(p.length === 2){
        ctx.lineTo(p1[0],p1[1])
    }
    ctx.stroke();
}