officialAccount/pages/addoldman/camera.vue

195 lines
4.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- 原生相机预览 -->
<camera id="idCamera" class="camera-preview" device-position="back" flash="off" binderror="onCameraError" />
<!-- 隐藏的裁剪 Canvas -->
<canvas canvas-id="cropCanvas"
style="width:100%;height:100%;position:absolute;top:0;left:0;display:none;"></canvas>
<!-- 遮罩层 -->
<view class="mask">
<view class="mask-block top" />
<view class="mask-block middle">
<view class="side" />
<view class="cutout">
<text class="hint">请将身份证放入此框内</text>
</view>
<view class="side" />
</view>
<view class="mask-block bottom" />
</view>
<!-- 底部拍照按钮 -->
<view class="controls">
<button class="shutter" @tap="takePhoto"> </button>
</view>
</view>
</template>
<script setup>
import {
onMounted,
ref
} from 'vue';
let cameraContext;
const sysInfo = uni.getSystemInfoSync(); // 获取屏幕宽高
onMounted(() => {
cameraContext = uni.createCameraContext();
});
function onCameraError(e) {
console.error('Camera Error:', e);
}
function takePhoto() {
cameraContext.takePhoto({
quality: 'high',
success: async (res) => {
const src = res.tempImagePath;
// 2.1 计算屏幕上 cutout 区域px
const screenW = sysInfo.windowWidth;
const screenH = sysInfo.windowHeight;
const topMaskH = screenH * 0.25; // mask-block.top 高度
const middleH = screenH * 0.50; // mask-block.middle 高度
const cutoutH = middleH * 0.60; // cutout 高度
const cutoutW = cutoutH * 1.586; // 按 aspect-ratio:1.586 计算宽度
const cutoutX = (screenW - cutoutW) / 2; // 居中
const cutoutY = topMaskH + (middleH - cutoutH) / 2;
// 2.2 拿到原图真实尺寸,用于映射
const info = await uni.getImageInfo({
src
});
const origW = info.width;
const origH = info.height;
// 2.3 计算裁剪在原图上的参数
const ratioW = origW / screenW;
const ratioH = origH / screenH;
const sx = cutoutX * ratioW;
const sy = cutoutY * ratioH;
const sWidth = cutoutW * ratioW;
const sHeight = cutoutH * ratioH;
// uni.authorize({
// scope: 'scope.camera',
// success: () => console.log('已授权相机'),
// fail: () => uni.showModal({ title: '权限不足', content: '请在设置里打开相机权限' })
// });
// 3. 在 Canvas 上裁切
const ctx = uni.createCanvasContext('cropCanvas', {
enableScroll: false
});
// 设置画布尺寸为裁剪区域的像素大小
ctx.drawImage(src, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'cropCanvas',
x: 0,
y: 0,
width: sWidth,
height: sHeight,
destWidth: sWidth,
destHeight: sHeight,
success: (cropRes) => {
// 裁切完成,存入缓存并返回
uni.setStorageSync('idcardPhoto', cropRes.tempFilePath);
uni.navigateBack({
delta: 1
});
},
fail: (err) => {
console.error('Canvas to TempFile Fail:', err);
}
});
});
},
fail: (err) => {
console.error('Take Photo Fail:', err);
}
});
}
</script>
<style scoped>
.container {
position: relative;
width: 100%;
height: 100vh;
background: #000;
}
.camera-preview {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.mask-block {
width: 100%;
background: rgba(0, 0, 0, 0.5);
}
.mask-block.top,
.mask-block.bottom {
height: 25%;
}
.mask-block.middle {
display: flex;
flex-direction: row;
height: 50%;
}
.side {
flex: 1;
background: rgba(0, 0, 0, 0.5);
}
.cutout {
width: 100%;
height: 60%;
aspect-ratio: 1.586;
border: 2rpx dashed #fff;
position: relative;
}
.hint {
position: absolute;
bottom: 20rpx;
width: 100%;
text-align: center;
color: #fff;
font-size: 24rpx;
}
.controls {
position: absolute;
bottom: 50rpx;
width: 100%;
display: flex;
justify-content: center;
}
.shutter {
width: 300rpx;
height: 120rpx;
border-radius: 60rpx;
background: rgba(255, 255, 255, 0.7);
display: flex;
justify-content: center;
align-items: center;
}
</style>