收藏
回答

canvas封装成组件,绘制图片不显示?

<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,
});




最后一次编辑于  04-10
回答关注问题邀请回答
收藏

1 个回答

  • Infinity
    Infinity
    04-10

    我又来了

    canvasToTempFilePath下载截取后的图又不行了,需要把canvas放到组件外才能获取否则获取不到

    没生效呢

    04-10
    有用
    回复
登录 后发表内容