CardMap.vue 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. <template>
  2. <b-overlay :show="loading" class="h-100">
  3. <svg
  4. width="100%" height="100%"
  5. ref="svg" id="vis"
  6. v-if="textsDepart"
  7. :view-box.camel="viewBox"
  8. >
  9. <g id="cards">
  10. <foreignObject
  11. v-for="(node, i) in nodes" :key="i"
  12. width="500" height="300"
  13. :x="node.x" :y="node.y"
  14. :transform="`rotate(${node.rotate})`"
  15. >
  16. <text-mini-card
  17. :id="textsDepart[i].id"
  18. :text-data="textsDepart[i]"
  19. />
  20. </foreignObject>
  21. </g>
  22. </svg>
  23. </b-overlay>
  24. </template>
  25. <script>
  26. import { mapGetters } from 'vuex'
  27. import { select, randomUniform, zoom } from 'd3'
  28. import {
  29. forceSimulation, forceCenter, forceCollide
  30. } from 'd3-force'
  31. import TextMiniCard from '@/components/text/TextMiniCard'
  32. export default {
  33. name: 'CardMap',
  34. components: {
  35. TextMiniCard
  36. },
  37. data () {
  38. return {
  39. loading: true,
  40. width: 100,
  41. height: 100,
  42. nodes: new Array(20).fill().map((_, i) => ({ x: i, rotate: randomUniform(-25, 25)() })),
  43. simulation: forceSimulation(),
  44. viewBox: null
  45. }
  46. },
  47. computed: {
  48. ...mapGetters(['textsDepart'])
  49. },
  50. methods: {
  51. updateSize () {
  52. const { width, height } = this.$refs.svg.getBoundingClientRect()
  53. Object.assign(this.$data, { width, height })
  54. }
  55. },
  56. created () {
  57. this.$store.dispatch('GET_TEXTS_DEPART').then(() => {
  58. this.loading = false
  59. this.$nextTick(() => {
  60. this.updateSize()
  61. this.viewBox = `0 0 ${this.width} ${this.height}`
  62. const svg = select('#vis')
  63. const g = select('#cards')
  64. svg.call(zoom()
  65. .extent([[0, 0], [this.width, this.height]])
  66. .scaleExtent([-5, 8])
  67. .on('zoom', zoomed))
  68. function zoomed ({ transform }) {
  69. g.attr('transform', transform)
  70. }
  71. this.simulation
  72. .nodes(this.nodes)
  73. .force('collide', forceCollide(d => randomUniform(250, 350)()))
  74. .force('center', forceCenter(this.width * 0.5, this.height * 0.5))
  75. .alphaDecay([0.02])
  76. })
  77. })
  78. }
  79. }
  80. </script>
  81. <style lang="scss" scoped>
  82. foreignObject {
  83. transform-origin: 250, 150;
  84. }
  85. </style>