1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
| <!DOCTYPE html> <html> <head> <title>SVG Graph</title> <script src="https://unpkg.com/vue"></script> <link rel="stylesheet" type="text/css" href="/style.css" />
<!-- template for the polygraph component. --> <script type="text/x-template" id="polygraph-template"> <g> <polygon :points="points"></polygon> <circle cx="100" cy="100" r="80"></circle> <axis-label v-for="(stat, index) in stats" :stat="stat" :index="index" :total="stats.length">2222 </axis-label> </g> </script>
<!-- template for the axis label component. --> <script type="text/x-template" id="axis-label-template"> <text :x="point.x" :y="point.y">{{stat.label}}</text> </script> </head> <body> <!-- demo root element --> <div id="demo"> <!-- Use the component --> <svg width="200" height="200"> <polygraph :stats="stats"></polygraph> </svg> <!-- controls --> <div v-for="stat in stats"> <label>{{stat.label}}</label> <input type="range" v-model="stat.value" min="0" max="100" /> <span>{{stat.value}}</span> <button @click="remove(stat)" class="remove">X</button> </div> <form id="add"> <input name="newlabel" v-model="newLabel" /> <button @click="add">Add a Stat</button> </form> <pre id="raw">{{ stats }}</pre> </div>
<p style="font-size:12px">* input[type="range"] requires IE10 or above.</p>
<script> // 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", { props: ["stats"], template: "#polygraph-template", 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": { props: { stat: Object, index: Number, total: Number }, template: "#axis-label-template", computed: { point: function() { return valueToPoint( +this.stat.value + 10, this.index, this.total ); } } } } });
// math helper... function valueToPoint(value, index, total) { var x = 0; var y = -value * 0.8; var angle = ((Math.PI * 2) / total) * index; var cos = Math.cos(angle); var sin = Math.sin(angle); var tx = x * cos - y * sin + 100; var ty = x * sin + y * cos + 100; return { x: tx, y: ty }; }
// bootstrap the demo new Vue({ el: "#demo", data: { newLabel: "", stats: stats }, methods: { add: function(e) { e.preventDefault(); if (!this.newLabel) return; this.stats.push({ label: this.newLabel, value: 100 }); this.newLabel = ""; }, remove: function(stat) { if (this.stats.length > 3) { this.stats.splice(this.stats.indexOf(stat), 1); } else { alert("Can't delete more!"); } } } }); </script> </body> </html>
|