vue3+uniapp实现微信小程序框选截取2寸照片功能
1.组件代码
src/components下面创建capturePhotos.vue文件 内容如下
<template>
<view class="picture-tailor" :class="{ 'picture-tailor-show': isShow }">
<movable-area class="picture-area">
<movable-view
class="picture-view"
:style="{ width: img_width / img_scaling + 'px', height: img_height / img_scaling + 'px' }"
direction="all"
:x="offsetX"
:y="offsetY"
scale="true"
:scale-min="scaleMin"
@change="movableChange"
@scale="movableScale">
<image :style="{ width: img_width / img_scaling + 'px', height: img_height / img_scaling + 'px' }" :src="pictureSrc"></image>
</movable-view>
</movable-area>
<view class="select-box"></view>
<button class="button-ok" @click="createImg">完成</button>
<button class="button-no" @click="hide">取消</button>
<canvas type="2d" id="picture-canvas" class="canvas-view"></canvas>
</view>
</template>
<script>
// rpx转px
function rpxToPx(rpx) {
const screenWidth = uni.getSystemInfoSync().screenWidth
return (screenWidth * Number.parseInt(rpx)) / 750
}
// 480rpx 转px
let tailorSizeX = rpxToPx(350);
let tailorSizeY = rpxToPx(530);// 需要截取的尺寸350rpx x 530rpx,此变量要和样式中的350rpx,175rpx相对应,175rpx为此变量的一半,若要修改成其他值一定要一一对应
let newOffsetX = 0; // 拖动缩放完成后的X轴偏移量
let newOffsetY = 0; // 拖动缩放完成后的Y轴偏移量
export default {
name: "lv-clip",
data() {
return {
pictureSrc:'',// 图片
offsetX: 0, // 图像初始化的X轴偏移量
offsetY: 0, // 图像初始化的Y轴偏移量
img_width: 0, // 图片真实宽度
img_height: 0, // 图片真实高度
img_scaling: 1, //图片初始化缩放比例
scale: 1, // 拖动缩放完成后的图片缩放比例
scaleMin: 0.5, // 最小缩放值
isShow: false
};
},
methods: {
// 显示组件
show(img) {
this.pictureSrc = img; // 赋值图片
this.getImgInfo(); // 初始化图片
this.isShow = true; // 显示组件
},
// 隐藏组件
hide() {
this.isShow = false;
},
// 初始化图片
getImgInfo() {
uni.getImageInfo({
src: this.pictureSrc,
success: res => {
// 图片宽高
this.img_width = res.width;
this.img_height = res.height;
// 把最小的边撑满
// let count = this.img_width <= this.img_height ? this.img_width : this.img_height;
let count = this.img_height;
this.img_scaling = count / tailorSizeY;
this.scaleMin = 1;
// 计算图片居中显示时的偏移量
this.offsetX = -(this.img_width / this.img_scaling / 2 - tailorSizeX / 2);
this.offsetY = -(this.img_height / this.img_scaling / 2 - tailorSizeY / 2);
// 获取新的偏移量
newOffsetX = this.offsetX;
newOffsetY = this.offsetY;
}
});
},
// 计算拖动偏移量
movableChange(e) {
newOffsetX = e.detail.x;
newOffsetY = e.detail.y;
},
// 计算缩放比例和偏移量
movableScale(e) {
newOffsetX = e.detail.x;
newOffsetY = e.detail.y;
this.scale = e.detail.scale;
},
// 截取图片
createImg() {
// #ifdef MP-WEIXIN
uni.createSelectorQuery().in(this).select('#picture-canvas').fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
canvas.width = tailorSizeX;
canvas.height = tailorSizeY;
const ctx = canvas.getContext('2d')
let headerImg = canvas.createImage(); //创建iamge实例
headerImg.src = this.pictureSrc; //临时图片路径
headerImg.onload = () => {
ctx.drawImage(
headerImg,
newOffsetX,//起点 X 坐标
newOffsetY,//起点 Y 坐标
(this.img_width / this.img_scaling) * this.scale,//终点 X 坐标
(this.img_height / this.img_scaling) * this.scale//终点 Y 坐标
);
ctx.restore(); //保存上下文
uni.canvasToTempFilePath({
quality: 1,// 图片质量0-1
canvas:canvas,
fileType: 'png',
success: res => {
// 在H5平台下,tempFilePath 为 base64
this.hide(); // 关闭
this.$emit("createImg", res.tempFilePath);
},
fail: function (res) {
this.hide(); // 关闭
}
},this);
};
})
// #endif
// #ifdef MP-DINGTALK
let ctx = uni.createCanvasContext("picture-canvas", this);
ctx.drawImage(
this.pictureSrc,
newOffsetX,//起点 X 坐标
newOffsetY,//起点 Y 坐标
(this.img_width / this.img_scaling) * this.scale,//终点 X 坐标
(this.img_height / this.img_scaling) * this.scale//终点 Y 坐标
);
ctx.draw(false, () => {
uni.canvasToTempFilePath(
{
quality: 1,// 图片质量0-1
canvasId: "picture-canvas",// 画布标识,传入 <canvas/> 的 canvas-id(支付宝小程序是id、其他平台是canvas-id)
success: res => {
this.hide(); // 关闭
this.$emit("createImg", res.tempFilePath);
},
fail: function (res) {
this.hide(); // 关闭
}
},
this
);
});
// #endif
}
}
};
</script>
<style scoped>
.picture-tailor {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
background-color: #000000;
z-index:100;
transform: translateX(100%);
transition: all 200ms ease;
overflow: hidden;
visibility: hidden;
}
.picture-tailor-show {
transform: translateY(0) !important;
visibility: visible;
}
/* 拖动域的位置和大小 */
.picture-tailor .picture-area {
width: 350rpx;
height: 530rpx;
position: absolute;
/* 使其居中定位 */
top: calc(50% - 265rpx);
left: calc(50% - 175rpx);
}
/* 拖动控件的大小 */
.picture-area .picture-view {
min-width: 350rpx;
min-height: 530rpx;
}
/* 中间选择框的大小,本意是视觉上模拟拖动域 */
.select-box {
position: absolute;
top: calc(50% - 265rpx);
left: calc(50% - 175rpx);
width: 350rpx;
height: 530rpx;
box-sizing: border-box;
/* border-radius: 50%; */
border: red 5rpx solid;
pointer-events: none;
}
.button-ok {
position: absolute;
bottom: 40rpx;
right: 40rpx;
width: 120rpx;
background-color: #007aff;
font-size: 28rpx;
color: #ffffff;
}
.button-no {
position: absolute;
bottom: 40rpx;
left: 40rpx;
width: 120rpx;
background-color: #007aff;
font-size: 28rpx;
color: #ffffff;
}
/* 画布大小,画布大小就是截取的原始大小 */
.canvas-view {
width: 350rpx;
height: 530rpx;
position: relative;
left: -9999rpx;
visibility: hidden;
}
</style>
2.vue3页面使用
<u-form-item label="上传头像" required :borderBottom="true">
<view class="page">
<image class="iii" :src="tailorPath" ></image>
<capture-photos ref="capturePhotosDom" @createImg="createImg"></capture-photos>
<button type="primary" size="mini" @click="choosePhoto">选择图片</button>
</view>
</u-form-item>
<script setup>
import capturePhotos from "@/components/capturePhotos.vue";
const capturePhotosDom = ref(null);
const tailorPath = ref('')
const choosePhoto = ()=>{
uni.chooseImage({
count: 1,
sizeType: ["compressed"],
success: res => {
if(res.tempFilePaths[0]){
// 传入临时路径
capturePhotosDom.value.show(res.tempFilePaths[0]);
}
}
});
}
// 拿到临时路径
async function createImg(e){
console.log(e)
tailorPath.value = e;
await upload(e)
}
function upload(e) {
// 调用uni.uploadFile将文件上传至服务器
uni.showLoading()
uni.uploadFile({
url: qlm_gateway_url + '/public/filemanage/upload', // 设置上传接口地址
filePath: e, // 需要上传的文件路径
name: 'file', // 后台接收文件时对应的字段名称
header: {
// "Content-Type": "multipart/form-data",
},
success(response) {
// TODO: 处理上传成功后的操作
console.log('文件上传成功');
uni.hideLoading()
const data = JSON.parse(response.data)
formData.value.photo = data.data.url
tailorPath.value=file_server_url + data.data.url
photourl.value = [{
name: "photo",
url: file_server_url + data.data.url
}]
// uni.hideLoading()
return
},
fail(error) {
console.log('文件上传失败');
console.log(error);
uni.hideLoading()
// uni.hideLoading()
// TODO: 处理上传失败后的操作
}
});
}
//查询
async function findPlayerInfo(id) {
uni.showLoading()
const res = await findPlayerInfoBO(id)
console.log('res',res.data)
uni.hideLoading()
if(res.retCode==88888888){
uni.showToast({
title: res.msg,
duration: 2000
});
formData.value=res.data
// getImageUrl(res.data.photo)
if (res.data.photo != null) {
tailorPath.value=file_server_url + res.data.photo
}
}else{
uni.showToast({
title: res.msg,
icon:'error',
duration: 2000
});
return false
}
}
</script>
<style lang="scss" scoped>
.iii{
width: 70px;
height: 106px;
border: 2rpx solid red;
image{
width: 100%;
height: 100%;
}
}
</style>