日々のコンピュータ情報の集積と整理

Dr.ウーパのコンピュータ備忘録

2014年5月29日木曜日

HTMLとJavaScriptで単純なアニメーション付きの横向き棒グラフを作成する-アニメーションパターン:増加割合固定

イントロダクション

記事「HTMLとJavaScriptで単純なアニメーション付きの横向き棒グラフを作成する-アニメーションパターン:増加量固定」では、Webページ上に単純な横向きの棒グラフを JavaScript を使って作成し、棒グラフが増加量固定でアニメーションする方法を紹介しました。

今回は、その時のコードを修正して、横向きの棒グラフが増加割合固定でアニメーションするようにしてみます。


コード

以下のコードをグラフを表示させたい<body>内の任意の位置に貼り付けてください。
(なお、<script type="text/javascript"></script>の部分は<head>内に設置することもできます。)

<form>
<input type="button" value="アニメーション実行" onclick="startAnimation()" />
</form>
<div id="graph-object"></div>

<script type="text/javascript">
<!--
    // 系列名と値
    function CGraphValue(name, value) {
        this.name = name;                       // 系列名
        this.value = value;                     // 値
    }

    // グラフのデータ
    var graphValues = [];
    graphValues.push(new CGraphValue("系列1", 100));
    graphValues.push(new CGraphValue("系列2", 200));
    graphValues.push(new CGraphValue("系列3", 215));
    graphValues.push(new CGraphValue("系列4", 105));
    graphValues.push(new CGraphValue("系列5", 118));

    // グラフの生成パラメータ
    function CGraphGenerateParam(pixel_per_value, graph_border_style, graph_border_width, graph_border_color, graph_background_color, graph_height) {
        this.pixel_per_value = pixel_per_value;                 // 棒グラフの単位値(1)に対応する長さ (ピクセル)
                                                                //   グラフの値が 1 の場合の、グラフの長さです。
                                                                //   グラフの値 * pixel_per_value がグラフの長さになります
        this.graph_border_style = graph_border_style;           // 棒グラフの棒のボーダーのスタイル
        this.graph_border_width = graph_border_width;           // 棒グラフの棒のボーダーの幅
        this.graph_border_color = graph_border_color;           // 棒グラフの棒のボーダーの色
        this.graph_background_color = graph_background_color;   // 棒グラフの棒の背景色
        this.graph_height = graph_height;                       // 棒グラフの高さ
    }

    // グラフのアニメーション用パラメータ
    function CGraphAnimationParam(interval, add_rate) {
        this.interval = interval;                               // 更新間隔 (ミリ秒)
        this.add_rate = add_rate;                               // 1フレームあたり、グラフが増加する割合(0~1.0)
    }


    /*
     数値の有効桁数を取得する

      str : 10進の数値を保持したテキスト
    */
    function getSignificantFigures(str) {

        // 不要な文字を消す(符号)
        str = str.replace(/[+-]/g, "");

        // 数値の終わりの 0 の列を消す
        //   1000 の場合 000 を消す
        //   1.000 の場合 000 は消さない
        if (!(/^\d*\./.test(str))) {
            str = str.replace(/0+$/g, "");
        }

        // 小数点を消す
        str = str.replace(/\./g, "");

        // 数値の先頭の 0 の列を消す
        str = str.replace(/^0+/g, "");

        // 指数形式の指数部を消す
        str = str.replace(/[eE]\d+/g, "");

        return str.length;
    }

    /*
    数値の指数部を取得する

    */
    function getExponent(value) {
        var value_exponential_str = value.toExponential();                  // 数値を指数形式の文字列へ変換

        var exponent_str = value_exponential_str.replace(/^.*[eE]/, "");    // 実数部からeの部分までを削除
        return Number(exponent_str);
    }

    /*
    アニメーションするグラフを作成する

    id_graph : グラフを設定する要素の id
    graphValues : グラフの系列データの配列 (CGraphValueのArray)
    valuesName : データの名前 (CGraphValue)
    graphGenParam : グラフの生成パラメータ (CGraphGenerateParam)
    graphAnimationParam : グラフのアニメーション用パラメータ
    */
    function createAnimationGraph(id_graph, graphValues, valuesName, graphGenParam, graphAnimationParam) {
        
        // アニメーション用のグラフデータ作成
        var temp_graphValues = [];
        for ( var i = 0; i < graphValues.length; i++ ) {
            temp_graphValues.push(new CGraphValue(graphValues[i].name, 0));
        }

        // アニメーションを実行
        var animationCount = 0;         // アニメーションカウンタ
        function runAnimation() {
            createGraph(id_graph, temp_graphValues, valuesName, graphGenParam);

            // 次のフレーム用のデータを作成
            var updateFlag = false;             // 情報が更新されたかどうか
            for ( var i = 0; i < graphValues.length; i++ ) {
                if ( graphValues[i].value > temp_graphValues[i].value ) {
                    temp_graphValues[i].value = animationCount * graphValues[i].value * graphAnimationParam.add_rate;

                    // --- 表示のための有効桁数を計算 ----------------------------------------
                    // 元々の数値の有効桁数を基本的に使用するが、
                    // 元々の数値の有効桁数がアニメーションを行うために十分でなければ、
                    // アニメーション用の有効桁数を数値の表示用に設定する

                    var org_value_str = graphValues[i].value.toString();
                    if (/[^.]/.test(org_value_str)) {           // 整数の場合、すべての桁を有効桁にするため、最後に . を追加
                        org_value_str += ".";
                    }

                    // 元々の数値の有効桁数を取得
                    var org_significantFigures = getSignificantFigures(org_value_str);

                    // 増加割合の指数部を取得
                    var add_rate_exponent = getExponent(graphAnimationParam.add_rate);


                    var view_significantFigures;        // 表示に使用する有効桁数

                    // 元々の数値の有効桁数が、アニメーションに必要な有効桁数
                    //    (有効桁数は (-1 * 増加割合の指数部) + 1 以上なければ増加を反映させられない) を割っていたら、
                    //     アニメーションに必要な有効桁数を表示に使用する有効桁数に設定する
                    if (org_significantFigures < -add_rate_exponent + 1) {
                        view_significantFigures = -add_rate_exponent + 1;
                    } else {
                        view_significantFigures = org_significantFigures;
                    }

                    temp_graphValues[i].value = Number(temp_graphValues[i].value.toPrecision(view_significantFigures));

                    // 上限を超え他場合は最大値を設定
                    if (temp_graphValues[i].value > graphValues[i].value) {             
                        temp_graphValues[i].value = graphValues[i].value;
                    }

                    updateFlag = true;
                }
            }

            // データの更新が確認されたら、次のフレームをタイマにセット
            if (updateFlag) {
                animationCount++;
                setTimeout(runAnimation, graphAnimationParam.interval);
            }
        }
        runAnimation();
    }

    /*
    グラフを作成する

    id_graph : グラフを設定する要素の id
    graphValues : グラフの系列データの配列 (CGraphValueのArray)
    valuesName : データの名前 (CGraphValue)
    graphGenParam : グラフの生成パラメータ (CGraphGenerateParam)
    */
    function createGraph(id_graph, graphValues, valuesName, graphGenParam) {

        // グラフ 出力
        var obj_graph = document.getElementById(id_graph);
        var graph_html = "<table>";
        graph_html += "<tr><td>" + valuesName.name + "</td><td>" + valuesName.value + "</td></tr>";

        
        for (var i = 0; i < graphValues.length; i++) {

            var width = graphGenParam.pixel_per_value * graphValues[i].value;
            
            var graph_div = "";
            if (graphValues[i].value > 0) {
                graph_div = "<div style=\"border-style:" + graphGenParam.graph_border_style +
                    ";border-width:" + graphGenParam.graph_border_width +
                    ";border-color:" + graphGenParam.graph_border_color +
                    ";background-color:" + graphGenParam.graph_background_color +
                    ";width:" + width +
                    "px;height:" + graphGenParam.graph_height + ";\"></div>";
            }

            graph_html += "<tr><td>" + graphValues[i].name + "</td><td><table><tr><td>" + graph_div + "</td><td>" + graphValues[i].value + "</td></tr></table></td></tr>";
        }

        graph_html += "</table>";
        obj_graph.innerHTML = graph_html;
    }


    // グラフのアニメーションを開始
    function startAnimation() {

        // 最大のカウントを取得する
        var max_value = 0;
        for (var i = 0; i < graphValues.length; i++) {
            if (graphValues[i].value > max_value) {
                max_value = graphValues[i].value;
            }
        }

        // グラフの単位値あたりの長さを計算
        var max_width = 300;                            // グラフの最大の長さ
        var pixel_per_value = max_width / max_value;    // グラフの単位値あたりの長さ


        // アニメーショングラフの作成
        createAnimationGraph(
            "graph-object", 
            graphValues, 
            new CGraphValue("系列名", "値"),
            new CGraphGenerateParam(pixel_per_value, "solid", "1px", "#999999", "#cccccc", "14px"), 
            new CGraphAnimationParam(1000/60, 0.01)
        );
    }

//-->
</script>


以下の部分が棒グラフのデータです。

    // グラフのデータ
    var graphValues = [];
    graphValues.push(new CGraphValue("系列1", 100));
    graphValues.push(new CGraphValue("系列2", 200));
    graphValues.push(new CGraphValue("系列3", 215));
    graphValues.push(new CGraphValue("系列4", 105));
    graphValues.push(new CGraphValue("系列5", 118));

任意の値を設定してください。

以下の部分で、アニメーションするグラフの生成処理を呼び出しています。

        // アニメーショングラフの作成
        createAnimationGraph(
            "graph-object",
            graphValues,
            new CGraphValue("系列名", "値"),
            new CGraphGenerateParam(pixel_per_value, "solid", "1px", "#999999", "#cccccc", "14px"),
            new CGraphAnimationParam(1000/60, 0.01)
        );


第1引数はグラフを挿入する要素の id です。
各自の環境に応じて、適切な id を設定してください。
(ここでは、<div id="graph-object"></div> 内に棒グラフが挿入されます。)

第2引数はグラフのデータです。

第3引数はグラフの各値のタイトルです。
任意の値を設定してください。

第4引数はグラフの書式設定です。
任意の値を設定してください。

第5引数はアニメーションの設定です。
任意の値を設定してください。

結果

以下のように、増加割合固定でアニメーションする横向きの棒グラフが作成できます。
「アニメーション実行」ボタンをクリックすると、アニメーションを開始できます。




なお、今回はアニメーションの実行開始のトリガーをボタンクリック時としましたが、ページ読み込み完了時や要素が画面内に表示されたときなど、いろいろなトリガーが考えられます。

最適なトリガーを検討してみてください。





関連記事

関連記事を読み込み中...

同じラベルの記事を読み込み中...