LoginSignup
26

More than 5 years have passed since last update.

Vue.js+d3.jsで折れ線グラフを描く

Last updated at Posted at 2016-12-11

Vue.js Advent Calendar 2016 11日目

まえがき

Vue.jsと描画機能とd3.jsの計算機能を合わせて折れ線グラフを描いてみます。
d3.jsで直接描画してもいいのですが、せっかくなのでVue.jsで宣言的に描けたらな、と。

デモ

デモ(jsfiddle)

コード

メインのコンポーネント

html
  <svg :width="width" :height="height" :viewBox="viewBox">
    <my-line v-for="(line,index) in lines" 
    :x-scale="xScale"
    :y-scale="yScale"
    :parameters="line"
    :index="index"></my-line>


    <horizontal-grid v-for="y in yTicks"
        :x-scale="xScale"
        :y-scale="yScale"
        :y="y"
        :x_max="100"
    ></horizontal-grid>
    <vertical-grid v-for="x in xTicks"
        :x-scale="xScale"
        :y-scale="yScale"
        :x="x"
        :y_max="100"
    ></vertical-grid>
  </svg>
js
new Vue({
  el: '#app',
  data: function(){
    return {
      width:300,
      height:300,
      lines:[]
    }
  },
  computed:{
    viewBox:function(){
        return '0 0 ' + this.width + ' ' + this.height;
    },
    xTicks:function(){
        return this.xScale.ticks();
    },
    yTicks:function(){
        return this.yScale.ticks();
    },
    xScale:function(){
        return d3.scaleLinear().domain([0,100]).range([0, this.width]);
    }, 
    yScale:function(y){
        return d3.scaleLinear().domain([0,100]).range([0, this.height]);
    }
  }
});

ポイントはxScaleyScaleでしょうか。
my-lineなどの各コンポーネントに渡してスケールを合わせています。
xTicksyTicksは目盛線の値です。

曲線コンポーネント

html
 <path class="line" :d="d" :style="{stroke: myColor}"></path>
js
Vue.component('my-line', {
  template: '#line',
  props:{
    parameters:Array,
    index:Number,
    xScale:Function,
    yScale:Function
  },
  computed:{
    d:function(){
      var xScale = this.xScale;
      var yScale = this.yScale;
      var line = d3.line()
                 .curve(d3.curveMonotoneX)
                 .x(function(d) {return xScale(d.x); })
                 .y(function(d) { return yScale(d.y); });

      return line(this.parameters);
    },
    myColor:function(){
        return d3.schemeCategory10[this.index % 10]
    }
  }
});

d属性がポイントですね。
手で描くとひたすらつらいので、d3.jsの恩恵感じます。

グリッド線コンポーネント

html
<line :x1="0" :y1="yValue" :x2="max" :y2="yValue" class="grid"></line>
js
Vue.component('horizontal-grid', {
  template: '#horizontal-grid',
  props:{
    x_max:Number,
    y:Number,
    xScale:Function,
    yScale:Function
  },
  computed:{
    yValue:function(){
        return this.yScale(this.y);
    },
    max:function(){
        return this.xScale(this.x_max);
    }
  }
})

横線を描くだけのコンポーネントです。
xScaleyScaleを受け取っているので、親のwidth,heightの変更に追従します。
縦線はこれと同じなので省略します。

Transition

曲線の変更についてだけなら次のCSSでChromeでは動きました。
Firefoxでは残念ながら動きません。というかなぜChromeで動くんだ……?

css
.line{
  fill:none;
  stroke-width: 2px;
  transition: all 1s;
}

追加/削除の変更にも対応する時はtransition-groupを使いましょう。
こちらはVueが-enter-leaveのclassを追加してくれるのでFirefox,Chromeのどちらも動きます。

html
  <transition-group name="line" tag="g">
    <my-line v-for="(line,index) in lines" 
      :key="index"
    :x-scale="xScale"
    :y-scale="yScale"
    :parameters="line"
    :index="index"></my-line>
 </transition-group>
css
.line-enter {
  opacity: 0;
  transform: translateX(-100px);
}
.line-leave-active {
  opacity: 0;
  transform: translateX(100px);
}

おわりに

曲線の変更のTransitionをどうしたものかよくわからないな……。

追記

svgのTransitionに悩んだけど、普通にガイドに書いてあった!
Vueのドキュメントの充実具合すげえや!

TweenLite.jsを使ったsvgのtransition

デモ(jsfiddle)

曲線のコンポーネントを次のように書き換えました。

js
Vue.component('my-line', {
  template: '#line',
  props:{
    parameters:Array,
    index:Number,
    xScale:Function,
    yScale:Function
  },
  data:function(){
    return {
      d:this.generateLineD(this.parameters)
    }
  },
  watch:{
    parameters:{
      handler:'animateLine',
      deep:true
    },
    xScale:function(){
      this.animateLine(this.parameters);
    },
    yScale:function(){
      this.animateLine(this.parameters);
    }
  },
  computed:{
    myColor:function(){
        return d3.schemeCategory10[this.index % 10]
    }
  },
  methods:{
    animateLine(parameters){
       TweenLite.to(
          this.$data, 
          1, 
          { d: this.generateLineD(parameters) }
       );
    },
    generateLineD(parameters){
      var xScale = this.xScale;
      var yScale = this.yScale;
        var line = d3.line()
                 .curve(d3.curveMonotoneX)
                 .x(function(d) {return xScale(d.x); })
                 .y(function(d) { return yScale(d.y); });

      return line(parameters);
    }
  }
});

TweetLite.jsはObjetやArrayの値を指定した秒数内で次第に変更してくれるライブラリです。
これを使ったanimateLineメソッドはvm.$data.dをじわじわ更新します。
watcherからanimateLineメソッドを呼び出すことで、
SVG曲線のアニメーションをすることが出来ました。

おわりに(追記)

ガイドを読もう!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26