[JavaScript] 3次ベジェ曲線をCanvasに引いてみたい | CreateJS, EaselJS

2015/12/22

こんにちは。きんくまです。

今回は3次ベジェ曲線をCanvasに引いてみたいです。

ベジェ曲線っていうのは、そうですIllustratorなんかでパスを操作するときのやつですね。
こんな感じの

bezier_illustrator

実は以前ActionScript3でも書いたことがあるんですが、今回はJavaScriptで書いてみたかったので。

>> [AS3] Illustratorのような3次ベジェ曲線を書く(5年も前の記事ですよ!びっくり)

実際にできたもの

イメージはこんな感じ(下の画像は動きません)

bezier_snapshot

動かせるやつは、これです

赤丸が始点と終点。緑がコントロール(アンカー)ポイントです。

どうやってやるのかというと公式があります。

>> Cubic Bézier curves

これだとよくわかんない!ってことで、もっとわかりやすく解説されているサイトがありました。どうもです。
Hakuhin先生ですね!AS1か2で書かれていました。

>> 3次ベジェ曲線を描画する

なので、これをもとに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();
    });
});
LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る