实现一个时间轴组件

本文讲述了一个时间轴实现的核心原理,搞懂这个后,可以在此基础上开发更多功能。

效果展示:

功能点:

  1. 分钟级高精度(支持"HH:MM"格式)
  2. 状态条基于百分比的精确定位
  3. 刻度线和时间标签自适应容器宽度

功能拆解:

时间轴本身就是一个白色背景的div,刻度线通过flex实现均分,五条刻度线为一个小时。核心点在于计算偏移量和宽度,先把时间转化为分钟如:"1:30" → 1 × 60 + 30 = 90 分钟,一天总分钟数:24 * 60 = 1440分钟。

偏移计算:开始时间在一天中的比例 × 100% = 左偏移百分比。 6:00:(360 ÷ 1440) × 100% = 25%

宽度计算:会议持续时间在一天中的比例 × 100% = 宽度百分比 6:00-7:30: (90 ÷ 1440) × 100% = 6.25%

具体实现:

基本结构:

html 复制代码
<!-- 时间轴 -->
<div class="meeting-time">
  <div class="time-container">
    <!-- 选中的时间端 -->
    <div class="time-line">
      <div v-for="(meeting, index) in meetings" :key="index" class="status-bar" :style="getStatusBarStyle(meeting)"
        :class="meeting.status"></div>
    </div>
    <div class="time-scale">
      <div v-for="hour in 24" :key="hour" class="hour-mark">
        <!-- 刻度线 -->
        <div class="tick-line first"></div>
        <div class="tick-line"></div>
        <div class="tick-line"></div>
        <div class="tick-line"></div>
        <div class="tick-line last"></div>
        <!-- 时间 -->
        <span class="time-text">{{ hour - 1 }}:00</span>
        <span v-if="hour === 24" class="time-text time-right">00:00</span>
      </div>
    </div>
  </div>
</div>

定位实现:

vue2 复制代码
methods: {
    // 将时间字符串转换为分钟数 (如 "14:30" -> 870分钟)
    timeToMinutes(timeStr) {
        const [hours, minutes] = timeStr.split(':').map(Number);
        return hours * 60 + minutes;
    },

    // 计算状态条的样式
    getStatusBarStyle(meeting) {
        const { startTime, endTime } = meeting;

        const startMinutes = this.timeToMinutes(startTime);
        const endMinutes = this.timeToMinutes(endTime);

        // 一天总共1440分钟
        const totalMinutesInDay = 24 * 60;

        // 计算左边距百分比:开始分钟数 / 总分钟数 * 100%
        const left = (startMinutes / totalMinutesInDay) * 100;
        // 计算宽度百分比:(结束分钟数 - 开始分钟数) / 总分钟数 * 100%
        const width = ((endMinutes - startMinutes) / totalMinutesInDay) * 100;

        return {
                left: `${left}%`,
                width: `${width}%`
        };
    }
},

样式部分:

scss 复制代码
.meeting-time {
  height: 156px;
  margin-top: 60px;
  position: relative;

  .time-container {
    height: 100%;
    padding: 0 30px;
    background: var(--base-bg-color);
    box-shadow: var(--bg-shadow);
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  .time-scale {
    width: 100%;
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
    font-size: 18px;
    color: #666;

    .hour-mark {
      flex: 1;
      text-align: center;
      position: relative;
      display: flex;
      justify-content: space-between;

      .tick-line {
        width: 1px;
        height: 8px;
        background: #ccc;
        margin-bottom: 2px;

        &.first {
          height: 15px;
          width: 2px;
        }

        &.last {
          height: 15px;
        }

      }

      .time-text {
        position: absolute;
        left: -20px;
        top: 15px;
        font-size: 18px;
        font-weight: bold;

        &.time-right {
          left: auto;
          right: -20px;
        }
      }
    }
  }

  .time-line {
    width: 100%;
    height: 14px;
    background: #fff;
    position: relative;
    display: flex;
    overflow: hidden;

    .status-bar {
      position: absolute;
      top: 0;
      height: 100%;
      border-radius: 2px;

      // 不同状态的颜色
      &.finished {
        background: #999; // 已结束 - 灰色
      }

      &.ongoing {
        background: var(--primary-bg-color); // 进行中 - 蓝色
      }

      &.reserved {
        background: #ccc; // 已预约 - 浅灰色
      }

      &.idle {
        background: #fff; // 空闲中 - 白色
        border: 1px solid #ddd;
      }
    }
  }
}

数据部分:

vue2 复制代码
data() {
    return {
        meetings: [
            {
                startTime: '1:15',
                endTime: '2:30',
                status: 'ongoing'   // 进行中
            },
            {
                startTime: '9:15',
                endTime: '18:45',
                status: 'finished'  // 已结束
            },
            {
                startTime: '23:00',
                endTime: '23:59',
                status: 'reserved'  // 已预约
            }
        ]
    }
},

★感谢您看到最后★

相关推荐
一只小风华~2 分钟前
Vue:条件渲染 (Conditional Rendering)
前端·javascript·vue.js·typescript·前端框架
灰海2 小时前
vue中通过heatmap.js实现热力图(多个热力点)热区展示(带鼠标移入弹窗)
前端·javascript·vue.js·heatmap·heatmapjs
码上暴富3 小时前
vue2迁移到vite[保姆级教程]
前端·javascript·vue.js
老华带你飞4 小时前
考研论坛平台|考研论坛小程序系统|基于java和微信小程序的考研论坛平台小程序设计与实现(源码+数据库+文档)
java·vue.js·spring boot·考研·小程序·毕设·考研论坛平台小程序
YAY_tyy4 小时前
基于 Vue3 + VueOffice 的多格式文档预览组件实现(支持 PDF/Word/Excel/PPT)
前端·javascript·vue.js·pdf·word·excel
m0_748461397 小时前
Spring Boot + Vue 项目中使用 Redis 分布式锁案例
vue.js·spring boot·redis
珍珠奶茶爱好者7 小时前
vue二次封装ant-design-vue的table,识别columns中的自定义插槽
前端·javascript·vue.js
Slice_cy7 小时前
深入剖析 Vue 响应式系统:从零实现一个精简版
vue.js
羊羊小栈9 小时前
基于「YOLO目标检测 + 多模态AI分析」的PCB缺陷检测分析系统(vue+flask+数据集+模型训练)
vue.js·人工智能·yolo·目标检测·flask·毕业设计·大作业
晚星star9 小时前
在 Web 前端实现流式 TTS 播放
前端·vue.js