svgでお絵描きサンプルを作ってみました。
線を滑らかにしたかったので色々と思考錯誤してみました。
やりたいこと
- svg上でドラッグしたらpathを追加したい
- pathをベジェにして滑らかにしたい
- 頂点を間引いて滑らかにしたい
svg上でドラッグしたらpathを追加したい
これは、単純にマウスイベントとフラグ管理でドラッグしている時にsvg要素にpath要素を追加していきました。
pathをベジェにして滑らかにしたい
これがけっこう大変でした。
通過点のみで曲線を描くのはcatmull曲線があるのですが、まだpath要素には実装されていないようでした。
ここによると下記のようです。
R,r1 Catmull-Romスプライン曲線を引く
SVG2での新機能.詳細仕様は策定中であり,動作する環境はまだない.
したがって、ベジェ曲線を使うことになるのですが、通過点の他に制御点が必要です。
イラレでよく見かけるアンカーポイントのことです。
通過点から制御点を求める関数を上げている人がいたので、これを拝借しました。
ソースコードのみ抜粋
function catmullRom2bezier(pts){
var cubics = [];
for ( var i = 0, iLen = pts.length; i < iLen ; i++ ){
var p = [
pts[i - 1],
pts[i],
pts[i + 1],
pts[i + 2]
];
if( i === 0 ){
p[0] = {
x: pts[ 0 ].x,
y: pts[ 0 ].y
}
}
if( i === iLen - 2 ){
p[3] = {
x: pts[iLen - 2].x,
y: pts[iLen - 2].y
};
}
if ( i === iLen - 1 ) {
p[2] = {
x: pts[iLen - 1].x,
y: pts[iLen - 1].y
};
p[3] = {
x: pts[iLen - 1].x,
y: pts[iLen - 1].y
};
}
const val = 6;
cubics.push([
(-p[0].x + val * p[1].x + p[2].x) / val,
(-p[0].y + val * p[1].y + p[2].y) / val,
(p[1].x + val * p[2].x - p[3].x) / val,
(p[1].y + val * p[2].y - p[3].y) / val,
p[2].x,
p[2].y
]);
}
return cubics;
}
引数に通過点の座標配列を入れれば、制御点を含んだベジェ曲線用の座標配列を返してくれます。
他にも、npmで公開している人もいました。
実際に使う時はこれをインストールしても良いさそうです。
頂点を間引いて滑らかにしたい
最初、単純に距離が近すぎる点を間引いていたのですが、あまりうまく機能しませんでした。
そこでポリゴンの線を単純化してくれるSimplify.jsを使うことにしました。
(CDNで公開されているものが見つからなかったので、成果物ではコードをコピペして使っています。)
成果物
http://codepen.io/kwst/pen/vgGgqN
上が滑らかにした線、下がフリーハンドで描いた線
割りとなめらかになったかなと思います。
単純なお絵描きですが、線をなめらかにするだけでも結構骨が折れました。
Githubに公開してみました。
SVGCatmullRomSpline