微信小程序使用canvas进行绘图之后,pc端运行报错,真机调试绘图成功,但是只有黑色背景,没有内容 ,求大佬指教?

代码稍长,还请大佬们担待

wxml

<view class="Jigsaw_modal" wx:if="{{visible}}">
        <view class="modal_content" style="position: relative;">
            <view class="modal_top">
                一键拼图
            </view>
            <canvas type="2d" id='MyCanvas' class="modal_middle" style="position: absolute;z-index: -1;"></canvas>

            <view class="modal_middle">
                <view class="desc_text">
                    <image src="{{teethIcon}}" mode="" />
                    <text>齿刻美 · 打造专属笑容</text>
                </view>
                <view class="desc_content">
                    <view>
                        <image src="{{beforeImage}}" mode="" />
                        <text>设计前</text>
                    </view>
                    <image class="vs_image" src="{{vsImage}}" mode="" />
                    <view>
                        <image src="{{afterImage}}" mode="" />
                        <text>设计后</text>
                    </view>
                </view>
            </view>
            <view class="modal_footer" bind:tap="initCanvas">
                保存拼图
            </view>
        </view>
        <view class="close_icon" bind:tap="handleCloseJigsawModa">
            <image src="{{closeIcon}}" mode="" />
        </view>
    </view>

wxjs

initCanvas() {
        const canvasQuery = wx.createSelectorQuery();
        canvasQuery.select('#MyCanvas')
            .fields({
                node: true,
                size: true,
                rect: true
            })
            .exec(res => this.CanvasInit(res));
    },

    CanvasInit(res) {
        console.log(res, '-----------');
        const width = res[0].width;
        this.canvasWidth = width;
        const height = res[0].height;
        this.canvasHeight = height;
        const canvas = res[0].node;
        this.canvas = canvas;
        const ctx = canvas.getContext('2d');

        // 适应各种屏幕
        const dpr = wx.getSystemSetting().pixelRatio;
        canvas.width = width * dpr;
        canvas.height = height * dpr;
        ctx.scale(dpr, dpr);

        ctx.save()
        // 绘制背景颜色
        ctx.fillStyle = '#fff';
        ctx.fillRect(0, 0, width, height);
        ctx.restore()

        // 初始化计数器为需要加载的图片数量
        let imageLoadCount = 4;

        // 绘制文字和图标部分
        ctx.save();
        ctx.beginPath();
        ctx.font = '22rpx';
        ctx.fillStyle = 'rgba(57, 77, 163, 1)';
        ctx.fillText('记录美好生活', width - 150, 40);
        ctx.translate(width - 50, 40);
        const teethImg = canvas.createImage();
        teethImg.src = this.data.teethIcon;
        teethImg.onload = () => {
            ctx.drawImage(teethImg, -15, -15, 30, 30);
            imageLoadCount--;
        };
        ctx.restore();

        // 绘制设计前部分
        ctx.save();
        ctx.beginPath();
        ctx.translate(0, 60);
        const beforeImg = canvas.createImage();
        beforeImg.src = this.data.afterImage;

        beforeImg.onload = () => {
            ctx.drawImage(beforeImg, 0, 0, width / 3, 390);
            ctx.font = '22rpx';
            ctx.fillStyle = 'rgba(57, 77, 163, 1)';
            ctx.fillText('设计前', width / 6, 390 + 20);
            imageLoadCount--;
        };
        ctx.restore();

        // 绘制对比图标
        ctx.save();
        ctx.beginPath();
        ctx.translate(width / 2, 200);
        const vsImg = canvas.createImage();
        vsImg.src = this.data.vsImage;

        vsImg.onload = () => {
            ctx.drawImage(vsImg, 0, 0, 50, 60);
            imageLoadCount--;
        };
        ctx.restore();

        // 绘制设计后部分
        ctx.save();
        ctx.beginPath();
        ctx.translate(width * 2 / 3, 60);
        const afterImg = canvas.createImage();
        afterImg.src = this.data.afterImage;

        afterImg.onload = () => {
            ctx.drawImage(afterImg, 0, 0, width / 3, 390);
            ctx.font = '22rpx';
            ctx.fillStyle = 'rgba(57, 77, 163, 1)';
            ctx.fillText('设计后', width * 5 / 6, 390 + 20);
            imageLoadCount--;
        };
        ctx.restore();

        // 检查图片加载完成状态
        const checkDrawingComplete = setInterval(() => {
            if (imageLoadCount === 0) {
                clearInterval(checkDrawingComplete);
                // 获取用户保存图片权限
                wx.getSetting({
                    success: (res) => {
                        if (['scope.writePhotosAlbum'] in res.authSetting) {
                            // 如果授权过且同意了
                            if (res.authSetting['scope.writePhotosAlbum']) {
                                this.canvasToImgSave();
                            } else {
                                wx.showModal({
                                    title: "请求授权相册存储",
                                    content: "需要获取您的相册存储权限,请确认授权",
                                    success: (resModal) => {
                                        if (resModal.confirm) {
                                            wx.openSetting({
                                                success: (resSetting) => {
                                                    this.canvasToImgSave();
                                                },
                                                fail: (err) => {
                                                    console.log(err);
                                                }
                                            });
                                        } else if (resModal.cancel) {
                                            console.log("你又取消了");
                                        }
                                    }
                                });
                            }
                        } else {
                            this.canvasToImgSave();
                        }
                    },
                    fail: (err) => {
                        console.log(err);
                    }
                });
            }
        }, 100);
    },

    canvasToImgSave() {
        wx.showLoading({
            title: '生成中...',
        });
        setTimeout(() => {
            wx.canvasToTempFilePath({
                canvas: this.canvas,
                x: 0,
                y: 0,
                width: this.canvasWidth * 2,
                height: this.canvasHeight * 2,
                destWidth: this.canvasWidth * 2,
                destHeight: this.canvasHeight * 2,
                success: (res) => {
                    wx.saveImageToPhotosAlbum({
                        filePath: res.tempFilePath,
                        success: (resSave) => {
                            wx.showModal({
                                title: "保存成功!",
                                content: "图片已保存到本地相册",
                                showCancel: false,
                                success: (resModal) => {

                                }
                            });
                        },
                        fail: (err) => {
                            console.log(err);
                        }
                    });
                },
                fail: (err) => {
                    console.log(err);
                },
                complete: () => {
                    wx.hideLoading()
                }
            });
        }, 1000)


    },
阅读 1.3k
avatarAI BotBETA

问题分析

在你的微信小程序代码中,有几个潜在的问题可能导致在PC端运行时报错,并且在真机调试时只显示黑色背景没有内容。这些问题主要涉及Canvas的绘制逻辑和微信小程序的兼容性。

  1. Canvas尺寸和DPI处理

    • 你使用了wx.getSystemSetting().pixelRatio来调整Canvas的DPI,这在微信小程序中是有效的,但在PC端模拟器或某些非标准环境中可能不可用或返回不正确的值。
  2. Canvas绘图顺序和保存/恢复状态

    • 你的代码中使用了多次ctx.save()ctx.restore(),但在某些情况下,如果restore调用没有正确匹配save,可能会导致绘图上下文的状态不正确。
  3. 字体大小单位

    • 你使用了'22rpx'作为字体大小,但在Canvas 2D API中,字体大小应该使用像素(px)或其他标准CSS单位,而不是rpx(微信小程序特有的单位)。
  4. 图片资源加载

    • 图片资源(如this.data.teethIcon等)必须确保在Canvas绘制前已正确加载。如果资源加载失败或路径错误,也会导致绘制失败。
  5. PC端兼容性

    • 微信小程序是为移动端设计的,其许多API在PC端可能无法正常工作,尤其是与设备相关的API如wx.getSystemSetting()

解决方案

  1. 修改字体大小单位

    • 将Canvas中的字体大小从rpx改为px,并根据需要调整大小。
    ctx.font = '22px'; // 改为像素单位
  2. 检查DPI处理

    • 考虑在PC端运行时使用默认DPI(即不调整Canvas的widthheight属性,或者设置为固定值)。
  3. 简化Canvas状态管理

    • 确保每个save()都有对应的restore(),并且尽量减少不必要的状态保存和恢复。
  4. 确保图片资源正确加载

    • 检查图片资源的URL是否正确,并在资源加载失败时添加错误处理。
  5. 条件编译或环境检测

    • 使用微信小程序的条件编译功能或运行时环境检测来区分PC端和移动端,对PC端进行特殊处理或禁用某些功能。
  6. 调试和测试

    • 在多个设备和环境中测试你的小程序,包括微信开发者工具、真机和可能的PC端模拟器。

通过这些修改,你的小程序应该能在更多的环境中稳定运行,并减少在PC端运行时出现的错误。

1 个回答
✓ 已被采纳

修正代码

以下是修正后的代码,确保 beforeImg 使用正确的图片来源,确保设备像素比使用正确的 API,并修正检查权限的 if 条件:

WXML

<view class="Jigsaw_modal" wx:if="{{visible}}">
    <view class="modal_content" style="position: relative;">
        <view class="modal_top">一键拼图</view>
        <canvas type="2d" id="MyCanvas" class="modal_middle" style="position: absolute; z-index: -1;"></canvas>
        <view class="modal_middle">
            <view class="desc_text">
                <image src="{{teethIcon}}" mode="" />
                <text>齿刻美 · 打造专属笑容</text>
            </view>
            <view class="desc_content">
                <view>
                    <image src="{{beforeImage}}" mode="" />
                    <text>设计前</text>
                </view>
                <image class="vs_image" src="{{vsImage}}" mode="" />
                <view>
                    <image src="{{afterImage}}" mode="" />
                    <text>设计后</text>
                </view>
            </view>
        </view>
        <view class="modal_footer" bindtap="initCanvas">保存拼图</view>
    </view>
    <view class="close_icon" bindtap="handleCloseJigsawModa">
        <image src="{{closeIcon}}" mode="" />
    </view>
</view>

JavaScript

initCanvas() {
    const canvasQuery = wx.createSelectorQuery();
    canvasQuery.select('#MyCanvas')
        .fields({
            node: true,
            size: true,
            rect: true
        })
        .exec(res => this.CanvasInit(res));
},

CanvasInit(res) {
    console.log(res, '-----------');
    const width = res[0].width;
    this.canvasWidth = width;
    const height = res[0].height;
    this.canvasHeight = height;
    const canvas = res[0].node;
    this.canvas = canvas;
    const ctx = canvas.getContext('2d');

    // 适应各种屏幕
    const dpr = wx.getSystemInfoSync().pixelRatio;
    canvas.width = width * dpr;
    canvas.height = height * dpr;
    ctx.scale(dpr, dpr);

    ctx.save();
    // 绘制背景颜色
    ctx.fillStyle = '#fff';
    ctx.fillRect(0, 0, width, height);
    ctx.restore();

    // 初始化计数器为需要加载的图片数量
    let imageLoadCount = 4;

    // 绘制文字和图标部分
    ctx.save();
    ctx.beginPath();
    ctx.font = '22rpx';
    ctx.fillStyle = 'rgba(57, 77, 163, 1)';
    ctx.fillText('记录美好生活', width - 150, 40);
    ctx.translate(width - 50, 40);
    const teethImg = canvas.createImage();
    teethImg.src = this.data.teethIcon;
    teethImg.onload = () => {
        ctx.drawImage(teethImg, -15, -15, 30, 30);
        imageLoadCount--;
    };
    ctx.restore();

    // 绘制设计前部分
    ctx.save();
    ctx.beginPath();
    ctx.translate(0, 60);
    const beforeImg = canvas.createImage();
    beforeImg.src = this.data.beforeImage; // 修正 beforeImg 的图片来源
    beforeImg.onload = () => {
        ctx.drawImage(beforeImg, 0, 0, width / 3, 390);
        ctx.font = '22rpx';
        ctx.fillStyle = 'rgba(57, 77, 163, 1)';
        ctx.fillText('设计前', width / 6, 390 + 20);
        imageLoadCount--;
    };
    ctx.restore();

    // 绘制对比图标
    ctx.save();
    ctx.beginPath();
    ctx.translate(width / 2, 200);
    const vsImg = canvas.createImage();
    vsImg.src = this.data.vsImage;
    vsImg.onload = () => {
        ctx.drawImage(vsImg, 0, 0, 50, 60);
        imageLoadCount--;
    };
    ctx.restore();

    // 绘制设计后部分
    ctx.save();
    ctx.beginPath();
    ctx.translate(width * 2 / 3, 60);
    const afterImg = canvas.createImage();
    afterImg.src = this.data.afterImage;
    afterImg.onload = () => {
        ctx.drawImage(afterImg, 0, 0, width / 3, 390);
        ctx.font = '22rpx';
        ctx.fillStyle = 'rgba(57, 77, 163, 1)';
        ctx.fillText('设计后', width * 5 / 6, 390 + 20);
        imageLoadCount--;
    };
    ctx.restore();

    // 检查图片加载完成状态
    const checkDrawingComplete = setInterval(() => {
        if (imageLoadCount === 0) {
            clearInterval(checkDrawingComplete);
            // 获取用户保存图片权限
            wx.getSetting({
                success: (res) => {
                    if ('scope.writePhotosAlbum' in res.authSetting) { // 修正 if 条件
                        // 如果授权过且同意了
                        if (res.authSetting['scope.writePhotosAlbum']) {
                            this.canvasToImgSave();
                        } else {
                            wx.showModal({
                                title: "请求授权相册存储",
                                content: "需要获取您的相册存储权限,请确认授权",
                                success: (resModal) => {
                                    if (resModal.confirm) {
                                        wx.openSetting({
                                            success: (resSetting) => {
                                                this.canvasToImgSave();
                                            },
                                            fail: (err) => {
                                                console.log(err);
                                            }
                                        });
                                    } else if (resModal.cancel) {
                                        console.log("你又取消了");
                                    }
                                }
                            });
                        }
                    } else {
                        this.canvasToImgSave();
                    }
                },
                fail: (err) => {
                    console.log(err);
                }
            });
        }
    }, 100);
},

canvasToImgSave() {
    wx.showLoading({
        title: '生成中...',
    });
    setTimeout(() => {
        wx.canvasToTempFilePath({
            canvas: this.canvas,
            x: 0,
            y: 0,
            width: this.canvasWidth * 2,
            height: this.canvasHeight * 2,
            destWidth: this.canvasWidth * 2,
            destHeight: this.canvasHeight * 2,
            success: (res) => {
                wx.saveImageToPhotosAlbum({
                    filePath: res.tempFilePath,
                    success: (resSave) => {
                        wx.showModal({
                            title: "保存成功!",
                            content: "图片已保存到本地相册",
                            showCancel: false,
                            success: (resModal) => {
                                // 图片保存成功后的逻辑
                            }
                        });
                    },
                    fail: (err) => {
                        console.log(err);
                    }
                });
            },
            fail: (err) => {
                console.log(err);
            },
            complete: () => {
                wx.hideLoading();
            }
        });
    }, 1000);
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏