支持可变粗细笔划

Support for variable thickness stroke

是否有支持可变粗细路径/笔画的矢量图形标准,例如来自手写笔输入:

一定程度的平滑可能是可以接受的。我假设最好的存储方式是将其存储为常规路径(例如 ),然后在路径中的各个点处逐点稀疏厚度信息,它们之间具有梯度。

我看过SVG,但似乎没有支持它的元素。是否有任何矢量图形标准可以?

不幸的是,这已经被提议但没有进一步发展为 SVG 标准:

https://www.w3.org/Graphics/SVG/WG/wiki/Proposals/Variable_width_stroke

最好的办法是根据所需的内部曲线和描边宽度生成自己的轮廓曲线。

Adobe Illustrator 在使用宽度工具时会执行此操作,Inkscape 也有一个 feature 会执行此操作。

所以从技术上回答你的问题,.ai 文件格式确实保存了笔画宽度信息,但当导出到 SVG 时,它是一个带填充的封闭路径。

当前实现的单一路径不允许可变厚度。有一个 W3.org proposal for SVG standard,但目前还没有在纯 SVG 中实现。

“厚度可变的路径”有多种实现方式,但它依赖于 svg 对象(例如,多路径)和 C++ 或 javascript 函数。

  • PowerStroke is an implementation of such idea of a variable thickness stroke in Inkscape. A good entry to the source in c++ is here.

SVG还有其他实现javascript,依赖多路径:

  • Tubefy,一组很少的js函数,原理是基于线性插值。 Tubefy有几种实现方式,最简单的是:

     $ = function (id) { return typeof id=='string'?document.getElementById(id):id };
     var root = document.rootElement;
    
     function lerp(p, a, b) { return Number(a)+(b-a)*p; }
    
     function lerpA(p, a, b) { var c=[];
         for(var i=0; i<a.length; i++) c[i]=lerp(p, a[i], b[i]);
         return c;
     }
    
     function toCss(a){ 
         for(var i=0; i<a.length; i++) a[i]=Math.round(a[i]);
         return "rgb(" + a.join() + ")";
     }
    
  • Variable Stroke-Width,基于多个路径,这可能是满足您需求的最佳答案。

其中一个例子,js函数使用了Tubefy,直接在svg文件中实现:

<script>//<![CDATA[
    var op=1, op1=1;

    function vsw0(p0, n, g){    p0=$(p0);
        var SW=p0.getAttribute('stroke-widths').replace(/ /g,'').split(',');
        var T=p0.getTotalLength();
        var n_1=n-1,  dt=T/n, dash=(dt+1)+','+T;
        p0.setAttribute('stroke-dasharray', dash);

        for(var i=0; i<n; i++){ p=i/n_1;
            var sw=lerp(p, SW[0], SW[1]); // current stroke width
            var off=-i*dt; // current dash offset
            var c=toCss(lerpA(p, [255,0,0], [255,255,0])); // curr color

            var newP=p0.cloneNode(true);
            newP.setAttribute('style', 'stroke-width:'+sw+';stroke-dashoffset:'+off+';stroke:'+c);
            $(g).appendChild(newP);
        }
    }

    function f(){ $('abg').setAttribute('stroke', $('bg').getAttribute('fill')) }

//]]></script>
</svg>