svg.html 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <script src="../../dist/vue.global.js"></script>
  2. <script>
  3. const { ref, reactive, 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(() => valueToPoint(
  28. +props.stat.value + 10,
  29. props.index,
  30. props.total
  31. ))
  32. }
  33. }
  34. }
  35. </script>
  36. <!-- template for the polygraph component. -->
  37. <script type="text/x-template" id="polygraph-template">
  38. <g>
  39. <polygon :points="points"></polygon>
  40. <circle cx="100" cy="100" r="80"></circle>
  41. <axis-label
  42. v-for="(stat, index) in stats"
  43. :stat="stat"
  44. :index="index"
  45. :total="stats.length">
  46. </axis-label>
  47. </g>
  48. </script>
  49. <script>
  50. const Polygraph = {
  51. props: ['stats'],
  52. template: '#polygraph-template',
  53. setup(props) {
  54. return {
  55. points: computed(() => {
  56. const total = props.stats.length
  57. return props.stats.map((stat, i) => {
  58. const point = valueToPoint(stat.value, i, total)
  59. return point.x + ',' + point.y
  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. createApp({
  98. components: {
  99. Polygraph
  100. },
  101. setup() {
  102. const newLabel = ref('')
  103. const stats = reactive(globalStats)
  104. function add(e) {
  105. e.preventDefault()
  106. if (!newLabel.value) return
  107. stats.push({
  108. label: newLabel.value,
  109. value: 100
  110. })
  111. newLabel.value = ''
  112. }
  113. function remove(stat) {
  114. if (stats.length > 3) {
  115. stats.splice(stats.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: .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>