h5调用摄像头和本地文件选择图片

前言

使用vue2+js语法

通过navigator.mediaDevices.getUserMedia()访问摄像头,但需要在localhost或https下才可以正常访问,否则没有权限。

通过input标签在浏览器环境访问本地文件图片;

拍照通过canvas的drawImage绘制video视频帧,再通过toDataURL将绘制的canvas转换为图片,获取到图片的base64码;获取本地图片同样获取base64码;

实现效果

完整代码

xml 复制代码
<template>
  <div>
    <!-- 已上传图片展示 -->
    <div id="uploader_img">
      <div v-for="(item, index) in imglists" :key="item.name" class="img-box">
        <div class="img-item">
          <div class="img-item-img">
            <img :src="item.url" alt="" class="img-item-img-content" />
          </div>

          <div class="img-info">
            <div class="img-name">{{ item.name }}</div>
            <div class="img-size">{{ item.size }}</div>
          </div>
        </div>
        <div @click="deleteImg(imglists, index)" class="img-delete">
          <!-- 删除按钮 -->
          <svg
            t="1744701515127"
            class="icon delete_btn"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="2626"
            width="200"
            height="200"
          >
            <path
              d="M512 177.980952c-181.638095 0-329.142857 147.504762-329.142857 329.142858s147.504762 329.142857 329.142857 329.142857 329.142857-147.504762 329.142857-329.142857-147.504762-329.142857-329.142857-329.142858z m140.190476 425.447619L608.304762 646.095238 512 549.790476 415.695238 646.095238l-42.666667-42.666667 96.304762-96.304761-96.304762-96.304762 42.666667-42.666667L512 463.238095l96.304762-96.304762 42.666667 42.666667-96.304762 96.304762 97.523809 97.523809z"
              fill="#bfbfbf"
              p-id="2627"
            ></path>
          </svg>
        </div>
      </div>
    </div>
    <!-- 点击上传 - 长框 -->
    <div
      @click="startCamera"
      :class="{ 'upload-box': !isShowCamera, 'upload-box-none': isShowCamera }"
    >
      <div>
        <svg
          t="1744621394979"
          class="icon uploader_clibtn"
          viewBox="0 0 1024 1024"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          p-id="2846"
          width="200"
          height="200"
        >
          <path
            d="M536.380952 288.377905V658.285714a24.380952 24.380952 0 0 1-48.761904 0V288.377905L391.314286 384.731429a24.380952 24.380952 0 0 1-34.474667-34.474667l137.898667-137.898667a24.283429 24.283429 0 0 1 34.523428 0l137.898667 137.898667a24.380952 24.380952 0 0 1-34.474667 34.474667L536.380952 288.377905zM97.52381 731.428571a24.380952 24.380952 0 0 1 48.761904 0v97.52381a24.380952 24.380952 0 0 0 24.380953 24.380952h682.666666a24.380952 24.380952 0 0 0 24.380953-24.380952v-97.52381a24.380952 24.380952 0 0 1 48.761904 0v97.52381a73.142857 73.142857 0 0 1-73.142857 73.142857H170.666667A73.142857 73.142857 0 0 1 97.52381 828.952381v-97.52381z"
            fill="#9DA7B2"
            p-id="2847"
          ></path>
        </svg>
      </div>

      <div>点击上传</div>
    </div>
    <!-- 点击上传 - 方框 -->
    <div @click="startCamera" style="display: none">
      <svg
        t="1744178513446"
        class="icon uploader_squarebtn"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="12086"
        width="200"
        height="200"
      >
        <path
          d="M997.28 56l-27.712 16A47.968 47.968 0 0 0 928 48v-32c28.864 0 55.072 15.456 69.28 40z m10.72 134.048h-32v-64h32v64z m0 128h-32v-64h32v64z m0 128h-32v-64h32v64z m0 128h-32v-64h32v64z m0 128h-32v-64h32v64z m0 128h-32v-64h32v64z m-8.8 134.464l-28.48-14.624c3.456-6.72 5.28-14.144 5.28-21.888v-33.952h32V928c0 12.864-3.04 25.28-8.8 36.48zM867.936 1008v-32h60.288l2.976-0.096 2.08 31.936-2.464 0.096-62.88 0.064z m-128 0v-32h64v32h-64z m-128 0v-32h64v32h-64z m-128 0v-32h64v32h-64z m-128 0v-32h64v32h-64z m-128 0v-32h64v32h-64z m-128 0v-32h64v32h-64zM16 901.888h32V928c0 9.728 2.88 18.976 8.192 26.88l-26.496 17.92A79.68 79.68 0 0 1 16 928v-26.112z m0-128h32v64h-32v-64z m0-128h32v64h-32v-64z m0-128h32v64h-32v-64z m0-128h32v64h-32v-64z m0-128h32v64h-32v-64z m0-128h32v64h-32v-64zM86.976 16.512l3.584 31.776c-17.088 1.92-31.776 12.928-38.592 28.544l-29.344-12.8A80.064 80.064 0 0 1 86.976 16.544zM216.128 16v32h-64v-32h64z m128 0v32h-64v-32h64z m128 0v32h-64v-32h64z m128 0v32h-64v-32h64z m128 0v32h-64v-32h64z m128 0v32h-64v-32h64zM928 16v32h-7.872v-32H928z"
          fill="#8a8a8a"
          p-id="12087"
        ></path>
        <path d="M255.872 544v-32h480v32z" fill="#8a8a8a" p-id="12088"></path>
        <path d="M479.872 288h32v480h-32z" fill="#8a8a8a" p-id="12089"></path>
      </svg>
    </div>
    <!-- 弹框选择:相册/拍照 -->
    <div id="myModal" class="choose_modal">
      <div class="choosemodal-content">
        <div class="choosemodal-option" id="openCamera" @click="openCamera">
          打开相机
        </div>
        <div class="choosemodal-option" id="openAlbum" @click="openAlbum">
          打开相册
        </div>
      </div>
    </div>
    <!-- 本地文件选择 仅相册 -->
    <input
      type="file"
      id="fileInput"
      accept="image/png,image/jpeg"
      capture="filesystem"
      hidden
    />
    <!-- 相机拍照界面 -->
    <div id="cameraModal" class="camera_modal">
      <!-- 相机拍照界面 -->
      <div v-show="!isCapture">
        <div class="camera-container">
          <video id="video" autoplay playsinline></video>

          <!-- 人像框图片 -->
          <div class="frame">
            <svg
              t="1744250348203"
              class="icon camera_portrait"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="38048"
              width="200"
              height="200"
            >
              <path
                d="M953 965.52a5.52 5.52 0 0 1-4.75-2.71c-6.03-10.24-13.6-18.69-23.15-25.84-15.75-11.78-34.15-21.8-54.67-29.78-20.81-8.09-42.51-15.34-64.51-21.53-22.29-6.26-43.23-13.44-62.24-21.32-21.62-8.82-38.73-19.05-50.83-30.4-12.15-11.39-21.31-23.6-27.22-36.29-5.92-12.71-9.33-25.61-10.11-38.34-0.78-12.46 0-24.93 2.33-37.05 3.21-20.68 9.88-35.75 19.81-44.77 8.97-8.15 20.8-18.25 35.15-30 5.46-4.97 10.53-12.09 15.08-21.17 4.76-9.49 9.04-19.33 12.72-29.25 3.72-11.58 7.49-24.1 11.22-37.23l0.81-2.87 2.85-0.88c6.72-2.08 13.24-6.02 19.38-11.69 5.53-5.02 10.63-12.17 15.2-21.29 4.57-9.1 7.61-21.66 9.05-37.33l0.02-0.16c1.43-11.83 1.27-21.87-0.47-29.84-1.78-8.07-4.06-14.68-6.79-19.63l-0.14-0.27c-2.65-5.5-6.17-9.82-10.76-13.22l-2.23-1.65v-2.77c0-31.71-1.89-63.46-5.61-94.35-3.7-26.84-10.1-55.56-19.03-85.29-8.81-29.32-22.96-56.53-42.03-80.88-7.99-10.55-19.22-21.23-33.44-31.82-14.3-10.66-30.89-20.32-49.31-28.73-18.41-8.41-38.42-15.2-59.47-20.18-20.97-4.96-42.41-7.47-63.74-7.47-16.99 0-34.79 1.36-52.9 4.05-17.91 2.65-36.09 7.85-54.06 15.45-17.96 7.61-35.81 18.23-53.05 31.55-17.15 13.25-32.9 30.76-46.81 52.03l-0.09 0.14c-15.39 22.03-27.1 47.51-34.82 75.71-7.79 28.51-13.26 55.47-16.23 80.14-3.72 29.35-5.23 59.06-4.48 88.34l0.05 2.13-1.4 1.61c-6.29 7.26-10.89 14.95-13.67 22.85l-0.06 0.15c-2.83 7.36-4.81 16.35-5.88 26.74-1.04 10.1 0.58 21.55 4.83 34.02l0.08 0.27c3.57 12.58 7.83 22.23 12.68 28.69 4.95 6.6 9.57 11.79 13.74 15.42 4.69 3.45 9.38 5.88 13.95 7.23l2.91 0.86 0.83 2.92c3.72 13.12 7.5 25.65 11.22 37.23 3.68 9.91 7.8 19.8 12.23 29.4 4.17 9.02 9.04 16.11 14.48 21.05 12.85 10.97 24.72 21.91 35.3 32.5 11.44 11.44 18.07 27 19.68 46.24l0.01 0.13c0.76 12.61 1.14 24.17 1.14 34.34 0 10.83-2.03 21.39-6.05 31.37-3.99 9.93-10.41 19.94-19.07 29.74-8.57 9.71-21.13 19.91-37.33 30.31-20.7 13.52-44.82 24.02-71.65 31.18-26.04 6.94-51.55 14.98-75.84 23.89-23.79 8.73-44.94 20.65-62.84 35.44-6.81 5.62-12.47 12.23-17.3 20.19a5.508 5.508 0 0 1-7.56 1.85 5.508 5.508 0 0 1-1.85-7.56c5.48-9.04 11.93-16.55 19.7-22.97 18.87-15.58 41.09-28.12 66.06-37.28 24.6-9.03 50.44-17.17 76.8-24.19 25.7-6.86 48.75-16.88 68.51-29.78 15.38-9.88 27.16-19.42 35.06-28.36 7.83-8.86 13.59-17.8 17.11-26.56 3.49-8.67 5.25-17.85 5.25-27.27 0-9.94-0.38-21.25-1.12-33.62-1.4-16.54-6.95-29.77-16.49-39.31-10.4-10.41-22.08-21.16-34.73-31.97l-0.12-0.11c-6.55-5.94-12.31-14.23-17.13-24.63-4.57-9.9-8.81-20.09-12.6-30.31l-0.08-0.23c-3.51-10.91-7.06-22.64-10.57-34.92-4.89-1.81-9.79-4.52-14.61-8.09l-0.33-0.26c-4.8-4.15-10.02-9.97-15.5-17.28-5.72-7.62-10.44-18.14-14.42-32.15-4.72-13.94-6.51-26.91-5.31-38.56 1.17-11.32 3.36-21.24 6.53-29.48 3.03-8.58 7.84-16.88 14.31-24.69-0.64-29.15 0.91-58.71 4.61-87.87 3.04-25.16 8.6-52.66 16.54-81.69 8.05-29.39 20.28-55.99 36.37-79.04 14.57-22.27 31.14-40.65 49.24-54.64 17.99-13.9 36.66-24.99 55.49-32.97 18.83-7.97 37.92-13.42 56.74-16.21 18.65-2.77 36.99-4.17 54.51-4.17 22.18 0 44.48 2.61 66.28 7.77 21.76 5.15 42.45 12.17 61.51 20.88 19.12 8.72 36.39 18.79 51.31 29.91 15.05 11.21 27.02 22.63 35.58 33.93 19.87 25.36 34.64 53.79 43.85 84.43 9.09 30.27 15.62 59.55 19.4 87.04 3.68 30.56 5.59 61.83 5.7 93.1 4.9 4.14 8.87 9.3 11.83 15.39 3.25 5.93 5.82 13.28 7.83 22.46 2 9.16 2.22 20.4 0.66 33.43-1.57 17.01-4.99 30.86-10.16 41.18-5.18 10.34-11.11 18.58-17.61 24.48-6.47 5.97-13.42 10.38-20.67 13.1-3.51 12.29-7.07 24.03-10.58 34.94l-0.08 0.23a304.982 304.982 0 0 1-13.24 30.46c-5.19 10.35-11.12 18.59-17.63 24.48l-0.21 0.18c-14.26 11.68-25.98 21.68-34.84 29.73-8.04 7.3-13.54 20.23-16.35 38.41l-0.04 0.2c-2.17 11.24-2.9 22.81-2.17 34.39 0.7 11.36 3.77 22.93 9.11 34.38 5.32 11.41 13.66 22.49 24.78 32.91 11.12 10.42 27.1 19.93 47.5 28.25 18.63 7.73 39.16 14.76 61.03 20.91 22.34 6.29 44.38 13.65 65.52 21.87 21.45 8.34 40.72 18.85 57.27 31.22 10.59 7.92 19.36 17.7 26.05 29.07 1.54 2.62 0.67 5.99-1.95 7.53-0.91 0.51-1.87 0.75-2.81 0.75z"
                fill="#1296db"
                p-id="38049"
              ></path>
            </svg>
          </div>
          <!-- <img src="https://ss-mpvolc.meipian.me/users/24620570/36a3481767dc4fbc9654cc87c008daeb__jpg.heic~tplv-s1ctq42ewb-s3-cC-q:750:0:0:0:q80.webp" alt="人像框" class="frame"> -->
        </div>
        <div class="camera-container">
          <!-- 返回上传页面 -->
          <div @click="backToUpload" id="back-to-upload">
            <svg
              t="1744709693613"
              class="icon capture_btn_other"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="3443"
              width="200"
              height="200"
            >
              <path
                d="M648 307.2H217.6l128-128c12.8-12.8 12.8-32 0-44.8-12.8-12.8-32-12.8-44.8 0L118.4 315.2c-6.4 6.4-9.6 14.4-9.6 22.4s3.2 16 9.6 22.4l180.8 180.8c12.8 12.8 32 12.8 44.8 0 12.8-12.8 12.8-32 0-44.8l-124.8-124.8h428.8c120 0 216 96 216 216s-96 216-216 216H320c-17.6 0-32 14.4-32 32s14.4 32 32 32h328c155.2 0 280-124.8 280-280s-124.8-280-280-280z"
                fill="#ffffff"
                p-id="3444"
              ></path>
            </svg>
          </div>
          <div id="capture-btn" @click="capture">
            <!-- 拍照按钮 -->
            <div id="capture-out">
              <div id="capture-inner"></div>
            </div>
          </div>
          <div></div>
        </div>
      </div>

      <canvas id="canvas" class="isnone"></canvas>
      <!-- 拍照后的图片展示 -->
      <div v-show="isCapture" class="camera-container">
        <div id="capture-img"></div>
        <div id="capture_img_result">
          <!-- 返回上传页面 -->
          <div @click="backToUpload">
            <svg
              t="1744709693613"
              class="icon capture_btn_other"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="3443"
              width="200"
              height="200"
            >
              <path
                d="M648 307.2H217.6l128-128c12.8-12.8 12.8-32 0-44.8-12.8-12.8-32-12.8-44.8 0L118.4 315.2c-6.4 6.4-9.6 14.4-9.6 22.4s3.2 16 9.6 22.4l180.8 180.8c12.8 12.8 32 12.8 44.8 0 12.8-12.8 12.8-32 0-44.8l-124.8-124.8h428.8c120 0 216 96 216 216s-96 216-216 216H320c-17.6 0-32 14.4-32 32s14.4 32 32 32h328c155.2 0 280-124.8 280-280s-124.8-280-280-280z"
                fill="#ffffff"
                p-id="3444"
              ></path>
            </svg>
          </div>
          <!-- 选择当前拍照图片 -->
          <div @click="chooseCurrentCapture">
            <svg
              t="1744709761210"
              class="icon capture_btn_other"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="4467"
              width="200"
              height="200"
            >
              <path
                d="M939.36 218.912a32 32 0 0 1 45.856 44.672l-538.016 552a32 32 0 0 1-43.776 1.92L63.872 526.048a32 32 0 1 1 41.696-48.544l316.768 271.936L939.36 218.88z"
                fill="#ffffff"
                p-id="4468"
              ></path>
            </svg>
          </div>
          <!-- 取消当前拍照图片 - 重新拍照 -->
          <div @click="reCapture">
            <svg
              t="1744709781811"
              class="icon capture_btn_other"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="5521"
              width="200"
              height="200"
            >
              <path
                d="M842.947458 778.116917 576.847937 512.013303 842.946434 245.883083c8.67559-8.674567 13.447267-20.208251 13.43908-32.477692-0.008186-12.232602-4.7727-23.715121-13.414521-32.332383-8.655124-8.677637-20.149922-13.450337-32.384571-13.4575-12.286838 0-23.808242 4.771677-32.474622 13.434987L512.019443 447.143876 245.88206 181.050496c-8.66331-8.66331-20.175505-13.434987-32.416294-13.434987-12.239765 0-23.75196 4.770653-32.414247 13.43294-8.66024 8.636704-13.428847 20.12434-13.437034 32.356942-0.008186 12.269441 4.76349 23.803125 13.437034 32.476669l266.135336 266.13022L181.050496 778.11794c-8.664334 8.66331-13.43601 20.173458-13.43601 32.41527 0 12.239765 4.7727 23.752983 13.437034 32.417317 8.662287 8.66331 20.173458 13.43294 32.413224 13.43294 12.240789 0 23.754007-4.770653 32.416294-13.43294l266.134313-266.100544 266.101567 266.100544c8.66331 8.66331 20.185738 13.43294 32.4429 13.43294 12.265348-0.008186 23.74889-4.771677 32.369222-13.412474C860.81643 825.081555 860.821547 795.991006 842.947458 778.116917z"
                fill="#ffffff"
                p-id="5522"
              ></path>
            </svg>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "CustomUpload",
  data() {
    return {
      //  预览图片
      previewImage: null,
      //  预览视频
      previewVideo: null,

      // 视频流
      mediaStream: null,

      //  点击上传
      isShowCamera: false,
      // 选择弹框
      chooseModal: null,
      // 相机弹框
      cameraModal: "block",
      // video元素
      videoDom: null,
      // canvas元素
      canvasDom: null,
      // 拍照图片
      imglistDom: null,
      // 拍照图片显示
      uploaderImgDom: null,
      // 拍照图片列表
      imglists: [],
      // 点击拍照按钮
      isCapture: false,
      // 当前拍照图片url
      currentCaptureImg: null,
    };
  },
  methods: {
    init() {
      this.chooseModal = document.getElementById("myModal");
      this.cameraModal = document.getElementById("cameraModal");
      this.videoDom = document.getElementById("video");
      this.canvasDom = document.getElementById("canvas");
      this.imglistDom = document.getElementById("capture-img");
      this.uploaderImgDom = document.getElementById("uploader_img");
      this.getAlbumImgInfo();
    },
    // 点击任意位置关闭弹框
    closeChooseModal() {
      let modal = this.chooseModal;
      // 点击任意位置关闭弹框
      window.onclick = function (event) {
        if (event.target == modal) {
          modal.style.display = "none";
        }
      };
    },
    // 点击上传-打开选择弹框
    openChooseModal() {
      let modal = this.chooseModal;
      modal.style.display = "block";
    },
    // 点击上传
    startCamera() {
      this.openChooseModal();
    },
    // 关闭选择弹框
    closeModal() {
      let modal = this.chooseModal;
      modal.style.display = "none";
    },
    // 打开相册
    openAlbum() {
      console.log("打开相册", window, window.nativeMethod);
      // document.getElementById("fileInput").click();
      // this.closeModal();
      // H5 调用原生方法
      window.nativeMethod.chooseImageFromAlbum();
    },
    // 监听获取相册选择图片信息
    getAlbumImgInfo(e) {
      const _this = this;
      document
        .getElementById("fileInput")
        .addEventListener("change", function (event) {
          console.log("change-本地图片", event);
          const file = event.target.files[0]; // 获取用户选择的文件
          console.log("获取用户选择的文件", file);
          console.log("获取用户选择的文件name", file.name);
          let imageName = file.name;
          let fileType = file.type;
          if (file) {
            const reader = new FileReader(); // 创建FileReader对象
            reader.onload = function (e) {
              console.log("eeeeeeeeeeeee", e);
              let imgUrl = e.target.result; // 获取文件内容的base64编码
              let sizeInKB = (e.total / 1024).toFixed(2) + "KB";
              // const img = document.createElement("img"); // 创建img元素
              // img.src = imgUrl; // 设置img的src属性为读取的文件内容
              // img.style.maxWidth = "100%"; // 设置图片最大宽度为容器宽度
              // img.style.maxHeight = "100%"; // 设置图片最大高度为容器高度

              // const container = document.getElementById("imageContainer");
              // container.innerHTML = ""; // 清空容器内容
              // container.appendChild(img); // 将img元素添加到容器中

              let currentTime = new Date().getTime();
              let imgInfo = {
                key: currentTime, // 唯一标识符
                name: imageName, // 名称
                size: sizeInKB, // 大小
                file: file, // 文件流
                url: imgUrl, // base64码
                createDate: currentTime,
                fileId: currentTime,
                fileSize: e.total,
                fileType: fileType,
                uid: `key_${currentTime}_image`,
              };
              console.log("imgInfo", imgInfo, "列表", _this);
              _this.imglists.push(imgInfo); // 添加到图片列表
            };
            reader.readAsDataURL(file); // 读取文件内容
          }
        });
    },
    // 打开相机弹框
    openCameraModal() {
      let modal = this.cameraModal;
      modal.style.display = "block";
    },
    // 关闭相机弹框
    closeCameraModal() {
      let modal = this.cameraModal;
      modal.style.display = "none";
      this.stopCamera();
    },
    // 打开相机
    openCamera() {
      this.accessCamera();
    },
    // webView
    webViewToImg() {},
    // 访问摄像头
    async accessCamera() {
      console.log("访问摄像头");
      this.videoDom.style.display = "block";
      let mediaDevices = navigator.mediaDevices;
      if (mediaDevices) {
        // 获取摄像头权限
        navigator.mediaDevices
          .getUserMedia({ video: { facingMode: "user" } }) // 使用前置摄像头
          .then((stream) => {
            this.mediaStream = stream;
            this.videoDom.srcObject = stream;
            this.videoDom.play();

            this.videoDom.style.display = "block";
            this.closeModal();
            this.openCameraModal();
          })
          .catch((err) => {
            console.error("无法访问摄像头:", err);
          });
      } else {
        console.error("无法访问摄像头");
      }
    },
    // 关闭摄像头
    stopCamera() {
      // 停止视频流
      if (this.mediaStream) {
        this.mediaStream.getTracks().forEach((track) => track.stop());
        this.videoDom.style.display = "none";
      }
    },
    // 根据时间戳生成图片名称
    generateImageName() {
      const timestamp = new Date().getTime();
      return `image_${timestamp}.png`;
    },

    // 获取图片信息
    getImgInfoToCapture(canvas, imgUrl) {
      console.log("canvas", canvas);
      // 将Canvas内容转换为Blob对象
      canvas.toBlob((blob) => {
        console.log("blob", blob);
        // 设置图片名称
        const imageName = this.generateImageName();

        // 获取图片大小
        const imageSize = blob.size; // 图片大小,单位为字节
        // 将字节换算成千字节
        const sizeInKB = (imageSize / 1024).toFixed(2) + "KB"; // 保留两位小数

        // 这里可以执行其他操作,比如将Blob对象转换为File对象
        const imageFile = new File([blob], imageName, { type: blob.type });

        let imgInfo = {
          key: new Date().getTime(), // 唯一标识符
          name: imageName, // 名称
          size: sizeInKB, // 大小
          file: imageFile, // 文件流
          url: imgUrl, // base64码
        };
        console.log("图片信息:", imgInfo);
        this.imglists.push(imgInfo); // 添加到图片列表
        // 如果需要,可以在这里执行上传或其他操作
      }, "image/png"); // 指定图片格式
    },
    // canvas转换为图片并添加到页面
    canvasToImg(canvas) {
      // 将绘制的canvas转换为图片
      const img = document.createElement("img");
      let imgUrl = canvas.toDataURL("image/png");
      img.src = imgUrl;
      img.style.width = "100%";
      img.style.height = "100%";
      img.style.objectFit = "cover";
      const imglistDom = this.imglistDom;
      const uploaderImgs = this.uploaderImgDom;
      this.currentCaptureImg = imgUrl;

      imglistDom.innerHTML = ""; // 清空所有子元素
      // 将图片添加到页面中
      imglistDom.appendChild(img);
      // uploaderImgs.appendChild(img);
    },
    // 拍照
    capture() {
      const canvas = this.canvasDom;
      const video = this.videoDom;

      // 设置 canvas 尺寸与视频一致
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      const ctx = canvas.getContext("2d");
      // 绘制视频帧
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

      this.canvasToImg(canvas);

      this.isCapture = true;
    },
    // 选择当前拍照图片
    chooseCurrentCapture() {
      this.closeCameraModal();
      this.getImgInfoToCapture(this.canvasDom, this.currentCaptureImg);
      this.isCapture = false;
    },
    // 返回上传页面
    backToUpload() {
      this.closeCameraModal();
    },
    // 重新拍照
    reCapture() {
      this.isCapture = false;
    },
    // 删除图片
    deleteImg(imgList, index) {
      imgList.splice(index, 1);
    },
  },
  created() {
    // 调用实例,未渲染DOM;
    console.log("created");
  },
  mounted() {
    this.init();
    this.closeChooseModal();
  },
};
</script>
<style scoped>
/* 全局样式 */
body {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background-color: #000;
  color: #fff;
  font-family: Arial, sans-serif;
}
.choose_modal {
  display: none;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgb(0, 0, 0);
  background-color: rgba(0, 0, 0, 0.4);
}

.choosemodal-content {
  background-color: #fefefe;
  margin: 15% auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
}

.choosemodal-option {
  margin: 10px 0;
  padding: 10px;
  background-color: #f2f3f5;
  text-align: center;
  cursor: pointer;
}

.choosemodal-option:hover {
  background-color: #f2f3f5;
  color: #1989fa;
}
.camera_modal {
  display: none;
  position: fixed;
  z-index: 99;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  overflow: auto;
  background-color: rgb(0, 0, 0);
}
#back-to-upload {
  display: flex;
  align-items: center;
  justify-content: center;

  position: fixed; /* 或者使用 position: absolute; 根据需要选择 */
  left: 8%; /* 水平居中 */
  /* transform: translateX(-50%); */
  bottom: 7vh;
}
#capture-btn {
  display: flex;
  align-items: center;
  justify-content: center;

  position: fixed; /* 或者使用 position: absolute; 根据需要选择 */
  left: 50%; /* 水平居中 */
  transform: translateX(-50%);
  bottom: 6vh;
}
#capture-out {
  border: 2px solid #fff;
  border-radius: 100%;
  cursor: pointer;
  width: 1.85rem;
  height: 1.85rem;
  display: flex;
  justify-content: center;
  align-items: center;
}
#capture-inner {
  background-color: #fff;
  border-radius: 100%;
  cursor: pointer;
  width: 1.65rem;
  height: 1.65rem;
}

/* 视频容器 */
.camera-container {
  position: relative;
  width: 100vw; /* 宽度为屏幕宽度 */
  height: 100vh; /* 高度为 4:3 比例 */
  max-height: 100vh;
  overflow: hidden;
}

/* 视频流 */
#video {
  width: 100%;
  height: 100%;
  object-fit: cover; /* 确保视频填充整个容器 */
}

/* 人像框 */
.frame {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none; /* 防止人像框干扰点击事件 */
}
.upload-box {
  border: 1px solid #dcdee0;
  text-align: center;
  font-size: 16px;
  display: flex;
  justify-content: center;
  padding: 5px 3px;
}
.upload-box-none {
  display: none;
}
.uploader-img {
}
.img-box {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 5px 3px;
  padding: 5px;
}
.img-item {
  display: flex;
}
.img-item-img {
  width: 60px;
  height: 60px;
}

.img-item-img-content {
  border-radius: 4px;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.img-info {
  margin: 0 5px;
}
.img-name {
  font-size: 17px;
  margin: 5px 0;
  width: 200px; /* 设置容器的宽度 */
  overflow: hidden; /* 隐藏溢出的内容 */
  white-space: nowrap; /* 防止文本换行 */
  text-overflow: ellipsis; /* 设置文本溢出时显示省略号 */
}
.img-size {
  font-size: 13px;
  color: #b5b8c2;
}
.uploader_img img {
  width: 48px;
  height: 48px;
}
#capture-img {
  width: 100vw;
  height: 85vh;
}
#capture_img_result {
  display: flex;
  justify-content: space-around;
  line-height: 15vh;
}
.uploader_clibtn {
  width: 25px;
  height: 18px;
}
.delete_btn {
  width: 28px;
  height: 28px;
}
.uploader_btn {
  width: 24px;
  height: 24px;
}
.uploader_squarebtn {
  width: 60px;
  height: 60px;
}
.camera_portrait {
  width: 100%;
  height: 100%;
}
.capture_btn_other {
  width: 32px;
  height: 32px;
}
.isnone {
  display: none;
}
</style>
相关推荐
恋猫de小郭34 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端