Vue.js SVG example
by sukobuto
HTML
<script src="http://vuejs.org/js/vue.min.js"></script>
<!-- template for the polygraph component. -->
<script type="text/x-template" id="polygraph-template">
<polygon v-attr="points: points"></polygon>
<circle cx="100" cy="100" r="80"></circle>
<text v-component="axis-label" v-repeat="stats" v-attr="x:x, y:y">
{{label}}
</text>
</script>
<div id="demo">
<!-- Use the component -->
<svg width="200" height="200" transform="scale(0.5)" v-component="polygraph" v-with="stats:stats"></svg>
<svg width="200" height="200" transform="scale(0.5)" v-component="polygraph" v-with="stats:stats"></svg>
<svg width="200" height="200" v-component="polygraph" v-with="stats:stats"></svg>
<svg width="200" height="200" v-component="polygraph" v-with="stats:stats"></svg>
<svg width="200" height="200" v-component="polygraph" v-with="stats:stats"></svg>
<!-- controls -->
<div v-repeat="stats">
<label>{{label}}</label>
<input type="range" v-model="value" min="0" max="100" />
<span>{{value}}</span>
<button v-on="click:remove(this)">X</button>
</div>
<input v-model="newLabel"> <button v-on="click:add">Add a Stat</button>
<pre id="raw">{{stats | format}}</pre>
</div>
<p style="font-size:12px">* input[type="range"] requires IE10 or above.</p>
CSS
body {
font-family: Helvetica Neue, Arial, sans-serif;
}
polygon {
fill: #42b983;
opacity: .75;
}
circle {
fill: transparent;
stroke: #999;
}
text {
font-family: Helvetica Neue, Arial, sans-serif;
font-size: 10px;
fill: #666;
}
label {
display: inline-block;
margin-left: 10px;
width: 20px;
}
#raw {
position: absolute;
top: 0;
left: 300px;
}
}
JavaScript
// The raw data to observe
var stats = [
{ label: 'A', value: 100 },
{ label: 'B', value: 100 },
{ label: 'C', value: 100 },
{ label: 'D', value: 100 },
{ label: 'E', value: 100 },
{ label: 'F', value: 100 }
]
// A resusable polygon graph component
Vue.component('polygraph', {
template: '#polygraph-template',
replace: true,
computed: {
// a computed property for the polygon's points
points: function () {
var total = this.stats.length
return this.stats.map(function (stat, i) {
var point = valueToPoint(stat.value, i, total)
return point.x + ',' + point.y
}).join(' ')
}
},
components: {
// a sub component for the labels
'axis-label': {
computed: {
point: function () {
return valueToPoint(+this.value + 10, this.$index, this.$parent.stats.length)
},
x: function () {
return this.point.x - 4
},
y: function () {
return this.point.y + 4
}
}
}
}
})
// math helper...
function valueToPoint (value, index, total) {
var x = 0,
y = -value * 0.8,
angle = Math.PI * 2 / total * index,
cos = Math.cos(angle),
sin = Math.sin(angle),
tx = x * cos - y * sin + 100,
ty = x * sin + y * cos + 100
return {
x: tx,
y: ty
}
}
// bootstrap the demo
new Vue({
el: '#demo',
data: {
newLabel: '',
stats: stats
},
filters: {
format: function (stats) {
return JSON.stringify(stats, null, 2)
}
},
methods: {
add: function () {
if (!this.newLabel) return
this.stats.push({
label: this.newLabel,
value: 100
})
this.newLabel...