<canvas id="image-cropping" canvas-id="image-cropping" :style="{ width: '408px', height: '510px' }"></canvas>
封装一个按想要比例截取图片的组件(imageCropping),页面上引用组件绘制图片时,显示不出来。id都是默认写死的。
创建画布也是获取成功的,加了try内部也没有报错。也用过创建画布加this。
然后就是显示不出来。
下面是调试好的功能;自测可以使用。
参考代码:
import { getCurrentInstance, ref } from 'vue';
import { useUploadFile } from '@/request/index';
import { getUuid, parseJson, showToast } from '@/utils/common';
import useRequestConfig from '@/request/config';
import type { UploadFileResponse } from './type';
const { REQUEST_SUCCESS_CODE } = useRequestConfig();
// 图片上传请求
const uploadRequest = useUploadFile();
// 获取当前实例: 否则绘制图片不显示
const instance = getCurrentInstance();
// canvas实例id
const canvasId = `image-cropping-${getUuid()}`;
interface PropsType {
imageUrl: string; // 图片路径
aspectRatio: number; // 图片宽高比例
// 截取框的宽高和位置
width: number;
height: number;
}
const props = withDefaults(defineProps<PropsType>(), {
imageUrl: '',
width: 0,
height: 0,
aspectRatio: 0.8, // 分享图片默认长宽比5:4
});
// 设置画布宽高
const canvasWidth = ref<number | string>(0);
const canvasHeight = ref<number | string>();
// 获取画布,开始绘图
const createCanvasByImg = () => {
return new Promise<void>(async (resolve) => {
try {
// 获取源图宽高信息
const imgInfo = await getOriginalImgInfo();
if (!imgInfo) return;
// 获取图片截取宽高,和截取位置起始点(左上角)
const { width, height, left, top } = getCanvasWH(imgInfo.width, imgInfo.height);
// 画布宽高 = 截取图片宽高
canvasHeight.value = `${height}px`;
canvasWidth.value = `${width}px`;
// 创建画布实例
const ctx = uni.createCanvasContext(canvasId, instance);
// 绘制图片: path-图片路径, (left,top)源图中截取的起始点对应(x,y)轴
// 第一组width/height,对应需要绘制的图片宽高,第二组width/height,对应图片截取的宽高
ctx.drawImage(imgInfo.path, left, top, width, height, 0, 0, width, height);
// 画完之后,将图片转为图片
ctx.draw(false, async () => {
// 等待生成图片地址
await getImageTemporaryUrl();
resolve();
});
// eslint-disable-next-line no-empty
} catch (e) {
resolve();
}
});
};
/**
* 根据宽高比例,设置画布高度
* @param imgH 图片高度
* @param imgW 图片宽度
*/
const getCanvasWH = (imgW: number, imgH: number) => {
// 初始剪切框宽高
const result = {
width: props.width,
height: props.height,
left: 0,
top: 0,
};
// 根据具体图片长宽比和实际截取的长宽比,计算截取框的宽高
// 以图片最短的一边进行计算,尽量保证短的一边完整
if (imgH >= imgW) {
// 初始化截取宽度 = 图片宽度
result.width = imgW;
// 截取高度 = 图片宽度 * 高宽比
result.height = imgW * props.aspectRatio;
// 如果截取高度比原图片高度大,需要按比例缩小截取宽度
if (result.height > imgH) {
result.width = (result.height / imgH) * result.width;
result.height = imgH;
}
} else {
// 初始化截取高度 = 图片高度
result.height = imgH;
// 截取宽度 = 图片高度 / 高宽比
result.width = imgH / props.aspectRatio;
// 如果截取宽度比原图片宽度大,需要按比例缩小截取高度
if (result.width > imgW) {
result.height = (result.width / imgW) * result.height;
result.width = imgW;
}
}
// 图片绘制开始位置偏移量
result.left = (imgW - result.width) / 2;
result.top = (imgH - result.height) / 2;
return result;
};
// 将原图片转为临时图片,获取图片高/宽信息
const getOriginalImgInfo = () => {
return new Promise<UniApp.GetImageInfoSuccessData | null>((resolve) => {
uni.getImageInfo({
src: props.imageUrl,
success: (res) => resolve(res),
fail: () => resolve(null),
});
});
};
// 将画布转为图片,获取临时图片地址(有时效,不能直接用于分享)
let currentCanvasImagUrl = '';
const getImageTemporaryUrl = () => {
return new Promise<string>((resolve) => {
// 已有地址,不重复获取
if (currentCanvasImagUrl) return resolve(currentCanvasImagUrl);
// 将画布转为图片获取临时地址
uni.canvasToTempFilePath(
{
canvasId: canvasId,
success: (res) => {
currentCanvasImagUrl = res.tempFilePath;
resolve(res.tempFilePath);
},
fail: () => resolve(''),
},
instance
);
});
};
// 图片上传到内部服务器
let currentUploadImgUrl = '';
const uploadImage = async () => {
// 已上传过的图片,不重复上传
if (currentUploadImgUrl) return currentUploadImgUrl;
// 获取图片临时地址
const imageTemporaryUrl = await getImageTemporaryUrl();
if (imageTemporaryUrl) {
// 上传图片
const res = await uploadRequest.upload({
tag: '分享图片裁剪',
timeout: 15000,
filePath: imageTemporaryUrl,
});
const uploadResult = parseJson<UploadFileResponse>(res as string);
if (uploadResult && REQUEST_SUCCESS_CODE.includes(uploadResult.code)) {
currentUploadImgUrl = uploadResult.data;
}
}
// 图片上传失败,返回原图片地址
return currentUploadImgUrl || props.imageUrl;
};
// 图片下载
const downloadImage = async () => {
// 校验用户相册权限
const hasPermission = await checkSaveImagePermission();
if (!hasPermission) {
// 自动调起获取相册权限
const result = await automaticSaveImagePermission();
// 如果没有授权成功,弹窗提示
if (!result) return showAuthTipModal();
}
// 获取图片临时地址
const imageTemporaryUrl = await getImageTemporaryUrl();
if (!imageTemporaryUrl) return showToast('保存失败,请重试~');
// 下载图片
uni.saveImageToPhotosAlbum({
filePath: imageTemporaryUrl,
success: () => showToast('保存成功'),
fail: () => showToast('保存失败'),
});
};
// 图片下载,相册权限校验
const checkSaveImagePermission = () => {
return new Promise((resolve) => {
uni.getSetting({
success: (res) => resolve(res.authSetting['scope.writePhotosAlbum']),
fail: () => resolve(false),
});
});
};
// 图片下载,相册权限校验未通过,自动呼起相册授权
const automaticSaveImagePermission = () => {
return new Promise((resolve) => {
uni.authorize({
scope: 'scope.writePhotosAlbum',
success: () => resolve(true),
fail: () => resolve(false),
});
});
};
// 授权提示
const showAuthTipModal = () => {
uni.showModal({
title: '授权提示',
content: '是否允许获取保存相册权限',
confirmColor: '#0a4cde',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: (res) => {
if (res.authSetting['scope.writePhotosAlbum']) {
// 授权成功,再次下载图片
downloadImage();
} else {
showToast('请确认相册权限已开启');
}
},
});
}
},
});
};
// 暴露接口
defineExpose({
createCanvasByImg,
uploadImage,
downloadImage,
});
我又来了
canvasToTempFilePath下载截取后的图又不行了,需要把canvas放到组件外才能获取否则获取不到
没生效呢