浮世
浮世
  • 发布:2025-07-29 14:38
  • 更新:2025-07-29 14:44
  • 阅读:156

【报Bug】 编译成微信小程序, canvas 背景图片 不显示 vue3 setup。

分类:uni-app

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

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: 11

HBuilderX类型: 正式

HBuilderX版本号: 4.75

第三方开发者工具版本号: 4.75

基础库版本号: 4.75

项目创建方式: HBuilderX

示例代码:

<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>  

</template>

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

</script>

<style scoped lang="scss">
view,
text {
line-height: normal;
}

.tf-Box-Bg {  
    position: fixed;  
    top: 0;  
    left: 0;  
    bottom: 0;  
    right: 0;  
    z-index: 100;  
    background-color: rgba(0, 0, 0, .3);  
    display: flex;  
    align-items: center;  
    justify-content: center;  

    .tf-Box {  
        width: 680rpx;  
        min-height: 584rpx;  
        background-color: #fff;  
        border-radius: 32rpx;  
        box-shadow: 0 0 50rpx 0rpx rgba(0, 0, 0, .2);  

        .tf-Box-title {  
            height: 100rpx;  
            line-height: 100rpx;  
            padding: 0 32rpx;  
            border-bottom: 1px solid #E1E3E9;  
            flex-direction: row;  
            display: flex;  
            align-items: center;  
            justify-content: space-between;  

            font-weight: 500;  
            font-size: 32rpx;  
            color: #111111;  

            .text {  
                font-size: 32rpx;  
                flex: 1;  
            }  

            .tf-close {  
                width: 28rpx;  
                height: 28rpx;  
            }  
        }  

        .tf-Box-center {  
            margin-top: 30rpx;  
            margin-bottom: 30rpx;  
            align-self: center;  
        }  

        .tf-Box-BtnBox {  
            margin-top: 30rpx;  
            margin-bottom: 30rpx;  
            align-self: center;  
            width: 580rpx;  
            height: 75rpx;  
            line-height: 75rpx;  
            text-align: center;  
            font-size: 28rpx;  
            border-radius: 6rpx;  
            background-color: #F7F8F9;  
            overflow: hidden;  
            position: relative;  
            border: 2rpx solid #f3f3f3;  

            .tf-Box-BtnBox-text {  

                width: 100%;  
                height: 100%;  
                position: absolute;  
                top: 0;  
                left: 0;  
                color: #424649;  
                text-align: center;  
                display: flex;  
                align-items: center;  
                justify-content: center;  
            }  

            .tf-Box-BtnNei {  
                height: 100%;  
                width: 88rpx;  
                background-color: #ffffff;  
                box-shadow: 0 0 10rpx 0rpx rgba(0, 0, 0, .2);  
                background-image: url(https://ggyx-img.oss-cn-zhangjiakou.aliyuncs.com/ggyx_uploads/20250726/293363f9e00350374eca61b61e14b821tf-arrows.png);  
                background-size: 34rpx;  
                background-position: center;  
                background-repeat: no-repeat;  

                .tf-Box-BtnNei-leftBox {  
                    position: absolute;  
                    top: 0;  
                    height: 100%;  
                    left: -580rpx;  
                    width: 580rpx;  
                }  
            }  
        }  
    }  
}  

.tf-content {  
    display: flex;  
    align-items: center;  
    justify-content: center;  
    flex-direction: column;  
}  

</style>

操作步骤:

uniapp 编译运行到 微信小程序

预期结果:

应该有背景图片的 ,滑块 会还原到初始状态。

实际结果:

没有背景图片的 ,滑块 没有还原到初始状态。

bug描述:

uniapp 编译成微信小程序, canvas 背景图片 不显示 vue3 setup。 movable-view 的 X 值改变了 页面不发生改变。

2025-07-29 14:38 负责人:无 分享
已邀请:
DCloud_UNI_JBB

DCloud_UNI_JBB

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

  • 浮世 (作者)

    没有

    2025-07-29 16:48

浮世

浮世 (作者)

没有

要回复问题请先登录注册