質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.45%

  • JavaScript

    21050questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • jQuery

    8383questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • canvas

    320questions

    HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

  • SVG

    130questions

    SVGは、XMLを基盤とした2Dベクター画像記述言語。画像を線・面といった図形の集合体として扱うベクター画像のため、環境に適した表示が可能です。アニメーション機能もサポートされており、簡単なインタラクティブコンテンツ作成もできます。

Chart.js、ドーナツグラフのデータのカウントアップ(ダウン)アニメーション表示

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,581

keen

score 2

実現したいこと

  • ドーナツグラフ
  • 円グラフに対する線形グラデーション
  • ドーナツグラフの真ん中に数値を表示する
  • 真ん中の数値をカウントアップ(ダウン)アニメーションをつける

Chart.jsを使っています。
あるデータを取ってきて、その割合をアニメーションでドーナツグラフの真ん中に表示させたいです。現段階ではランダムな値でデータ更新をしています。

他の質問などを参考にさせて頂いてグラフの真ん中に数値を表示させることはできたのですが、カウントアップ(ダウン)アニメーションの実装方法が分からないので教えていただきたいです。

他のライブラリにカウントアップアニメーションがついているものがあるのですが、円グラフに対して線形グラデーションをつけたいので今のところchart.js(2.x)を使っています。
他のライブラリで線形グラデーションをつけられるものもあれば、そのライブラリでの実装でも全く構いません。

追記
カウントアップアニメーションは以下のようなイメージです。不明確な表現で申し訳ないです。
イメージ説明

よろしくお願いします。
以下ソースコードです。

ソースコード

<!DOCTYPE html>
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
</head>
<html>
<body>
<canvas id="myChart"></canvas>
<script>
    Chart.pluginService.register({
        afterUpdate: function (chart) {
            if (chart.config.options.elements.center) {
                var helpers = Chart.helpers;
                var centerConfig = chart.config.options.elements.center;
                var globalConfig = Chart.defaults.global;
                var ctx = chart.chart.ctx;
                var fontStyle = helpers.getValueOrDefault(centerConfig.fontStyle, globalConfig.defaultFontStyle);
                var fontFamily = helpers.getValueOrDefault(centerConfig.fontFamily, globalConfig.defaultFontFamily);
                if (centerConfig.fontSize)
                    var fontSize = centerConfig.fontSize;
                else {
                    ctx.save();
                    var fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
                    var maxFontSize = helpers.getValueOrDefault(centerConfig.maxFontSize, 256);
                    var maxText = helpers.getValueOrDefault(centerConfig.maxText, centerConfig.text);
                    do {
                        ctx.font = helpers.fontString(fontSize, fontStyle, fontFamily);
                        var textWidth = ctx.measureText(maxText).width;
                        if (textWidth < chart.innerRadius * 2 && fontSize < maxFontSize)
                            fontSize += 1;
                        else {
                            fontSize -= 1;
                            break;
                        }
                    } while (true)
                    ctx.restore();
                }

                chart.center = {
                    font: helpers.fontString(fontSize, fontStyle, fontFamily),
                    fillStyle: helpers.getValueOrDefault(centerConfig.fontColor, globalConfig.defaultFontColor)
                };
            }
        },
        afterDraw: function (chart) {
            if (chart.center) {
                var centerConfig = chart.config.options.elements.center;
                var ctx = chart.chart.ctx;

                ctx.save();
                ctx.font = chart.center.font;
                ctx.fillStyle = chart.center.fillStyle;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                var centerX = (chart.chartArea.left + chart.chartArea.right) / 2;
                var centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2;
                ctx.fillText(centerConfig.text, centerX, centerY);
                ctx.restore();
            }
        },
    })

    var ctx = document.getElementById("myChart").getContext("2d");

    var gradient = ctx.createLinearGradient(600, 0, 0, 200);
    gradient.addColorStop(0,'rgb(160, 240, 216)');
    gradient.addColorStop(1,'rgb(212, 222, 246)');

    var rand = Math.random()*100;
    var config = {
            type: 'doughnut',
            data: {
                datasets: [{
                    data: [
                        rand,
                        100- rand
                    ],
                    backgroundColor: [
                          gradient,
                          "#d6d6d6",
                    ],
                }],
            },

            options: {
                maintainAspectRatio: false,
                events: [],
                cutoutPercentage :65,
                elements: {
                    arc: {
                         borderWidth: 0
                    },
                    center: {
                        maxText: '100%',
                        text: this.Math.floor(rand)+"%",
                        fontColor: 'rgb(15, 33, 58)',
                        fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
                        fontStyle: 'normal',
                        minFontSize: 1,
                        maxFontSize: 46,
                    },
                },
            },

    };

    var myChart = new Chart(ctx, config);

    setInterval(function(){
        rand = Math.random()*100;
        myChart.data.datasets[0].data[0] = rand;
        myChart.data.datasets[0].data[1] = 100- rand;
        myChart.options.elements.center.text = Math.floor(rand)+"%";
        myChart.update();     
    },1500);
</script>
</body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • namimon

    2017/07/15 08:32

    カウントアップアニメーションとは、具体的にどのようなものでしょうか。参考にできるものを提示していただけますか。

    キャンセル

回答 2

0

charjsのAnimationの項目を見ると、アニメーション中にコールバックを呼び出す方法が書かれています。
これを利用して、アニメーションの進捗状況を知ることができます。

ご質問のコードではafterUpdate, afterDrawコールバックを用いてチャートの中心にテキストを描画するようになっていますが、チャート更新中のアニメーションでもafterDrawが呼ばれますので、中心に描画すべきテキストを「固定的なテキスト」ではなく「progressの値によって現在表示すべきテキストを計算するような関数」とすればお望みのことはできそうです。

オプションは次のように・・・

var progress = 0;
var conifig {
  ...
  options: {
    ...
    elements: {
      ...
      center: {
        maxText: '100%',
        textFunc: function () { return '0%' }, // *** FAIL SAFE
        //text: this.Math.floor(rand)+"%",     // *** 固定的なテキストではうまくない
        ...
      },
    },
    animation: {
      onProgress: function(animation) {
        var cur = animation.animationObject.currentStep,
            total = animation.animationObject.numSteps;
        progress = cur / total;
      },
      onComplete: function() {
        progress = 1;
      }
    }
  }
};

そしてsetIntervalは次のように・・・

var rand = Math.random()*100,
    oldValue = 0, newValue = rand;
...
setInterval(function() {
  rand = Math.random()*100;
  oldValue = newValue;
  newValue = rand;

  myChart.data.datasets[0].data[0] = rand;
  myChart.data.datasets[0].data[1] = 100- rand;
  //myChart.options.elements.center.text = Math.floor(rand)+"%"; // ***固定的なテキストではうまくない
  myChart.options.elements.center.textFunc = function() {
    var v = progress * newValue + (1 - progress) * oldValue;
    return Math.floor(v)+"%"
  };
  myChart.update();
},1500);

あとは元のコードでoptions.elements.center.textを参照していた箇所をoptions.elements.center.textFunc()に置き換えればほぼ期待通りの動きにできると思います。


ちなみにアニメーションカープはリニアではなくeasingオプションに応じて様々な変化のしかとなるようですが、onProgressコールバックの呼ばれ方を見ると、リニアな進捗度しか求められないように見えました。onProgressが呼び出される時間間隔とprogressの上がり方の比率をデバッグプリントしてみると、easingがeaseOutQuart(デフォルト値でリニアな変化でない)にも関わらずprogressがリニアに変化しているような感じです。そのあたりは真面目に考える程の問題ではないと思いますが・・・

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

こういう解釈でしょうか。
サンプル

 Chart.pluginService.register({
        afterUpdate: function (chart) {
            if (chart.config.options.elements.center) {
                var helpers = Chart.helpers;
                var centerConfig = chart.config.options.elements.center;
                var globalConfig = Chart.defaults.global;
                var ctx = chart.chart.ctx;
                var fontStyle = helpers.getValueOrDefault(centerConfig.fontStyle, globalConfig.defaultFontStyle);
                var fontFamily = helpers.getValueOrDefault(centerConfig.fontFamily, globalConfig.defaultFontFamily);
                if (centerConfig.fontSize)
                    var fontSize = centerConfig.fontSize;
                else {
                    ctx.save();
                    var fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
                    var maxFontSize = helpers.getValueOrDefault(centerConfig.maxFontSize, 256);
                    var maxText = helpers.getValueOrDefault(centerConfig.maxText, centerConfig.text);
                    do {
                        ctx.font = helpers.fontString(fontSize, fontStyle, fontFamily);
                        var textWidth = ctx.measureText(maxText).width;
                        if (textWidth < chart.innerRadius * 2 && fontSize < maxFontSize)
                            fontSize += 1;
                        else {
                            fontSize -= 1;
                            break;
                        }
                    } while (true)
                    ctx.restore();
                }

                chart.center = {
                    font: helpers.fontString(fontSize, fontStyle, fontFamily),
                    fillStyle: helpers.getValueOrDefault(centerConfig.fontColor, globalConfig.defaultFontColor)
                };

            }
        },

    })

    var ctx = document.getElementById("myChart").getContext("2d");

    var gradient = ctx.createLinearGradient(600, 0, 0, 200);
    gradient.addColorStop(0,'rgb(160, 240, 216)');
    gradient.addColorStop(1,'rgb(212, 222, 246)');

    var rand = Math.random()*100;
    var config = {
            type: 'doughnut',
            data: {
                datasets: [{
                    data: [
                        rand,
                        100- rand
                    ],
                    backgroundColor: [
                          gradient,
                          "#d6d6d6",
                    ],
                }],
            },

            options: {
                maintainAspectRatio: false,
                events: [],
                cutoutPercentage :65,
                elements: {
                    arc: {
                         borderWidth: 0
                    },
                    center: {
                        maxText: '100%',
                        text: this.Math.floor(rand)+"%",
                        fontColor: 'rgb(15, 33, 58)',
                        fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
                        fontStyle: 'normal',
                        minFontSize: 1,
                        maxFontSize: 46,
                    },
                },
                animation: {
                    onProgress: function(animation) {
                      var currentPercent =   animation.animationObject.currentStep / animation.animationObject.numSteps  ;
                      var currentValue = Math.floor ( (  this.data.datasets[0].data[0] * currentPercent ) )  ;
                      var ctx = this.ctx ;

                      ctx.save();
                      ctx.font = this.center.font;
                      ctx.fillStyle = this.center.fillStyle;
                      ctx.textAlign = 'center';
                      ctx.textBaseline = 'middle';
                      var centerX = (this.chartArea.left + this.chartArea.right) / 2;
                      var centerY = (this.chartArea.top + this.chartArea.bottom) / 2;
                      ctx.fillText( currentValue + "%"  , centerX, centerY );
                      ctx.restore();
                    }
                },
            },

    };

    var myChart = new Chart(ctx, config);

    setInterval(function(){
        rand = Math.random()*100;
        myChart.data.datasets[0].data[0] = rand;
        myChart.data.datasets[0].data[1] = 100- rand;
        myChart.update();     
    },1500);

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.45%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • JavaScript

    21050questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • jQuery

    8383questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • canvas

    320questions

    HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

  • SVG

    130questions

    SVGは、XMLを基盤とした2Dベクター画像記述言語。画像を線・面といった図形の集合体として扱うベクター画像のため、環境に適した表示が可能です。アニメーション機能もサポートされており、簡単なインタラクティブコンテンツ作成もできます。