アプリ開発やサイト制作のスマホ端末実機検証・テスト-Remote TestKit

Raphaël入門2 (Raphaëlを使って動きのある図を作る)

Raphaëlを使って、動きのある画面を作っていきます。

概要

前回のRaphaël入門では、Raphaëlの初歩的な使い方を説明しましたが、今回はRaphaëlを使って、動きのある画面を作っていきます。

図形の移動とサイズ変更

作成した図形を移動させる方法について説明します。
SVGでは、タグ内の属性値を変更することで、描画した内容を変更することができます。
しかし、SVGデータの値を変更することは、複雑な計算を行う必要があるため、容易ではありません。

そこで、Raphaëlに用意されているメソッドを利用し、描画を変更します。
はじめは図形の移動とサイズ変更を行います。

まずは、クリックすると、X座標に10、Y座標に5、移動する図形を作成します。
X座標、Y座標の値を直接変更することもできますが、その方法では複雑な計算を自分で行う必要があるため、手間がかかります。
そこで、図形の移動は、translateメソッドを利用します。

試しに、円をクリックすると移動するようにしてみます。

//描画領域の設定
var paper = Raphael("svg", 800, 200);
//円を描画する。
var circle = paper.circle(100, 100, 50);
circle.attr({
    'fill' : '#87CEFA',
    'stroke' : '#1E90FF',
    'stroke-width' : 4
});
circle.click(function(event){
    // X座標に10、Y座標に5移動させる。
    this.translate(10, 5);
}

translateメソッドは、Raphaëlで用意されている図形以外にも、
三角形や直線など、パスを利用して作成した図形に対しても、利用することができます。

// 三角形を描画する。
var triangle = paper.path("M250,10 L200,100 L300,100 Z");
triangle.attr({
    'fill' : '#F08080',
    'stroke' : '#CD5C5C',
    'stroke-width' : 4
});
triangle.click(function(event){
    // X座標に10、Y座標に5移動させる。
    this.translate(10, 5);
});

図形を移動させる

動作するサンプルはこちらで確認することができます。

次に作成した図形のサイズを変更してみます。
図形のサイズの変更は、幅(width)や高さ(height)を直接変更することもできますが、幅や高さを変更する場合、
大きさは自分でロジックを書き、計算する必要があります。

そこで、scaleメソッドを利用します。

circle.click(function(event){
    // 幅に1.2倍に、高さを0.8倍にさせる。
    this.scale(1.2, 0.8);
});

図形のサイズを変更する

動作するサンプルはこちらで確認することができます。

これで、図形を移動させたり、サイズ変更することができるようになりました。

図形をドラッグして移動させる

クリックすると、一定の大きさで移動、サイズ変更する方法を説明しました。
今度は、ドラッグに合わせて、図形を移動させてみます。

図形をドラッグして移動させる

動作するサンプルはこちらで確認することができます。

まずは、長方形の生成する関数を作成します。

//描画領域の設定
function create(paper, target) {
    var rect = paper.rect(target.cx, target.cy, target.width, target.height);
    rect.attr({
        "fill" : target.fill,
        "stroke" : target.stroke,
        "stroke-width" : target.stroke_width,
    });
    // ID要素をタグに追加する。
    rect.node.id = target.id;
    rect.drag(dragOnMove, dragOnStart, dragOnEnd);
    return rect;
};

rect.node.idを定義することで、ユーザが任意の属性(ここではid)をSVGタグに追加することができます。
この後で説明しますが、ドラッグを開始した時に対象の図形(SVGオブジェクト)をeventから取得するために必要になります。

呼び出し元では次のようにすることで図形を画面に描画できます。

var paper = Raphael("svg", 800, 300);
var objectMap = []; // 作成したオブジェクトを保持する配列
var selectId; // 選択中のオブジェクトのIDを表す。
var id = 0; // 長方形のID

var rectangle = create(paper, {
    cx : 10,
    cy : 10,
    width : 100,
    height : 100,
    objectType : "rectangle",
    stroke : "#1E90FF",
    stroke_width : 4,
    fill : "#87CEFA",
    id : id
});
// 作成した長方形を格納する。
objectMap[id] = rectangle;

それでは、ドラッグ&ドロップにより、移動できるようにしましょう。
移動時のイベントは、dragメソッドを利用します。
引数は順に、移動時のイベントを処理する関数、移動開始時のイベントを処理する関数、移動終了時のイベントを処理する関数となっています。
それぞれのイベントを処理する関数を定義します。

function dragOnStart(x, y, event) {
    //選択された図形オブジェクトのidを保存する
    selectId = event.target.id;
    var rect = objectMap[selectId];
    rect.node.dx = 0;
    rect.node.dy = 0;
};

引数は順に、X座標、Y座標、イベントオブジェクトなっています。 マウスが図形から離れてしまうと、イベントオブジェクトからSVGタグが取れない場合があるため、選択中のオブジェクトを移動開始時に保持しておきます。

function dragOnMove(dx, dy, x, y, event) {
    var rect = objectMap[selectId];
    var diffX = dx - rect.node.dx;
    var diffY = dy - rect.node.dy;

    // 選択中のオブジェクトを移動させる。
    rect.translate(diffX, diffY);
    // 移動量を保持する。
    rect.node.dx = dx;
    rect.node.dy = dy;
};

引数は順に、ドラッグ開始時からのX方向の移動量、Y方向の移動量、X座標、Y座標、イベントオブジェクトなっています。
ドラッグ時にリアルタイムに更新する場合には、移動量を覚えておく必要があります。

最後に、ドラッグの終了時の処理です。

function dragOnEnd(event) {
    // 選択状態を解除する。
    selectId = null;
};

これで、完成です。
長方形をドラッグ&ドロップすることで、移動することができます。

他の図形についても、同じようにイベントをつけることで、マウス操作により移動する長方形が作成できるので、試してみてください

曲線に沿って円を移動させる

曲線の上に、円を描き、時間が経過するとともに、曲線の上を移動させてみます。
まずは、曲線と曲線の上に配置する円を作成します。

曲線に沿って円を移動させる

動作するサンプルはこちらで確認することができます。

var paper = new Raphael("svg", 800, 200);
// 曲線
var linePath = [["M", 50, 30], ["C", 200, 50, 50, 150, 200, 150]];
var curveLine = paper.path(linePath);
curveLine.attr({
    'stroke' : '#CD5C5C',
    'stroke-width' : 3,
});

// 円
var circle = paper.circle(0, 0, 7);
circle.attr({
    fill : "#0000FF"
});

// 曲線と円を関連付ける。
circle.line = curveLine;

次に、円の属性を変化したときに、円の表示位置を変更します。
Rapahëlで作成したオブジェクトの属性の変更時のイベントは、初期描画時に作成するRapahëlの描画領域のcustomAttributesを利用します。
本項では、refreshという属性を変更したときに実行されるイベントを定義します。

paper.customAttributes.refresh = function (v) {
    var len = this.line.getTotalLength();
    var point = this.line.getPointAtLength(v * len);
    return {
        transform: "t" + [point.x, point.y]
    };
};

getTotalLengthメソッドは、線(曲線含む)全体の長さ(ピクセル)を取得するメソッド、getPointAtLengthメソッドは、指定した長さにおける座標を取得するメソッドです。getPointAtLengthメソッドで取得した座標は、x(X座標)、y(Y座標)とalpha(線の傾き)です。
取得したこれらの値から取得したX座標、Y座標に対し、transform属性を返すことで、アニメーション時に円を変更させます。
ここでは、X座標、Y座標を変更したいため、tオプション(translateを表す)を変更します。回転(rオプション)やスケール(sオプション)を変更することもできます。
定期的に曲線のrefresh属性を変更するメソッドを作成します。animateメソッドを利用し、refresh属性を0~1の間で徐々に変更します

function run() {
    circle.animate({refresh: 1}, 1000, function () {
        circle.attr({refresh: 0});
        setTimeout(run)
    });
}

setTimeoutにより、変更が終了時に再度同じメソッドを実行するようにします。 後は、初期化時に作成したrunメソッドを実行すれば作成完了です。曲線の上を円が移動する様子が確認できると思います。

変化する曲線に沿って円を移動させる

先ほど作成したサンプルをもとに、大きさの変更に合わせて線の上をアニメーションさせてみます。
まずは、大きさを変更するために、曲線を描くパスに補助線と移動させるためのオブジェクトを作成します。

変化する曲線に沿って円を移動させる

動作するサンプルはこちらで確認することができます。

var cornerAttr = {fill: "#000", stroke: "none"};
var subLinePath = [["M", linePath[0][1], linePath[0][2]], ["L", linePath[1][1], linePath[1][2]], ["M", linePath[1][3], linePath[1][4]], ["L", linePath[1][5], linePath[1][6]]];

var controls = paper.set(
        paper.path(subLinePath).attr({stroke: "#111", "stroke-dasharray": ". "}),
        paper.circle(linePath[0][1], linePath[0][2], 5).attr(cornerAttr),
        paper.circle(linePath[1][1], linePath[1][2], 5).attr(cornerAttr),
        paper.circle(linePath[1][3], linePath[1][4], 5).attr(cornerAttr),
        paper.circle(linePath[1][5], linePath[1][6], 5).attr(cornerAttr)
);

subLinePathで表した補助線と四隅の円を作成します。
次に、四隅の円に対してマウスで移動できるようにします。

function dragOnMove(dx, dy, x, y, event) {
    this.update(dx - diffX, dy - diffY);
    diffX = dx;
    diffY = dy;
}

function dragOnStart(x, y, event) {
    diffX = 0;
    diffY = 0;
}

controls.drag(dragOnMove, dragOnStart);

後は、dragOnMoveメソッド内で呼び出されるupdateメソッドを作成します。

    controls[1].update = function (x, y) {
        var X = this.attr("cx") + x;
        var Y = this.attr("cy") + y;
        this.attr({cx: X, cy: Y});
        linePath[0][1] = X;
        linePath[0][2] = Y;
        subLinePath[0][1] = X;
        subLinePath[0][2] = Y;
        controls[2].update(x, y);
    };
    controls[2].update = function (x, y) {
        var X = this.attr("cx") + x;
        var Y = this.attr("cy") + y;
        this.attr({cx: X, cy: Y});
        linePath[1][1] = X;
        linePath[1][2] = Y;
        subLinePath[1][1] = X;
        subLinePath[1][2] = Y;
        curveLine.attr({path: linePath});
        controls[0].attr({path: subLinePath});
    };
    controls[3].update = function (x, y) {
        var X = this.attr("cx") + x;
        var Y = this.attr("cy") + y;
        this.attr({cx: X, cy: Y});
        linePath[1][3] = X;
        linePath[1][4] = Y;
        subLinePath[2][1] = X;
        subLinePath[2][2] = Y;
        curveLine.attr({path: linePath});
        controls[0].attr({path: subLinePath});
    };
    controls[4].update = function (x, y) {
        var X = this.attr("cx") + x;
        var Y = this.attr("cy") + y;
        this.attr({cx: X, cy: Y});
        linePath[1][5] = X;
        linePath[1][6] = Y;
        subLinePath[3][1] = X;
        subLinePath[3][2] = Y;
        controls[3].update(x, y);
    };
    

これで完成です。四隅の円を選択し、ドラッグしてみてください。
曲線の変更に合わせて、曲線を移動する円の動きが変わることが確認できます。

おわりに

これで、Raphaëlを利用した動きのある画面を作る方法の説明は終わりです。
今回は移動とリサイズを行いましたが、色の変更や回転についても、基本的には同じように変更することができます。

例えば、このようなネットワーク上をアニメーションで移動させる画面についても、今回作成した内容を組み合わせることで作成できます。
今回学んだ内容を元に、いろいろと変更を加えてみてください。

執筆者プロフィール 村田賢一郎

Acroquest Technology 株式会社勤務。Javaによるミッションクリティカルな集中監視システムのフレームワーク開発、およびライフラインを支えるシステム開発に携わる。非同期処理、メッセージング、HAなどが本業である傍ら、Webによる新しいUI表現、開発手法に興味があり、あれこれ模索している。