こんにちは。きんくまです。
今回は3次ベジェ曲線をCanvasに引いてみたいです。
ベジェ曲線っていうのは、そうですIllustratorなんかでパスを操作するときのやつですね。
こんな感じの
実は以前ActionScript3でも書いたことがあるんですが、今回はJavaScriptで書いてみたかったので。
>> [AS3] Illustratorのような3次ベジェ曲線を書く(5年も前の記事ですよ!びっくり)
実際にできたもの
イメージはこんな感じ(下の画像は動きません)
動かせるやつは、これです
赤丸が始点と終点。緑がコントロール(アンカー)ポイントです。
どうやってやるのかというと公式があります。
これだとよくわかんない!ってことで、もっとわかりやすく解説されているサイトがありました。どうもです。
Hakuhin先生ですね!AS1か2で書かれていました。
なので、これをもとにJavaScriptにしてみます。
アルゴリズムを手短にまとめると、こんな感じです。
・あるtの値があるとします(0 <= t <= 1)
・そのtと始点, アンカー1, アンカー2, 終点の4つの座標があれば、公式からそのときのベジェ曲線の座標が求められます
・tの値を0から1の間で適当に変化させて、ベジェ曲線の点をいくつかもとめます。
・最後にそれをつなぎ合わせれば、短い直線が曲線に見えるようになるっていうことです。
tの分割数が少なければカクカクにみえて、分割数が多ければなめらかな曲線が得られます。
てっく煮さんがずっと前にくわしく書かれてました。
>> ベジエ曲線の仕組み (1) – 昔話
で、今回の描画ライブラリはCreateJSの中のEaselJSを使ってます。
ソースコード
久しぶりにTypeScriptじゃなくて生のJavaScriptで書いてみました。
/** * =============================== * Bezier curve sample * =============================== */ function ControlCircle(colorStr, radius){ this.color = colorStr; this.radius = radius; this.position = new createjs.Point(0,0); this.view = new createjs.Shape(); this.view.alpha = 0.5; var self = this; this.view.on("pressmove", function(e){ self.position.x = e.stageX; self.position.y = e.stageY; }); this.view.on("pressup", function(e){ }); } ControlCircle.prototype.render = function(){ var g = this.view.graphics; g.clear(); g.beginFill(this.color); g.drawCircle(this.position.x, this.position.y, this.radius); g.endFill(); }; ControlCircle.prototype.setPosition = function(x, y) { this.position.x = x; this.position.y = y; }; //http://hakuhin.jp/as/curve.html#CURVE_02 function calcCubicBezierPoint(p0, p1, p2, p3, t){ var resultPoint = {x:0, y:0}; var t2 = 1 - t; var v = t2 * t2 * t2; resultPoint.x += v * p0.x; resultPoint.y += v * p0.y; v = 3 * t2 * t2 * t; resultPoint.x += v * p1.x; resultPoint.y += v * p1.y; v = 3 * t * t * t2; resultPoint.x += v * p2.x; resultPoint.y += v * p2.y; v = t * t * t; resultPoint.x += v * p3.x; resultPoint.y += v * p3.y; return resultPoint; } function drawCubicBezier(context, startPoint, anchorPoint1, anchorPoint2, endPoint){ var devideNum = 40.0; //分割数 多いほどなめらか var dt = 1.0 / devideNum; var drawPoint; context.clear(); context.setStrokeStyle(2).beginStroke("#000"); var t; for(t = 0; t < 1; t = t + dt){ drawPoint = calcCubicBezierPoint(startPoint, anchorPoint1, anchorPoint2, endPoint, t); if(t == 0){ context.moveTo(drawPoint.x, drawPoint.y); }else{ context.lineTo(drawPoint.x, drawPoint.y); } } drawPoint = calcCubicBezierPoint(startPoint, anchorPoint1, anchorPoint2, endPoint, 1); context.lineTo(drawPoint.x, drawPoint.y); } function startBezierCurveSample(){ var stage = new createjs.Stage("mycanvas"); var edge1 = new ControlCircle("#f00", 20); var edge2 = new ControlCircle("#f00",20); var anchor1 = new ControlCircle("#0f0", 15); var anchor2 = new ControlCircle("#0f0", 15); var bezierShape = new createjs.Shape(); stage.addChild(bezierShape); edge1.setPosition(100, 100); stage.addChild(edge1.view); edge2.setPosition(500, 400); stage.addChild(edge2.view); anchor1.setPosition(300, 100); stage.addChild(anchor1.view); anchor2.setPosition(300, 400); stage.addChild(anchor2.view); createjs.Ticker.on("tick", handleTick); createjs.Ticker.framerate = 30; function handleTick(e) { edge1.render(); edge2.render(); anchor1.render(); anchor2.render(); drawCubicBezier(bezierShape.graphics, edge1.position, anchor1.position, anchor2.position, edge2.position); stage.update(); } } window.addEventListener("load", function() { var cover = document.getElementById('container_cover'); cover.addEventListener('click', function() { cover.setAttribute('class', 'hide'); startBezierCurveSample(); }); });
■ 自作iPhoneアプリ 好評発売中!
・フォルメモ - シンプルなフォルダつきメモ帳
・ジッピー電卓 - 消費税や割引もサクサク計算!
■ LINEスタンプ作りました!
毎日使える。とぼけたウサギ