目录

一、自定义相机(Camera.vue)

1、图例

2、代码 

二、图片预览(Preview.vue)

1、图例 

2、代码 

三、裁剪插件 (Cropping.vue)

1、图例

2、代码 

四、父组件   

五、链接 

封装自定义相机,并使用剪切插件对自定义相机拍照的图片实现剪切功能,且如不需要剪切功能也可禁用使用图片仅预览功能,进行后续上传服务器操作(图片上传功能后续补充

  

一、自定义相机(Camera.vue)

注: 如不开启裁剪模式 isCrop:true 则默认为仅预览状态!(可根据自身业务需求更改)

1、图例

 

uniapp ios相机权限授权 uniapp自定义相机_数码相机

2、代码 
<template>
	<view class="camera">
		<!-- 相机组件 -->
		<view v-show="isShow">
			<camera v-if="isAuth" :device-position="devPosition?'front':'back'" flash="off" @error="error" />
			<view class="footer">
				<view class="back" v-if="back">
					<view @click="Back">
						<image :src="require('../Camera/IconFont/back.png')" mode="aspectFill" />
					</view>
				</view>
				<view class="album" v-else>
					<view @click="Album">
						<image :src="require('../Camera/IconFont/album.png')" mode="aspectFill" />
					</view>
				</view>
				<view class="takePhoto">
					<view @click="TakePhoto">
						<image :src="require('../Camera/IconFont/takePhoto.png')" mode="aspectFill" />
					</view>
				</view>
				<view class="devPosition">
					<view @click="CameraPosition">
						<image :src="require('../Camera/IconFont/devPosition.png')" mode="aspectFill" />
					</view>
				</view>
			</view>
		</view>
		<!-- 裁剪 -->
		<CroppingImg ref="croppingRef" v-if="isCrop" @close="closeCrop" />
		<!-- 预览 -->
		<PreViewImg v-else ref="previewRef" />
	</view>
</template>
<script>
	import PreViewImg from './Preview/index.vue'
	import CroppingImg from './Cropping/index.vue'
	export default {
		components: {
			PreViewImg,
			CroppingImg
		},
		data() {
			return {
				isAuth: false, //用户是否授权调用相机权限
				isShow: true,
				devFront: ""
			}
		},
		props: {
			/**
			 * 是否开启裁剪(使用裁剪功能将会禁用照片仅预览功能)
			 * @param {default:false}  
			 */
			isCrop: {
				type: Boolean,
				default: false
			},
			/**
			 * 前后置摄像头状态
			 * 默认前置状态
			 * @param {default:true}  
			 */
			devPosition: {
				type: Boolean,
				default: true
			},
			/**
			 * 是否开启返回(开启返回按钮,选择相册功能将禁用,仅可拍照)
			 * @param {default:false}  
			 */
			back: {
				type: Boolean,
				default: false
			},
			/**
			 * 图片预览临时路径
			 * @param {default:""}  
			 */
			tempImagePath: {
				type: String,
				default: ""
			}
		},
		watch: {
			// 监听前后置镜头转换状态变化
			devPosition: {
				deep: true,
				immediate: true,
				handler(newValue, oldValue) {
					return newValue
				}
			}
		},
		methods: {
			/**
			 * 打开相机回调函数方法
			 * @param {Boolean} isAuth
			 */
			open() {
				const that = this
				uni.getSetting({
					success: res => {
						if (res.authSetting['scope.camera']) {
							// 用户已经授权
							that.isAuth = true
						} else {
							// 用户还没有授权,向用户发起授权请求
							uni.authorize({
								scope: 'scope.camera',
								success() { // 用户同意授权
									that.isAuth = true
								},
								fail() { // 用户不同意授权
									that.OpenSetting().then(res => {
										that.isAuth = true
									})
								}
							})
						}
					},
					fail: res => {
						uni.$u.toast('获取用户授权信息失败' + res)
					}
				})
			},
			/**
			 * 打开授权设置界面
			 * @param {type}  = [value] 
			 */
			OpenSetting() {
				const that = this
				let promise = new Promise((resolve, reject) => {
					uni.showModal({
						title: '授权',
						content: '请先授权获取摄像头权限',
						success(res) {
							if (res.confirm) {
								uni.openSetting({
									success(res) {
										if (res.authSetting['scope.camera']) { // 用户打开了授权开关
											resolve(true)
										} else { // 用户没有打开授权开关, 继续打开设置页面
											that.OpenSetting().then(res => {
												resolve(true)
											})
										}
									},
									fail(res) {
										uni.$u.toast('错误代码:' + res)
									}
								})
							} else if (res.cancel) {
								that.OpenSetting().then(res => {
									resolve(true)
								})
							}
						}
					})
				})
				return promise;
			},
			/**
			 * 返回回调函数方法
			 * @param {$emit} Back  
			 */
			Back() {
				const that = this
				that.$emit('Back')
			},
			/**
			 * 选择相册照片回调函数方法
			 * @param {String} tempFilePaths[0]  
			 */
			async Album() {
				const that = this
				uni.chooseImage({
					sourceType: ['album'],
					success(res) {
						if (res.errMsg == 'chooseImage:ok') {
							that.setMode(res.tempFilePaths[0])
						}
					},
					fail(res) {
						uni.$u.toast('错误代码:' + res)
					}
				})
			},
			/**
			 * 拍照回调函数方法
			 * @param {String} tempImagePath  
			 */
			TakePhoto() {
				const that = this
				const ctx = uni.createCameraContext()
				ctx.takePhoto({
					quality: 'high',
					success: (res) => {
						that.setMode(res.tempImagePath)
					}
				})
			},
			/**
			 * 判断是否使用裁剪或仅预览模式
			 * @param {Object} data
			 */
			setMode(data) {
				if (this.isCrop) {
					this.$refs.croppingRef.open(data) //裁剪
					this.isShow = false
				} else {
					this.$refs.previewRef.open(data) //仅预览
				}
			},
			/**
			 * 切换前后置摄像头回调函数方法
			 * @param {$emit} CameraPosition
			 */
			CameraPosition() {
				this.$emit("CameraPosition")
			},
			/**
			 * 关闭照片裁剪功能回调函数方法
			 * @param {Boolean} isShow
			 */
			closeCrop() {
				this.isShow = true
			}
		}
	}
</script>
<style lang="scss" scoped>
	.camera {
		width: 100%;
		height: 100%;
		position: relative;
	}

	.camera camera {
		height: 100vh;
	}

	.footer {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		background: #FFF;
		border-radius: 10rpx 10rpx 0 0;
		display: flex;
		justify-content: center;
		align-items: center;
		padding-top: 20rpx;
		padding-bottom: 30rpx;
		box-shadow: 0 0 15rpx rgba(244, 244, 244, 0.4);
		z-index: 5;

		.back,
		.album,
		.takePhoto,
		.devPosition {
			margin: auto;

			image {
				width: 75rpx;
				height: 75rpx;
			}
		}

		.album {
			image {
				width: 85rpx;
				height: 85rpx;
			}
		}

		.takePhoto {
			image {
				width: 120rpx;
				height: 120rpx;
			}
		}

	}
</style>

二、图片预览(Preview.vue)

1、图例 

uniapp ios相机权限授权 uniapp自定义相机_uniapp ios相机权限授权_02

2、代码 
<template>
	<u-transition :show="isShow" mode="zoom-out">
		<view class="preview">
			<image :src="tempFilePaths" mode="aspectFill" />
			<view class="footer">
				<view class="btn-1">
					<u-button shape="circle" type="warning" text="重新拍照" @click="close" />
				</view>
				<view class="btn-2">
					<u-button shape="circle" type="primary" text="确认选择" @click="confirm" />
				</view>
			</view>
		</view>
	</u-transition>
</template>
<script>
	export default {
		data() {
			return {
				isShow: false,
				tempFilePaths: "" //预览图片临时路径
			}
		},
		methods: {
			// 打开预览
			open(tempFilePaths) {
				this.isShow = true
				this.tempFilePaths = tempFilePaths
			},
			// 确认选择照片进行后续操作回调函数
			confirm() {
				let pages = getCurrentPages() //获取当前页面栈实例
				let prevPage = pages[pages.length - 2] //获取上一页面栈实例,-3 == 上上一个页面栈实例
				//返回上一页并传递所需当前页面栈参数
				prevPage.$vm.getChildValue(JSON.stringify({
					tempFilePath: this.tempFilePaths
				}))
				// 返回页面栈
				uni.navigateBack({
					delta: 1
				})
				this.close()
			},
			// 关闭相片预览
			close() {
				this.isShow = false
			}
		}
	}
</script>
<style lang="scss" scoped>
	.preview {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 20;
		background: #000;
		padding: 160rpx 40rpx;

		image {
			width: 100%;
			height: 100%;
		}

		.footer {
			position: fixed;
			left: 0;
			right: 0;
			bottom: 55rpx;
			display: flex;
			align-items: center;

			.btn-1,
			.btn-2 {
				width: 40%;
				margin: auto;
			}
		}
	}
</style>

三、裁剪插件 (Cropping.vue)

插件bt-cropper图片裁剪插件 - DCloud 插件市场 

 1、图例

uniapp ios相机权限授权 uniapp自定义相机_uni-app_03

2、代码 
<template>
	<view class="container" v-show="isShow">
		<bt-cropper ref="cropper" :fileType="'jpg'" :ratio="16/20" :imageSrc="tempImagePath" />
		<view class="footer">
			<view class="btn-1">
				<u-button shape="circle" type="warning" text="重新拍照" @click="close" />
			</view>
			<view class="btn-2">
				<u-button shape="circle" type="primary" text="确定选择" @click="crop" />
			</view>
		</view>
	</view>
</template>
<script>
	export default {
		data() {
			return {
				isShow: false,
				tempImagePath: "",
			}
		},
		methods: {
			// 打开回调函数方法
			open(tempImagePath) {
				this.isShow = true
				this.tempImagePath = tempImagePath
			},
			// 裁剪回调函数方法
			crop() {
				// 通过组件定义的ref调用cropper方法,返回一个promise对象
				this.$refs.cropper.crop().then((res) => {
					let pages = getCurrentPages() //获取当前页面栈实例
					let prevPage = pages[pages.length - 2] //获取上一页面栈实例,-3 == 上上一个页面栈实例
					//返回上一页并传递所需当前页面栈参数
					prevPage.$vm.getChildValue(JSON.stringify({
						tempFilePath: res
					}))
					// 返回页面栈
					uni.navigateBack({
						delta: 1
					})
				})
			},
			// 重新拍照,关闭裁剪
			close() {
				this.isShow = false
				this.$emit('close')
			}
		}
	}
</script>
<style lang="scss" scoped>
	.container {
		/** 外层一定要指定大小 */
		height: 100vh;
		z-index: 20;

		.footer {
			width: 100%;
			position: fixed;
			left: 0;
			right: 0;
			bottom: 0;
			z-index: 30;
			background: #000;
			display: flex;
			align-items: center;
			padding-top: 25rpx;
			padding-bottom: 50rpx;

			.btn-1,
			.btn-2 {
				width: 40%;
				margin: auto;
			}
		}
	}
</style>

四、父组件   

组件命名可根据真是喜好更改(无限制) ,只需注意引用方式即可。全局引入或按需引用皆可

<BaseCamera ref="BaseCamera" :dev-position="devPosition" @CameraPosition="CameraPosition" />
<script>
	import BaseCamera from "@/components/Camera/index.vue"
	export default {
		components: {
			BaseCamera
		},
		data() {
			return {
				devPosition: true
			}
		},
		onLoad() {
			this.$refs.BaseCamera.open()//调用相机
		},
		methods: {
			// 切换前后置镜头回调函数方法
			CameraPosition() {
				this.devPosition = !this.devPosition
			},
		}
	}
</script>

五、项目链接 

Gitee地址: https://gitee.com/huangzixing/components.git