首页 > 其他分享 >Vant之手机端上传图片只允许拍照上传

Vant之手机端上传图片只允许拍照上传

时间:2024-09-23 15:47:16浏览次数:5  
标签:拍照 const Vant value height videoElementObj let file 上传

1.开发拍照上传页面 - andImgCapture.vue

<template>
  <div>
    <van-button icon="plus" type="primary" :disabled="disabled" @click.stop="clickCamera">拍照上传</van-button>
    <div class="carma-content" v-show="carmaVisible">
      <van-button icon="cross" class="close-photo" type="primary" @click.stop="stopVideoStream"></van-button>
      <video class="carma-video" ref="videoElement" playsinline webkit-playsinline></video>
      <van-button icon="photograph" class="take-photo" @click.stop="capturePhoto"></van-button>
      <canvas ref="canvasElement" style="display: none;"></canvas>
    </div>
    <div class="captured-image-content" v-if="photoVisible">
      <van-button class="cancel-photo" @click="cancelPhoto">取消</van-button>
      <van-button class="confirm-photo" @click="confirmPhoto">完成</van-button>
      <img class="captured-image" v-if="capturedImage" :src="capturedImage" alt="Captured Image">
    </div>
  </div>
</template>
<script setup>
import { onBeforeUnmount, ref, reactive, watch } from 'vue'
import { Toast } from "vant";
const props = defineProps({
  disabled: {
    type: Boolean,
    default: false
  },
  afterRead: {
    type: Function,
    default: () => {
      return true
    }
  },
  beforeRead: {
    type: Function,
    default: () => {}
  }
});
const emit = defineEmits([ 'clickUpload' ])
const carmaVisible = ref(false);
const photoVisible = ref(false);
const capturedImage = ref(null);
const videoElement = ref(null);
const canvasElement = ref(null);
const file = reactive({
  content: '',
  file: {}
});

watch(carmaVisible, newVal => {
  if(newVal){
    document.getElementsByTagName('body')[0].style.backgroundColor = '#000000'
  } else {
    document.getElementsByTagName('body')[0].style.backgroundColor = '#ffffff'
  }
});

onBeforeUnmount(() => {
  stopVideoStream();
});

const clickCamera = async (e) => {
  emit('clickUpload', e);
  // 开启摄像头前检测是否触发了阻止默认事件
  if(e.defaultPrevented){
    return 
  } else {
    setupCamera();
  }
};

const setupCamera = async () => {
  try {
    initFile();
    navigator.mediaDevices.getUserMedia({ 
      video: { facingMode: "environment" },
    }).then(stream => {
      const videoElementObj = videoElement.value;
      videoElementObj.srcObject = stream;
      carmaVisible.value = true;
      if ("srcObject" in videoElementObj) {
        videoElementObj.srcObject = stream;
      } else {
        // 防止在新的浏览器里使用它,因为它已经不再支持了
        videoElementObj.src = window.URL.createObjectURL(stream);
      }
      videoElementObj.onloadedmetadata = function(e) {
        videoElementObj.play();
      };
    }).catch(err => {
      Toast.fail('访问用户媒体设备失败:', err.message);
    });
  } catch (error) {
    carmaVisible.value = false;
    console.log('获取摄像头失败:', error);
    Toast.fail('获取摄像头失败');
  }
}

const capturePhoto = () => {
  const videoElementObj = videoElement.value;
  const canvasElementObj = canvasElement.value;
  const context = canvasElementObj.getContext('2d');

  canvasElementObj.width = videoElementObj.videoWidth;
  canvasElementObj.height = videoElementObj.videoHeight;

  context.drawImage(videoElementObj, 0, 0, canvasElementObj.width, canvasElementObj.height);
  capturedImage.value = canvasElementObj.toDataURL();
  photoVisible.value = true;
}

const cancelPhoto = () => {
  photoVisible.value = false;
  capturedImage.value = '';
}

const confirmPhoto = () => {
  stopVideoStream();
  file.content = capturedImage.value;
  file.file = base64ToFile(capturedImage.value, 'photo'+ Date.now());

  const passBeforeRead = props.beforeRead(file);
  if(passBeforeRead || passBeforeRead === undefined){
    props.afterRead(file);
  }
}

const initFile = () => {
  file.content = '';
  file.file = '';
  capturedImage.value = '';
}

const stopVideoStream = () => {
  photoVisible.value = false;
  carmaVisible.value = false;
  if (videoElement.value && videoElement.value.srcObject) {
    const tracks = videoElement.value.srcObject.getTracks();
    tracks.forEach((track) => {
      track.stop();
    });
  }
}

const base64ToFile = (dataurl, fileName) => {
  let arr = dataurl.split(',');
  let mime = arr[0].match(/:(.*?);/)[1];
  // suffix是该文件的后缀
  let suffix = mime.split('/')[1];
  // atob 对经过 base-64 编码的字符串进行解码
  let bstr = atob(arr[1]);
  // n 是解码后的长度
  let n = bstr.length;
  // Uint8Array 数组类型表示一个 8 位无符号整型数组 初始值都是 数子0
  let u8arr = new Uint8Array(n);
  // charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  // new File返回File对象 第一个参数是 ArraryBuffer 或 Bolb 或Arrary 第二个参数是文件名
  // 第三个参数是 要放到文件中的内容的 MIME 类型
  return new File([u8arr], `${ fileName }.${ suffix }`, {
    type: mime,
  });
}
</script>
<style lang="less" scoped>
.carma-content{
  width: 100%;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  background: #000000;
  z-index: 99;
}
.close-photo{
  position: absolute;
  left: 0;
  z-index: 100;
  border: unset;
  background: #000000;
}
.carma-video{
  width: 100vw;
  height: auto;
  position: absolute;
  top: 50%;
  left: 0px;
  transform: translateY(-55%);
}
.take-photo{
  width: 70px;
  height: 70px;
  position: absolute;
  z-index: 100;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 50%;
  font-size: 28px;
  background: #ffffff;
}
.captured-image-content{
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  background: #000000;
}
.cancel-photo{
  color: #ffffff;
  border: unset;
  background: none;
  top: 0;
  left: 0;
  position: fixed;
}
.confirm-photo{
  color: #0570db;
  border: unset;
  background: none;
  top: 0;
  right: 0;
  position: fixed;
}
.captured-image{
  width: 100%;
  height: auto;
}
</style>

2.编写androidOrIos.js

export const checkMobileModel = () => {
  let u = navigator.userAgent;
  let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
  let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);

  if (isAndroid) {
    return 'Android'
  }

  if (isIOS) {
    return 'IOS'
  }

   return ''
}

3.导入两个文件

import AndImgCapture from "@/components/andImgCapture/andImgCapture.vue";
import { checkMobileModel } from "@/utils/androidOrIos";

4.在主页面实现拍照上传基本功能

<AndImgCapture
  v-if="checkMobileModel() === 'Android'"
  :after-read="(file) => uploaderAfter(file, 'promotionPhoto')"
  @click-upload="openUpload($event, item)"
>
</AndImgCapture>
<van-uploader
  v-else
  :multiple="true"
  :preview-image="false"
  :max-size="50000 * 1024"
  :after-read="(file) => uploaderAfter(file, 'promotionPhoto')"
  @click-upload="openUpload($event, item)"
  @oversize="onOversize"
  capture="camera"
  accept="image/*"
>
  <van-button icon="plus" type="primary">拍照上传</van-button>
</van-uploader>

即可。

标签:拍照,const,Vant,value,height,videoElementObj,let,file,上传
From: https://www.cnblogs.com/luoyihao/p/18427158

相关文章

  • 文件上传日志包含详解与CTF实战
    1.日志简介1.1日志介绍日志是记录系统或应用程序运行时事件的文件。这些记录可以包括错误信息、用户活动、系统性能指标等,帮助开发者和管理员监控和排查问题。日志通常会记录多种内容,包括:时间戳:事件发生的具体时间。用户代理(UA)头:浏览器或客户端的类型和版本。IP地址:发起......
  • wordpress网站维护教程:不能上传文件,数据库报错的解决方法
    当WordPress网站遇到不能上传文件或数据库报错的问题时,这可能会影响网站的正常使用。下面分别针对这两种情况提供一些可能的解决方法。不能上传文件权限问题:检查上传文件的目标目录权限是否正确。通常,WordPress的上传目录(默认为/wp-content/uploads/)应该具有可写的权限。你......
  • Spring Boot+MinIO实战:掌握分片上传、秒传与断点续传,让文件管理更高效!
    在现代应用中,随着文件大小的不断增大和网络环境的复杂化,传统的文件上传方式已难以满足用户需求。通过将SpringBoot与MinIO集成,可以轻松实现文件的分片上传、秒传和续传功能,为用户提供更流畅的上传体验。分片上传分片上传是将大文件拆分成多个小块分别上传,避免单次上传大文件带......
  • C# base64转pdf + 上传至指定url
    base64topdf1usingSystem;2usingSystem.Collections.Generic;3usingSystem.IO;4usingSystem.Linq;5usingSystem.Text;6usingSystem.Threading.Tasks;78namespaceHS.Common.Helper9{10publicclassPdfHelper11{......
  • 为何生成静态页的时候或者上传附件过程中有报错:Maximum execution time of 30 seconds
    错误信息 Maximumexecutiontimeof30secondsexceeded 表明PHP脚本的执行时间超过了服务器设定的最大执行时间限制。这通常发生在生成静态页面或上传大文件等耗时较长的操作中。解决方案方法一:修改 php.ini 文件找到 php.ini 文件:通常 php.ini 文件位于服务......
  • 后台uedit编辑器内容上传视频失败
    如果在后台使用UEditor编辑器上传视频失败,可以按照以下步骤进行排查和解决:1.检查配置文件首先检查UEditor的配置文件 config.json 是否正确配置了视频上传的相关参数。定位配置文件:找到 UEditor 的配置文件 config.json。检查视频上传配置:确保以下配置项正确......
  • 后台上传大文件时提示上传接口错误
    当网站后台上传文件大于50M时提示上传接口错误,即使已经调整了PHP的文件上传限制和POST限制,以及后台的附件上传大小限制,可能还需要调整Apache的配置。解决方法1.调整Apache配置打开Apache配置文件:打开Apache的主配置文件 httpd.conf。通常该文件位于 /etc/httpd/conf/ 或......
  • LeetCode力扣——并查集:947. 移除最多的同行或同列石头,1971. 寻找图中是否存在路径,24
    947.移除最多的同行或同列石头题目描述947.移除最多的同行或同列石头n 块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。给你一个长度为 n 的数组 stones ,其......
  • 帝国CMS搬家后无法发文章或者上传图片无法显示?
    如果你在搬家后遇到无法发文章或上传图片的问题,通常是因为权限设置不当导致的。以下是一些详细的步骤来解决这些问题:1.检查目录权限无法发文章检查栏目目录及子文件夹权限确保栏目目录及其子文件夹具有适当的权限。通常需要将这些目录设置为 777 权限。sh chmo......
  • git上传github
    git上传github创建仓库文件夹(任意位置),打开gitBashHere初始化git输入gitinit生成该文件夹输入上传的信息gitconfig--globaluser.name"用户名"gitconfig--globaluser.email"邮箱"复制需要上传的文件输入gitadd.输入检查状态gitstatus输......