Chart made without a charting library like d3, but composed of Vue directives. Showing also how to transition state with watchers
A Pen by Sarah Drasner on CodePen.
Chart made without a charting library like d3, but composed of Vue directives. Showing also how to transition state with watchers
A Pen by Sarah Drasner on CodePen.
<div id="app"> | |
<select v-model="selected"> | |
<option v-for="option in options" v-bind:value="option.value"> | |
{{ option.text }} | |
</option> | |
</select> | |
<svg viewBox="0 0 400 400"> | |
<!--xaxis --> | |
<g targetVal="targetVal" class="xaxis"> | |
<line x1="0" y1="1" x2="350" y2="1"/> | |
<g v-for="(select, index) in targetVal"> | |
<line y1="0" y2="7" v-bind="{ 'x1':index*10, 'x2':index*10 }"/> | |
<text v-if="index % 5 === 0" v-bind="{ 'x':index*10, 'y':20 }">{{ index }}</text> | |
</g> | |
</g> | |
<!--yaxis --> | |
<g class="yaxis"> | |
<line x1="0" y1="1" :x2="getMax" y2="1"/> | |
<g v-for="n in getMaxRange"> | |
<line y1="0" y2="7" v-bind="{ 'x1':n*10, 'x2':n*10 }"/> | |
<text v-if="n % 5 === 0" v-bind="{ 'x':getMax-(n*10)-5, 'y':20 }">{{ n }}</text> | |
</g> | |
</g> | |
<!-- bars --> | |
<g v-for="(select, index) in targetVal" class="bars"> | |
<rect v-bind="{ 'x':index*10+20, 'y':getMax-select*10 }" width="10" :height="select*10"/> | |
</g> | |
</svg> | |
</div> |
new Vue({ | |
el: '#app', | |
data() { | |
return { | |
selected: [25, 37, 15, 13, 25, 30, 11, 17, 35, 10, 25, 15, 5, 27, 15, 13, 25, 36, 15, 14, 35, 10, 14, 15, 35, 17, 12, 13, 25, 30, 14, 17, 35, 10, 25, 15], | |
targetVal: [25, 37, 15, 13, 25, 30, 11, 17, 35, 10, 25, 15, 5, 27, 15, 13, 25, 36, 15, 14, 35, 10, 14, 15, 35, 17, 12, 13, 25, 30, 14, 17, 35, 10, 25, 15], | |
options: [ | |
{ text: 'First Dataset', value: [25, 37, 15, 13, 25, 30, 11, 17, 35, 10, 25, 15, 5, 27, 15, 13, 25, 36, 15, 14, 35, 10, 14, 15, 35, 17, 12, 13, 25, 30, 14, 17, 35, 10, 25, 15] }, | |
{ text: 'Second Dataset', value: [13, 25, 30, 11, 17, 35, 10, 25, 15, 5, 27, 15, 13, 25, 36, 15, 14, 35, 10, 14, 15, 35, 17, 12, 13, 25, 30, 14, 17, 35, 10, 25, 15, 25, 37, 15] }, | |
{ text: 'Third Dataset', value: [35, 10, 25, 15, 5, 27, 15, 13, 25, 36, 15, 14, 35, 10, 14, 15, 35, 17, 12, 13, 25, 30, 14, 17, 35, 10, 25, 15, 25, 37, 15, 13, 25, 30, 11, 17] } | |
] | |
} | |
}, | |
computed: { | |
getMax() { | |
return Math.max.apply( Math, this.selected )*10; | |
}, | |
getMaxRange() { | |
let maxi = Math.max.apply( Math, this.selected ); | |
return _.range(maxi); | |
} | |
}, | |
watch: { | |
selected(newValue, oldValue) { | |
// Create a dummy object that will get updated by GSAP | |
var tweenedData = {} | |
// Update function that is invoked on each tween step | |
// we use this to push the data | |
var update = function() { | |
let obj = Object.values(tweenedData); | |
obj.pop(); | |
this.targetVal = obj; | |
} | |
// Create an object to hold the source data to be tweened and the | |
// function pointer for update events | |
var tweenSourceData = { onUpdate: update, onUpdateScope: this} | |
for (let i = 0; i < oldValue.length; i++) { | |
// Turn the current index into a string | |
let key = i.toString() | |
tweenedData[key] = oldValue[i] | |
tweenSourceData[key] = newValue[i] | |
} | |
// Tween over the our target dummy object, but only for the specific key | |
TweenMax.to(tweenedData, 1, tweenSourceData) | |
} | |
} | |
}); |
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script> |
body { | |
font-family: 'Mada', sans-serif; | |
background: #222; | |
} | |
#app { | |
text-align: center; | |
max-width: 400px; | |
margin: 30px auto; | |
display: table; | |
} | |
span { | |
color: white; | |
} | |
svg { | |
width: 400px; | |
text { | |
fill: grey; | |
font-family: 'Mada', sans-serif; | |
} | |
} | |
line { | |
stroke: #555; | |
stroke-width: 2px; | |
} | |
select { | |
font-family: 'Mada', sans-serif; | |
background: #444; | |
color: #ccc; | |
border: 0; | |
width: 200px; | |
margin: 0 0 25px; | |
outline: 0; | |
cursor: pointer; | |
height: 35px; | |
option { | |
font-family: 'Mada', sans-serif; | |
} | |
} | |
.xaxis { | |
transform: translate(20px, 370px); | |
} | |
.yaxis { | |
transform: translate(20px, 0px) rotate(90deg); | |
} | |
$amt : 350px; | |
$max: 40; | |
$color: 300/$max; | |
@for $i from 1 through $max { | |
.bars:nth-child(#{$i}) rect { | |
fill: hsl(($i - 10)*($color*1.25), ($i - 1)*$color, 40%); | |
} | |
} |