首页 > 其他分享 >时间选择器(免费获取)

时间选择器(免费获取)

时间:2025-01-21 10:29:21浏览次数:3  
标签:const type itemHeight 获取 滚动 year container 免费 选择器

成果展示:

完整代码:
 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态阻尼下拉刷新</title>
    <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<style>
  .hide-scrollbar::-webkit-scrollbar {
    display: none;
    /* Safari 和 Chrome */
  }
</style>
<body class="bg-gray-100 flex items-center justify-center h-screen" x-data="{showDatePicker:false}">
  <button @click="showDatePicker = true" class="bg-blue-500 text-white px-4 py-2 rounded-full">打开日期选择器</button>
  <div x-show="showDatePicker" x-data="datePicker()" class="bg-white p-4 rounded-lg shadow-md w-80" x-init="$watch('showDatePicker', value => { if (value) $nextTick(() => init()); })">
  <div class="relative flex justify-between">
    <!-- 两条分割线 -->
    <div class="absolute w-full border-b border-gray-600 top-1/2 transition -translate-y-4"></div>
    <div class="absolute w-full border-b border-gray-600 bottom-1/2 transition translate-y-4"></div>
    <!-- 年份选择 -->
    <div class=" flex-1 mx-1">
      <div class="h-[320px] pb-[150px] pt-[150px] overflow-y-scroll hide-scrollbar  rounded" x-ref="yearScroll">
        <template x-for="year in years" :key="year">
          <div
            class="text-center py-2 cursor-pointer"
            :class="{ 'font-bold ': selectedYear === year }"
            @click="selectYear(year)"
          >
            <span x-text="`${year}年`"></span>
          </div>
        </template>
      </div>
    </div>

    <!-- 月份选择 -->
    <div class="flex-1 mx-1">
      <div class="h-[320px] pb-[150px] pt-[150px] overflow-y-scroll hide-scrollbar  rounded" x-ref="monthScroll">
        <template x-for="month in months" :key="month">
          <div
            class="text-center py-2 cursor-pointer"
            :class="{ 'font-bold': selectedMonth === month }"
            @click="selectMonth(month)"
          >
            <span x-text="`${month}月`"></span>
          </div>
        </template>
      </div>
    </div>

    <!-- 日期选择 -->
    <div class="flex-1 mx-1">
      <div class="h-[320px] pb-[150px] pt-[150px] overflow-y-scroll hide-scrollbar  rounded" x-ref="dayScroll">
        <template x-for="day in days" :key="day">
          <div
            class="text-center py-2 cursor-pointer"
            :class="{ 'font-bold': selectedDay === day }"
            @click="selectDay(day)"
          >
            <span x-text="`${day}日`"></span>
          </div>
        </template>
      </div>
    </div>
  </div>

  <button
  class="w-full bg-red-500 text-white px-4 py-2 rounded-full mt-4"
  >
    确认
  </button>

  <!-- 显示选中结果 -->
  <div class="mt-4 text-center">
    <span class="text-lg font-bold">选中日期:</span>
    <span x-text="`${selectedYear}年 ${selectedMonth}月 ${selectedDay}日`"></span>
  </div>
</div>

</body>

<script>
  document.addEventListener('alpine:init', () => {
      Alpine.data('datePicker', () => ({
          years: [],
          months: Array.from({ length: 12 }, (_, i) => i + 1),
          days: [],
          systemDate: {
              year: new Date().getFullYear(),
              month: new Date().getMonth() + 1, 
              day: new Date().getDate()
          },
          selectedYear: null,
          selectedMonth: null,
          selectedDay: null,
          scrollTimeouts: {},
  
          // 初始化函数
          init() {
              this.years = this.generateYearRange(1920, 2025);
              this.resetToSystemDate();
              this.updateDays();
              this.addScrollListeners();
  
              this.$nextTick(() => {
                  const observer = new ResizeObserver(() => {
                      this.scrollToCurrent();
                  });
                  observer.observe(this.$el);
                  setTimeout(() => observer.disconnect(), 300);
              });
          },
  
          // 重置为系统时间
          resetToSystemDate() {
              this.selectedYear = this.systemDate.year;
              this.selectedMonth = this.systemDate.month;
              this.selectedDay = this.systemDate.day;
          },
  
          // 生成年份
          generateYearRange(start, end) {
              return Array.from({ length: end - start + 1 }, (_, i) => start + i);
          },
  
          // 检查闰年
          isLeapYear(year) {
              return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
          },
  
          // 获取特定年和月的天数
          daysInMonth(year, month) {
              const monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
              if (month === 2 && this.isLeapYear(year)) return 29;
              return monthDays[month - 1] || 0;
          },
  
          // 更新天数列表
          updateDays() {
              const daysInMonth = this.daysInMonth(this.selectedYear, this.selectedMonth);
              this.days = Array.from({ length: daysInMonth }, (_, i) => i + 1);
              this.selectedDay = Math.min(this.selectedDay, daysInMonth);
          },
  
          // 获取滚动元素的动态内边距和项高度
          getScrollElementDimensions(ref) {
            const container = this.$refs[ref];
            if (container) {
              const style = getComputedStyle(container);
              const paddingTop = parseInt(style.paddingTop, 10);
              const paddingBottom = parseInt(style.paddingBottom, 10);
              const itemHeight = container.querySelector('li') ? container.querySelector('li').offsetHeight : 40;
              // 想知道每一项多高就把注释解了
              // console.log(`${ref}的内边距:`, paddingTop, paddingBottom, `项高度:`, itemHeight);
              return { paddingTop, paddingBottom, itemHeight };
            }
            return { paddingTop: 140, paddingBottom: 140, itemHeight: 40 }; // 默认值
          },
  
          // 滚动到当前日期
          scrollToCurrent() {
              this.$nextTick(() => {
                  this.scrollToPosition('yearScroll', this.selectedYear - this.years[0]);
                  this.scrollToPosition('monthScroll', this.selectedMonth - 1);
                  this.scrollToPosition('dayScroll', this.selectedDay - 1);
              });
          },
  
          // 滚动到指定位置
          scrollToPosition(type, index) {
              const { paddingTop, paddingBottom, itemHeight } = this.getScrollElementDimensions(type);
              const container = this.$refs[type];
              if (!container || index < 0) return;
  
              const scrollPosition = Math.max(
                  0,
                  index * itemHeight - (container.clientHeight / 2 - itemHeight / 2) + paddingTop
              );
              container.scrollTo({
                  top: scrollPosition,
                  behavior: 'smooth',
              });
          },
  
          // 添加滚动事件监听
          addScrollListeners() {
              ['yearScroll', 'monthScroll', 'dayScroll'].forEach(type => {
                  const ref = this.$refs[type];
                  if (!ref) return;
                  ref.addEventListener('scroll', () => this.debounceScrollEnd(ref, type));
              });
          },
  
          // 防抖处理的滚动结束事件
          debounceScrollEnd(container, type) {
              if (this.scrollTimeouts[type]) clearTimeout(this.scrollTimeouts[type]);
              this.scrollTimeouts[type] = setTimeout(() => this.handleScrollEnd(container, type), 150);
          },
  
          // 处理滚动结束事件
          handleScrollEnd(container, type) {
              const { itemHeight, paddingTop } = this.getScrollElementDimensions(type);
              const scrollPosition = container.scrollTop - paddingTop + container.clientHeight / 2- itemHeight / 2;
              let closestIndex = Math.round(scrollPosition / itemHeight);
              console.log(`${type}滚动结束,选择索引:`, scrollPosition);

              let maxIndex;
              if (type === 'yearScroll') {
                  maxIndex = this.years.length - 1;
                  closestIndex = Math.max(0, Math.min(closestIndex, maxIndex));
                  this.selectYear(this.years[closestIndex]);
              } else if (type === 'monthScroll') {
                  maxIndex = this.months.length - 1;
                  closestIndex = Math.max(0, Math.min(closestIndex, maxIndex));
                  this.selectMonth(this.months[closestIndex]);
              } else if (type === 'dayScroll') {
                  maxIndex = this.days.length - 1;
                  closestIndex = Math.max(0, Math.min(closestIndex, maxIndex));
                  this.selectDay(this.days[closestIndex]);
              }
              // 校正滚动
              const expectedScrollPosition = closestIndex * itemHeight - (container.clientHeight / 2 - itemHeight / 2) + paddingTop;
              if (Math.abs(container.scrollTop - expectedScrollPosition) > 1) {
                  container.scrollTo({
                      top: expectedScrollPosition,
                      behavior: 'smooth',
                  });
              }
          },
  
          // 选择年份
          selectYear(year) {
              if (this.selectedYear !== year) {
                  this.selectedYear = year;
                  this.selectedMonth = 1;
                  this.selectedDay = 1;
                  this.updateDays();
                  this.scrollToCurrent();
              }
          },
  
          // 选择月份
          selectMonth(month) {
              if (this.selectedMonth !== month) {
                  this.selectedMonth = month;
                  this.selectedDay = 1;
                  this.updateDays();
                  this.scrollToCurrent();
              }
          },
  
          // 选择日期
          selectDay(day) {
              if (this.selectedDay !== day) {
                  this.selectedDay = day;
                  this.scrollToCurrent();
              }
          },
      }));
  });
</script>
  
  
</html>

开发逻辑说明

这个时间选择器的设计逻辑主要围绕如何实现滚动选择 年、月、日 并保证选中项居中展开。以下是其开发思路的分解:


1. 使用数组存储数据
  • 使用 yearsmonthsdays 三个数组分别存储年份、月份和日期。
  • 动态生成年份范围(如 1920-2025),并通过月份和年份计算出特定月份的天数。

2. 自动调整容器与视口的关系
  • 动态获取容器的高度,在函数getScrollElementDimensions(ref)中使用getComputedStyle计算容器内边距以及每一项的高度。
  • 通过计算容器高度,内边距高度以及每一项高度实现元素的正确选取。

3. 上下留白实现居中显示
  • 为滚动容器添加上下留白(paddingToppaddingBottom),通过内边距带来的位置偏移实现容器的居中显示。

4. 平滑滚动与居中校正
  • 通过 scrollTo 方法,将选中的项目平滑滚动到中心。
  • 在滚动停止后,根据当前滚动位置校正到最近的有效日期项。

5. 事件监听与防抖
  • 为滚动容器添加 scroll 事件监听器,并使用防抖处理(setTimeoutclearTimeout),避免频繁触发滚动结束逻辑。
  • 在滚动停止时,计算最接近的日期项并自动调整滚动位置。

6. 数据联动
  • 当年份或月份发生变化时,重新计算日期数组,确保日期数据与所选年份和月份一致。
  • 用户交互时(如滚动选择年份),自动联动更新月份和日期,避免选择无效日期。

7. 初始化与动态更新
  • 在组件初始化时,通过 ResizeObserver 自动监听元素尺寸变化,确保时间选择器能正确获取到容器高度。
  • 提供重置功能(如恢复系统时间)以便快速回到默认状态。

    标签:const,type,itemHeight,获取,滚动,year,container,免费,选择器
    From: https://blog.csdn.net/sahjiwij/article/details/145277507

    相关文章

    • 写一个方法来获取div到浏览器窗口的高度
      在前端开发中,你可以使用JavaScript的getBoundingClientRect()方法来获取一个元素(比如div)相对于浏览器窗口的位置和大小。这个方法返回一个DOMRect对象,其中包含了top、right、bottom和left等属性,分别表示元素各边到视口(viewport)的距离。为了获取一个div元素到浏览器窗口顶部的高度......
    • 2025年更新「GIS数据」全国的GeoJSON、shp格式数据下载获取(精确到乡镇街道级)
      发现个可以免费下载全国 geojson 数据的网站,推荐一下。支持全国、省级、市级、区/县级、街道/乡镇级以及各级的联动数据,支持导入矢量地图渲染框架中使用,例如:D3、Echarts等geojson数据下载地址:https://geojson.hxkj.vip该项目github地址:https://github.com/TangSY/echarts-m......
    • SecureCRT 9.1 免费版下载及安装使用教程
      SecureCRT是一款专业的电脑终端仿真器,将坚如磐石的终端仿真与强大的加密功能结合在一起,严格保证用户的信息安全。SecureCRT具备安全传输,高度可配置的会话,SSH加密等等功能,支持SSH2、SSH1、Telnet、Telnet/SSH、Relogin、Serial、TAPI、RAW等协议。软件特色1、标签组现在在所有......
    • 【开源免费】基于Vue和SpringBoot的智慧图书管理系统(附论文)
      本文项目编号T152,文末自助获取源码\color{red}{T152,文末自助获取源码}......
    • 【开源免费】基于Vue和SpringBoot的文档管理系统(附论文)
      本文项目编号T151,文末自助获取源码\color{red}{T151,文末自助获取源码}......
    • vue3 tsx ref获取dom写法
      在Vue3中使用TSX并结合ref来获取DOM元素或组件实例,可以通过Vue提供的组合式API(CompositionAPI)来实现。Vue3支持JSX/TSX语法,允许你在函数式组件中编写类似JSX的代码。下面是一个具体的示例,展示了如何在TSX中使用ref来获取DOM元素。步骤安装必要的依赖......
    • MySQL 中单独获取已知日期的年月日
      在MySQL中,处理日期和时间是一项常见任务。通常,我们需要从已知的日期中提取年、月、日等部分信息。MySQL提供了一些内置函数,可以方便地进行这些操作。本文将详细介绍如何在MySQL中单独获取已知日期的年、月、日部分。一、提取年份(Year)要从日期中提取年份,可以使用 YEAR() 函数......
    • mysql 获取当前时间戳13
      mysql获取当前时间戳13在MySQL中,您可以使用CURRENT_TIMESTAMP或NOW()函数来获取当前的时间戳。这将以'YYYY-MM-DDHH:MM:SS'格式返回当前的日期和时间。如果您需要的是一个UNIX时间戳(即自1970年1月1日以来的秒数),您可以使用UNIX_TIMESTAMP()函数。以下是获取当前UN......
    • pg 获取当前时间戳
      pg获取当前时间戳获取当前时间戳有多种实现方法:方法一:使用Python内置的time模块。  importtime timestamp=int(time.time())方法二:使用datetime模块,结合strftime函数将当前时间转换为指定格式的字符串,再通过strptime函数将其转换为时间对象,最后通过time......
    • oracle 获取当前时间戳
      oracle获取当前时间戳Oracle中获取当前时间戳有多种实现方法: 使用SYSTIMESTAMP函数:    SELECTSYSTIMESTAMPFROMDUAL;   使用CURRENT_TIMESTAMP函数:    SELECTCURRENT_TIMESTAMPFROMDUAL;   使用SYSDATE函数......