【问题标题】:Mimick photoshop/painter smooth draw on HTML5 canvas?在 HTML5 画布上模仿 photoshop/painter 平滑绘制?
【发布时间】:2013-07-26 11:35:15
【问题描述】:

正如许多人所知,HTML5 Canvas lineTo() 将在每个角落为您提供一条非常锯齿状的线。此时,更可取的解决方案是实现quadraticCurveTo(),这是生成平滑绘图的非常好的方法。但是,我希望在 HTML5 画布上创建一个流畅而准确的绘图。二次曲线方法在平滑绘制方面效果很好,但它不会遍历所有样本点。换句话说,当我尝试使用二次曲线绘制快速曲线时,有时曲线似乎被应用程序“校正”了。一些线段没有按照我的绘图路径弯曲,而是从其原始路径弯曲以遵循二次曲线。

我的应用程序是为在 HTML5 画布上进行专业绘图而设计的,因此绘图的流畅和精确是非常重要的。我不确定我是否通过尝试将 HTML5 画布与 Photoshop 或任何其他画家应用程序(SAI、painterX 等)置于同一级别来要求不可能

谢谢

【问题讨论】:

  • 不完全清楚你正在尝试什么。您是否使用鼠标跟踪曲线并期望画布绘制鼠标跟踪的确切曲线?
  • 嗯,+1:非常有趣。但是,如果您打算做一个专业的绘图应用程序,恕我直言,您最好选择原生应用程序。浏览器应用程序的唯一优势是交叉兼容性,但这需要付出巨大的代价。

标签: html canvas


【解决方案1】:

您想要的是Cardinal spline,因为基数样条线穿过您绘制的实际点。

注意:要获得专业的结果,您还需要对较短的阈值实施移动平均,同时对较大的阈值使用基数样条曲线,并使用拐点值在尖角处折断线条,这样您就不会平滑整条线条。我不会在这里讨论移动平均线或拐点(也不是锥度),因为它们超出了范围,但展示了一种使用基数样条的方法。

附带说明 - 应用程序似乎修改线条的效果是不可避免的,因为平滑发生在后期。存在在您绘制时平滑的算法,但它们不保留膝盖值,并且在您绘制时线条似乎“摆动”。我猜这是一个偏好问题。

这是一个演示以下内容的小提琴:
ONLINE DEMO

首先需要一些先决条件(我正在使用我的 easyCanvas 库来设置演示中的环境,因为它为我节省了大量工作,但这不是该解决方案工作的必要条件):

  • 我建议您将新笔画绘制到位于主画布之上的单独画布上。
  • 笔画完成后(鼠标向上),将其通过平滑器并将其存储在笔画堆栈中。
  • 然后将平滑线绘制到主线。

当您的点按 X / Y 排列时(即[x1, y1, x2, y2, ... xn, yn]),您可以使用此函数对其进行平滑处理:

张力值(ts,默认 0.5)是平滑曲线的原因。数字越大,曲线越圆。您可以超出正常区间 [0, 1] 进行卷曲。

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

功能(优化):

/// 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;
}

然后在积分存储后从mouseup 事件中调用它:

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

膝盖值的短cmets:

在此上下文中的拐点值是指线中的点之间的角度(作为线段的一部分)大于某个阈值(通常在 45 - 60 度之间)。当发生膝关节时,这些线将被分成一条新线,以便仅使用由它们之间的角度小于阈值的点组成的线(您会在演示中看到由于不使用膝关节而导致的小卷曲)。

对移动平均线的简短评论:

Moving average 通常用于统计目的,但对于绘图应用程序也非常有用。当你有一个由许多点组成的集群时,它们之间的距离很短,样条曲线就不能很好地工作。所以这里可以使用 MA 来平滑点。

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

【讨论】:

  • 由于您构建的画布库丢失,演示已关闭 - 我非常非常有兴趣看到一个移动平均线的示例以实时平滑鼠标噪音 - 那是什么你有演示吗?
猜你喜欢
  • 1970-01-01
  • 2018-09-05
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
相关资源
最近更新 更多