Mimick photoshop / painter平滑绘制在HTML5画布上? [英] Mimick photoshop/painter smooth draw on HTML5 canvas?

查看:277
本文介绍了Mimick photoshop / painter平滑绘制在HTML5画布上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很多人都知道,HTML5 Canvas lineTo()会在每个角落给你一个非常崎岖的线条。在这一点上,更优选的解决方案是实现 quadraticCurveTo(),这是一种非常好的方式来生成平滑的绘图。然而,我希望创建一个平滑,但准确,在画布HTML5上绘制。二次曲线方法在平滑绘制中效果很好,但它不会遍历所有的采样点。换句话说,当我尝试使用二次曲线绘制一个快速曲线时,曲线似乎被应用程序校正。代替跟随我的绘制路径,一些段从其原始路径弯曲以遵循二次曲线。



我的应用程序专用于HTML5画布上的专业绘图,因此绘图既平滑又精确,这是非常重要的。我不知道如果我试图把HTML5画布放在与Photoshop或任何其他画家应用程序(SAI,painterX等)相同的水平,要求不可能。



感谢

解决方案

您想要的是

注意:要获得专业的结果,您将获得一个专业的结果。还需要实现短阈值的移动平均值,同时使用基本样条来获得较大的阈值,并使用拐点值来折断尖角处的线,因此您不会平滑整条线。我不会在这里讨论移动平均线或拐点(也不是渐变),因为这些都在范围之外,但显示了使用基数样条的方法。



以及 - 应用程序似乎修改线的效果是可避免的,因为平滑发生post。存在算法,当你绘制时平滑,但他们不保留拐点值,线条似乎摆动,而你绘制。



这里是一个小提琴演示如下:

在线示范



先决条件(我使用我的 easyCanvas 库在演示中设置环境,因为它节省了我很多工作,但这个是不需要这个解决方案工作):




  • 我建议你绘制新的笔画到一个单独的画布,

  • 当笔触完成时(鼠标向上),将它通过平滑器并将其存储在笔画堆栈中。


  • 当数组中的点按X / Y排序时(即 [x1,y1,x2,y2,... xn,yn] ),那么你可以使用这个函数来平滑它:



    张力值( ts ,默认值0.5)是使曲线平滑。数字越大,曲线变得越圆。

    nos

    code>,或段数)是每个点之间的分辨率。在大多数情况下,你可能不需要高于9-10。但是在较慢的计算机上或需要绘制快速更高值的位置。



    功能(优化):

      ///基本样条由Ken Fyrstenberg,CC属性
    function smoothCurve(pts,ts,nos){

    //使用输入值,或者使用默认值
    ts =(typeof ts ==='undefined')? 0.5:ts;
    nos =(typeof nos ==='undefined')? 16:nos;

    var _pts = [],res = [],// clone array
    x,y,//我们的x,y坐标
    t1x,t2x,t1y,t2y, /张力向量
    c1,c2,c3,c4,//基点
    st,st2,st3,st23,st32,//步骤
    t,i,r = 0,
    len = pts.length,
    pt1,pt2,pt3,pt4;

    _pts.push(pts [0]); // copy 1.指向并插入在开始
    _pts.push(pts [1]);

    _pts = _pts.concat(pts);

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

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

    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 ++){

    //预计算步骤
    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

    return res;
    }

    然后只需从 mouseup 事件在点存储后:

      stroke = smoothCurve(stroke,0.5,16); 
    strokes.push(stroke);

    关于膝盖值的简短注释:



    在该上下文中的拐点值是线中的点(作为线段的一部分)之间的角度大于特定阈值(通常在45-60度之间)的角度。当膝盖出现时,线被分成一条新的线,因此只使用由在它们之间的角度小于阈值的点组成的线(你看到由于不使用膝盖而导致的演示中的小卷曲)。

    关于移动平均线的简短评论:



    移动平均通常用于统计目的,但对绘图应用程序也非常有用。当你有一个很多点的集群,它们之间的短距离样条不工作得很好。所以在这里你可以使用MA来平滑点。



    也可以使用点减少算法,如 Ramer / Douglas / Peucker 一个,但它有更多的用于存储目的,以减少数据量。


    As many people knew, HTML5 Canvas lineTo() is going to give you a very jaggy line at each corner. At this point, a more preferable solution would be to implement quadraticCurveTo(), which is a very great way to generate smooth drawing. However, I desire to create a smooth, yet accurate, draw on canvas HTML5. Quadratic curve approach works well in smoothing out the draw, but it does not go through all the sample points. In other word, when I try to draw a quick curve using quadratic curve, sometime the curve appears to be "corrected" by the application. Instead of following my drawing path, some of the segment is curved out of its original path to follow a quadratic curve.

    My application is intended for a professional drawing on HTML5 canvas, so it is very crucial for the drawing to be both smooth and precise. I am not sure if I am asking for the impossible by trying to put HTML5 canvas on the same level as photoshop or any other painter applications (SAI, painterX, etc.)

    Thanks

    解决方案

    What you want is a Cardinal spline as cardinal splines goes through the actual points you draw.

    Note: to get a professional result you will also need to implement moving average for short thresholds while using cardinal splines for larger thresholds and using knee values to break the lines at sharp corner so you don't smooth the entire line. I won't be addressing the moving average or knee here (nor taper) as these are outside the scope, but show a way to use cardinal spline.

    A side note as well - the effect that the app seem to modify the line is in-avoidable as the smoothing happens post. There exists algorithms that smooth while you draw but they do not preserve knee values and the lines seem to "wobble" while you draw. It's a matter of preference I guess.

    Here is an fiddle to demonstrate the following:
    ONLINE DEMO

    First some prerequisites (I am using my easyCanvas library to setup the environment in the demo as it saves me a lot of work, but this is not a requirement for this solution to work):

    • I recommend you to draw the new stroke to a separate canvas that is on top of the main one.
    • When stroke is finished (mouse up) pass it through the smoother and store it in the stroke stack.
    • Then draw the smoothed line to the main.

    When you have the points in an array order by X / Y (ie [x1, y1, x2, y2, ... xn, yn]) then you can use this function to smooth it:

    The tension value (ts, default 0.5) is what smooths the curve. The higher number the more round the curve becomes. You can go outside the normal interval [0, 1] to make curls.

    The segment (nos, or number-of-segments) is the resolution between each point. In most cases you will probably not need higher than 9-10. But on slower computers or where you draw fast higher values is needed.

    The function (optimized):

    /// cardinal spline by Ken Fyrstenberg, CC-attribute
    function smoothCurve(pts, ts, nos) {
    
        // use input value if provided, or use a default value   
        ts = (typeof ts === 'undefined') ? 0.5 : ts;
        nos = (typeof nos === '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, r = 0,
            len = 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[len - 2]);    //copy last point and append
        _pts.push(pts[len - 1]);
    
        for (i = 2; i < len; i+=2) {
    
            pt1 = _pts[i];
            pt2 = _pts[i+1];
            pt3 = _pts[i+2];
            pt4 = _pts[i+3];
    
            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
    
        return res;
    }
    

    Then simply call it from the mouseup event after the points has been stored:

    stroke = smoothCurve(stroke, 0.5, 16);
    strokes.push(stroke);
    

    Short comments on knee values:

    A knee value in this context is where the angle between points (as part of a line segment) in the line is greater than a certain threshold (typically between 45 - 60 degrees). When a knee occur the lines is broken into a new line so that only the line consisting of points with a lesser angle than threshold between them are used (you see the small curls in the demo as a result of not using knees).

    Short comment on moving average:

    Moving average is typically used for statistical purposes, but is very useful for drawing applications as well. When you have a cluster of many points with a short distance between them splines doesn't work very well. So here you can use MA to smooth the points.

    There is also point reduction algorithms that can be used such as the Ramer/Douglas/Peucker one, but it has more use for storage purposes to reduce amount of data.

    这篇关于Mimick photoshop / painter平滑绘制在HTML5画布上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆