v1.1.0
1、增加调试模式,调试模式下显示鸭子范围和目标区域范围 2、canvas中所有元素定位需要使用rpx转换处理 3、鸭子放开后,需要自动恢复原来位置
This commit is contained in:
parent
c0897a3d86
commit
fd020b935d
|
|
@ -44,25 +44,48 @@ const showDuck = ref(true) // 调试时默认显示鸭子
|
|||
const deskImage = ref('/static/dzm/img_desk1.png')
|
||||
const showGuideElements = ref(true)
|
||||
|
||||
// Canvas 触摸是否禁用
|
||||
const canvasDisabled = ref(false)
|
||||
|
||||
// 调试模式
|
||||
const debugMode = ref(false)
|
||||
|
||||
// Canvas 上下文
|
||||
const ctx = ref(null)
|
||||
|
||||
// 鸭子图片路径
|
||||
const duckImagePath = ref(null)
|
||||
|
||||
// 鸭子位置(相对于 Canvas)
|
||||
const duckX = ref(275)
|
||||
const duckY = ref(110)
|
||||
// rpx 转换比例
|
||||
const rpxRatio = ref(0.5) // 默认 iPhone 6/7/8 的 375px / 750rpx = 0.5
|
||||
|
||||
// 鸭子尺寸(与图片实际尺寸一致)
|
||||
const duckWidth = 36
|
||||
const duckHeight = 70
|
||||
// 鸭子位置和尺寸(使用 rpx 单位,在 750rpx 设计稿下)
|
||||
const duckRpx = ref({
|
||||
x: 550, // 275px * 2 = 550rpx
|
||||
y: 220, // 110px * 2 = 220rpx
|
||||
width: 72, // 36px * 2 = 72rpx
|
||||
height: 140 // 70px * 2 = 140rpx
|
||||
})
|
||||
|
||||
// 鸭子当前位置(px 单位,用于绘制)
|
||||
const duckX = ref(0)
|
||||
const duckY = ref(0)
|
||||
const duckWidth = ref(0)
|
||||
const duckHeight = ref(0)
|
||||
|
||||
// 拖拽偏移量
|
||||
const dragOffsetX = ref(0)
|
||||
const dragOffsetY = ref(0)
|
||||
|
||||
// Canvas 位置信息
|
||||
// Canvas 位置和尺寸(使用 rpx 单位)
|
||||
const canvasRpx = ref({
|
||||
x: 0,
|
||||
y: 1550, // 800px * 2 = 1600rpx
|
||||
width: 750, // 375px * 2 = 750rpx (全屏宽度)
|
||||
height: 600 // 300px * 2 = 600rpx
|
||||
})
|
||||
|
||||
// Canvas 当前位置和尺寸(px 单位)
|
||||
const canvasRect = ref({
|
||||
left: 0,
|
||||
top: 0,
|
||||
|
|
@ -70,12 +93,20 @@ const canvasRect = ref({
|
|||
height: 0
|
||||
})
|
||||
|
||||
// 目标区域 (餐桌区域) - 相对于 Canvas 的坐标
|
||||
// 目标区域 (餐桌区域) - 使用 rpx 单位
|
||||
const targetAreaRpx = ref({
|
||||
x: 0,
|
||||
y: 160, // 80px * 2 = 160rpx
|
||||
width: 400, // 200px * 2 = 400rpx
|
||||
height: 300 // 150px * 2 = 300rpx
|
||||
})
|
||||
|
||||
// 目标区域当前位置和尺寸(px 单位)
|
||||
const targetArea = ref({
|
||||
x: 0,
|
||||
y: 80,
|
||||
width: 200,
|
||||
height: 150
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
})
|
||||
|
||||
// 动画帧 ID
|
||||
|
|
@ -115,20 +146,70 @@ const loadDuckImage = () => {
|
|||
})
|
||||
}
|
||||
|
||||
// rpx 转 px
|
||||
const rpxToPx = (rpx) => {
|
||||
return rpx * rpxRatio.value
|
||||
}
|
||||
|
||||
// 更新鸭子位置(根据 rpx 计算 px)
|
||||
const updateDuckPosition = () => {
|
||||
duckX.value = rpxToPx(duckRpx.value.x)
|
||||
duckY.value = rpxToPx(duckRpx.value.y)
|
||||
duckWidth.value = rpxToPx(duckRpx.value.width)
|
||||
duckHeight.value = rpxToPx(duckRpx.value.height)
|
||||
}
|
||||
|
||||
// 更新目标区域(根据 rpx 计算 px)
|
||||
const updateTargetArea = () => {
|
||||
targetArea.value = {
|
||||
x: rpxToPx(targetAreaRpx.value.x),
|
||||
y: rpxToPx(targetAreaRpx.value.y),
|
||||
width: rpxToPx(targetAreaRpx.value.width),
|
||||
height: rpxToPx(targetAreaRpx.value.height)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 Canvas 位置和尺寸(根据 rpx 计算 px)
|
||||
const updateCanvasRect = () => {
|
||||
canvasRect.value = {
|
||||
left: rpxToPx(canvasRpx.value.x),
|
||||
top: rpxToPx(canvasRpx.value.y),
|
||||
width: rpxToPx(canvasRpx.value.width),
|
||||
height: rpxToPx(canvasRpx.value.height)
|
||||
}
|
||||
}
|
||||
|
||||
// Canvas 在视口中的实际位置(用于触摸计算)
|
||||
const canvasBoundingRect = ref({
|
||||
left: 0,
|
||||
top: 0
|
||||
})
|
||||
|
||||
// 获取 Canvas 元素位置
|
||||
const getCanvasPosition = () => {
|
||||
// 计算 rpx 比例:屏幕宽度 / 750
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
rpxRatio.value = systemInfo.windowWidth / 750
|
||||
console.log('屏幕宽度:', systemInfo.windowWidth, 'rpxRatio:', rpxRatio.value)
|
||||
|
||||
// 更新 Canvas、鸭子和目标区域的 px 值
|
||||
updateCanvasRect()
|
||||
updateDuckPosition()
|
||||
updateTargetArea()
|
||||
|
||||
console.log('Canvas 位置信息:', canvasRect.value)
|
||||
}
|
||||
|
||||
// 获取 Canvas 在视口中的实际位置(用于触摸坐标计算)
|
||||
const getCanvasBoundingRect = () => {
|
||||
const query = uni.createSelectorQuery().in(instance)
|
||||
query.select('#dragDuckCanvas').boundingClientRect(res => {
|
||||
if (res) {
|
||||
canvasRect.value = {
|
||||
canvasBoundingRect.value = {
|
||||
left: res.left,
|
||||
top: res.top,
|
||||
width: res.width,
|
||||
height: res.height
|
||||
top: res.top
|
||||
}
|
||||
console.log('Canvas 位置信息:', canvasRect.value)
|
||||
} else {
|
||||
console.error('无法获取 Canvas 位置信息')
|
||||
console.log('Canvas 视口位置:', canvasBoundingRect.value)
|
||||
}
|
||||
}).exec()
|
||||
}
|
||||
|
|
@ -163,6 +244,8 @@ const drawCanvas = () => {
|
|||
const drawTarget = () => {
|
||||
const target = targetArea.value
|
||||
|
||||
if (debugMode.value) {
|
||||
// 调试模式:显示目标区域边框和文字
|
||||
// 绘制目标区域背景
|
||||
ctx.value.setFillStyle('rgba(52, 152, 219, 0.3)')
|
||||
ctx.value.fillRect(target.x, target.y, target.width, target.height)
|
||||
|
|
@ -183,44 +266,61 @@ const drawTarget = () => {
|
|||
target.x + target.width / 2,
|
||||
target.y + target.height / 2
|
||||
)
|
||||
}
|
||||
// 非调试模式:完全透明,不绘制任何内容
|
||||
}
|
||||
|
||||
// 绘制鸭子
|
||||
const drawDuck = () => {
|
||||
if (!showDuck.value) return
|
||||
|
||||
// 非调试模式下,只有拖拽时才显示鸭子
|
||||
if (!debugMode.value && !isDragging.value) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果正在拖拽,设置半透明
|
||||
if (isDragging.value) {
|
||||
ctx.value.globalAlpha = 0.6
|
||||
} else {
|
||||
ctx.value.globalAlpha = 1.0
|
||||
}
|
||||
|
||||
// 如果图片已加载,绘制图片
|
||||
if (duckImagePath.value) {
|
||||
ctx.value.drawImage(duckImagePath.value, duckX.value, duckY.value, duckWidth, duckHeight)
|
||||
ctx.value.drawImage(duckImagePath.value, duckX.value, duckY.value, duckWidth.value, duckHeight.value)
|
||||
} else {
|
||||
// 图片未加载时,绘制占位方块
|
||||
ctx.value.setFillStyle('#ffcc00')
|
||||
ctx.value.fillRect(duckX.value, duckY.value, duckWidth, duckHeight)
|
||||
ctx.value.fillRect(duckX.value, duckY.value, duckWidth.value, duckHeight.value)
|
||||
|
||||
// 绘制边框
|
||||
ctx.value.setStrokeStyle('#333')
|
||||
ctx.value.setLineWidth(2)
|
||||
ctx.value.strokeRect(duckX.value, duckY.value, duckWidth, duckHeight)
|
||||
ctx.value.strokeRect(duckX.value, duckY.value, duckWidth.value, duckHeight.value)
|
||||
|
||||
// 绘制文字
|
||||
ctx.value.setFontSize(14)
|
||||
ctx.value.setFillStyle('#FFFFFF')
|
||||
ctx.value.setTextAlign('center')
|
||||
ctx.value.setTextBaseline('middle')
|
||||
ctx.value.fillText('鸭', duckX.value + duckWidth / 2, duckY.value + duckHeight / 2)
|
||||
ctx.value.fillText('鸭', duckX.value + duckWidth.value / 2, duckY.value + duckHeight.value / 2)
|
||||
}
|
||||
|
||||
// 如果正在拖拽,绘制拖拽效果
|
||||
if (isDragging.value) {
|
||||
// 恢复透明度
|
||||
ctx.value.globalAlpha = 1.0
|
||||
|
||||
// 调试模式下显示拖拽效果红框
|
||||
if (debugMode.value && isDragging.value) {
|
||||
// 绘制拖拽阴影
|
||||
ctx.value.setFillStyle('rgba(231, 76, 60, 0.3)')
|
||||
ctx.value.fillRect(duckX.value, duckY.value, duckWidth, duckHeight)
|
||||
ctx.value.fillRect(duckX.value, duckY.value, duckWidth.value, duckHeight.value)
|
||||
|
||||
// 绘制拖拽边框
|
||||
ctx.value.setStrokeStyle('#e74c3c')
|
||||
ctx.value.setLineWidth(2)
|
||||
ctx.value.setLineDash([5, 5])
|
||||
ctx.value.strokeRect(duckX.value - 3, duckY.value - 3, duckWidth + 6, duckHeight + 6)
|
||||
ctx.value.strokeRect(duckX.value - 3, duckY.value - 3, duckWidth.value + 6, duckHeight.value + 6)
|
||||
ctx.value.setLineDash([])
|
||||
}
|
||||
}
|
||||
|
|
@ -228,9 +328,9 @@ const drawDuck = () => {
|
|||
// 检查触摸点是否在鸭子内
|
||||
const checkTouchInDuck = (x, y) => {
|
||||
return x >= duckX.value &&
|
||||
x <= duckX.value + duckWidth &&
|
||||
x <= duckX.value + duckWidth.value &&
|
||||
y >= duckY.value &&
|
||||
y <= duckY.value + duckHeight
|
||||
y <= duckY.value + duckHeight.value
|
||||
}
|
||||
|
||||
// 检查鸭子是否在目标区域内
|
||||
|
|
@ -238,8 +338,8 @@ const checkDuckInTarget = () => {
|
|||
const target = targetArea.value
|
||||
|
||||
// 计算鸭子中心点
|
||||
const duckCenterX = duckX.value + duckWidth / 2
|
||||
const duckCenterY = duckY.value + duckHeight / 2
|
||||
const duckCenterX = duckX.value + duckWidth.value / 2
|
||||
const duckCenterY = duckY.value + duckHeight.value / 2
|
||||
|
||||
// 检查中心点是否在目标区域内
|
||||
return duckCenterX >= target.x &&
|
||||
|
|
@ -250,6 +350,12 @@ const checkDuckInTarget = () => {
|
|||
|
||||
// 触摸开始事件
|
||||
const handleTouchStart = (e) => {
|
||||
// 如果 Canvas 已禁用,不处理触摸事件
|
||||
if (canvasDisabled.value) {
|
||||
console.log('Canvas 已禁用,忽略触摸事件')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('触摸开始事件触发', e)
|
||||
|
||||
// 获取触摸点
|
||||
|
|
@ -259,17 +365,20 @@ const handleTouchStart = (e) => {
|
|||
return
|
||||
}
|
||||
|
||||
// 确保 Canvas 位置信息是最新的
|
||||
getCanvasPosition()
|
||||
// 获取 Canvas 在视口中的实际位置
|
||||
getCanvasBoundingRect()
|
||||
|
||||
// 异步获取 Canvas 位置后执行检查
|
||||
setTimeout(() => {
|
||||
// 计算相对于 Canvas 的坐标
|
||||
const touchX = touch.clientX - canvasRect.value.left
|
||||
const touchY = touch.clientY - canvasRect.value.top
|
||||
// 计算相对于 Canvas 的坐标(使用视口位置)
|
||||
const touchX = touch.clientX - canvasBoundingRect.value.left
|
||||
const touchY = touch.clientY - canvasBoundingRect.value.top
|
||||
|
||||
console.log(`触摸点 client: (${touch.clientX}, ${touch.clientY})`)
|
||||
console.log(`Canvas 视口位置: (${canvasBoundingRect.value.left}, ${canvasBoundingRect.value.top})`)
|
||||
console.log(`触摸点相对 Canvas: (${touchX}, ${touchY})`)
|
||||
console.log(`鸭子位置: (${duckX.value}, ${duckY.value})`)
|
||||
console.log(`鸭子尺寸: (${duckWidth.value}, ${duckHeight.value})`)
|
||||
|
||||
// 检查是否点击在鸭子上
|
||||
if (checkTouchInDuck(touchX, touchY)) {
|
||||
|
|
@ -296,9 +405,9 @@ const handleTouchMove = (e) => {
|
|||
const touch = e.touches[0]
|
||||
if (!touch) return
|
||||
|
||||
// 计算相对于 Canvas 的坐标
|
||||
const touchX = touch.clientX - canvasRect.value.left
|
||||
const touchY = touch.clientY - canvasRect.value.top
|
||||
// 计算相对于 Canvas 的坐标(使用视口位置)
|
||||
const touchX = touch.clientX - canvasBoundingRect.value.left
|
||||
const touchY = touch.clientY - canvasBoundingRect.value.top
|
||||
|
||||
// 更新鸭子位置
|
||||
duckX.value = touchX - dragOffsetX.value
|
||||
|
|
@ -308,8 +417,8 @@ const handleTouchMove = (e) => {
|
|||
const canvasW = canvasRect.value.width || 400
|
||||
const canvasH = canvasRect.value.height || 600
|
||||
|
||||
duckX.value = Math.max(0, Math.min(canvasW - duckWidth, duckX.value))
|
||||
duckY.value = Math.max(0, Math.min(canvasH - duckHeight, duckY.value))
|
||||
duckX.value = Math.max(0, Math.min(canvasW - duckWidth.value, duckX.value))
|
||||
duckY.value = Math.max(0, Math.min(canvasH - duckHeight.value, duckY.value))
|
||||
|
||||
console.log(`鸭子新位置: (${duckX.value}, ${duckY.value})`)
|
||||
}
|
||||
|
|
@ -330,10 +439,27 @@ const handleTouchEnd = () => {
|
|||
showGuideElements.value = false
|
||||
// 隐藏鸭子
|
||||
showDuck.value = false
|
||||
// 禁用 Canvas 触摸事件
|
||||
canvasDisabled.value = true
|
||||
console.log('Canvas 触摸事件已禁用')
|
||||
} else {
|
||||
// 未放入目标区域,回归到原始位置
|
||||
console.log('未放入目标区域,回归原始位置')
|
||||
resetDuckPosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置鸭子位置到初始值
|
||||
const resetDuckPosition = () => {
|
||||
// 恢复原始 rpx 值
|
||||
duckRpx.value.x = 550
|
||||
duckRpx.value.y = 220
|
||||
// 更新 px 值
|
||||
updateDuckPosition()
|
||||
console.log('鸭子已回归原始位置:', duckX.value, duckY.value)
|
||||
}
|
||||
|
||||
// 开始拖拽(从触发区域开始)
|
||||
const startDrag = (e) => {
|
||||
console.log('开始拖拽', e)
|
||||
|
|
@ -343,9 +469,10 @@ const startDrag = (e) => {
|
|||
initCanvas()
|
||||
}
|
||||
|
||||
// 设置鸭子初始位置(在触发区域附近)
|
||||
duckX.value = 50
|
||||
duckY.value = 50
|
||||
// 设置鸭子初始位置(使用 rpx 值,会自动转换为 px)
|
||||
duckRpx.value.x = 100 // 100rpx 对应约 50px (iPhone 6/7/8)
|
||||
duckRpx.value.y = 100
|
||||
updateDuckPosition()
|
||||
showDuck.value = true
|
||||
|
||||
// 触发触摸开始事件处理
|
||||
|
|
@ -413,6 +540,12 @@ onUnmounted(() => {
|
|||
canvas-id="dragDuckCanvas"
|
||||
id="dragDuckCanvas"
|
||||
class="drag-canvas"
|
||||
:style="{
|
||||
left: canvasRect.left + 'px',
|
||||
top: canvasRect.top + 'px',
|
||||
width: canvasRect.width + 'px',
|
||||
height: canvasRect.height + 'px'
|
||||
}"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchmove="handleTouchMove"
|
||||
@touchend="handleTouchEnd"
|
||||
|
|
@ -514,10 +647,6 @@ onUnmounted(() => {
|
|||
|
||||
.drag-canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 800px;
|
||||
width: 375px;
|
||||
height: 300px;
|
||||
z-index: 100;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue