svg.html 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <script src="../../dist/vue.min.js"></script>
  2. <script>
  3. const { ref, computed, createApp } = Vue
  4. // math helper...
  5. function valueToPoint(value, index, total) {
  6. var x = 0
  7. var y = -value * 0.8
  8. var angle = ((Math.PI * 2) / total) * index
  9. var cos = Math.cos(angle)
  10. var sin = Math.sin(angle)
  11. var tx = x * cos - y * sin + 100
  12. var ty = x * sin + y * cos + 100
  13. return {
  14. x: tx,
  15. y: ty
  16. }
  17. }
  18. const AxisLabel = {
  19. template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
  20. props: {
  21. stat: Object,
  22. index: Number,
  23. total: Number
  24. },
  25. setup(props) {
  26. return {
  27. point: computed(() =>
  28. valueToPoint(+props.stat.value + 10, props.index, props.total)
  29. )
  30. }
  31. }
  32. }
  33. </script>
  34. <!-- template for the polygraph component. -->
  35. <script type="text/x-template" id="polygraph-template">
  36. <g>
  37. <polygon :points="points"></polygon>
  38. <circle cx="100" cy="100" r="80"></circle>
  39. <axis-label
  40. v-for="(stat, index) in stats"
  41. :stat="stat"
  42. :index="index"
  43. :total="stats.length">
  44. </axis-label>
  45. </g>
  46. </script>
  47. <script>
  48. const Polygraph = {
  49. props: ['stats'],
  50. template: '#polygraph-template',
  51. setup(props) {
  52. return {
  53. points: computed(() => {
  54. const total = props.stats.length
  55. return props.stats
  56. .map((stat, i) => {
  57. const point = valueToPoint(stat.value, i, total)
  58. return point.x + ',' + point.y
  59. })
  60. .join(' ')
  61. })
  62. }
  63. },
  64. components: {
  65. AxisLabel
  66. }
  67. }
  68. </script>
  69. <!-- demo root element -->
  70. <div id="demo">
  71. <!-- Use the polygraph component -->
  72. <svg width="200" height="200">
  73. <polygraph :stats="stats"></polygraph>
  74. </svg>
  75. <!-- controls -->
  76. <div v-for="stat in stats">
  77. <label>{{stat.label}}</label>
  78. <input type="range" v-model="stat.value" min="0" max="100" />
  79. <span>{{stat.value}}</span>
  80. <button @click="remove(stat)" class="remove">X</button>
  81. </div>
  82. <form id="add">
  83. <input name="newlabel" v-model="newLabel" />
  84. <button @click="add">Add a Stat</button>
  85. </form>
  86. <pre id="raw">{{ stats }}</pre>
  87. </div>
  88. <script>
  89. const globalStats = [
  90. { label: 'A', value: 100 },
  91. { label: 'B', value: 100 },
  92. { label: 'C', value: 100 },
  93. { label: 'D', value: 100 },
  94. { label: 'E', value: 100 },
  95. { label: 'F', value: 100 }
  96. ]
  97. new Vue({
  98. components: {
  99. Polygraph
  100. },
  101. setup() {
  102. const newLabel = ref('')
  103. const stats = ref(globalStats)
  104. function add(e) {
  105. e.preventDefault()
  106. if (!newLabel.value) return
  107. stats.value.push({
  108. label: newLabel.value,
  109. value: 100
  110. })
  111. newLabel.value = ''
  112. }
  113. function remove(stat) {
  114. if (stats.value.length > 3) {
  115. stats.value.splice(stats.value.indexOf(stat), 1)
  116. } else {
  117. alert("Can't delete more!")
  118. }
  119. }
  120. return {
  121. newLabel,
  122. stats,
  123. add,
  124. remove
  125. }
  126. }
  127. }).$mount('#demo')
  128. </script>
  129. <style>
  130. body {
  131. font-family: Helvetica Neue, Arial, sans-serif;
  132. }
  133. polygon {
  134. fill: #42b983;
  135. opacity: 0.75;
  136. }
  137. circle {
  138. fill: transparent;
  139. stroke: #999;
  140. }
  141. text {
  142. font-family: Helvetica Neue, Arial, sans-serif;
  143. font-size: 10px;
  144. fill: #666;
  145. }
  146. label {
  147. display: inline-block;
  148. margin-left: 10px;
  149. width: 20px;
  150. }
  151. #raw {
  152. position: absolute;
  153. top: 0;
  154. left: 300px;
  155. }
  156. </style>