Webカメラの上に透かし的なものを重ねたい

完成形


コード

<template>
  <div class="root">
    <div class="photo">
      <video ref="video" id="video"></video>
      <canvas ref="canvas" id="canvas"></canvas>

      <div class="l2">
        <canvas id="camera_record_box"></canvas>
      </div>
    </div>

    <div>
      <div class="button1" @click="capture()">写真を撮影</div>
      <div class="button1" @click="takePicture()">写真を選択</div>
      <div class="button1" @click="captures = []">リセット</div>
    </div>

    <div class="captureWrap">
      <div class="capture" v-for="c in captures" v-bind:key="c.d">
        <img v-bind:src="c" />
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted } from "vue";
import { Camera } from "@capacitor/camera";

export default defineComponent({
  setup() {
    const video = ref();
    const canvas = ref();
    const captures = ref([]) as any;

    const constraints = {
      audio: false,
      video: {
        width: 390,
        height: 390,
        facingMode: "user",
        // facingMode: {
        //   exact: "environment",
        // },
      },
    };

    onMounted(() => {
      if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices
          .getUserMedia(constraints)
          .then((stream) => {
            video.value.srcObject = stream;
            video.value.play();
          })
          .catch((e: any) => {
            console.log("ERROR: ", e);
          });
      }
    });

    const capture = () => {
      if (captures.value.length < 4) {
        canvas.value.getContext("2d").drawImage(video.value, 0, 0, 390, 390);
        captures.value.push(canvas.value.toDataURL("image/png"));
        console.log(captures.value);
      }
    };

    const takePicture = async () => {
      const image = await Camera.pickImages({
        quality: 90,
        limit: 4,
        // source: CameraSource.Photos,
        // allowEditing: true,
        // resultType: CameraResultType.Base64,
      });

      for (let item in image.photos) {
        if (captures.value.length < 4) {
          const base64Data = await base64FromPath(image.photos[item].webPath);
          captures.value.push(base64Data);
        }
      }
    };

    const base64FromPath = async (path: string): Promise<string> => {
      const response = await fetch(path);
      const blob = await response.blob();
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onerror = reject;
        reader.onload = () => {
          if (typeof reader.result === "string") {
            resolve(reader.result);
          } else {
            reject("method did not return a string");
          }
        };
        reader.readAsDataURL(blob);
      });
    };

    return { capture, video, canvas, captures, takePicture };
  },
});
</script>

<style lang="scss">
.root {
  margin: 0 auto;
  width: 390px;
  position: relative;
}

.photo {
  width: 390px;
  height: 390px;
  margin-bottom: 24px;
}
.l2 {
  top: 0;
  position: absolute;
}
#camera_record_box {
  position: absolute;
  width: 390px;
  height: 390px;
  background-image: url("../square-1.png");
  opacity: 0.5;
}
#canvas {
  display: none;
}

.captureWrap {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  .capture {
    img {
      width: 100px;
      height: 100px;
    }
  }
}

.button1 {
  background-color: red;
  color: #fff;
  padding: 16px;
  margin-bottom: 12px;
}
</style>