hldy_app/component/public/newgame/joysticknew.vue

383 lines
8.9 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` }" @touchmove="onTouchMove"
@touchend="onPressEnd" @touchcancel="onPressEnd">
<!-- 长按持续波纹动画 -->
<view v-if="showShadow && pao" class="light-shadow ripple-loop" :style="{
left: shadow.x + 'px',
top: shadow.y + 'px',
transform: 'translate(-50%, -50%)'
}"></view>
<!-- 点击单次波纹动画,用 key 刷新 -->
<view v-if="showRippleOnce && pao" :key="rippleKey" class="light-shadow ripple-once" :style="{
left: shadow.x + 'px',
top: shadow.y + 'px',
transform: 'translate(-50%, -50%)'
}" @animationend="onRippleAnimationEnd"></view>
<image :src="!notext?`/static/index/newruler/direction_1.png`: `/static/index/newruler/suere.png`" v-show="type==-1 || type==4" class="move-circle-all" />
<!-- <image src="/static/index/newruler/direction_2.png" v-show="type==-2" class="move-circle-all" />
<image src="/static/index/newruler/direction_3.png" v-show="type==-3" class="move-circle-all" /> -->
<image :src="!notext?`/static/index/newruler/direction_3.png`: `/static/index/newruler/sure_2.png`" v-show="type==3" class="move-circle-all" />
<image :src="!notext?`/static/index/newruler/direction_5.png`: `/static/index/newruler/sure_4.png`" v-show="type==2" class="move-circle-all" />
<image :src="!notext?`/static/index/newruler/direction_4.png`: `/static/index/newruler/sure_3.png`" v-show="type==0" class="move-circle-all" />
<image :src="!notext?`/static/index/newruler/direction_2.png`: `/static/index/newruler/sure_1.png`" v-show="type==1" class="move-circle-all" />
<view class="pulse-circle" v-if="getblue" :key="pulseKey">
</view>
<!-- 四个方向按钮 -->
<view class="click-box-top" @tap="onTap(0, $event)" @longpress="(e) => onLongPressStart(0, e)" />
<view class="click-box-left" @tap="onTap(3, $event)" @longpress="(e) => onLongPressStart(3, e)" />
<view class="click-box-bottom" @tap="onTap(2, $event)" @longpress="(e) => onLongPressStart(2, e)" />
<view class="click-box-right" @tap="onTap(1, $event)" @longpress="(e) => onLongPressStart(1, e)" />
<view class="click-box-center" @tap="onTap(4, $event)" @longpress="(e) => onLongPressStart(4, e)" />
</view>
</template>
<script setup lang="ts">
import { ref, reactive, watch, nextTick, onBeforeUnmount } from 'vue'
const emit = defineEmits<{
(e : 'movecard', dir : number) : void
}>()
const key = ref(-1)
let clickResetTimer : ReturnType<typeof setTimeout> | null = null
let longPressInterval : ReturnType<typeof setInterval> | null = null
let isLongPress = false
const type = ref(-1)
const shadow = reactive({ x: 0, y: 0 })
const showShadow = ref(false) // 长按持续波纹
const showRippleOnce = ref(false) // 点击单次波纹
const rippleKey = ref(0) // 动画 key用来强制刷新
const RADIUS = uni.upx2px(175)
const DIAMETER = uni.upx2px(350)
const LEFT = uni.upx2px(-50)
const BOTTOM = uni.upx2px(100)
const windowHeight = uni.getSystemInfoSync().windowHeight
const TOP = windowHeight - BOTTOM - DIAMETER
const props = defineProps({
getblue: {
type: Boolean,
default: false
},
movebottom: {
type: Number,
default: 0
},
moveleft: {
type: Number,
default: 0
},
pao:{
type: Boolean,
default: true
},
notext:{
type: Boolean,
default: false
}
});
const getblue = ref(false)
const pulseKey = ref(0)
let timeout1 : ReturnType<typeof setTimeout> | null = null
let timeout2 : ReturnType<typeof setTimeout> | null = null
let timeout3 : ReturnType<typeof setTimeout> | null = null
const types = [-1, -2, -3]
const index = ref(0)
let timer = null;
const switchType = () => {
index.value = (index.value + 1) % types.length
type.value = types[index.value]
}
watch(() => props.getblue, (val) => {
// if (timer) clearInterval(timer)
// timer = setInterval(() => {
// switchType()
// }, 100)
// 清除上次的定时器,防止重复播放
// if (timeout1) clearTimeout(timeout1)
// if (timeout2) clearTimeout(timeout2)
// if (timeout3) clearTimeout(timeout3)
// // 重置动画状态
// getblue.value = false
// nextTick(() => {
// pulseKey.value++
// getblue.value = true
// // 第一阶段结束
// timeout1 = setTimeout(() => {
// getblue.value = false
// }, 3000)
// // 第二阶段开始
// timeout2 = setTimeout(() => {
// getblue.value = true
// }, 3010)
// // 第二阶段结束
// timeout3 = setTimeout(() => {
// getblue.value = false
// }, 6500)
// })
// }
})
onBeforeUnmount(() => {
if (timer) clearInterval(timer)
})
function onTap(dir : number, e : any) {
if (timer) clearInterval(timer)
if (isLongPress) return
clearClickTimer()
key.value = dir
emit('movecard', dir)
// console.log("?????",e.touches)
if (e?.touches && e.touches.length) {
const touch = e.touches[0]
updateShadowPosition(touch.pageX, touch.pageY)
}
showRippleOnce.value = false
rippleKey.value++
type.value = dir
setTimeout(() => {
type.value = -1
}, 300)
setTimeout(() => {
showRippleOnce.value = true
}, 16)
clickResetTimer = setTimeout(() => {
key.value = -1
clickResetTimer = null
}, 300)
}
function onRippleAnimationEnd() {
showRippleOnce.value = false
}
function onLongPressStart(dir : number, e : TouchEvent) {
if (timer) clearInterval(timer)
clearLongPressInterval()
isLongPress = true
showShadow.value = true
type.value = dir
const touch = (e?.touches || [])[0]
if (touch) updateShadowPosition(touch.pageX, touch.pageY)
key.value = dir
emit('movecard', dir)
longPressInterval = setInterval(() => {
key.value = dir
emit('movecard', dir)
}, 500)
}
function onPressEnd() {
clearClickTimer()
clearLongPressInterval()
isLongPress = false
showShadow.value = false
key.value = -1;
type.value = -1
}
function clearClickTimer() {
if (clickResetTimer) {
clearTimeout(clickResetTimer)
clickResetTimer = null
}
}
function clearLongPressInterval() {
if (longPressInterval) {
clearInterval(longPressInterval)
longPressInterval = null
}
}
function updateShadowPosition(pageX : number, pageY : number) {
let x = pageX
let y = pageY - TOP - 50 + props.movebottom/2
const dx = x - RADIUS
const dy = y - RADIUS
const dist = Math.sqrt(dx * dx + dy * dy)
if (dist > RADIUS) {
const angle = Math.atan2(dy, dx)
x = RADIUS + RADIUS * Math.cos(angle)
y = RADIUS + RADIUS * Math.sin(angle)
}
shadow.x = x
shadow.y = y
}
function onTouchMove(e : any) {
if (!isLongPress) return
const touch = (e.detail?.touches || e.touches || [])[0]
if (!touch) return
updateShadowPosition(touch.pageX, touch.pageY)
}
</script>
<style lang="less" scoped>
.move-circle {
position: absolute;
bottom: 0rpx;
left: 0rpx;
width: 350rpx;
height: 350rpx;
display: flex;
justify-content: center;
align-items: center;
z-index: 99;
touch-action: none;
.click-box-top {
position: absolute;
top: 20rpx;
left: 70rpx;
width: 220rpx;
height: 80rpx;
}
.click-box-bottom {
position: absolute;
bottom: 20rpx;
left: 70rpx;
width: 220rpx;
height: 80rpx;
}
.click-box-left {
position: absolute;
bottom: 100rpx;
left: 0;
width: 90rpx;
height: 150rpx;
}
.click-box-right {
position: absolute;
bottom: 100rpx;
right: 0;
width: 90rpx;
height: 150rpx;
}
.click-box-center {
position: absolute;
bottom: 130rpx;
right: 130rpx;
width: 90rpx;
height: 90rpx;
// background-color: red;
}
}
.move-circle-all {
width: 350rpx;
height: 350rpx;
}
.light-shadow {
position: absolute;
width: 40rpx;
height: 40rpx;
background-color: transparent;
border: 60rpx solid #3da6ff;
border-radius: 50%;
pointer-events: none;
opacity: 1;
}
/* 无限循环波纹动画,长按时用 */
.ripple-loop {
animation: rippleLoop 1.2s ease-out infinite;
}
/* 点击一次的波纹动画 */
.ripple-once {
animation: rippleLoop 1.2s ease-out forwards;
}
@keyframes rippleLoop {
0% {
transform: translate(-50%, -50%) scale(0.5);
opacity: 0.6;
}
100% {
transform: translate(-50%, -50%) scale(2.5);
opacity: 0;
}
}
.light-circle {
position: relative;
width: 150px;
height: 150px;
border-radius: 50%;
background: #111;
/* 你背景色自己改 */
overflow: visible;
}
.circle {
position: relative;
width: 150px;
height: 150px;
border-radius: 50%;
background: #222;
margin: 50px;
}
.pulse-circle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
background: radial-gradient(circle, #03a4ff 0%, transparent 70%);
animation: pulse 3s forwards;
}
@keyframes pulse {
0% {
width: 0;
height: 0;
opacity: 0.8;
}
50% {
width: 350rpx;
height: 350rpx;
opacity: 0.4;
}
100% {
width: 0;
height: 0;
opacity: 0;
}
}
</style>