dyqc_hdapp/packageA/cropper.vue

415 lines
10 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="content">
<view class="cropper-wrapper" :style="{ height: cropperOpt.height + 'px' }">
<canvas
class="cropper"
:disable-scroll="true"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
:style="{ width: cropperOpt.width, height: cropperOpt.height }"
canvas-id="cropper"
id="cropper"
></canvas>
<view class="cropper-circle-mask" v-show="circularCrop" :style="{
left: `${cropperOpt.cut.x}px`,
top: `${cropperOpt.cut.y}px`,
width: `${cropperOpt.cut.width}px`,
height: `${cropperOpt.cut.height}px`,
zIndex: 100
}"></view>
<canvas
class="cropper"
:disable-scroll="true"
:style="{
position: 'fixed',
top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`,
left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`,
width: `${cropperOpt.width * cropperOpt.pixelRatio}px`,
height: `${cropperOpt.height * cropperOpt.pixelRatio}`,
}"
canvas-id="targetId"
id="targetId"
></canvas>
</view>
<view class="cropper-buttons safe-area-padding" :style="{ height: bottomNavHeight + 'px' }">
<!-- #ifdef H5 -->
<view class="upload" @tap="uploadTap">选择图片</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="upload" @tap="uploadTap">重新选择</view>
<!-- #endif -->
<view class="getCropperImage" @tap="getCropperImage(false)">确定</view>
</view>
</view>
</template>
<script>
import WeCropper from '@/utils/weCropper.js';
export default {
props: {
// 裁剪矩形框的样式其中可包含的属性为lineWidth-边框宽度(单位rpx)color: 边框颜色,
// mask-遮罩颜色一般设置为一个rgba的透明度如"rgba(0, 0, 0, 0.35)"
boundStyle: {
type: Object,
default() {
return {
lineWidth: 4,
borderColor: 'rgb(245, 245, 245)'
};
}
},
// 是否使用圆形裁剪
circularCrop: {
type: Boolean,
default: true
}
},
data() {
return {
// 底部导航的高度
bottomNavHeight: 150,
originWidth: 200,
width: 0,
height: 0,
cropper: '',
cropperOpt: {
id: 'cropper',
targetId: 'targetId',
pixelRatio: 1,
width: 0,
height: 0,
scale: 2.5,
zoom: 8,
cut: {
x: (this.width - this.originWidth) / 2,
y: (this.height - this.originWidth) / 2,
width: this.originWidth,
height: this.originWidth
},
boundStyle: {
lineWidth: uni.upx2px(this.boundStyle.lineWidth)
}
},
// 裁剪框和输出图片的尺寸,高度默认等于宽度
// 输出图片宽度单位px
destWidth: 200,
// 裁剪框宽度单位px
rectWidth: 200,
// 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
fileType: 'png', // 改为png以支持透明背景
src: '' // 选择的图片路径,用于在点击确定时,判断是否选择了图片
};
},
onLoad(option) {
let rectInfo = uni.getSystemInfoSync();
this.width = rectInfo.windowWidth;
this.height = rectInfo.windowHeight - this.bottomNavHeight;
this.cropperOpt.width = this.width;
this.cropperOpt.height = this.height;
this.cropperOpt.pixelRatio = rectInfo.pixelRatio;
if (option.destWidth) this.destWidth = Number(option.destWidth);
if (option.rectWidth) {
let rectWidth = Number(option.rectWidth);
this.cropperOpt.cut = {
x: (this.width - rectWidth) / 2,
y: (this.height - rectWidth) / 2,
width: rectWidth,
height: rectWidth
};
}
this.rectWidth = Number(option.rectWidth);
if (option.fileType) this.fileType = option.fileType;
// 圆形裁剪需要PNG格式支持透明
if (this.circularCrop && this.fileType === 'jpg') {
this.fileType = 'png';
}
// 设置圆形样式边界
if (this.circularCrop) {
this.cropperOpt.boundStyle.isCircle = true;
}
// 初始化
this.cropper = new WeCropper(this.cropperOpt)
.on('ready', ctx => {
// wecropper is ready for work!
})
.on('beforeImageLoad', ctx => {
// before picture loaded, i can do something
})
.on('imageLoad', ctx => {
// picture loaded
})
.on('beforeDraw', (ctx, instance) => {
// before canvas draw,i can do something
});
// 设置导航栏样式以免用户在page.json中没有设置为黑色背景
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#000000'
});
uni.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
this.src = res.tempFilePaths[0];
// 获取裁剪图片资源后给data添加src属性及其值
this.cropper.pushOrign(this.src);
}
});
},
methods: {
touchStart(e) {
this.cropper.touchStart(e);
},
touchMove(e) {
this.cropper.touchMove(e);
},
touchEnd(e) {
this.cropper.touchEnd(e);
},
getCropperImage(isPre = false) {
if (!this.src) return this.$toast('请先选择图片再裁剪');
// 如果是圆形裁剪,使用与 directCircleCrop 相同的方法
if (this.circularCrop) {
// 使用最简单直接的方式裁剪
const size = 300;
// 1. 先使用weCropper获取矩形裁剪图片
const options = {
destWidth: size,
destHeight: size,
fileType: 'png'
};
this.cropper.getCropperImage(options, (rectPath, err) => {
if (err) {
console.error('矩形裁剪失败:', err);
return;
}
console.log('矩形裁剪成功,路径:', rectPath);
// 2. 创建新画布进行圆形裁剪
const ctx = uni.createCanvasContext('targetId');
// 先清空画布
ctx.clearRect(0, 0, size, size);
// 设置背景为透明
ctx.setFillStyle('rgba(0,0,0,0)');
ctx.fillRect(0, 0, size, size);
// 创建圆形裁剪区域
ctx.save();
ctx.beginPath();
ctx.arc(size/2, size/2, size/2, 0, 2 * Math.PI);
ctx.clip();
// 绘制图片到圆形区域
ctx.drawImage(rectPath, 0, 0, size, size);
ctx.restore();
// 绘制并导出
ctx.draw(false, () => {
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: 'targetId',
x: 0,
y: 0,
width: size,
height: size,
destWidth: size,
destHeight: size,
fileType: 'png',
backgroundColor: 'transparent',
success: (res) => {
console.log('圆形裁剪成功:', res.tempFilePath);
if (isPre) {
uni.previewImage({
urls: [res.tempFilePath]
});
} else {
uni.$emit('uAvatarCropper', res.tempFilePath);
uni.navigateBack();
}
},
fail: (err) => {
console.error('圆形裁剪失败:', err);
// 失败时使用矩形图片
if (isPre) {
uni.previewImage({
urls: [rectPath]
});
} else {
uni.$emit('uAvatarCropper', rectPath);
uni.navigateBack();
}
}
});
}, 300);
});
});
return;
}
// 原始矩形裁剪方法
this.useOriginalCrop(isPre);
},
// 使用原始裁剪方法
useOriginalCrop(isPre = false) {
// 确保使用合适的尺寸
const size = 300; // 增大尺寸
let cropper_opt = {
destHeight: size,
destWidth: size,
fileType: this.fileType
};
console.log('使用原始裁剪方法,参数:', cropper_opt);
try {
this.cropper.getCropperImage(cropper_opt, (path, err) => {
console.log('裁剪回调被调用', path, err);
if (err) {
console.error('裁剪出错:', err);
uni.showModal({
title: '温馨提示',
content: err.message
});
} else {
console.log('裁剪成功,路径:', path);
if (isPre) {
console.log('预览图片');
uni.previewImage({
current: '', // 当前显示图片的 http 链接
urls: [path] // 需要预览的图片 http 链接列表
});
} else {
console.log('返回上一页');
uni.$emit('uAvatarCropper', path);
uni.navigateBack();
}
}
});
} catch (error) {
console.error('调用 getCropperImage 出错:', error);
uni.showModal({
title: '裁剪出错',
content: error.message || '未知错误'
});
}
},
uploadTap() {
const self = this;
uni.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
self.src = res.tempFilePaths[0];
// 获取裁剪图片资源后给data添加src属性及其值
self.cropper.pushOrign(this.src);
}
});
},
}
};
</script>
<style scoped lang="scss">
.content {
background: rgba(255, 255, 255, 1);
}
.cropper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 11;
}
.cropper-circle-mask {
position: absolute;
border-radius: 50%;
box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.35);
z-index: 12;
pointer-events: none; /* Allow touches to pass through to the canvas underneath */
box-sizing: border-box;
}
.cropper-buttons {
background-color: #000000;
color: #eee;
}
.cropper-wrapper {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
background-color: #000;
}
.cropper-buttons {
width: 100vw;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
left: 0;
font-size: 28rpx;
}
.cropper-buttons .upload,
.cropper-buttons .getCropperImage,
.cropper-buttons .test,
.cropper-buttons .test2,
.cropper-buttons .test3,
.cropper-buttons .test4 {
width: 16.6%;
text-align: center;
}
.cropper-buttons .upload {
text-align: left;
padding-left: 50rpx;
}
.cropper-buttons .test {
color: #ffeb3b;
}
.cropper-buttons .test2 {
color: #4caf50;
}
.cropper-buttons .test3 {
color: #ff9800;
}
.cropper-buttons .test4 {
color: #e91e63;
}
.cropper-buttons .getCropperImage {
text-align: right;
padding-right: 50rpx;
}
</style>