vue-微信H5-拍照和视频,加人像框

发布时间:2024年01月19日

在这里插入图片描述
图片拍照:

<template>
  <div>
    <v-easy-camera
      :fullscreen="true"
      ref="easyCamera"
      v-model="pictureData.picture"
      class="main-camera"
    >
      <template #header>
        <div class="top">
          <van-image
            class="mask_control_close"
            width="20px"
            height="20px"
            fit="cover"
            @click="cameraStop"
            :src="require('@/assets/img/camera/叉叉.png')"
          />
        </div>
      </template>
    </v-easy-camera>
    <van-image
      class="mask"
      :src="require('@/assets/img/camera/333.png')"
    />
    <van-image
      class="mask_control"
      width="80px"
      height="80px"
      fit="cover"
      @click="cameraSnap"
      :src="require('@/assets/img/camera/拍照.png')"
    />
    <van-image
      v-show="confirmIsShow"
      class="mask_control_confirm"
      width="40px"
      height="40px"
      fit="cover"
      @click="confirm"
      :src="require('@/assets/img/camera/确定.png')"
    />
    <van-image
      v-show="confirmIsShow"
      class="mask_control_return"
      width="40px"
      height="40px"
      fit="cover"
      @click="cancel"
      :src="require('@/assets/img/camera/撤销.png')"
    />
    <van-image
      v-show="!confirmIsShow && isAndroid"
      class="mask_control_confirm"
      width="40px"
      height="40px"
      fit="cover"
      @click="flip"
      :src="require('@/assets/img/camera/翻转镜头.png')"
    />
  </div>
</template>

  <script>
import EasyCamera from "easy-vue-camera";

export default {
  name: "cameraCustom",
  components: {
    "v-easy-camera": EasyCamera,
  },
  data() {
    return {
      pictureData: {
        picture: "",
        pictureSize: "",
      },
      isEnable: true,
      myEasyCamera: null,
      confirmIsShow: false,
      isFlip: false,
      isAndroid: false,
      currentFacingMode: "",
    };
  },

  mounted() {
    // 判断用户的操作系统
    const isAndroid = /android/i.test(navigator.userAgent);
    const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);

    //获取原始控件区并隐藏(处理为移除子元素)
    let aaa = document.getElementsByClassName("camera-stack")[0];
    let div = document.createElement("div");
    div.className = "bott";
    div.style.height = "180px";
    div.style.widht = "100%";

    aaa.appendChild(div);
    let controlModel = document.getElementsByClassName(
      "camera-stack-controls"
    )[0];
    controlModel.removeChild(controlModel.firstChild);
    //获取拍照组件实例
    this.myEasyCamera = this.$refs.easyCamera;

    if (isAndroid) {
      this.isAndroid = false;
      console.log("This device is using Android.");
      // 在 Android 上执行的逻辑
    } else if (isIOS) {
      this.isAndroid = true;
      setInterval(() => {
        if (this.isFlip == false) {
          this.flip();
        }
      }, 500);
      // 在 iOS 上执行的逻辑
    } else {
      console.log("This device is using a different operating system.");
      // 在其他操作系统上执行的逻辑
    }
  },
  methods: {
    // 点击关闭
    cameraStop() {
      // console.log("关闭");
      this.myEasyCamera.close();
      this.$emit("close");
    },
    // 点击拍照
    cameraSnap() {
      if (!this.isEnable) {
        return;
      }
      // console.log("拍照");
      this.myEasyCamera.snap();
      document.getElementsByClassName(
        "camera-stack-controls"
      )[0].style.display = "none";
      this.confirmIsShow = true;
      // this.pictureData.pictureSize =
      // this.getImageSize(this.pictureData.picture) / 1024 / 1024;
      this.isEnable = false;
    },
    // 点击对号完成
    confirm() {
      // console.log("完成");
      this.isEnable = true;
      this.confirmIsShow = false;
      this.$emit("setPictureUrl", this.pictureData);
      this.$emit("close");
    },
    // 点击返回重拍
    cancel() {
      // console.log("重拍");
      this.isEnable = true;
      this.confirmIsShow = false;
      this.myEasyCamera.start();
    },
    // 获取照片大小
    getImageSize(base64Str) {
      const indexBase64 = base64Str.indexOf("base64,");
      if (indexBase64 < 0) return false;
      const str = base64Str.substr(indexBase64 + 6);
      return (str.length * 0.75).toFixed(2);
    },
    flip() {
      this.myEasyCamera.switchCamera();
      this.isFlip = true;
    },
  },
};
</script>

  <style   scoped>
.top {
  background-color: black;
  height: 50px;
  width: 100%;
}
.bottom {
  background-color: black;
  height: 60px;
  width: 100%;
}
.bott {
  position: absolute;
  background-color: black;
  width: 100%;
  z-index: 18;
}
.mask {
  position: absolute;
  top: 50px;
  left: 0;
  z-index: 18;
  height: calc(100% - 200px);
  width: 100%;
}
.mask /deep/ img {
  height: auto;
}
.mask_control {
  position: absolute;
  z-index: 18;
  left: calc(50% - 40px);
  bottom: 40px;
}
.mask_control_close {
  position: absolute;
  z-index: 18;
  right: 20px;
  top: 20px;
}
.mask_control_confirm {
  position: absolute;
  z-index: 18;
  right: 40px;
  bottom: 60px;
}
.mask_control_return {
  position: absolute;
  z-index: 18;
  left: 40px;
  bottom: 60px;
}
.main-camera {
  background-color: #000;
}
.fullscreen-camera /deep/ .camera-stack{
  transform: scaleX(-1);
}
</style>


视频录制:

<template>
  <div class="publish">
    <div class="top">
      <van-image
        class="mask_control_close"
        width="20px"
        height="20px"
        fit="cover"
        @click="CloseCamera"
        :src="require('@/assets/img/camera/叉叉.png')"
      />
    </div>
    <div id="myElement" class="videomain">
      <video
        ref="video"
        class="video_class"
        autoplay
        playsinline
        webkit-playsinline="true"
      ></video>
<!--      <img class="mask" :src="require('@/assets/img/camera/333.png')" alt="" />-->
      <!-- <div class="mask_div">
        <div class="mask_mb"></div>
        <img
          class="mask"
          :src="require('@/assets/img/camera/333.png')"
          alt=""
        />
      </div> -->
      <van-image
        class="mask"
        :src="require('@/assets/img/camera/333.png')"
      />
    </div>
    <div class="bottom">
      <div class="time" id="timer">
        {{ padNumber(hours) }} : {{ padNumber(minutes) }} :
        {{ padNumber(seconds) }}
      </div>

      <div class="bottom02">
        <div
          style="width: 40px; height: 40px"
          v-show="isRecording && isSure"
        ></div>

        <img
          v-show="confirmIsShow"
          class="mask_control_return"
          width="40px"
          height="40px"
          fit="cover"
          @click="stopCancel"
          :src="require('@/assets/img/camera/撤销.png')"
        />

        <img
          v-if="isRecording && isSure"
          class="mask_control"
          width="80px"
          height="80px"
          fit="cover"
          @click="startRecording"
          :src="require('@/assets/img/camera/拍照.png')"
        />

        <img
          v-if="!isRecording && isSure"
          class="mask_control"
          width="80px"
          height="80px"
          fit="cover"
          @click="stopRecording"
          :src="require('@/assets/img/camera/拍照 (1).png')"
        />

        <van-image
          v-show="confirmIsShow"
          class="mask_control_confirm"
          width="40px"
          height="40px"
          fit="cover"
          @click="downloadVideo"
          :src="require('@/assets/img/camera/确定.png')"
        />

        <van-image
          v-show="isRecording && isAndroid"
          class="mask_control_confirm"
          width="40px"
          height="40px"
          fit="cover"
          @click="toggleCamera"
          :src="require('@/assets/img/camera/翻转镜头.png')"
        />

        <div
          style="width: 40px; height: 40px"
          v-show="!isAndroid && isRecording"
        ></div>
      </div>
    </div>
  </div>
</template>

    <script>
    import { Toast } from 'vant';

export default {
  data() {
    return {
      mediaRecorder: null,
      recordedChunks: [],
      recording: false,
      isSure: true,
      isRecording: true,
      isAndroid: false,
      confirmIsShow: false,
      hours: 0,
      minutes: 0,
      seconds: 0,
      intervalId: null,
      currentFacingMode: "environment", // 'user' for front camera, 'environment' for back camera
    };
  },
  computed: {
    elapsedTime() {
      return this.startTime
        ? Math.floor((Date.now() - this.startTime) / 1000)
        : 0;
    },
    hours() {
      return Math.floor(this.elapsedTime / 3600);
    },
    minutes() {
      return Math.floor((this.elapsedTime % 3600) / 60);
    },
    seconds() {
      return this.elapsedTime % 60;
    },
    formattedTime() {
      return `${this.pad(this.hours)}:${this.pad(this.minutes)}:${this.pad(
        this.seconds
      )}`;
    },
  },
  mounted() {
    // 判断用户的操作系统
    const isAndroid = /android/i.test(navigator.userAgent);
    const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent);

    if (isAndroid) {
      this.isAndroid = true;
      this.currentFacingMode = "user";
      console.log("This device is using Android.");
      // 在 Android 上执行的逻辑
    } else if (isIOS) {
      this.isAndroid = true;
      console.log("This device is using iOS.");
      // 在 iOS 上执行的逻辑
    } else {
      console.log("This device is using a different operating system.");
      // 在其他操作系统上执行的逻辑
    }

    const screenHeight =
      window.innerHeight ||
      document.documentElement.clientHeight ||
      document.body.clientHeight;
    this.screenHeight = screenHeight - 50 - 180;
    console.log("🚀 ~ mounted ~  this.screenHeight:", this.screenHeight);

    const myElement = document.getElementById("myElement"); // 替换 'myElement' 为你实际元素的ID
    // 设置元素高度
    if (myElement) {
      myElement.style.height = this.screenHeight + "px"; // 替换 '100px' 为你想要设置的高度
    }
    this.canvas = document.createElement('canvas')
    this.canvas.width = this.$refs.video.videoWidth
    this.canvas.height = this.$refs.video.videoHeight
    const ctx = this.canvas.getContext('2d')
    const self = this
    function drawCanvas() {
      ctx.clearRect(0, 0, self.canvas.width, self.canvas.height)
      ctx.drawImage(self.$refs.video, 0, 0, self.canvas.width, self.canvas.height)
      window.requestAnimationFrame(drawCanvas)
    }
    // 获取视频流并将其绑定到视频元素
    navigator.mediaDevices
      .getUserMedia({
        video: {
          facingMode: this.currentFacingMode,
        },
      })
      .then((stream) => {
        this.$refs.video.srcObject = stream;
        this.$refs.video.play();
        drawCanvas()
      })
      .catch((error) => {
        console.error("获取摄像头失败:", error);
      });
  },
  methods: {
    startRecording() {
      this.recordedChunks = [];
      this.isRecording = false;
      this.recording = true;
      this.startTimer(); //开始计时
      // 创建 MediaRecorder 对象,将视频流传入
      const isIOS = /iphone|ipad|ipod/.test(navigator.userAgent)
      if (isIOS) {
        try {
          this.mediaRecorder = new MediaRecorder(this.$refs.video.srcObject, { mimeType:  'video/mp4;codecs=avc1,mp4a' });
        } catch (e) {
          try {
            this.mediaRecorder = new MediaRecorder(this.canvas.captureStream(15), { mimeType:  'video/mp4;codecs=avc1,mp4a' });
          } catch (e) {
            console.error(e)
            this.$toast.fail("当前浏览器不支持录屏,请直接上传文件");
          }
        }
      } else {
        try {
          this.mediaRecorder = new MediaRecorder(this.$refs.video.srcObject, { mimeType:  'video/webm;codecs=h264' });
        } catch(e) {
          console.error(e)
          this.$toast.fail("当前浏览器不支持录屏,请直接上传文件");
        }
      }


      // 监听数据可用事件,将数据块存储到数组
      this.mediaRecorder.ondataavailable = (event) => {
        if (event.data.size > 0) {
          this.recordedChunks.push(event.data);
        }
      };

      this.mediaRecorder.onerror = (e) => {
        console.error('error:', e)
      }

      // 监听录制结束事件
      this.mediaRecorder.onstop = () => {
        this.recording = false;
      };

      // 开始录制
      this.mediaRecorder.start();
    },
    stopRecording() {
      this.stopTimer();
      this.isRecording = true;
      this.isSure = false;

      if (this.mediaRecorder && this.recording) {
        // 停止录制
        this.mediaRecorder.stop();
        this.confirmIsShow = true;
      }
    },
    stopCancel() {
      this.resetTimer();
      this.isSure = true;
      this.confirmIsShow = false;
    },
    downloadVideo() {
      console.log("seconds", this.seconds);
      if (this.minutes == 0 && this.seconds < 5) {
        Toast.fail('录制时长最少5秒钟');
        return
      }

      // 将录制的数据块合并为 Blob
      const blob = new Blob(this.recordedChunks, { type: "video/webm" });
      // 假设 videoBlob 是一个录制的视频的 Blob 对象
      const videoFile = new File(this.recordedChunks, "recorded-video.webm", {
        type: "video/webm",
      });
      console.log(
        "🚀 ~ file: videoCamera.vue:148 ~ downloadVideo ~ videoFile:",
        videoFile
      );

      // 现在,videoFile 就是一个 File 对象,可以像处理文件一样使用它

      console.log("🚀 ~ file: chatbb.vue:66 ~ downloadVideo ~ blob:", blob);

      this.$emit("setPictureUrl2", videoFile);
      this.$emit("close");

      this.$refs.video.srcObject.getTracks().forEach((track) => {
        track.stop()
      })

      // 创建下载链接并触发下载
      //   const url = URL.createObjectURL(blob);
      //   const a = document.createElement("a");
      //   a.href = url;
      //   a.download = "recorded-video.webm";
      //   a.click();

      // 释放资源
      //   URL.revokeObjectURL(url);
    },
    CloseCamera() {
      this.stopRecording();
      this.$emit("close");
      this.$refs.video.srcObject.getTracks().forEach((track) => {
        track.stop()
      })
      // 停止录制
    },
    // 切换摄像头
    toggleCamera() {
      console.log(1);
      this.currentFacingMode =
        this.currentFacingMode === "user" ? "environment" : "user";
      this.restartVideo();
    },
    async restartVideo() {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: {
          facingMode: this.currentFacingMode,
        },
      });
      this.$refs.video.srcObject = stream;
    },
    padNumber(num) {
      return String(num).padStart(2, "0");
    },
    startTimer() {
      this.intervalId = setInterval(() => {
        this.seconds++;
        if (this.seconds === 60) {
          this.seconds = 0;
          this.minutes++;
          if (this.minutes === 60) {
            this.minutes = 0;
            this.hours++;
          }
        }
      }, 1000);
    },
    stopTimer() {
      clearInterval(this.intervalId);
      this.intervalId = null;
    },
    resetTimer() {
      this.hours = 0;
      this.minutes = 0;
      this.seconds = 0;
      this.stopTimer();
    },
  },
  beforeDestroy() {
    // 清理定时器
    this.stopTimer();
  },
};
</script>


    <style scoped>
.publish {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  /* align-items: center; */
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
  background: #000;
}

.top {
  background-color: black;
  min-height: 50px;
  width: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.video_class {
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 100;
}
.bottom {
  position: relative;
  min-height: 180px;
  width: 100%;
  background-color: #000;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 999;
}
.bottom02 {
  width: 100%;
  background-color: #000;
  display: flex;
  justify-content: space-around;
  align-items: center;
  z-index: 999;
}
.mask_div {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.mask_mb {
  position: absolute;
  /* background-color: rgba(0.129, 0.129, 0.125, 0.5); */
  width: 100%;
  height: 100%;
  z-index: 333;
}
.mask {
  /* background-color: #fff; */
  position: absolute;
  top: 0;
  left: 0%;
  display: flex;
  justify-content: center;
  max-width: 100%;
  width: 100%;
  height: 100%;
  background-color: transparent; /* 中间区域透明,保持正常 */
}
.mask /deep/ img {
  height: 140vw;
  width: 100vw;

}
.mask_control_return {
  /* position: absolute; */
  z-index: 18;
  left: 40px;
  bottom: 60px;
}
.mask_control_close {
  margin-right: 10px;
}
.flip {
  background-color: #000;
}
.videomain {
  position: relative;
  background-color: #000;
  flex-shrink: 0;
  /* flex: 1; */
  /* max-width: 500px; */
}
.time {
  color: #fff;
  position: absolute;
  top: 5px;
  left: 50%;
  transform: translate(-50%);
  z-index: 9999;
}
</style>


文章来源:https://blog.csdn.net/weixin_45148022/article/details/135696629
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。