实现一个时间轴组件

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

效果展示:

功能点:

  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'  // 已预约
            }
        ]
    }
},

★感谢您看到最后★

相关推荐
anyup10 分钟前
🔥🔥 10 天 Star 破百!uView Pro 文档也开源啦:完全免费、无广告、高效上手
前端·vue.js·uni-app
南半球与北海道#1 小时前
el-table合并单元格
javascript·vue.js·elementui·表格合并
啷咯哩咯啷1 小时前
element-plus el-tree-v2大数据量勾选节点卡顿问题
前端·javascript·vue.js
索西3 小时前
Vue 响应式原理
vue.js
焦小风Zephyr3 小时前
Vue3组件通信:父子相传
vue.js
RainbowSea4 小时前
伙伴匹配系统(移动端 H5 网站(APP 风格)基于Spring Boot 后端 + Vue3 - 05
vue.js·spring boot·后端
文艺理科生5 小时前
Nuxt 组件渲染进阶:服务端与客户端组件的协作艺术
前端·javascript·vue.js
天客5 小时前
Vue3.5 + Element-Plus 二次封装 el-table、el-form、el-descriptions
前端·vue.js
XiaoMu_0016 小时前
【Vue vs React:前端框架深度对比分析】
vue.js·react.js·前端框架