CNCEC_APP/uni_modules/uview-pro/components/u-avatar-cropper/u-avatar-cropper.vue

300 lines
8.4 KiB
Vue
Raw Permalink 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, backgroundColor: 'rgba(0, 0, 0, 0.8)' }"
canvas-id="cropper"
id="cropper"
></canvas>
<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">{{ t('avatarCropper.select') }}</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="upload" @tap="uploadTap">{{ t('avatarCropper.reselect') }}</view>
<!-- #endif -->
<view class="getCropperImage" @tap="() => getCropperImage(false)">{{ t('avatarCropper.confirm') }}</view>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'u-avatar-cropper',
options: {
addGlobalClass: true,
// #ifndef MP-TOUTIAO
virtualHost: true,
// #endif
styleIsolation: 'shared'
}
};
</script>
<script setup lang="ts">
// @ts-nocheck
import { ref, reactive, onMounted } from 'vue';
import { $u, useLocale } from '../..';
// 兼容 UMD/ESM 导入 weCropper.js
import WeCropper from './weCropper';
import { AvatarCropperProps } from './types';
/**
* 裁剪矩形框的样式其中可包含的属性为lineWidth-边框宽度(单位rpx)color: 边框颜色,
* mask-遮罩颜色一般设置为一个rgba的透明度如"rgba(0, 0, 0, 0.35)"
*/
const props = defineProps(AvatarCropperProps);
const { t } = useLocale();
/**
* 组合式API变量声明
* 保留所有说明注释
*/
const bottomNavHeight = ref(50); // 底部导航的高度
const originWidth = ref(200);
const width = ref(0);
const height = ref(0);
const src = ref(''); // 选择的图片路径,用于在点击确定时,判断是否选择了图片
const destWidth = ref(200); // 输出图片宽度单位px
const rectWidth = ref(200); // 裁剪框宽度单位px
const fileType = ref('jpg'); // 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
// cropperOpt 需响应式,且类型安全
const cropperOpt = reactive({
id: 'cropper',
targetId: 'targetCropper',
pixelRatio: 1,
width: 0,
height: 0,
scale: 2.5,
zoom: 8,
cut: {
x: 0,
y: 0,
width: originWidth.value,
height: originWidth.value
},
boundStyle: {
lineWidth: 0,
mask: '',
color: ''
}
});
let cropper: any = null; // WeCropper 实例,类型 any建议后续补充类型声明
/**
* 组件挂载时初始化 cropper
* 保留所有说明注释
*/
onMounted(() => {
// 获取系统信息
const rectInfo = uni.getSystemInfoSync();
width.value = rectInfo.windowWidth;
height.value = rectInfo.windowHeight - bottomNavHeight.value;
cropperOpt.width = width.value;
cropperOpt.height = height.value;
cropperOpt.pixelRatio = rectInfo.pixelRatio;
// 初始化裁剪框
cropperOpt.cut = {
x: (width.value - originWidth.value) / 2,
y: (height.value - originWidth.value) / 2,
width: originWidth.value,
height: originWidth.value
};
cropperOpt.boundStyle = {
lineWidth: uni.upx2px(props.boundStyle.lineWidth),
mask: props.boundStyle.mask,
color: props.boundStyle.borderColor
};
// 设置导航栏样式以免用户在page.json中没有设置为黑色背景
uni.setNavigationBarColor({
frontColor: 'var(--u-white-color)',
backgroundColor: 'var(--u-bg-black)'
});
// 初始化 cropper 实例
cropper = new WeCropper(cropperOpt)
.on('ready', (ctx: any) => {
// wecropper is ready for work!
})
.on('beforeImageLoad', (ctx: any) => {
// before picture loaded, i can do something
})
.on('imageLoad', (ctx: any) => {
// picture loaded
})
.on('beforeDraw', (ctx: any, instance: any) => {
// before canvas draw,i can do something
});
// 选择图片
uni.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: (res: UniApp.ChooseImageSuccessCallbackResult) => {
src.value = res.tempFilePaths[0];
// 获取裁剪图片资源后给data添加src属性及其值
cropper.pushOrign(src.value);
}
});
});
/**
* 触摸开始事件
* @param e 事件对象
*/
function touchStart(e: any) {
cropper && cropper.touchStart(e);
}
/**
* 触摸移动事件
* @param e 事件对象
*/
function touchMove(e: any) {
cropper && cropper.touchMove(e);
}
/**
* 触摸结束事件
* @param e 事件对象
*/
function touchEnd(e: any) {
cropper && cropper.touchEnd(e);
}
/**
* 获取裁剪后的图片
* @param isPre 是否预览
*/
function getCropperImage(isPre = false) {
if (!src.value) return $u.toast(t('avatarCropper.noSelect'));
const cropper_opt = {
destHeight: Number(destWidth.value), // uni.canvasToTempFilePath要求这些参数为数值
destWidth: Number(destWidth.value),
fileType: fileType.value
};
cropper.getCropperImage(cropper_opt, (path: string, err: any) => {
if (err) {
uni.showModal({
title: t('avatarCropper.modalTitle'),
content: err.message
});
} else {
if (isPre) {
uni.previewImage({
current: '', // 当前显示图片的 http 链接
urls: [path] // 需要预览的图片 http 链接列表
});
} else {
uni.$emit('uAvatarCropper', path);
$u.route({
type: 'back'
});
}
}
});
}
/**
* 重新选择图片
*/
function uploadTap() {
uni.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: (res: UniApp.ChooseImageSuccessCallbackResult) => {
src.value = res.tempFilePaths[0];
// 获取裁剪图片资源后给data添加src属性及其值
cropper.pushOrign(src.value);
}
});
}
</script>
<style scoped lang="scss">
@import '../../libs/css/style.components.scss';
.content {
background: rgba(255, 255, 255, 1);
}
.cropper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 11;
}
.cropper-buttons {
background-color: var(--u-bg-black);
color: var(--u-divider-color);
}
.cropper-wrapper {
position: relative;
@include vue-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
background-color: var(--u-bg-black);
}
.cropper-buttons {
width: 100vw;
@include vue-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 {
width: 50%;
text-align: center;
}
.cropper-buttons .upload {
text-align: left;
padding-left: 50rpx;
}
.cropper-buttons .getCropperImage {
text-align: right;
padding-right: 50rpx;
}
</style>