メルカリ風UIでの画像アップロード


やりたいこと

メルカリ風の画像アップロードを実装したい
要件は以下の通り

  • 画像アップロードできるボタンが並んでいる
  • ボタンを押すと画像をアップロードできる
  • アップロードした画像が表示されてその分画像アップロードできるボタンが減る

実装後のイメージ

画像アップロード前

画像アップロード後

実装

環境

今回はnuxt.jsで実装を行います。また、CSSデータは載せないので以下のコードを真似しても見た目が同じものはできませんのでご注意ください!

概観

  • 画像やボタンの表示数のmaxを決めておく
  • 画像を表示させつつ、(表示数のmax)- (画像数)の数だけボタンを表示
  • 画像をアップロードしたらデータに画像を入れてからそのボタン(今回はinputで実装)のvalueを削除してファイル名が見えないようにする
  • 画像枚数についてのバリデーション

コード

index.vue
<template>
  <div class="#">
    <div class="#">
      <!-- 以下で画像を表示 -->
      <div
        v-for="(image, index) in imageList"
        :key="index"
        class="#"
      >
        <div class="#">
          <v-img
            :src="image.file"
            max-width="64"
            max-height="64"
          />
        </div>
        <div
          class="#"
          @click="editImage(index)"
        >
          削除
        </div>
      </div>
      <!-- 以下でボタンを表示 -->
      <div
        v-for="n in maxImageNum-imageList.length"
        class="#"
      >
        <div class="#">
          <input
            :id="n"
            type="file"
            accept="image/*"
            @change="addImage(n, $event)"
          >
        </div>
      </div>
    </div>
    <!-- 以下でエラーを表示 -->
    <div
      v-if="!validInput"
      class="error-text"
      style="color: red"
    >
      最低一枚以上の写真が必要です
    </div>
  </div>
</template>

<script>
export default {
  data(){
    return {
      maxImageNum: 4
      imageList: []
    }
  },
  computed(){
    // v-file-inputのバリデーション
    validInput(){
      return this.imageList.length > 0 ? true : false;
    },
  },
  method(){
    // 画像を追加
    addImage(id, e){
      // 画像を読み込み
      const file = e.target.files[0];
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = f => {
        this.imageList.push({file: f.target.result, type: file.type, name: file.name});
      };
      // inputのvalueを削除
      document.getElementById(id).value='';
    },
    // クリックされた時の削除機能
    editImage(index){
      this.imageList.splice(index, 1);
    },
  }
}
</script>

終わりに

もし参考になったという方がいましたらいいね等お願いします!!