hldy_app/component/public/newgame/arrowkeys.vue

227 lines
5.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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