Vue.jsとsvgで図形をドラッグ＆ドロップする

初めての記事で書き方が分からないけど、とりあえず書いてみたいと思います。

## 作ったもの
![svg1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659065/f35d01a4-5efd-f2b2-7ef6-7f6da5075a85.png)
↓
![svg2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659065/7bde9887-7b26-8997-af22-960dc4c0d7cb.png)
↓
![svg3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659065/c1ee43d4-3e8f-a6ff-b6dd-c27c9cfd07ac.png)
※GIFが表示されなかったので画像になります・・・

svg内に図形（四角形）を作り、それをドラッグ＆ドロップで移動させることができます。

```App.vue
<template>
  <div class="container">
    <div class="text">枠の中に図形を移動できます</div>
    <!-- SVG定義 -->
    <svg>
      <rect v-for="(r, idx) in rects" :key="idx"
        @mousedown="move($event, idx)"
        :fill="r.color" :stroke="r.stroke"
        :rx="r.rx"
        :x="r.x" :y="r.y" :width="r.w" :height="r.h">{{message}}
      </rect>
      <rect stroke="darkblue" stroke-width="5" fill="none" 
      x="600" :y="100" :width="300" :height="300">
      </rect>
    </svg>
  </div>
</template>

<script>
export default {
  name: 'SVGDemo',
  data () {
    return {
      ratio: 1,
      dx: 0,
      dy: 0,
      viewport: '0 0 500 500',
      isMove: false,
      beforeMouseX: null,
      beforeMouseY: null,
      selectIdx: 0,
      rects: [
        {
          x: 10,
          y: 10,
          w: 100,
          h: 100,
          id: "green",
          color: 'green',
          stroke: 'black',
        },
        {
          x: 200,
          y: 150,
          w: 100,
          h: 100,
          color: 'red',
          stroke: 'black',
        },
        {
          x: 310,
          y: 410,
          w: 200,
          h: 100,
          color: 'blue',
          stroke: 'black',
        },
        {
          x: 100,
          y: 200,
          w: 200,
          h: 100,
          color: 'yellow',
          stroke: 'black',
        },
        {
          x: 300,
          y: 300,
          w: 50,
          h: 50,
          color: 'skyblue',
          stroke: 'black',
        },
        {
          x: 280,
          y: 280,
          w: 50,
          h: 50,
          color: 'white',
          stroke: 'black',
        },
        {
          x: 300,
          y: 300,
          w: 50,
          h: 50,
          color: 'pink',
          stroke: 'black',
        },
        {
          x: 150,
          y: 300,
          w: 50,
          h: 50,
          color: 'lightgreen',
          stroke: 'black',
        },
        {
          x: 50,
          y: 400,
          w: 200,
          h: 50,
          color: 'black',
          stroke: 'black',
        },
        {
          x: 50,
          y: 450,
          w: 200,
          h: 50,
          color: 'brown',
          stroke: 'black',
        },
      ]
    } 
  },
  // マウス操作関連
  mounted () {
    console.log('MOUNT LISTENER ON')
    document.addEventListener('mouseup', this.mouseUp)
    document.addEventListener('mousemove', this.mouseMove)
  },
  beforeDestroy () {
    console.log('MOUNT LISTENER OFF')
    document.removeEventListener('mouseup', this.mouseUp)
    document.removeEventListener('mousemove', this.mouseMove)
  },
  methods: {
    
    // 図形を動かすフラグを立てる
    move (e, i) {
      this.isMove = true
      this.selectIdx = i
      e.preventDefault()
    },
    // マウスのクリックが終わった段階で初期化
    mouseUp (e) {
      this.isMove = false
      this.beforeMouseX = null
      this.beforeMouseY = null
      e.preventDefault()
    },
    // move中は前回まで動かした差分を取りながら座標を変化させていく
    mouseMove (e) {
      if (!this.isMove) return
      var mouseX = e.offsetX * this.ratio + this.dx
      var mouseY = e.offsetY * this.ratio + this.dy
      var dx = 0
      var dy = 0
      if (this.beforeMouseX && this.beforeMouseY) {
          dx = mouseX - this.beforeMouseX
          dy = mouseY - this.beforeMouseY
      }
      this.beforeMouseX = mouseX
      this.beforeMouseY = mouseY
      var tempX = dx + Number(this.rects[this.selectIdx].x)
      var tempY = dy + Number(this.rects[this.selectIdx].y)
      //var tempX = dx + Number(this.polygons[this.selectIdx].x)
      //var tempY = dy + Number(this.polygons[this.selectIdx].y)
      if (tempX > 0) this.rects[this.selectIdx].x = tempX
      if (tempY > 0) this.rects[this.selectIdx].y = tempY
      e.preventDefault()
    }
  },
  
}
</script>

<style>
.text {
  height:50px;
  width:1000px;
  font-size:35px;
  text-align: center;
  background-color: tomato;
}

svg {
  width : 1000px;
  height: 500px;
  background-color: silver;
}
</style>
```

## 作った理由
javascriptが苦手なので勉強のために作りました。また、vue.jsはスクールで学ばなかったことと、比較的簡単（本当か？）とのことで取り組んでみました。
できたものに関してですが、本当はタングラムパズル（↓こんなやつ）
![tangrampazle.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659065/94ec3e83-d7d1-8a76-d2a6-099551dbd6cb.jpeg)
を作りたかったのですが、polygonのx座標y座標をvue.js上にdataとして渡すことができなかったためにrectのみで作成しました。
<font color="red">もしご存知の方は教えてください！！</font>

## 試したこと
①rect図形に斜線を引いて２分割し、片側を透明にしてしまえば三角形になるのでは？
→結果的にできず
②rect図形にクラス名を付けて個別にstyleを付ければ三角形になるのでは？
→結果的にできず
③polygon図形自体は表示できたので動かしてみよう
→結果的に動かず

いろいろ考えましたが、自分の調べたこと・理解では解決できませんでした。

## 今後の展望
もっと勉強してタングラムパズルにしたいです。

## 参考文献
①https://qiita.com/masa0209/items/27fb36e939d08b77def7
②https://fuwafuwac.com/?p=748

こんなので良いのかわかりませんが、これから備忘録として記事を書いていきたいと思います。