实现时间轴内容,使用el-slider?

想根据element-ui组件里的slider滑块实现时间轴,效果如下
1721276149068.png目前已使用单独组建将次时间轴进行样式显示,现在的问题是,不知道el-slider 滑动时怎么让对应标签在正上方显示。以及怎么时间根据不同间隔(5分钟、6分钟、10分钟、30分钟、 1小时)提示标签对应拖动后增加目前我实现的vue组件内容如下
image.png

<template>
<div class="slider">
    <div class="minipanel">
        <div class="refresh"><el-checkbox v-model="checked">自动刷新</el-checkbox></div>
        <div class="timeleap"><label for="">间隔:</label>
            <select v-model="timeStep" class="timer-select">
                <option v-for="(item,index) in timeOptions" :key="item.value"
                    :label="item.label"
                    :value="item.value"/>
            </select>
        </div>
        <div class="datetime">
            <el-date-picker 
                v-model="timeCur"
                type="date"
                placeholder="选择日期">``
            </el-date-picker>
        </div>
        <div class="play">
            <div class="panelgap" :class="{'noClick':checked}" title="刷新"></div>
            <div class="leftbtn" :class="{'noClick':checked}" title="往前"></div>
            <div v-show="playing" class="playbtn" :class="{'noClick':checked}" title="播放"></div>
            <div v-show="!playing" class="pausebtn" :class="{'noClick':checked}" title="暂停"></div>
            <div class="rightbtn" :class="{'noClick':checked}" title="往后"></div>
        </div>
    </div>
    <div class="slider-timer">
        <!-- <el-slider v-model="nowCur"></el-slider> -->
        <div class="custom-tooltip" v-if="tooltipVisible" >  
            <span class="custom-tooltip-tip">{{ tooltipText }} </span> 
        </div>
        <el-slider ref="slider" :show-tooltip="false" v-model="nowCur" :max="maxTime" @change="handleSliderChange" :step="5"></el-slider> 
        <div class="time-marks">
            <span class="slider-pip" v-for="(item,index) in timeMarks">
                <span class="slider-label">
                    <span class="slider-longline"></span>
                    <div class="_sider_time" style="position: inherit;margin-left:4px;position: inherit;top: 26px;width: 40px;font-weight: normal;">{{ item.hour.replace(/0(\d)/g, '$1') }}</div>
                </span>
            </span>
        </div>
    </div>
</div>
</template>

<script>
import * as moment from 'moment';

export default{
    data(){
        return{
            checked:false,
            timeOptions:[{value: '10',label: '10min'},{value: '30',label: '30min'},{value: '60',label: '1h'}],
            timeStep:10,timeCur:null,nowCur:null,
            playing:true,
            minTime:0,//new Date(new Date().getTime() - 24 * 3600 * 1000 - 3600 * 1000).getTime(), 
            // 当前时间前1小时的过去24小时  
            maxTime:100,//new Date(new Date().getTime() + 10 * 3600 * 1000).getTime(),
            // 当前时间未来10小时  
            timeMarks: [],  
            tooltipVisible: true,  
            tooltipText: '',  
            tooltipStyle: {},  
        }
    },
    prop:{},
    mounted(){this.initTime()},
    methods:{
        initTime(){
            // 获取当前时间
            this.timeCur = moment().format('YYYY-MM-DD');
            this.nowCur =  moment().hour();
            // this.minTime = moment(this.timeCur).subtract(25, 'hours').hour()
            // this.maxTime = moment(this.timeCur).add(12, 'hours').hour()
            // 时间轴时次 
            this.getTimeCur()
            // 时间轴显示toolip
        },
        getTimeCur(){
            const now = moment(this.timeCur); 
            const year = now.year();
            const month = now.format('MM');
            const time = now.valueOf();
            const hourInMs = 60 * 60 * 1000;
            const scaleCountPast = 25;
            const scaleCountFuture = 0; // 36 48
            const scaleWidth = 70;
            const tipsMark = [];const Mark = []

            for (let i = -scaleCountPast; i <= scaleCountFuture; i++) {
                const scaleTime = moment(time + i * hourInMs);
                const day = scaleTime.format('DD');
                const hour = scaleTime.format('HH');
                const minute = scaleTime.format('mm');
                Mark.push({index:i,hour:hour})
            }
            this.timeMarks = Mark
        },
        formatTime(timeStamp) {  
            const date = new Date(timeStamp);  
            return `${date.getDate()}日${date.getHours().toString().padStart(2, '0')}时${date.getMinutes().toString().padStart(2, '0')}分`;  
        },  
        handleSliderChange(value) {  
            // 当 slider 值改变时,更新 tooltip  
            this.updateTooltip(value);  
        },  
        updateTooltip(value) {  
            this.tooltipText = '';  
            this.tooltipVisible = true;  
            const sliderRect = this.$refs.slider.$el.getBoundingClientRect();  
            const sliderMax = this.maxTime;
            const percent = (value / sliderMax) * 100;  
            const left = `${(percent / 100) * 20 - 10}px`; 
            this.tooltipStyle = {  
                left: `${left}%`, // 假设 tooltip 宽度为 100px  
            }; 
        },  
    },
    computed:{

    },
    watch:{},
}
</script>

<style lang="scss" scoped>
$bg:#fff;
.slider{
    margin-left: -350px;
    left: 50%;
    position: absolute;
    z-index: 1;
    width: 700px;
    height: 56px;
    bottom: 32px;
    opacity: 0.8;
    box-sizing: border-box;
    
    .minipanel{
        float: left;
        margin-left: -35px;
        margin-top: -42px;
        color: #000;
        .refresh{
            margin-bottom: 2px;
            width: 100%;
            height: 22px;
            display: flex;justify-content: center;
            -webkit-box-align: center;align-items: center;
            border-radius: 20px;
            border:1px solid #06608a;
            background: $bg;
        }
        .timeleap{
            display: -webkit-box;display: -ms-flexbox;display: flex;
            -webkit-box-pack: center;-ms-flex-pack: center;
            justify-content: center;-webkit-box-align: center;-ms-flex-align: center;
            align-items: center;
            border-radius: 20px;
            border: 1px solid #06608a;
            background: #fff;
            height: 22px;
            line-height: 22px;
            -webkit-box-sizing: border-box;box-sizing: border-box;
            padding: 5px;
            ::v-deep .timer-select{
                width: 60px;
                border: none;
                color: #06608a;
                text-transform: none;
                option{line-height: 20px;height: 20px;}
            }
        }
        .datetime{display: inline-block;line-height: normal;
           ::v-deep .el-date-editor.el-input, .el-date-editor.el-input__inner {
                margin-top: 2px;
                border: 1px solid #06608a;
                display: block;
                background: #fff;
                height: 22px;
                width: 120px;
                border-radius: 12px;
                color: #06608a;
                text-align: center;
                line-height: 22px;
            } 
            ::v-deep .el-input__inner{
                height: 22px;
                line-height: 22px;
                border-radius: 22px;
                font-size: 12px;
                color: #06608a;
                padding-right: 10px;
            }
            ::v-deep .el-input__prefix{top:-8px;}
        }
        .play{
            display: flex;
            justify-content: space-around;
            .panelgap{width: 30px;
            height: 30px;background: url(../assets/home/time_refresh.png);margin-top: 5px;}
            .leftbtn{width: 30px;
            height: 30px;background: url(../assets/home/move_btn.png) center no-repeat;transform: rotateZ(180deg);overflow: hidden;margin-top: 2px;}
            .playbtn{width: 30px;
            height: 30px;background: url(../assets/home/move_play.png) center no-repeat;overflow: hidden;margin-top: 5px;}
            .pausebtn{width: 30px;
            height: 30px;background: url(../assets/home/pause_btn.png) center no-repeat;overflow: hidden;margin-top: 5px;}
            .rightbtn{width: 30px;
            height: 30px;background: url(../assets/home/move_btn.png) center no-repeat;overflow: hidden;margin-top: 5px;}
            // 
            .panelgap.noClick{background: url(../assets/home/time_refresh_stop.png);}
            .leftbtn.noClick{background: url(../assets/home/stop_btn.png) center no-repeat;transform:rotateZ(0deg);cursor: no-drop;pointer-events: none;margin-top: 5px;}
            .playbtn.noClick{background: url(../assets/home/stop_play.png) center no-repeat;cursor: no-drop;pointer-events: none;}
            .rightbtn.noClick{background:url(../assets/home/stop_btn.png) center no-repeat ;transform: rotateZ(180deg);cursor: no-drop;pointer-events: none;margin-top: 1px;}
        }
    }
    &-timer{
        ::v-deep .el-slider{
            margin-left: 120px;
            margin-top:-26px;
            margin-right: 15px;
            height: 8px;
            .el-slider__runway{
                height: 8px;border-radius: 5px;
                border: 1px solid #007ca0!important;
                background: rgba(75,92,108,0.5)!important; //background-color: #4b5c6c80;
            } 
            .el-slider__bar{
                height: 8px;
                background-color: #3090f0;
            } 
            .el-slider__button{
                width: 10px;
                height: 10px;
                z-index: 2;
                border: 3px solid #fff;
                background: -moz-linear-gradient(top, #a7d8ff, #69b7f7);
                background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#a7d8ff), to(#69b7f7));
                background: #3090f0;
                border-radius: 50%;
                -webkit-transform: translate(36%, 0px);
                transform: translate(36%, 0px);
            }  
        } 
    }
    .custom-tooltip{
        position: absolute;
        margin-left: 132px;
        margin-top: 10px;
        margin-right: 15px;
        &-tip{
            position: absolute;
            visibility: hidden;
            top: -40px;
            display: block;
            width: 34px;
            margin-left: -18px;
            left: 50%;
            height: 20px;
            line-height: 20px;
            background: #fff;
            border-radius: 3px;
            border: 1px solid #888;
            text-align: center;
            font-size: 12px;
            opacity: 0;
            color: #333;
            -webkit-transition-duration: .2s,.2s,0;
            transition-duration: .2s,.2s,0;
            -webkit-transition-property: opacity,top,visibility;
            transition-property: opacity,top,visibility;
            -webkit-transition-delay: 0,0,.2s;
            transition-delay: 0,0,.2s;
            visibility: visible;
            opacity: 1;
            top: -25px;
            margin-left: -40px;
            width: 80px;color: #000;background: #fff;
            &::before {
                content: " ";
                width: 0;
                height: 0;
                border: 5px solid hsla(0,0%,100%,0);
                border-top-color: #888;
                position: absolute;
                bottom: -11px;
                left: 50%;
                margin-left: -5px;
            }
            &::after {
                content: " ";
                width: 0;
                height: 0;
                border: 5px solid hsla(0,0%,100%,0);
                border-top-color: #fff;
                position: absolute;
                bottom: -10px;
                left: 50%;
                margin-left: -5px;
            }
        }
    }
    .time-marks{
        display: flex;
        margin: auto;
        margin-top: 30px;
        margin-left: 120px;
        display: none;
        .slider-pip{
            width: 2em;
            height: 1em;
            line-height: 1em;
            position: absolute;
            font-size: .8em;
            color: #999;
            overflow: visible;
            text-align: center;
            top: 20px;
            left: 20px;
            margin-left: -1em;
            cursor: pointer;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            .slider-label{

            }
        }
    }
}

</style>

1.拖动滑块时能在对应正上方标签显示当前x日x时x分(根据间隔进行分、时叠加) 2.滑块条为对应整下方为时次显示(左侧选中时间值的过去24小时逐小时内容) 3.点击播放若花开位置已在100%不执行,反之往前走动(每个1s请求)

阅读 1.8k
2 个回答

效果

image.png

思路

手搓,只能且必须手搓一个Tool,这个组件只支持鼠标经过隐藏与显示,初始化常态显示压根就不支持。方法就是css伪类。赋值的时候要给content传字符串内容。

代码


      <template>
        <div style="width: 800px; margin: 30px">
          <el-slider
            v-model="time"
            :min="0"
            :max="24"
            :show-tooltip="false"
            @change="handleTimeChange"
          ></el-slider>
        </div>
      </template>
      
      <script>
      import moment from "moment";

      export default {
        data() {
          return {
            time: 0, // 初始化时显示tooltip
          };
        },

        mounted() {
          let hours = moment().format("HH");
          this.showTime(hours);
        },

        methods: {
          showTime(v) {
            const that = this;
            that.time = Number(v); //赋值小时注意这里必须为Number型

            //tool显示文本
            let sliderBar = document.getElementsByClassName("el-slider__bar");
            let sliderButton = document.getElementsByClassName(
              "el-slider__button-wrapper"
            );
            let showTime = moment().format("YYYY-M-D");
            sliderButton[0].setAttribute(
              "data-attr",
              showTime + "\n" + that.time + ":00"
            );

            // 值为0的时候滑块位于最右端
            if (that.time == 0) {
              sliderBar[0].style.width = "0%";
              sliderButton[0].style.left = "0%";
            }
          },

          handleTimeChange(newTime) {
            console.log("🚀 ~ handleTimeChange ~ newTime:", newTime)
            this.showTime(newTime);
          },
        },
      };
      </script>

      <style lang="scss" scoped>
      ::v-deep .el-slider__button-wrapper::before {
        content: attr(data-attr); // 给伪类添加内容,值为上述传递的值
        position: absolute;
        width: 110px;
        height: 25px;
        border-radius: 5px;
        border: 1px solid #ddd;
        top: -25px;
        left: -40px;
        background: #fff;
        font-size: 12px;
        line-height: 25px;
        text-align: center;
        color: #000;
      }
      </style>

简单的方案:最大最小值使用时间戳,下面的marks根据开始结束时间进行等量分割,然后格式化,上面拖拽时的内容使用format-tooltip格式化显示就好了,

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题