css绘制蛇形时间轴线图

蛇形时间轴线图绘制

        本人在之前的项目中(vue项目),由于业务需求根据设计图自行利用css样式绘制了一个简单的时间轴线图,可自适应收缩并且左右来回循环,在某些方面可满足大部分的需求,并且添加了图例,特此分享。如有需要,大家可自行复制代码应用到自己的项目之中,并且根据自己的业务需求加以调整。

1. 效果图展示

在这里插入图片描述

2、代码呈现(代码全部粘贴应用之后即可呈现除小汽车图片之外的上图效果)

  • HTML
<template>
	<div class="only-content">
        <!--图例显示-->
        <div class="legend-block">
            <div class="every-legend" v-for="item in cloneLegend" @click="clickLegend(item)" :style="item.statusName === '无计划' ? 'cursor: default;' : ''">
                <span class="color-block" :style="'background:' + item.background"></span>
                <span class="name-block">{{item.statusName}}</span>
            </div>
            <div class="clear"></div>
        </div>
        <!--时间轴线显示-->
        <div class="time-line">
            <div class="every-block" v-for="(item,index) in cloneTimeDatas"
                 :style="item.type === 0 ? 'float: left;' + 'width:' + item.width + '%;' : item.type === 1 ? 'float: right;' + 'width:' + item.width : item.type === 2 ? 'float: right;' + 'width:' + item.width + '%;' : 'float: left;' + 'width:' + item.width + '%;'" style="height: 100px;">
                <div class="identical to-right" v-if="item.type === 0">
                    <!--分类名称-->
                    <div class="class-name" :style="item.status === 2 ? 'color: #6078AB;' : 'color: #000;'">
                        <span :title="item.className">{{item.className}}</span>
                    </div>
                    <!--进行中小车图标-->
                    <span class="small-car" v-if="item.status === 2">
                            <img src="/static/images/carPlan1.png" alt="">
                        </span>
                    <!--时间线-->
                    <div class="class-line" :style="item.className !== null && item.className !== '' ? 'background:' + item.background : 'background:' + item.background + ';cursor: default;'"></div>
                    <!--开始时间-->
                    <span class="start-time" v-if="index === 0">{{item.startTime}}</span>
                    <!--结束时间-->
                    <span class="end-time" v-if="!(cloneTimeDatas[index+1] && cloneTimeDatas[index+1].type === 1)">
                            <span v-if="cloneTimeDatas.length == (index + 1)">{{item.endTime}}</span>
                            <span v-else>{{item.nextTime}}</span>
                        </span>
                    <!--结束箭头-->
                    <div class="end-arrow" v-if="cloneTimeDatas.length === (index + 1)" :style="'border-color: transparent transparent transparent' + item.background"></div>
                    <!--方向箭头-->
                    <div class="small-arrow-left" v-if="cloneTimeDatas.length !== (index + 1)"></div>
                    <div class="small-arrow-right" v-if="cloneTimeDatas.length !== (index + 1)"></div>
                </div>
                <!--右回转空心半圆环-->
                <div class="right-box"  v-if="item.type === 1">
                    <div class="big-circular" :style="item.className !== null && item.className !== '' ? 'background:' + item.background : 'background:' + item.background + ';cursor: default;'">
                        <div class="small-circular"></div>
                        <!--结束时间-->
                        <span class="end-time" v-if="cloneTimeDatas.length == (index + 1)">{{item.endTime}}</span>
                        <span class="end-time" v-else>{{item.nextTime}}</span>
                    </div>
                    <!--结束箭头-->
                    <div class="end-arrow" v-if="cloneTimeDatas.length === (index + 1)" :style="'border-color: transparent' + ' ' + item.background + ' ' + 'transparent transparent'"></div>
                </div>
                <div class="identical to-left" v-if="item.type === 2">
                    <!--分类名称-->
                    <div class="class-name" :style="item.status === 2 ? 'color: #6078AB;' : 'color: #000;'">
                        <span :title="item.className">{{item.className}}</span>
                    </div>
                    <!--进行中小车图标-->
                    <span class="small-car" v-if="item.status === 2">
                            <img src="/static/images/carPlan.png" alt="">
                        </span>
                    <!--时间线-->
                    <div class="class-line" :style="item.className !== null && item.className !== '' ? 'background:' + item.background : 'background:' + item.background + ';cursor: default;'"></div>
                    <!--开始时间-->
                    <span class="start-time" v-if="index === 0">{{item.startTime}}</span>
                    <!--结束时间-->
                    <span class="end-time" v-if="!(cloneTimeDatas[index+1] && cloneTimeDatas[index+1].type === 3)">
                            <span v-if="cloneTimeDatas.length == (index + 1)">{{item.endTime}}</span>
                            <span v-else>{{item.nextTime}}</span>
                        </span>
                    <!--结束箭头-->
                    <div class="end-arrow" v-if="cloneTimeDatas.length === (index + 1)" :style="'border-color: transparent' + ' ' + item.background + ' ' + 'transparent transparent'"></div>
                    <!--反向箭头-->
                    <div class="small-arrow-left" v-if="cloneTimeDatas.length !== (index + 1)"></div>
                    <div class="small-arrow-right" v-if="cloneTimeDatas.length !== (index + 1)"></div>
                </div>
                <!--左回转空心半圆环-->
                <div class="left-box" v-if="item.type === 3">
                    <div class="big-circular" :style="item.className !== null && item.className !== '' ? 'background:' + item.background : 'background:' + item.background + ';cursor: default;'">
                        <div class="small-circular"></div>
                        <!--结束时间-->
                        <span class="end-time" v-if="cloneTimeDatas.length == (index + 1)">{{item.endTime}}</span>
                        <span class="end-time" v-else>{{item.nextTime}}</span>
                    </div>
                    <!--结束箭头-->
                    <div class="end-arrow" v-if="cloneTimeDatas.length === (index + 1)" :style="'border-color: transparent transparent transparent' + item.background"></div>
                </div>
            </div>
            <div class="clear"></div>
        </div>
    </div>
</template>
  • 定义参数
data() {
   return {
        // 所有分类节点数据
        allTimeListDatas: [],
        // 深克隆后的数据
        cloneTimeDatas: [],
        // 所有计划目前状态集合
        planLegend: [
            {"statusName": "已完成","background": "#4ECB74","status": 3,"bool": true,},
            {"statusName": "进行中","background": "#F6AF80","status": 2,"bool": true,},
            {"statusName": "未开始","background": "#A8C7FC","status": 1,"bool": true,},
            {"statusName": "无计划","background": "#E4E4E4","status": 0,"bool": false,},
        ],
        // 深克隆后的图例数据
        cloneLegend: [],
    }
},
  • JS
    注:克隆数据是为了操作数据后不影响原数据
methods: {
  // 点击图例显示隐藏
  clickLegend(item){
      item.bool = !item.bool;
      if (item.statusName !== '无计划') {
          this.cloneTimeDatas.forEach( value => {
              if (value.status === item.status) {
                  if (item.bool) {
                      this.planLegend.forEach( cloneItem => {
                          if (cloneItem.status === item.status) {
                              value.background = cloneItem.background;
                              item.background = cloneItem.background;
                          }
                      });
                  } else {
                      value.background = '#E4E4E4';
                      item.background = '#E4E4E4';
                  }
              }
          })
      }
  },
  // 获取计划分类数据
  getPlanClassData(){
      //调取接口————————可根据自己的业务需求在此调接口获取展示数据
      let data = [
          {className: "单车计划",status: 1,day: 60,startTime: "2019-01-01",endTime: "2019-01-31",nextTime: "2019-02-01",},
          {className: "整车计划",status: 2,day: 120,startTime: "2019-02-01",endTime: "2019-03-31",nextTime: "2019-04-01",},
          {className: "修复计划",status: 3,day: 120,startTime: "2019-04-01",endTime: "2019-05-31",nextTime: "2019-06-01",},
          {className: "空闲",status: 0,day: 60,startTime: "2019-06-01",endTime: "2019-06-30",nextTime: "2019-07-01",},
          {className: "拆解计划",status: 2,day: 150,startTime: "2019-07-01",endTime: "2019-09-30",nextTime: "2019-10-01",},
          {className: "组装计划",status: 3,day: 180,startTime: "2019-10-01",endTime: "2019-12-31",nextTime: "2020-01-01",},
          {className: "检测计划",status: 2,day: 100,startTime: "2020-01-01",endTime: "2020-06-30",nextTime: "2020-07-01",},
          {className: "保养计划",status: 3,day: 100,startTime: "2020-07-01",endTime: "2020-12-31",nextTime: "2020-12-31",},
      ];
      let allPlanClass = $.extend(true,[],data); // 所有的计划分类数据
      let typeIndex = 0; //类型标记
      let addLength = 0;  // 计划分类赋值前叠加的总长度
      let otherLength = 0; // 计划分类赋值后叠加的总长度
      let moreThanPlan = []; // 每行结尾多于出的计划分类集合
      allPlanClass.forEach( (value,index) => {
          // 根据计划分类状态值显示不同的颜色条(1—未开始;2—进行中;3—已完成;0—无计划)
          if (value.status === 1) {
              value.background = '#A8C7FC';
          } else if (value.status === 2) {
              value.background = '#F6AF80';
          } else if (value.status === 3) {
              value.background = '#4ECB74';
          } else if (value.status === 0) {
              value.background = '#E4E4E4';
          }
          let moreThan = {}; // 每行结尾多于出的计划分类
          // 根据计划周期(天数)计算显示长度(最短为10%)
          // 计算每一段计划分类的长度
          let thisWidth = 0;
          if (value.day < 30) {  // width的数字代表百分比
              thisWidth = 10;
          } else if (value.day%30 < 15) {
              thisWidth = parseInt(value.day/30) * 10
          } else if (value.day%30 >= 15) {
              thisWidth = (parseInt(value.day/30) + 1) * 10
          }
          // 叠加每个计划分类的长度
          addLength = addLength + thisWidth;
          if (value.day == 375) {
              console.log(addLength,otherLength,typeIndex,thisWidth,"时间呢?????");
          }
          // 根据总的长度来判断是否大于100%
          if (addLength < 100) {
              value.width = thisWidth;  // 每段计划分类的长度
              value.type = typeIndex; // 每段计划分类的类型(1—从左往右;2—右半圆弧;3—从右往左;4—左半圆弧)
              otherLength = otherLength + value.width;
          } else {
              value.width = 100 - otherLength;
              value.type = typeIndex;
              typeIndex++;
              // 处理每行结尾多于出长度的计划分类
              moreThan = $.extend(true,{},value);
              moreThan.width = 0;
              moreThan.type = typeIndex;
              moreThanPlan.push(moreThan);
              typeIndex++;
              if (typeIndex === 4) {
                  typeIndex = 0;
              }
              addLength = 0;
              otherLength = 0;
          }
      });
      // 处理后的计划分类数据集合
      this.allTimeListDatas = this.sorts(allPlanClass,moreThanPlan);
      // 深克隆计划分类数据
      this.cloneTimeDatas = $.extend(true,[],this.allTimeListDatas);
      // 深克隆图例(计划状态)数据
      this.cloneLegend = $.extend(true,[],this.planLegend);
      if (this.cloneTimeDatas.length > 0 && (this.cloneTimeDatas[this.cloneTimeDatas.length - 1].type === 1 || this.cloneTimeDatas[this.cloneTimeDatas.length - 1].type === 3)) {
          $('.only-content').attr('style',"padding-bottom: 50px;")
      } else {
          $('.only-content').removeAttr('style',"padding-bottom")
      }
  },
  // 循环排序(将每行多于长度的计划分类插入原数组)
  sorts(initArr,insertArr){
      let arr = initArr.concat();    //拷贝数组
      initArr.forEach((value,index) => {
          insertArr.forEach((val,ind) => {
              if(value.startTime == val.startTime){   //判断值相同  插入
                  let idx =[];
                  arr.forEach((data,tt) => {
                      if(data.startTime == val.startTime){
                          idx.push(tt);   // 获取所有相同值得下标数组
                      }
                  });
                  arr.splice(idx[idx.length-1] + 1,0,val);    //取最后相同值插入数据
              }
          })
      });
      return arr
  },
},
created () {
  this.getPlanClassData();
},
  • CSS
<style scoped lang="less">
	.only-content{
		width: 100%;
		height: 100%;
		font-size: 12px;
        background-color: #fff;
        .clear{
            clear: both;
        }
        /*图例样式*/
        .legend-block{
            padding: 15px 20px 0 20px;
            .every-legend{
                float: left;
                margin-right: 30px;
                cursor: pointer;
                user-select: none;
                span{
                    display: inline-block;
                }
                .color-block{
                    width: 23px;
                    height: 11px;
                }
            }

        }
        /*计划分类蛇形道样式*/
        .time-line{
            padding: 20px 100px;
            .every-block{
                position: relative;
                cursor: default;
                .identical{
                    position: relative;
                }
                // 从左向右的方向直线样式
                .to-right{
                    position: relative;
                    /*分类名称样式*/
                    .class-name{
                        width: 100%;
                        height: 20px;
                        text-align: center;
                        overflow: hidden;
                        white-space: nowrap;
                        text-overflow: ellipsis;
                        span{
                            cursor: pointer;
                        }
                    }
                    // 小车样式
                    .small-car{
                        position: absolute;
                        top: -18px;
                        left: calc(50% - 20px);
                        z-index: 200;
                    }
                    /*分类颜色线样式*/
                    .class-line{
                        width: 100%;
                        height: 18px;
                        border-right: 1px solid #ccc;
                        cursor: pointer;
                    }
                    /*开始时间*/
                    .start-time{
                        z-index: 200;
                        position: absolute;
                        bottom: -24px;
                        left: -32px;
                        color: #000;
                    }
                    /*结束时间*/
                    .end-time{
                        z-index: 200;
                        position: absolute;
                        bottom: -24px;
                        right: -30px;
                        color: #000;
                    }
                    /*结束箭头样式*/
                    .end-arrow{
                        position: absolute;
                        bottom: -9px;
                        right: -36px;
                        width: 0;
                        height: 0;
                        border: 18px solid;
                    }
                    /*方向箭头样式*/
                    .small-arrow-left{
                        z-index: 20;
                        position: absolute;
                        bottom: 7px;
                        right: 0;
                        float: left;
                        width: 8px;
                        height: 4px;
                        background-color: #fff;
                    }
                    .small-arrow-right{
                        z-index: 20;
                        position: absolute;
                        bottom: 3px;
                        right: -12px;
                        float: left;
                        width: 0;
                        height: 0;
                        border: 6px solid;
                        border-color: transparent transparent transparent #fff;
                    }
                }
                // 从右向左的方向直线样式
                .to-left{
                    position: relative;
                    /*分类名称样式*/
                    .class-name{
                        width: 100%;
                        height: 20px;
                        text-align: center;
                    }
                    // 小车样式
                    .small-car{
                        position: absolute;
                        top: -18px;
                        left: calc(50% - 20px);
                        z-index: 200;
                    }
                    /*分类颜色线样式*/
                    .class-line{
                        height: 18px;
                        border-right: 1px solid #ccc;
                        position: relative;
                        cursor: pointer;
                    }
                    /*开始时间*/
                    .start-time{
                        z-index: 200;
                        position: absolute;
                        bottom: -24px;
                        right: -32px;
                        color: #000;
                    }
                    /*结束时间*/
                    .end-time{
                        z-index: 200;
                        position: absolute;
                        bottom: -24px;
                        left: -30px;
                        color: #000;
                    }
                    /*结束箭头样式*/
                    .end-arrow{
                        position: absolute;
                        bottom: -9px;
                        left: -36px;
                        width: 0;
                        height: 0;
                        border: 18px solid;
                    }
                    /*方向箭头样式*/
                    .small-arrow-left{
                        z-index: 20;
                        position: absolute;
                        bottom: 7px;
                        left: 0;
                        float: left;
                        width: 8px;
                        height: 4px;
                        background-color: #fff;
                    }
                    .small-arrow-right{
                        z-index: 20;
                        position: absolute;
                        bottom: 3px;
                        left: -12px;
                        float: left;
                        width: 0;
                        height: 0;
                        border: 6px solid;
                        border-color: transparent #fff transparent transparent ;
                    }
                }
                // 左空心半圆样式
                .left-box{
                    position: relative;
                    top: 20px;
                    left: -60px;
                    .big-circular {
                        float: left;
                        width: 60px;
                        height: 118px;
                        cursor: pointer;
                        -webkit-border-radius: 118px 0 0 118px;
                        position: relative;
                        .small-circular {
                            width: 42px;
                            height: 82px;
                            background-color: #fff;
                            -webkit-border-radius: 82px 0 0 82px;
                            position: absolute;
                            left: 18px;
                            top: 18px;
                        }
                        /*结束时间*/
                        .end-time{
                            z-index: 200;
                            position: absolute;
                            bottom: -24px;
                            left: 30px;
                            white-space: nowrap;
                            color: #000;
                        }
                    }
                    /*结束箭头样式*/
                    .end-arrow{
                        position: absolute;
                        bottom: -128px;
                        right: -96px;
                        width: 0;
                        height: 0;
                        border: 18px solid;
                    }
                }
                // 右空心半圆样式
                .right-box{
                    position: relative;
                    top: 20px;
                    right: -60px;
                    .big-circular {
                        float: right;
                        width: 60px;
                        height: 118px;
                        cursor: pointer;
                        -webkit-border-radius: 0 118px 118px 0;
                        position: relative;
                        .small-circular {
                            width: 42px;
                            height: 82px;
                            background-color: #fff;
                            -webkit-border-radius: 0 82px 82px 0;
                            position: absolute;
                            left: 0;
                            top: 18px;
                        }
                        /*结束时间*/
                        .end-time{
                            z-index: 200;
                            position: absolute;
                            bottom: -24px;
                            left: -30px;
                            white-space: nowrap;
                            color: #000;
                        }
                    }
                    /*结束箭头样式*/
                    .end-arrow{
                        position: absolute;
                        bottom: -128px;
                        left: -96px;
                        width: 0;
                        height: 0;
                        border: 18px solid;
                    }
                }
            }
        }
    }
</style>
发布了3 篇原创文章 · 获赞 4 · 访问量 141

猜你喜欢

转载自blog.csdn.net/yuanbotao219/article/details/103803493