feat(工具箱): 完善“原神时钟”的交互逻辑

This commit is contained in:
2024-10-13 17:25:05 +08:00
parent 865ef1e383
commit 53299b83b3
4 changed files with 197 additions and 22 deletions

View File

@@ -6,6 +6,7 @@
'clock-rotation': clockState.isRotation, 'clock-rotation': clockState.isRotation,
}" }"
:style="elStyle" :style="elStyle"
@touchmove.prevent
> >
<!-- 外部 --> <!-- 外部 -->
@@ -43,7 +44,7 @@
<div class="pointer-wrapper pointer-upper bg-contain"> <div class="pointer-wrapper pointer-upper bg-contain">
<div <div
class="pointer-content" class="pointer-content"
@mousedown="handleDragPointer('upper')" @pointerdown="handleDragPointer('upper')"
></div> ></div>
</div> </div>
@@ -71,6 +72,9 @@
</div> </div>
</div> </div>
<!-- 遮罩层用于阻止操作 -->
<div v-show="isAutoRotating" class="clock-mask"></div>
</div> </div>
</template> </template>
@@ -79,6 +83,7 @@ import {
computed, computed,
onBeforeUnmount, onMounted, onBeforeUnmount, onMounted,
reactive, ref, reactive, ref,
watch,
} from 'vue'; } from 'vue';
import { import {
@@ -96,6 +101,10 @@ import {
IMAGE_TIME_ICON_MORNING, IMAGE_TIME_ICON_MORNING,
IMAGE_TIME_ICON_NIGHT, IMAGE_TIME_ICON_NIGHT,
IMAGE_TIME_ICON_NOON, IMAGE_TIME_ICON_NOON,
isAutoRotating, isTimeExceeded, isTimeTooEarly,
timeCurrHour, timeCurrMinute,
timeDiffLabel, timeDiffLabelStill,
timeNewHour, timeNewMinute,
} from './common-data'; } from './common-data';
import ClockColor from './ClockColor.vue'; import ClockColor from './ClockColor.vue';
@@ -138,6 +147,8 @@ const upperPointer = reactive({
}); });
window.upperPointer = upperPointer;
/** 元素 CSS */ /** 元素 CSS */
const elStyle = computed(() => { const elStyle = computed(() => {
return { return {
@@ -217,7 +228,11 @@ function handleDragPointer() {
// 节流 // 节流
let last = 0; let last = 0;
document.onmousemove = function (ev) { /**
* @description 处理光标移动
* @param {PointerEvent} ev
*/
let handleMove = function (ev) {
let curr = Date.now(); let curr = Date.now();
@@ -283,10 +298,10 @@ function handleDragPointer() {
}; };
document.onmouseup = function () { window.addEventListener('pointermove', handleMove);
document.onmousemove = null; window.addEventListener('pointerup', function () {
document.onmouseup = null; window.removeEventListener('pointermove', handleMove);
}; }, { once: true });
} }
@@ -304,6 +319,8 @@ function handleSubmitTime() {
// 结束 // 结束
if (upperAngleCurr === 0) { if (upperAngleCurr === 0) {
clearInterval(timer); clearInterval(timer);
isAutoRotating.value = false;
timeDiffLabelStill.value = '';
} }
let upperAngleDiff = upperAngleStart - upperAngleCurr; let upperAngleDiff = upperAngleStart - upperAngleCurr;
@@ -315,12 +332,87 @@ function handleSubmitTime() {
}, 50); }, 50);
/** 更新状态 */
isAutoRotating.value = true;
// 固定时间差文本
timeDiffLabelStill.value = timeDiffLabel.value;
} }
defineExpose({ defineExpose({
handleSubmitTime, handleSubmitTime,
}); });
// 检测角度变化,计算时间信息(自动旋转时)
watch(() => {
return lowerPointer.viewAngle;
}, (viewAngle) => {
// 转换为对应 24 小时的角度
let timeAngle = viewAngle + (viewAngle < 180 ? 180 : -180);
let timeValue = timeAngle / 15;
let currHour = Math.floor(timeValue);
let currMinute = Math.round((timeValue - currHour) * 60);
// 计算时间值
timeCurrHour.value = String(currHour).padStart(2, '0');
timeCurrMinute.value = String(currMinute).padStart(2, '0');
}, { immediate: true });
// 检测角度变化,计算时间信息(用户操作时)
watch(() => {
return upperPointer.dataAngle;
}, (dataAngle) => {
// 注15° / 小时
let isSecond = upperPointer.isSecond;
let currAngle = lowerPointer.viewAngle;
let viewAngle = upperPointer.viewAngle;
let diffAngle = dataAngle + (isSecond ? 360 : 0);
let diffAngle1 = 0; // +1 日角度差
let diffAngle2 = 0; // +2 日角度差
let diffLabel = '';
// 转换为对应 24 小时的角度
let timeAngle = viewAngle + (viewAngle < 180 ? 180 : -180);
let timeValue = timeAngle / 15;
let newHour = Math.floor(timeValue);
let newMinute = Math.round((timeValue - newHour) * 60);
if (currAngle < 180) {
diffAngle1 = 180 - currAngle;
diffAngle2 = diffAngle1 + 360;
} else {
diffAngle1 = 540 - currAngle; // 360 + 180
diffAngle2 = diffAngle1 + 360;
}
// 处理时间差信息
if (diffAngle < diffAngle1) {
diffLabel = '今日';
} else if (diffAngle < diffAngle2) {
diffLabel = '次日';
} else {
diffLabel = '+2日';
}
// 处理提示信息显示
isTimeTooEarly.value = diffAngle < 7.5;
isTimeExceeded.value = diffAngle === 720;
// 更新时间差信息
timeDiffLabel.value = diffLabel;
// 计算时间值
timeNewHour.value = String(newHour).padStart(2, '0');
timeNewMinute.value = String(newMinute).padStart(2, '0');
}, { immediate: true });
onMounted(() => { onMounted(() => {
timerInit(); timerInit();
}); });
@@ -610,6 +702,16 @@ onBeforeUnmount(() => {
} }
} }
.clock-mask {
position: absolute;
left: 0;
top: 0;
z-index: 100;
width: 100%;
height: 100%;
cursor: wait;
}
// 顺时针旋转 // 顺时针旋转
@keyframes rotation-forward { @keyframes rotation-forward {
0% { 0% {

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="tool-detail-page"> <div class="tool-detail-page has-radius">
<!-- --> <!-- -->
<div class="page-column"> <div class="page-column">
@@ -13,17 +13,27 @@
<clock-element ref="clockRef" /> <clock-element ref="clockRef" />
<!-- 上限提示 --> <!-- 上限提示 -->
<div class="time-notice"> <div
:class="{
'is-hide': isAutoRotating,
'time-notice': true,
}"
>
<span <span
v-show="true" v-show="isTimeExceeded"
class="time-notice-text" class="time-notice-text"
>时间到达上限</span> >时间到达上限</span>
</div> </div>
<!-- 确认时间 --> <!-- 确认时间 -->
<div class="time-submit"> <div
:class="{
'is-hide': isAutoRotating,
'time-submit': true,
}"
>
<span <span
v-if="false" v-if="isTimeTooEarly"
class="time-notice-text" class="time-notice-text"
>时间少于30分钟</span> >时间少于30分钟</span>
<genshin-button <genshin-button
@@ -44,6 +54,10 @@ import {
ref, ref,
} from 'vue'; } from 'vue';
import {
isAutoRotating, isTimeTooEarly, isTimeExceeded,
} from './common-data';
import ClockElement from './ClockElement.vue'; import ClockElement from './ClockElement.vue';
import GenshinButton from './GenshinButton.vue'; import GenshinButton from './GenshinButton.vue';
import TimeInfo from './TimeInfo.vue'; import TimeInfo from './TimeInfo.vue';
@@ -68,7 +82,6 @@ function handleConfirm() {
<style lang="less" scoped> <style lang="less" scoped>
.tool-detail-page { .tool-detail-page {
border-radius: 8px;
background-color: #000; background-color: #000;
font-size: 16px; font-size: 16px;
text-align: center; text-align: center;
@@ -89,9 +102,23 @@ function handleConfirm() {
} }
} }
.time-notice { .time-notice, .time-submit {
display: flex; display: flex;
height: 40px; height: 40px;
opacity: 1;
visibility: visible;
transition: opacity 0.25s, visibility 0s 0s;
// 注:
// visibility 动画时长用于等待 opacity 动画过渡完毕
&.is-hide {
visibility: hidden;
opacity: 0;
transition: opacity 0.25s, visibility 0.25s 0s;
}
}
.time-notice {
margin-top: 16px; margin-top: 16px;
} }
@@ -102,9 +129,4 @@ function handleConfirm() {
opacity: 0.5; opacity: 0.5;
font-size: 14px; font-size: 14px;
} }
.time-submit {
display: flex;
height: 40px;
}
</style> </style>

View File

@@ -11,7 +11,11 @@
<div class="time-title time-title-current">当前时间</div> <div class="time-title time-title-current">当前时间</div>
<!-- 当前时间值 --> <!-- 当前时间值 -->
<div class="time-value time-value-current">12:00</div> <div class="time-value time-value-current">
<span>{{ timeCurrHour }}</span>
<span>:</span>
<span>{{ timeCurrMinute }}</span>
</div>
<!-- 三角形图标 --> <!-- 三角形图标 -->
<div class="triangle-icon"> <div class="triangle-icon">
@@ -27,10 +31,16 @@
</div> </div>
<!-- 目标时间值 --> <!-- 目标时间值 -->
<div class="time-value time-value-target">12:00</div> <div class="time-value time-value-target">
<span>{{ timeNewHour }}</span>
<span>:</span>
<span>{{ timeNewMinute }}</span>
</div>
<!-- 时间差 --> <!-- 时间差 -->
<div class="time-diff">+2</div> <div class="time-diff">
<span>{{ timeDiffLabelStill || timeDiffLabel }}</span>
</div>
<!-- 装饰元素 --> <!-- 装饰元素 -->
<div class="arrow-element"></div> <div class="arrow-element"></div>
@@ -41,6 +51,9 @@
<script setup> <script setup>
import { import {
IMAGE_TIME_INFO_ARROW, IMAGE_TIME_INFO_ARROW,
timeCurrHour, timeCurrMinute,
timeDiffLabel, timeDiffLabelStill,
timeNewHour, timeNewMinute,
} from './common-data'; } from './common-data';
</script> </script>
@@ -66,8 +79,17 @@ import {
} }
.time-diff { .time-diff {
color: #ECE3D6; display: flex;
width: 3.5em;
height: 1.75em;
border-radius: 1.75em;
background-color: #282C33;
color: rgba(255, 255, 255, 0.75);
font-size: 0.75em; font-size: 0.75em;
span {
margin: auto;
}
} }
.time-title { .time-title {

View File

@@ -1,3 +1,5 @@
import { ref } from "vue";
export const IMAGE_BASE = `https://c.frost-zx.top/data/static/image/genshin-impact-clock`; export const IMAGE_BASE = `https://c.frost-zx.top/data/static/image/genshin-impact-clock`;
export const IMAGE_CLOCK_BG_INNER = `url("${IMAGE_BASE}/clock_bg_inner.png")`; export const IMAGE_CLOCK_BG_INNER = `url("${IMAGE_BASE}/clock_bg_inner.png")`;
export const IMAGE_CLOCK_BG_OUTER = `url("${IMAGE_BASE}/clock_bg_outer.png")`; export const IMAGE_CLOCK_BG_OUTER = `url("${IMAGE_BASE}/clock_bg_outer.png")`;
@@ -15,3 +17,30 @@ export const IMAGE_TIME_ICON_NIGHT = `url("${IMAGE_BASE}/time_icon_night.png")`;
export const IMAGE_TIME_ICON_NOON = `url("${IMAGE_BASE}/time_icon_noon.png")`; export const IMAGE_TIME_ICON_NOON = `url("${IMAGE_BASE}/time_icon_noon.png")`;
export const IMAGE_TIME_INFO_ARROW = `url("${IMAGE_BASE}/time_info_arrow.png")`; export const IMAGE_TIME_INFO_ARROW = `url("${IMAGE_BASE}/time_info_arrow.png")`;
export const IMAGE_TIME_ZONE_COLOR = `${IMAGE_BASE}/time_zone_color.png`; export const IMAGE_TIME_ZONE_COLOR = `${IMAGE_BASE}/time_zone_color.png`;
/** 是否正在自动旋转 */
export const isAutoRotating = ref(false);
/** 是否时间少于 30 分钟 */
export const isTimeTooEarly = ref(false);
/** 是否时间到达上限 */
export const isTimeExceeded = ref(false);
/** 当前时 */
export const timeCurrHour = ref('00');
/** 当前分 */
export const timeCurrMinute = ref('00');
/** 时间差(动态)*/
export const timeDiffLabel = ref('');
/** 时间差(固定)*/
export const timeDiffLabelStill = ref('');
/** 新的时 */
export const timeNewHour = ref('00');
/** 新的分 */
export const timeNewMinute = ref('00');