<template><div class="time-axis-container"><div class="time-axis" ref="axisRef"><!-- 刻度线 - 共25个刻度(0-24) --><divv-for="hour in 25":key="hour - 1"class="tick-mark":class="{'major-tick': isMajorTick(hour - 1),'active-tick': currentHour === hour - 1}":style="{ left: `${(hour - 1) * (100 / 24)}%` }"></div><!-- 刻度文字 --><divv-for="hour in 25":key="`label-${hour - 1}`"class="tick-label":style="{ left: `${(hour - 1) * (100 / 24)}%` }"v-show="isMajorTick(hour - 1)">{{ hour - 1 }}:00</div><!-- 可拖动滑块 --><divclass="slider"ref="sliderRef":style="{ left: `${currentHour * (100 / 24)}%` }"@mousedown="startDrag"@touchstart="startDrag"><div class="slider-time">{{ formatTime(currentHour) }}</div><div class="slider-icon"></div></div></div></div>
</template><script setup lang="ts">
import { ref, onMounted, onUnmounted, defineExpose } from 'vue';const props = defineProps<{initialHour?: number;
}>();const emit = defineEmits<{(e: 'time-change', hour: number): void;(e: 'stopChange', hour: number): void;
}>();const axisRef = ref<HTMLElement | null>(null);
const sliderRef = ref<HTMLElement | null>(null);
const currentHour = ref(0); // 先初始化为0,在onMounted中设置实际值
const isDragging = ref(false);
// 获取当前时间的小时数
const getCurrentHour = () => {const now = new Date();const currentMinutes = now.getMinutes();return currentMinutes > 0 ?Math.min(now.getHours() + 1, 24) : // 确保不超过24now.getHours();
};// 初始化当前小时
const initCurrentHour = () => {currentHour.value = props.initialHour !== undefined ?Math.max(0, Math.min(24, props.initialHour)) :getCurrentHour();
};// 判断是否是主要刻度(0/4/8/12/16/20/24)
const isMajorTick = (hour: number) => hour % 4 === 0;// 格式化时间显示
const formatTime = (hour: number) => {return `${hour.toString().padStart(2, '0')}:00`;
};// 开始拖动
const startDrag = (e: MouseEvent | TouchEvent) => {e.preventDefault();isDragging.value = true;document.addEventListener('mousemove', handleDrag);document.addEventListener('touchmove', handleDrag);document.addEventListener('mouseup', stopDrag);document.addEventListener('touchend', stopDrag);
};
// 共用计算方法
const calculateHourFromEvent = (e: MouseEvent | TouchEvent, axisRect: DOMRect) => {let clientX;if (e instanceof MouseEvent) {clientX = e.clientX;} else {clientX = e.touches[0].clientX;}// 严格限制在轴范围内计算const position = Math.max(0, Math.min(1, (clientX - axisRect.left) / axisRect.width));return Math.round(position * 24);
};
// 处理拖动
const handleDrag = (e: MouseEvent | TouchEvent) => {if (!isDragging.value || !axisRef.value) return;const axisRect = axisRef.value.getBoundingClientRect();const newHour = calculateHourFromEvent(e, axisRect);if (newHour !== currentHour.value) {currentHour.value = newHour;emit('time-change', newHour);}
};// 停止拖动
const stopDrag = () => {isDragging.value = false;emit('stopChange', currentHour.value);document.removeEventListener('mousemove', handleDrag);document.removeEventListener('touchmove', handleDrag);document.removeEventListener('mouseup', stopDrag);document.removeEventListener('touchend', stopDrag);
};// 点击时间轴直接跳转
const handleAxisClick = (e: MouseEvent) => {if (!axisRef.value || isDragging.value) return;const axisRect = axisRef.value.getBoundingClientRect();// 添加点击位置的安全边距检查if (e.clientX < axisRect.left || e.clientX > axisRect.right) return;const newHour = calculateHourFromEvent(e, axisRect);if (newHour !== currentHour.value) {currentHour.value = newHour;emit('time-change', newHour);}
};defineExpose({getCurrentHour
})onMounted(() => {initCurrentHour()if (axisRef.value) {axisRef.value.addEventListener('click', handleAxisClick);}
});onUnmounted(() => {if (axisRef.value) {axisRef.value.removeEventListener('click', handleAxisClick);}stopDrag();
});
</script><style scoped>
.time-axis-container {width: 100%;padding: 20px 0;position: relative;
}.time-axis {position: relative;height: 20px;width: 100%;border-radius: 4px;border-bottom: 2px solid #FFFFFF;cursor: pointer;
}.tick-mark {position: absolute;bottom: 0;width: 1px;height: 6px;background-color: #FFFFFF;transform: translateX(-50%);
}.tick-mark.major-tick {height: 12px;background-color: #FFFFFF;
}/*.tick-mark.active-tick {*/
/* background-color: #ff3d00;*/
/*}*/.tick-label {position: absolute;bottom: -36px;transform: translateX(-50%);font-size: 20px;color: #FFFFFF;
}.slider {position: absolute;top: -36px;transform: translateX(-50%);border-radius: 50%;display: flex;flex-direction: column;align-items: center;justify-content: center;cursor: grab;user-select: none;z-index: 10;
}.slider:active {cursor: grabbing;
}.slider-icon {width: 32px;height: 38px;background-image: url("/src/assets/realTimeScheduling/ranged-thumb.png");background-repeat: no-repeat;background-size: 32px 38px;background-position: center center;
}.slider-time {font-size: 24px;color: white;
}
</style>
vue3仿高德地图官网路况预测时间选择器
2025/6/19 15:44:28
来源:https://blog.csdn.net/qq_41853674/article/details/148735956
浏览:
次
关键词:vue3仿高德地图官网路况预测时间选择器
版权声明:
本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。
我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com