uni-app 编译成微信小程序 canvas 背景图片 不显示 vue3 setup

uni-app 编译成微信小程序 canvas 背景图片 不显示 vue3 setup

开发环境 版本号 项目创建方式
Windows 11 HBuilderX

产品分类:uniapp/小程序/微信


示例代码:

<template>
<view class="tf-Box-Bg">
<view class="tf-Box">
<view class="tf-Box-title">
<text class="text">请完成安全验证</text>
<u-icon name="close-circle" @click="close"></u-icon>
</view>  
<view class="tf-content">  
    <canvas type="2d" :style="{ width:state.canvasW + 'px', height: state.canvasH + 'px' }"  
        class="tf-Box-center" canvas-id="tf-verify-canvas" id="tf-verify-canvas"></canvas>  

    <movable-area class="tf-Box-BtnBox" :style="{ width: state.canvasW + 'px' }">  
        <view class="tf-Box-BtnBox-text">滑动滑块完成拼图</view>  
        <movable-view class="tf-Box-BtnNei" :style="{ width: state.tfBoxBtnNeiElWidth + 'px' }"  
            direction="horizontal" :damping="50" :friction="50" :x="state.canvasX2" @change="changePath"  
            @touchend="endTouch" @mouseup="endTouch">  
            <view class="tf-Box-BtnNei-leftBox"  
                :style="{ backgroundColor: ImagesList[state.verifyIndex].color }"></view>  
        </movable-view>  
    </movable-area>  
</view>  
</view>  
</view>  
<script setup>
// 定义emit向父组件传递的事件
const emit = defineEmits(['close', 'succeed']);

import {  
    ref,  
    reactive,  
    computed,  
    onMounted,  
    nextTick,  

} from 'vue';  

// mounted  
// 接收props  
const props = defineProps({  
    ImagesList: {  
        type: Array,  
        default: function() {  
            return [{  
                    color: "#38a7b7",  
                    src: "https://file.ggyx666.com/ggyx_uploads/20250728/7ade1d9decfa35643de864386c629a1c123458.jpg"  

                },  
                {  
                    color: "#485967",  
                    src: "https://file.ggyx666.com/ggyx_uploads/20250728/c590fd0a82429931b503fe418424b1dc123457.jpg"  
                }  
            ]  
        }  
    }  
})  

const canvasX2 = ref(0) // 用于拼图块的初始X坐标  

const state = reactive({  
    verifyIndex: 0, // 当前显示的验证图片的索引  
    canvasW: uni.upx2px(640), // canvas和滑块容器的宽度,单位转换为px  
    canvasH: uni.upx2px(640 / (16 / 9)),  
    canvasX2: 0, // 用于拼图块的初始X坐标  
    canvasX: 0, // 实时移动的X坐标,用于拼图块的位置  
    ctx: false, // canvas的绘图上下文  
    jgX: 0, // 拼图块的目标X位置(用于判断是否成功滑动到目标位置)  
    dqImgPath: '', //本地临时图片路径  
    RR: uni.upx2px(10), // 拼图块的半径,单位转换为px  
    YY: uni.upx2px(100), // 拼图块的Y轴坐标,单位转换为px  
    CS: uni.upx2px(0), // 拼图块的偏移量,单位转换为px  
    tfBoxBtnNeiElWidth: 0, // 用户手动触发的拼图块宽度,用于计算滑动距离  
})  

onMounted(() => {  
    console.log(props.ImagesList)  

    nextTick(() => {  
        init()  
    })  
})  

// 生成从minNum到maxNum的随机数  
const randomNum = (minNum, maxNum) => {  
    switch (arguments.length) {  
        case 1:  
            return parseInt(Math.random() * minNum + 1, 10);  
            break;  
        case 2:  
            return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);  
            break;  
        default:  
            return 0;  
            break;  
    }  
}  

// 关闭  
const close = () => {  
    emit('close');  
}  

const init = () => {  
    if (!props.ImagesList.length) {  
        uni.showToast({  
            title: 'ImagesList不能为空',  
            icon: "none"  
        });  
        return;  
    }  

    // state.canvasX2--;  
    // state.canvasX = 0;  

    state.verifyIndex = randomNum(0, props.ImagesList.length - 1);  
    state.ctx = uni.createCanvasContext('tf-verify-canvas', this);  
    state.jgX = randomNum(150 / 2, 450 / 2);  
    const imageUrl = props.ImagesList[state.verifyIndex].src;  

    // #ifdef MP  
    // 在小程序执行 下载图片并绘制  
    uni.downloadFile({  
        url: imageUrl,  
        success: (res) => {  
            if (res.statusCode === 200) {  
                console.log(res);  

                uni.$u.toast("图片下载成功");  
                state.dqImgPath = res.tempFilePath; // 保存临时路径  
                huatu(); // 下载完成后开始绘制  
            } else {  
                uni.$u.toast("图片下载失败");  
            }  
        },  
        fail: () => {  
            uni.$u.toast("图片下载失败");  
        }  
    });  
    // #endif  

    // #ifndef MP  
    // 非此平台使用  
    state.dqImgPath = props.ImagesList[state.verifyIndex].src;  
    huatu();  
    // #endif  

}  
const endTouch = (e) => {  
    if (state.canvasX <= 5) {  
        // 滑块移动x小于5,不做任何变动。  
        return;  
    } else {  
        if (Math.abs(state.canvasX - state.jgX) <= 5) {  
            setTimeout(() => {  
                emit('succeed');  
            }, 100)  
        } else {  
            uni.$u.toast("验证失败");  
            resetSlider();  
        }  
    }  
}  

// 绘制不可移动的拼图块this.ctx, this.jgX, y, r  
const drawFixedPuzzle = ({  
    XX,  
    YY,  
    r,  
    cs  
}) => {  
    state.ctx.beginPath();  
    state.ctx.moveTo(-2 * r + state.jgX + cs + 2 * r, YY - 2 * r + 2 * r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 5.5 * r, YY - 2 * r + 2 * r);  
    state.ctx.arcTo(-2 * r + state.jgX + cs + 5.5 * r, YY - 2 * r + 3 * r, XX - 2 + state.jgX * r + cs + 6.5 * r,  
        YY - 2 * r + 3 * r, r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 7.5 * r, YY - 2 * r + 3 * r);  
    state.ctx.arcTo(-2 * r + state.jgX + cs + 8.5 * r, YY - 2 * r + 3 * r, -2 * r + state.jgX + cs + 8.5 * r, YY -  
        2 * r + 2 * r, r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 12 * r, YY - 2 * r + 2 * r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 12 * r, YY - 2 * r + 11 * r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 8.5 * r, YY - 2 * r + 11 * r);  
    state.ctx.arcTo(-2 * r + state.jgX + cs + 8.5 * r, YY - 2 * r + 12 * r, -2 * r + state.jgX + cs + 7.5 * r,  
        YY - 2 * r + 12 * r, r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 6.5 * r, YY - 2 * r + 12 * r);  
    state.ctx.arcTo(-2 * r + state.jgX + cs + 5.5 * r, YY - 2 * r + 12 * r, -2 * r + state.jgX + cs + 5.5 * r,  
        YY - 2 * r + 11 * r, r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 2 * r, YY - 2 * r + 11 * r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 2 * r, YY - 2 * r + 8 * r);  
    state.ctx.arcTo(-2 * r + state.jgX + cs + 3 * r, YY - 2 * r + 8 * r, -2 * r + state.jgX + cs + 3 * r, YY - 2 *  
        r + 7 * r, r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 3 * r, YY - 2 * r + 6 * r);  
    state.ctx.arcTo(-2 * r + state.jgX + cs + 3 * r, YY - 2 * r + 5 * r, -2 * r + state.jgX + cs + 2 * r, YY - 2 *  
        r + 5 * r, r);  
    state.ctx.lineTo(-2 * r + state.jgX + cs + 2 * r, YY - 2 * r + 2 * r);  
    state.ctx.shadowBlur = 10;  
    state.ctx.shadowColor = "#ffffff";  
    state.ctx.fillStyle = "rgba(0,0,0,0.5)";  
    state.ctx.fill();  
    state.ctx.restore();  
}  
// 绘制可移动拼图块  
const drawMovablePuzzle = ({  
    XX,  
    YY,  
    r,  
    cs  
}) => {  
    state.ctx.beginPath();  
    state.ctx.save(); //画布区域进行保存  
    state.ctx.moveTo(XX - 2 * r + cs + 2 * r, YY - 2 * r + 2 * r);  
    state.ctx.lineTo(XX - 2 * r + cs + 5.5 * r, YY - 2 * r + 2 * r);  
    state.ctx.arcTo(XX - 2 * r + cs + 5.5 * r, YY - 2 * r + 3 * r, XX - 2 * r + cs + 6.5 * r, YY - 2 * r + 3 *  
        r, r);  
    state.ctx.lineTo(XX - 2 * r + cs + 7.5 * r, YY - 2 * r + 3 * r);  
    state.ctx.arcTo(XX - 2 * r + cs + 8.5 * r, YY - 2 * r + 3 * r, XX - 2 * r + cs + 8.5 * r, YY - 2 * r + 2 *  
        r, r);  
    state.ctx.lineTo(XX - 2 * r + cs + 12 * r, YY - 2 * r + 2 * r);  
    state.ctx.lineTo(XX - 2 * r + cs + 12 * r, YY - 2 * r + 11 * r);  
    state.ctx.lineTo(XX - 2 * r + cs + 8.5 * r, YY - 2 * r + 11 * r);  
    state.ctx.arcTo(XX - 2 * r + cs + 8.5 * r, YY - 2 * r + 12 * r, XX - 2 * r + cs + 7.5 * r, YY - 2 * r + 12 *  
        r, r);  
    state.ctx.lineTo(XX - 2 * r + cs + 6.5 * r, YY - 2 * r + 12 * r);  
    state.ctx.arcTo(XX - 2 * r + cs + 5.5 * r, YY - 2 * r + 12 * r, XX - 2 * r + cs + 5.5 * r, YY - 2 * r + 11 *  
        r, r);  
    state.ctx.lineTo(XX - 2 * r + cs + 2 * r, YY - 2 * r + 11 * r);  
    state.ctx.lineTo(XX - 2 * r + cs + 2 * r, YY - 2 * r + 8 * r);  
    state.ctx.arcTo(XX - 2 * r + cs + 3 * r, YY - 2 * r + 8 * r, XX - 2 * r + cs + 3 * r, YY - 2 * r + 7 * r,  
        r);  
    state.ctx.lineTo(XX - 2 * r + cs + 3 * r, YY - 2 * r + 6 * r);  
    state.ctx.arcTo(XX - 2 * r + cs + 3 * r, YY - 2 * r + 5 * r, XX - 2 * r + cs + 2 * r, YY - 2 * r + 5 * r,  
        r);  
    state.ctx.lineTo(XX - 2 * r + cs + 2 * r, YY - 2 * r + 2 * r);  
    state.ctx.shadowBlur = 10;  
    state.ctx.shadowColor = "#ffffff";  
    state.ctx.fill();  
    state.ctx.clip(); //从原始画布中剪切任意形状和尺寸  
    state.ctx.restore(); //进行恢复 绘画  
    // 根据canvas上的滑块,生成手动滑块的宽度  
    state.tfBoxBtnNeiElWidth = (XX - 2 * r + cs + 12 * r) - (XX - 2 * r + cs + 2 * r);  
}  
// 主绘制逻辑  

const huatu = () => {  
    let XX = state.canvasX;  
    let obj = {  
        r: state.RR,  
        cs: state.CS,  
        XX,  
        YY: state.YY  
    }  
    // 清空画布,重新绘制  
    state.ctx.clearRect(0, 0, state.canvasW, state.canvasH);  
    // 绘制背景图  
    state.ctx.drawImage(state.dqImgPath, 0, 0, state.canvasW, state.canvasH);  
    // 绘制固定拼图块  
    drawFixedPuzzle(obj);  
    // 绘制可移动拼图块  
    drawMovablePuzzle(obj);  
    // 绘制最终图像  
    state.ctx.draw();  
}  

const resetSlider = () => {  

    state.canvasX = 0; // 重置实时滑块位置  
    state.canvasX2 = canvasX2.value; // 重置滑块初始位置  
    nextTick(() => {  
        state.canvasX2 = 0; // 重置滑块初始位置  
        init(); // 延迟调用重新绘制拼图  
    })  
}  

const changePath = (e) => {  
    // #ifdef H5  
    state.canvasX = e.detail.x;  
    canvasX2.value = e.detail.x;  
    // #endif  

    // #ifndef H5  
    state.canvasX = e.target.x;  
    canvasX2.value = e.target.x;  
    // #endif  

    // console.log(canvasX2.value);  

    huatu();  
}  

更多关于uni-app 编译成微信小程序 canvas 背景图片 不显示 vue3 setup的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

测试一下原生微信小程序是否有此问题

更多关于uni-app 编译成微信小程序 canvas 背景图片 不显示 vue3 setup的实战教程也可以访问 https://www.itying.com/category-93-b0.html


没有

在uni-app中,微信小程序的canvas背景图片不显示问题通常与图片加载和绘制时机有关。从你的代码分析,主要问题可能出现在以下几个方面:

  1. 图片跨域问题:虽然微信小程序对网络图片有域名限制,但你的图片链接已配置在downloadFile合法域名中,这点应该没问题。

  2. Canvas上下文创建:在Vue3 setup中,uni.createCanvasContext的第二个参数this可能不正确。建议改为:

state.ctx = uni.createCanvasContext('tf-verify-canvas');
  1. 图片绘制时机:确保图片完全下载后再进行绘制。你的代码中已经通过downloadFile的success回调处理,这是正确的。

  2. Canvas尺寸适配:检查canvas的实际尺寸与样式设置是否匹配。建议在init函数中添加尺寸验证:

const query = uni.createSelectorQuery().select('#tf-verify-canvas');
query.boundingClientRect(rect => {
  if (rect.width === 0) {
    console.error('Canvas尺寸异常');
    return;
  }
  huatu();
}).exec();
  1. Type 2D支持:微信小程序对type="2d"的canvas支持可能存在兼容性问题,可以尝试移除type属性使用默认canvas。

  2. 临时路径有效性:在huatu函数中增加图片路径验证:

if (!state.dqImgPath) {
  console.error('图片路径无效');
  return;
}
回到顶部