SVG流畅的徒手绘图 [英] SVG smooth freehand drawing

查看:135
本文介绍了SVG流畅的徒手绘图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用原生JS实现了一个路径的徒手画。但正如预期的那样,路径边缘不那么具有攻击性且不平滑所以我可以选择使用simplifyJS来简化点然后重绘路径。但是像

解决方案

以下代码片段通过计算最后鼠标位置的平均值使曲线更平滑。平滑程度取决于保持这些值的缓冲区的大小。您可以尝试下拉列表中提供的不同缓冲区大小。使用12点缓冲区的行为有点类似于您在上面提到的 Mike Bostock的代码段问题。



可以实现更复杂的技术,从存储在缓冲区中的位置获得平滑点(加权平均,线性回归,三次样条平滑等)但是这个简单的平均方法可能足以满足您的需求。



  var strokeWidth = 2; var bufferSize; var svgElement = document.getElementById(svgElement); var rect = svgElement.getBoundingClientRect(); var path = null; var strPath; var buffer = []; //包含鼠标的最后位置cursorsvgElement.addEventListener(mousedown,function(e){bufferSize = document.getElementById(cmbBufferSize)。value; path = document.createElementNS('http://www.w3。 org / 2000 / svg','path'); path.setAttribute(fill,none); path.setAttribute(stroke,#000); path.setAttribute(stroke-width,strokeWidth );; buffer = []; var pt = getMousePosition(e); appendToBuffer(pt); strPath =M+ pt.x ++ pt.y; path.setAttribute(d,strPath); svgElement。 appendChild(path);}); svgElement.addEventListener(mousemove,function(e){if(path){appendToBuffer(getMousePosition(e)); updateSvgPath();}}); svgElement.addEventListener(mouseup, function(){if(path){path = null;}}); var getMousePosition = function(e){return {x:e.pageX  -  rect.left,y:e.pageY  -  rect.top}}; var appendToBuffer = function(pt){buffer.push(pt); while(buffer.length> bufferSize){buffer.shift();从缓冲区中的偏移开始计算平均点getAveragePoint = function(offset){var len = buffer.length; if(len%2 === 1 || len> = bufferSize){var totalX = 0; var totalY = 0; var pt,i; var count = 0; for(i = offset; i< len; i ++){count ++; pt = buffer [i]; totalX + = pt.x; totalY + = pt.y; } return {x:totalX / count,y:totalY / count}} return null;}; var updateSvgPath = function(){var pt = getAveragePoint(0); if(pt){//得到路径的平滑部分,不会改变strPath + =L+ pt.x ++ pt.y; //获取路径的最后一部分(靠近当前鼠标位置)//如果鼠标再次移动,此部分将发生变化var tmpPath =; for(var offset = 2; offset< buffer.length; offset + = 2){pt = getAveragePoint(offset); tmpPath + =L+ pt.x ++ pt.y; } //设置完整的当前路径坐标path.setAttribute(d,strPath + tmpPath); }  

  html,body {padding:0px; margin:0px;} #svgElement {border:1px solid; margin-top:4px; margin-left:4px; cursor:default;}#divSmoothingFactor {position:absolute;左:14px;顶部:12px;}  

 < div id =divSmoothingFactor > < label for =cmbBufferSize>缓冲区大小:< / label> < select id =cmbBufferSize> < option value =1> 1  - 没有平滑< / option> < option value =4> 4  - 锐利曲线< / option> < option value =8selected =selected> 8  - 平滑曲线< / option> < option value =12> 12  - 非常平滑的曲线< / option> < option value =16> 16  - 超平滑曲线< / option> < option value =20> 20  - 超平滑曲线< / option> < / select>< / div>< svg xmlns =http://www.w3.org/2000/svgxmlns:xlink =http://www.w3.org/1999/xlinkversion =1.1id =svgElementx =0pxy =0pxwidth =600pxheight =400pxviewBox =0 0 600 400enable-background =new 0 0 600 400xml: space =preserve>  


I implemented a freehand drawing of a path using native JS. But as expected path edges are little aggressive and not smooth. So I have an option of using simplifyJS to simplify points and then redraw path. But like here, instead of smoothening after drawing, I am trying to find simplified edges while drawing

Here is my code:

    var x0, y0;

    var dragstart = function(event) {
        var that = this;
        var pos = coordinates(event);
        x0 = pos.x;
        y0 = pos.y;
        that.points = [];
    };

    var dragging = function(event) {
        var that = this;
        var xy = coordinates(event);
        var points = that.points;
        var x1 = xy.x, y1 = xy.y, dx = x1 - x0, dy = y1 - y0;
        if (dx * dx + dy * dy > 100) {
            xy = {
                x: x0 = x1, 
                y: y0 = y1
            };
        } else {
            xy = {
                x: x1, 
                y: y1
            };
        }
        points.push(xy);
    };

But it is not working as in the link added above. Still edges are not good. Please help.

解决方案

The following code snippet makes the curve smoother by calculating the average of the last mouse positions. The level of smoothing depends on the size of the buffer in which these values are kept. You can experiment with the different buffer sizes offered in the dropdown list. The behavior with a 12 point buffer is somewhat similar to the Mike Bostock's code snippet that you refer to in the question.

More sophisticated techniques could be implemented to get the smoothed point from the positions stored in the buffer (weighted average, linear regression, cubic spline smoothing, etc.) but this simple average method may be sufficiently accurate for your needs.

var strokeWidth = 2;
var bufferSize;

var svgElement = document.getElementById("svgElement");
var rect = svgElement.getBoundingClientRect();
var path = null;
var strPath;
var buffer = []; // Contains the last positions of the mouse cursor

svgElement.addEventListener("mousedown", function (e) {
    bufferSize = document.getElementById("cmbBufferSize").value;
    path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute("fill", "none");
    path.setAttribute("stroke", "#000");
    path.setAttribute("stroke-width", strokeWidth);
    buffer = [];
    var pt = getMousePosition(e);
    appendToBuffer(pt);
    strPath = "M" + pt.x + " " + pt.y;
    path.setAttribute("d", strPath);
    svgElement.appendChild(path);
});

svgElement.addEventListener("mousemove", function (e) {
    if (path) {
        appendToBuffer(getMousePosition(e));
        updateSvgPath();
    }
});

svgElement.addEventListener("mouseup", function () {
    if (path) {
        path = null;
    }
});

var getMousePosition = function (e) {
    return {
        x: e.pageX - rect.left,
        y: e.pageY - rect.top
    }
};

var appendToBuffer = function (pt) {
    buffer.push(pt);
    while (buffer.length > bufferSize) {
        buffer.shift();
    }
};

// Calculate the average point, starting at offset in the buffer
var getAveragePoint = function (offset) {
    var len = buffer.length;
    if (len % 2 === 1 || len >= bufferSize) {
        var totalX = 0;
        var totalY = 0;
        var pt, i;
        var count = 0;
        for (i = offset; i < len; i++) {
            count++;
            pt = buffer[i];
            totalX += pt.x;
            totalY += pt.y;
        }
        return {
            x: totalX / count,
            y: totalY / count
        }
    }
    return null;
};

var updateSvgPath = function () {
    var pt = getAveragePoint(0);

    if (pt) {
        // Get the smoothed part of the path that will not change
        strPath += " L" + pt.x + " " + pt.y;

        // Get the last part of the path (close to the current mouse position)
        // This part will change if the mouse moves again
        var tmpPath = "";
        for (var offset = 2; offset < buffer.length; offset += 2) {
            pt = getAveragePoint(offset);
            tmpPath += " L" + pt.x + " " + pt.y;
        }

        // Set the complete current path coordinates
        path.setAttribute("d", strPath + tmpPath);
    }
};

html, body
{
    padding: 0px;
    margin: 0px;
}
#svgElement
{
    border: 1px solid;
    margin-top: 4px;
    margin-left: 4px;
    cursor: default;
}
#divSmoothingFactor
{
    position: absolute;
    left: 14px;
    top: 12px;
}

<div id="divSmoothingFactor">
    <label for="cmbBufferSize">Buffer size:</label>
    <select id="cmbBufferSize">
        <option value="1">1 - No smoothing</option>
        <option value="4">4 - Sharp curves</option>
        <option value="8" selected="selected">8 - Smooth curves</option>
        <option value="12">12 - Very smooth curves</option>
        <option value="16">16 - Super smooth curves</option>
        <option value="20">20 - Hyper smooth curves</option>
    </select>
</div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="svgElement" x="0px" y="0px" width="600px" height="400px" viewBox="0 0 600 400" enable-background="new 0 0 600 400" xml:space="preserve">

这篇关于SVG流畅的徒手绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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