Vue.jsとCodeSandboxでトリミング機能の実装 

#0. はじめに
CodeSandboxとvue-cropperjsを使用してVue.jsで簡単なトリミング機能を作成しました。

![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/d4672b13-d3c5-f93d-9eb5-48e3b8878a62.gif)

トリミングの比率を入力することで、様々なサイズにトリミングをすることができます。

- デモページは[こちら](https://64c99.codesandbox.io/)

#1. 実行環境
エディタ : CodeSandbox

### CodeSandbox
- https://codesandbox.io/
- オンラインコードエディター
- Vue.jsのフレームワークを簡単に使える
- ライブラリやコンポーネントの追加も簡単に行える

#2. vue-cropperjsとは
- https://github.com/Agontuk/vue-cropperjs
- [公式デモページ](https://agontuk.github.io/vue-cropperjs/)
- Cropper.jsというJavaScriptの画像トリミング用のライブラリをラッパーして、Vue.js用にコンポーネントしたもの

#3. 準備

1. [CodeSandbox](https://codesandbox.io/)を開く
2. 右上の Create Sandbox をクリック
<img width="1440" alt="vue1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/b5a07abd-d9f2-0139-c0c2-7e379b537912.png">
3. Vueのアイコンをクリック
<img width="1440" alt="vue2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/d4c30725-de35-a18c-c540-8eaa7aaca3a6.png">
4. Vueのプロジェクトが出てくるので、左側のDependenciesの中の Add Dependecy をクリック
<img width="1440" alt="vue3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/86b7f719-a49f-951b-83e6-ff783823252a.png">
5. 検索ボックスで vue-cropperjs を検索
<img width="1440" alt="vue4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/f884c0b1-236b-f114-6d55-a154d51ecae9.png">
6. 1番上に出てきたものをクリック
<img width="1440" alt="vue5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484145/930b77f0-98d1-463b-5946-854fb65b577d.png">
7. Vue.jsのプロジェクトの立ち上げとvue-cropperjsのインストールが終了


#4. プロジェクト作成

1. Cropper.vueの作成

- src/components の中にある HelloWorld.vue の名前を Cropper.vue に変更
<br><br>
- 比率入力フォーム

```Vue
<template>
    <h3>比率を入力</h3>
    横：
    <input v-model="yoko">
    <br>
    縦：
    <input v-model="tate">
    <br>
</template>

<script>
export default {
  data() {
    return {
      yoko: 2, //横の初期値２
      tate: 1, //縦の初期値1 
    };
  }
}
</script>
```

- vue-cropperの実装

```Vue
<template>
<h3>画像を選択</h3>
    <input type="file" name="image" accept="image/*" @change="setImage">
    <br>
    <div
      v-if="imgSrc != ''"
      style="width: 200px; height:200px; border: 1px solid gray; display: inline-block;"
    >
      <vue-cropper
        ref="cropper"
        :guides="true"
        :view-mode="2"
        drag-mode="crop"
        :auto-crop-area="0.5"
        :min-container-width="200"
        :min-container-height="200"
        :background="true"
        :rotatable="false"
        :src="imgSrc"
        :img-style="{ 'width': '200px', 'height': '200px' }"
        :aspect-ratio="yoko / tate"
      >
      <!-- :img-styleで読み込んだ画像の大きさを指定
           :aspect-ratioで比率を指定 -->
      </vue-cropper>
      <br>

      <button @click="cropImage" v-if="imgSrc != ''">トリミングする</button>
</template>

<script>
import VueCropper from "vue-cropperjs"; //ダウンロードしたコンポーネントのインポート
import "cropperjs/dist/cropper.css";
export default {
  components: {
    VueCropper
  },
  data() {
    return {
      imgSrc: "",  //トリミング前の画像のソース
      filename: "" //画像の名前
    };
  },
  methods: {
    setImage(e) {
      const file = e.target.files[0];
      this.filename = file.name;
      if (!file.type.includes("image/")) {    //選択されたものが画像ではなかった場合の処理
        alert("Please select an image file");
        return;
      }
      if (typeof FileReader === "function") {
        const reader = new FileReader();
        reader.onload = event => {
          this.imgSrc = event.target.result;
          this.$refs.cropper.replace(event.target.result);
        };
        reader.readAsDataURL(file);
      } else {
        alert("Sorry, FileReader API not supported");
      }
    }
  }
};
</script>

```

- トリミングされた画像の表示

```Vue
<template>
<div v-if="cropImg != ''">
      <img
        :src="cropImg"
        style="width: yoko; height: tate; border: 1px solid gray;"
        alt="Cropped Image"
      >
</template>

<script>
export default {
  data() {
    return {
      cropImg: "" //トリミング後の画像のソース
    };
  },
  methods: {
    cropImage() {
      this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
    }
  }
};
</script>

```
- ダウンロード

```Vue
<template>
<p>
    <a :href="cropImg" :download="filename">画像を保存</a>
</p>
</template>
```

###  Cropper.vue

```Vue:Cropper.vue
<template>
  <div style="font-size: 14px; text-align: center; width: 100vw;">
    <h2>画像のトリミング</h2>
    <hr>
    <h3>比率を入力</h3>
    横：
    <input v-model="yoko">
    <br>
    縦：
    <input v-model="tate">
    <br>
    <h3>画像を選択</h3>
    <input type="file" name="image" accept="image/*" @change="setImage">
    <br>
    <div
      v-if="imgSrc != ''"
      style="width: 200px; height:200px; border: 1px solid gray; display: inline-block;"
    >
      <vue-cropper
        ref="cropper"
        :guides="true"
        :view-mode="2"
        drag-mode="crop"
        :auto-crop-area="0.5"
        :min-container-width="200"
        :min-container-height="200"
        :background="true"
        :rotatable="false"
        :src="imgSrc"
        :img-style="{ 'width': '200px', 'height': '200px' }"
        :aspect-ratio="yoko / tate"
      ></vue-cropper>
      <br>

      <button @click="cropImage" v-if="imgSrc != ''">トリミングする</button>
    </div>
    <br>
    <br>
    <br>
    <div v-if="cropImg != ''">
      <img
        :src="cropImg"
        style="width: yoko; height: tate; border: 1px solid gray;"
        alt="Cropped Image"
      >
      <p>
        <a :href="cropImg" :download="filename">画像を保存</a>
      </p>
      <br>
    </div>
  </div>
</template>

<script>
import VueCropper from "vue-cropperjs";
import "cropperjs/dist/cropper.css";
export default {
  components: {
    VueCropper
  },
  data() {
    return {
      yoko: 2,
      tate: 1,
      imgSrc: "",
      cropImg: "",
      filename: ""
    };
  },
  methods: {
    setImage(e) {
      const file = e.target.files[0];
      this.filename = file.name;
      if (!file.type.includes("image/")) {
        alert("Please select an image file");
        return;
      }
      if (typeof FileReader === "function") {
        const reader = new FileReader();
        reader.onload = event => {
          this.imgSrc = event.target.result;
          this.$refs.cropper.replace(event.target.result);
        };
        reader.readAsDataURL(file);
      } else {
        alert("Sorry, FileReader API not supported");
      }
    },
    cropImage() {
      this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
    }
  }
};
</script>

<style scoped>
h2 {
  font-size: 25px;
  margin-top: 20px;
}
h3 {
  font-size: 20px;
}
</style>

```

２. App.vueの編集


- ロゴのサイズ調整

```Vue
<img width="25%" src="./assets/logo.png">
```

- Cropper.vueの読み込み

```Vue
<template>
    <div id='app'>
        <Cropper />
    </div>
</template>

<script>
import Cropper from "./components/Cropper";

export default {
  name: "App",
  components: {
    Cropper
  }
};
</script>
```
### App.vue

```vue:App.vue
<template>
  <div id="app">
    <img width="25%" src="./assets/logo.png">
    <Cropper/>
  </div>
</template>

<script>
import Cropper from "./components/Cropper";

export default {
  name: "App",
  components: {
    Cropper
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

#5. デモページ
- [動作のみ](https://64c99.codesandbox.io/)
- [コード付き](https://codesandbox.io/embed/vue-image-64c99?fontsize=14)




#参考

- [【Vue.js】アップロードした画像をトリミングできるコンポーネントを調べた](https://kic-yuuki.hatenablog.com/entry/2019/06/02/170403)
- [君は2クリックでVue appを立ち上げたことがあるか!!<CodeSandboxを世の中に広めなければと思った件>](http://studio-andy.hatenablog.com/entry/codesandbox-vue)