Browse Source

add CardMap view

axolotle 3 years ago
parent
commit
142da32978
1 changed files with 90 additions and 2 deletions
  1. 90 2
      src/pages/library/CardMap.vue

+ 90 - 2
src/pages/library/CardMap.vue

@@ -1,12 +1,100 @@
 <template>
-  <component-debug :component="this" />
+  <b-overlay :show="loading" class="h-100">
+    <svg
+      width="100%" height="100%"
+      ref="svg" id="vis"
+      v-if="textsDepart"
+      :view-box.camel="viewBox"
+    >
+      <g id="cards">
+        <foreignObject
+          v-for="(node, i) in nodes" :key="i"
+          width="500" height="300"
+          :x="node.x" :y="node.y"
+          :transform="`rotate(${node.rotate})`"
+        >
+          <text-mini-card
+            :id="textsDepart[i].id"
+            :text-data="textsDepart[i]"
+          />
+        </foreignObject>
+      </g>
+    </svg>
+  </b-overlay>
 </template>
 
 <script>
+import { mapGetters } from 'vuex'
+
+import { select, randomUniform, zoom } from 'd3'
+import {
+  forceSimulation, forceCenter, forceCollide
+} from 'd3-force'
+
+
+import TextMiniCard from '@/components/text/TextMiniCard'
+
+
 export default {
-  name: 'CardMap'
+  name: 'CardMap',
+
+  components: {
+    TextMiniCard
+  },
+
+  data () {
+    return {
+      loading: true,
+      width: 100,
+      height: 100,
+      nodes: new Array(20).fill().map((_, i) => ({ x: i, rotate: randomUniform(-25, 25)() })),
+      simulation: forceSimulation(),
+      viewBox: null
+    }
+  },
+
+  computed: {
+    ...mapGetters(['textsDepart'])
+  },
+
+  methods: {
+    updateSize () {
+      const { width, height } = this.$refs.svg.getBoundingClientRect()
+      Object.assign(this.$data, { width, height })
+    }
+  },
+
+  created () {
+    this.$store.dispatch('GET_TEXTS_DEPART').then(() => {
+      this.loading = false
+
+      this.$nextTick(() => {
+        this.updateSize()
+        this.viewBox = `0 0 ${this.width} ${this.height}`
+        const svg = select('#vis')
+        const g = select('#cards')
+        svg.call(zoom()
+          .extent([[0, 0], [this.width, this.height]])
+          .scaleExtent([-5, 8])
+          .on('zoom', zoomed))
+
+        function zoomed ({ transform }) {
+          g.attr('transform', transform)
+        }
+
+        this.simulation
+          .nodes(this.nodes)
+          .force('collide', forceCollide(d => randomUniform(250, 350)()))
+          .force('center', forceCenter(this.width * 0.5, this.height * 0.5))
+          .alphaDecay([0.02])
+      })
+    })
+  }
 }
 </script>
 
 <style lang="scss" scoped>
+foreignObject {
+  transform-origin: 250, 150;
+}
 </style>