D3 with Vue Reactivity

by samonela

HTML

<script src="https://cdn.rawgit.com/chrisvfritz/459a93673c5f2e362464125381e51515/raw/9e07d5bda30203dc788894cba18749a3bb44e79d/get-players.js"></script>
<script src="https://unpkg.com/d3"></script>
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app"></div>

JavaScript

var chart = new Vue({
  data: {
  	players: getPlayers()
  },
  computed: {
    scores: function () {
    	return this.players.map(function (player) {
      	return player.score
      })
    },
  	highestScore: function () {
    	return d3.max(this.scores)
    },
  	scale: function () {
    	return d3.scaleLinear()
      	.domain(d3.extent(this.scores))
        .range([100, 300])
    }
  }
})

var app = d3.select('#app')

function updateBars (players) {
  var bars = app.selectAll('.bar')
    .data(players, function (player) {
    	return player.name
 	 	})
    
  // When bars enter...
  bars.enter().append('div')
    .attr('class', 'bar')
    // Permanent styles
    .style('padding-left', '10px')
    .style('line-height', '30px')
    .style('transition', 'width 1s')
    // Styles before entering
    .style('opacity', 0)
    .style('transform', 'translateX(-100px)')
    .transition().duration(1000)
      // Styles after entering
    	.style('height', '30px')
      .style('opacity', 1)
      .style('transform', 'none')

  // When bars enter and update...
	bars
    .text(function (player) {
    	return player.name
  	})
    .style('width', function (player) {
    	return vm.scale(player.score) + 'px'
  	})
    .style('background', function (player) {
    	return player.score === vm.highestScore
        ? 'orange'
        : 'lightblue'
  	})

  // When bars exit...
  bars.exit()
  	// Have to remove the 'bar' class on exit or
  	// else d3 will freak out on the next tick
    .attr('class', '')
  	.transition().duration(1000)
      // Had to use '0px' here because unlike CSS,
      // d3's interpolation is not smart enough
      // to tween 30px to 0 (even has a string)
    	.style('height', '0px')
      .style('opacity', 0)
      .style('transform', 'translateX(-100px)')
      .remove()
}

updateBars(vm.players)
vm.$watch('players', updateBars, { deep: true })