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
测试一下原生微信小程序是否有此问题
更多关于uni-app 编译成微信小程序 canvas 背景图片 不显示 vue3 setup的实战教程也可以访问 https://www.itying.com/category-93-b0.html
没有
没有
在uni-app中,微信小程序的canvas背景图片不显示问题通常与图片加载和绘制时机有关。从你的代码分析,主要问题可能出现在以下几个方面:
-
图片跨域问题:虽然微信小程序对网络图片有域名限制,但你的图片链接已配置在downloadFile合法域名中,这点应该没问题。
-
Canvas上下文创建:在Vue3 setup中,
uni.createCanvasContext的第二个参数this可能不正确。建议改为:
state.ctx = uni.createCanvasContext('tf-verify-canvas');
-
图片绘制时机:确保图片完全下载后再进行绘制。你的代码中已经通过downloadFile的success回调处理,这是正确的。
-
Canvas尺寸适配:检查canvas的实际尺寸与样式设置是否匹配。建议在init函数中添加尺寸验证:
const query = uni.createSelectorQuery().select('#tf-verify-canvas');
query.boundingClientRect(rect => {
if (rect.width === 0) {
console.error('Canvas尺寸异常');
return;
}
huatu();
}).exec();
-
Type 2D支持:微信小程序对type="2d"的canvas支持可能存在兼容性问题,可以尝试移除type属性使用默认canvas。
-
临时路径有效性:在huatu函数中增加图片路径验证:
if (!state.dqImgPath) {
console.error('图片路径无效');
return;
}

