hldy_app/component/public/newgame/arrowkeys.vue

227 lines
5.8 KiB
Vue
Raw Normal View History

2025-08-13 17:19:40 +08:00
<template>
<view class="move-circle" :style="{ bottom: `${movebottom}rpx`, left: `${moveleft}rpx` }" @touchend="onLongPressEnd"
@touchcancel="onLongPressEnd">
<!-- 返回 -->
<view :class="beblue === 5 ? 'click-box-target' : 'click-box'" @tap="onTap(5)"
@longpress="(e) => onLongPressStart(5, e)">
<span :class="beblue === 5 ? 'grad-text' : ''">返回</span>
</view>
<!-- -->
<view :class="beblue === 0 ? 'click-box-target' : 'click-box'" @tap="onTap(0)"
@longpress="(e) => onLongPressStart(0, e)">
<image :src="`/static/index/newruler/arrow_1${beblue===0 ? '_1' : ''}.png`" class="image-photo" />
</view>
<!-- 确定 -->
<view :class="beblue === 4 ? 'click-box-target' : 'click-box'" @tap="onTap(4)"
@longpress="(e) => onLongPressStart(4, e)">
<span :class="beblue === 4 ? 'grad-text' : ''">确定</span>
</view>
<!-- -->
<view :class="beblue === 3 ? 'click-box-target' : 'click-box'" @tap="onTap(3)"
@longpress="(e) => onLongPressStart(3, e)">
<image :src="`/static/index/newruler/arrow_3${beblue===3 ? '_3' : ''}.png`" class="image-photo" />
</view>
<!-- -->
<view :class="beblue === 2 ? 'click-box-target' : 'click-box'" @tap="onTap(2)"
@longpress="(e) => onLongPressStart(2, e)">
<image :src="`/static/index/newruler/arrow_2${beblue===2 ? '_2' : ''}.png`" class="image-photo" />
</view>
<!-- -->
<view :class="beblue === 1 ? 'click-box-target' : 'click-box'" @tap="onTap(1)"
@longpress="(e) => onLongPressStart(1, e)">
<image :src="`/static/index/newruler/arrow_4${beblue===1 ? '_4' : ''}.png`" class="image-photo" />
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onBeforeUnmount } from 'vue'
const emit = defineEmits<{ (e : 'movecard', dir : number) : void }>()
let clickResetTimer : ReturnType<typeof setTimeout> | null = null
let longPressInterval : ReturnType<typeof setInterval> | null = null
let isLongPress = false
const beblue = ref<number>(-1)
let activeLongPressDir : number | null = null
const props = defineProps({
movebottom: {
type: Number,
default: 0,
},
moveleft: {
type: Number,
default: 0,
},
})
function clearClickResetTimer() {
if (clickResetTimer) {
clearTimeout(clickResetTimer)
clickResetTimer = null
}
}
function clearLongPressInterval() {
if (longPressInterval) {
clearInterval(longPressInterval)
longPressInterval = null
}
isLongPress = false
activeLongPressDir = null
}
// 单击(或短按)
function onTap(dir : number) {
// 立刻触发一次
clearLongPressInterval()
clearClickResetTimer()
beblue.value = dir
emit('movecard', dir)
// 800ms 后恢复(如果期间有新点击会清除并重置)
clickResetTimer = setTimeout(() => {
beblue.value = -1
clickResetTimer = null
}, 500)
}
// 长按开始(由 longpress 事件触发)
function onLongPressStart(dir : number, e ?: any) {
// 先清理点击计时器,确保长按状态保持
clearClickResetTimer()
clearLongPressInterval()
beblue.value = dir
emit('movecard', dir)
// 开始每 500ms 发一次
activeLongPressDir = dir
isLongPress = true
longPressInterval = setInterval(() => {
if (activeLongPressDir !== null) emit('movecard', activeLongPressDir)
}, 500)
}
// 长按结束touchend / touchcancel
function onLongPressEnd() {
// 如果没有长按在进行,直接返回(防止意外触发)
if (!isLongPress) return
// 停止 interval
clearLongPressInterval()
// 0.8s 后恢复选中态
clearClickResetTimer()
clickResetTimer = setTimeout(() => {
beblue.value = -1
clickResetTimer = null
}, 500)
}
onBeforeUnmount(() => {
clearClickResetTimer()
clearLongPressInterval()
})
</script>
<style lang="less" scoped>
.move-circle {
position: absolute;
bottom: 0rpx;
left: 0rpx;
width: 400rpx;
display: flex;
flex-wrap: wrap;
z-index: 99;
touch-action: none;
}
.click-box,
.click-box-target {
width: 100rpx;
height: 100rpx;
margin-left: 15rpx;
margin-bottom: 15rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 30rpx;
font-size: 28rpx;
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.25s ease;
-webkit-tap-highlight-color: transparent;
}
.click-box {
background-color: #f6f6f9;
box-shadow: 0 4px 12px #e5e7ea;
border: 2rpx solid #e6e7eb;
color: #888d99;
}
/* 选中态:背景径向渐变 + 中心缩放动画(从中间放大再回到原样) */
.click-box-target {
background: radial-gradient(circle at 30% 30%, #f2f7fd 0%, #deeaf9 100%);
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.08);
border: 2rpx solid #c3ccd9;
color: transparent;
/* 文字使用渐变填充 */
animation: scalePulse 360ms cubic-bezier(.2, .8, .2, 1);
transform-origin: center center;
}
@keyframes scalePulse {
0% {
transform: scale(1);
}
25% {
/* 先收缩一点点 */
transform: scale(0.94);
}
65% {
/* 再放大到略超出的感觉 */
transform: scale(1.08);
}
100% {
transform: scale(1);
}
}
/* 文本渐变(用于返回/确定文字) */
.grad-text {
background-image: linear-gradient(90deg, #5b8bb3, #87a1bd);
background-size: 200% 100%;
background-position: 0% 50%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
transition: background-position 0.8s linear;
}
/* 选中时文字渐变滚动效果 */
.click-box-target .grad-text {
background-position: 100% 50%;
}
.image-photo {
width: 30%;
height: 30%;
transition: transform 0.18s ease, filter 0.18s ease;
}
/* 选中时图片略微放大 */
.click-box-target .image-photo {
/* 让图片跟随父元素缩放,不额外放大,保留平滑过渡 */
transform: none;
transition: transform 0.18s ease, filter 0.18s ease;
filter: none;
}
</style>