使用vue+原生js编写时间轴--cesium三维球

突然感觉自己是天生的牛马了,今天同事接到个需求,要写一个关于气象的插件,其他的功能还好说,但是这个时间轴的功能,网上的插件貌似比较少,自己手搓呢,他又不想这么麻烦。然后我看到这个功能感觉觉得挺考验技术的,顿时心里就特别痒痒。哈哈,看我贱不贱。

随后我利用周末的时间就手搓了一个时间轴。本来想用canvas来绘制来着,后来想到用canvas绘制的图形性能方面也许会好很多,但是想修改样式缺很不容易。并且我拿给同事去用的话,里面有些计算的地方一时半会也不容易捋清楚,所有我就用JavaScript+Dom的形式来渲染时间轴。样式修改起来会相对容易很多!!!

好了,话不多说,直接上正题

实现效果

实现思路

当前的时间轴就是利用dom,来进行展示,本来想使用canvas来绘制的,但是后来想到canvas绘制出来后样式不容易修改,就没有使用了,使用dom修改样式特别方便。

我们实现时可以首先用除法计算出每一个时间段所需要的宽度,称为tx,然后给时间线添加一个鼠标按下事件,通过事件的回调参数获取当前鼠标在时间线上点击的偏移量,称为offsetx。通过相除得出rx,公式为rx=offsetx/tx。让rx+1就是当前鼠标点击的通过的时间段。然后我们计算更精确的时间,就是把每个时间段分为多个小段,如一个时间段是一个小时,那么一个小时是60分钟,分60小段。称为_tx,让使用如下公式即可得出更_rx = (offsetx%tx) / tx * _tx;此时就能计算出当前鼠标的具体为几点了;

但是当一个时间段表示一月时,计算的方式就不一样了,我们需要首先计算出当前时间段是哪个月份,然后根据当前的月份来计算出本月有多少天,称为month,计算更详细的时间步骤就变成了_rx = (offsetx%tx) / tx * this.getDaysByMonth(month)

判断每个月有多少天的代码如下:

    /**
     * 通过月份来返回本月的时间
     * @param month 月份
     * @returns 本月的时间
     */
    getDaysByMonth(month: number) {
        let a = [1, 3, 5, 7, 8, 10, 12];
        let b = [2]
        if (a.includes(month)) {
            return 31
        } else if (b.includes(month)) {
            return 28
        } else {
            return 30
        }
    }
大致内容讲解

1、本实例中可以展示的时间轴分为4级,分别可以显示小时,周,年等。

此实例显示的数据都是在界面渲染前将要显示的数据保存在相应的数组中,等渲染时之间根据下标查找数据即可。

    /**
     * 要显示的宏观文本数组--就是时间轴中最高级的显示单位
     */
    MacroTextArray: string[] = []
    /**
     * 要显示的微观数组
     */
    DateTextArray: number[] = []

展示层级为0级时要展示的信息内容

/**
 * 设置显示的时间文本数组
 * 0级
 */
setDateTextArrayZore() {

    let date = new Date();
    // 月份
    let month = date.getMonth() + 1;
    // 小时
    let hours = date.getHours();

    this.MacroTextArray = []
    this.DateTextArray = []

    for (let i = 0; i < this.ReviewNum; i++) {
        this.MacroTextArray.push(month + '')
        this.DateTextArray.push(hours)
        hours += 1;
        if (hours == 24) {
            month++
            hours = 0;
        }
    }

}

展示层级为1级时要展示的信息内容

/**
 * 设置显示的时间文本数组
 * 1级
 */
setDateTextArrayOne() {

    let date = new Date();
    // 周几
    let week = date.getDay();
    // 几号 ,因为下面会+1 因此这里要 -1
    let day = date.getDate() - 1;
    // 月份
    let month = date.getMonth() + 1;
    // 本月的天数
    let monthDay = this.getDaysByMonth(month);
    let _monthDay = 0;

    this.MacroTextArray = []
    this.DateTextArray = []

    for (let i = 0; i < this.ReviewNum; i++) {
        this.MacroTextArray.push(((week + i) % 7) + '')

        _monthDay++;
        // 避免月份出错
        if (_monthDay > monthDay) {
            month++
            monthDay = this.getDaysByMonth(month);
            _monthDay = 0;
        }
        this.DateTextArray.push(((day + i) % (monthDay)) + 1)
    }
}
/**
 * 通过月份来返回本月的时间
 * @param month 月份
 * @returns 本月的时间
 */
getDaysByMonth(month: number) {
    let a = [1, 3, 5, 7, 8, 10, 12];
    let b = [2]
    if (a.includes(month)) {
        return 31
    } else if (b.includes(month)) {
        return 28
    } else {
        return 30
    }
}

 展示层级为2级时要展示的信息内容

/**
 * 设置显示的时间文本数组
 * 2级
 */
setDateTextArrayTwo() {
    let date = new Date();
    // 月份
    let month = date.getMonth() + 1;
    // 年份
    let year = date.getFullYear();

    this.MacroTextArray = []
    this.DateTextArray = []

    for (let i = 0; i < this.ReviewNum; i++) {
        this.MacroTextArray.push(year + '')
        this.DateTextArray.push(month)
        month += 1
        if (month == 13) {
            year++
            month = 1
        }
    }
}

  展示层级为3级时要展示的信息内容

/**
 * 设置显示的时间文本数组
 * 3级
 */
setDateTextArrayThree() {
    let date = new Date();
    // 年份
    let year = date.getFullYear();

    this.MacroTextArray = []
    this.DateTextArray = []

    for (let i = 0; i < this.ReviewNum; i++) {
        this.MacroTextArray.push(year + '')
        this.DateTextArray.push(0)
        year++
    }

}

2、下面是比较重要的方法,将相应的长度传给此方法,自动将长度转换为我们自己想要的数据

需要注意:长度的起点是相对时间线的,不是屏幕哦

/**
 * @description 将长度转换为时间
 * @description 本长度的起点是指时间线0点距离,不是屏幕的0点
 */
getDateByOffsetX(len: number) {
    // 跨越的时间段
    let timeSolt = Math.floor(len / this.TimeSlotWidth);
    // 剩余的时间段距离
    let _timeSolt = Math.floor(len % this.TimeSlotWidth);

    let macro = this.MacroTextArray[Math.floor(timeSolt)];

    let day = this.DateTextArray[timeSolt] + 1;

    // 记录下当前走到哪里了
    this.CurrentDay = day;
    // 具体时间
    let date = Math.floor(_timeSolt / this.TimeSlotIntervalWidth);

    // 如果时间轴的层级是2,则因为月份会有不同,因此要使用另一种计算方法
    if (this.TimeLevel == 2) {
        // getDaysByMonth方法上面有讲到
        let a = this.getDaysByMonth(this.DateTextArray[Math.floor(timeSolt)])
        let b = a / this.TimeSlotWidth;

        date = (Math.ceil(_timeSolt * b) % a) + 1;
    }
    return {
        macro,
        day,
        date
    }
}

 3、在实例中我们需要定义useTimeShowTextByLevel方法,在使用时需要更换显示层级时,可以调用useTimeShowTextByLevel方法,但是此方法只会更改时间线的显示层级,所以调用完后需要调用下init()的初始化方法。

/**
 * 按照显示级别来给时间轴设置显示文本
 */
useTimeShowTextByLevel(level?: number) {
    let l = level ? level : this.TimeLevel;
    
    switch (l) {
        case 0:
            this.setDateTextArrayZore()
            // 一个小时有60分钟
            this.TimeSlotInterval = 60;
            break;
        case 1:
            this.setDateTextArrayOne()
            // 一天有24小时
            this.TimeSlotInterval = 24;
            break;
        case 2:
            this.setDateTextArrayTwo()
            // 一月有30天,但是每个月有特殊性
            this.TimeSlotInterval = 30;
            break;
        case 3:
            this.setDateTextArrayThree()
            // 一月有30天,但是每个月有特殊性
            this.TimeSlotInterval = 12;
            break;
        default:
            throw new Error("当前显示设置级别不合理")
            break;
    }
}

4、此时大致的内容已经讲完了,下面是销毁事件,当我们在使用中不需要时间轴时,我们可以调用销毁方法来接触绑定的事件

这里说一下,在绑定事件时,因为使用addEventListener使绑定事件,所以我们需要使bind来改变下this的指向。

/**
 * 销毁时间线中实例和绑定的事件
 */
DestoryTimeLine(){
    let time_progressbar = this.getByClass("time_progressbar")
    if(!time_progressbar || time_progressbar?.length == 0) return false;
    time_progressbar[0].removeEventListener("mousedown",this.Time_MouseDown)
    window.removeEventListener("mousemove",this.WindowMouseMove_time)
    window.removeEventListener("mouseup",this.WindowMouseUp_time)
    // 清空父元素
    this.ParentNode && (this.ParentNode.innerHTML = "");
}
具体实现

1、css样式

因为我们的时间轴使用的是dom来渲染的,为的是容易修改它的样式,这里我写了点初始的样式,下面是具体的内容

.timeLine{
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
.time_switch{
  width: 35px;
  height: 35px;
  background-color: #fcfcfc;
  display: flex;
  justify-content: center;
  align-items: center;
  box-shadow: 0px 0px 5px 3px #8f8b8b;
  border-radius: 50%;
}
.time_content{
  width: calc(100% - 60px);
  height: 100%;
  margin-left: 10px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.time_progressbar_tooltip{
  position: absolute;
  left: 0;
  top: 0;
  padding: 10px 20px;
  border-radius: 4px;
  background-color: #e3ecec;
  z-index: -1;
  opacity: 0;
}
.time_progressbar{
  width: 100%;
  height: 10px;
  background-color: #464749;
  cursor: pointer;
  position: relative;
  border-radius: 10px;
}
.t_progressbar{
  width: 0;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
  border-top-left-radius: 10px;
  border-bottom-left-radius: 10px;
  background-color: #fff;
}
.time_progressbar_remind{
  width: 100%;
  height: 20px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
.time_progressbar_remind span{
  color: #fff;
  font-size: 12px;
  border-right: 2px solid #626262;
  align-items: flex-end;
  display: flex;
  height: 100%;
  text-indent: 1em;
}
.time_progressbar_remind span:last-child{
  border: none !important;
}

2、TypeScript/JavaScript代码

创建一个TimeLine.ts文件


export class useTimeLine {
    /**
     * 时间数组
     */
    DateArray: string[] = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]
    /**
     * 记录单位的数组 ["号","周","月","年"]
     */
    UnitArray: string[] = ["号", "周", "年", "年"]
    /**
     * 记录是否是初次加载 默认:是
     */
    isInitLoad:boolean = true;
    /**
     * 当前显示时间轴的级别,默认:1
     */
    TimeLevel: number = 1;
    /**
     * 最外层的元素元素
     */
    ParentNode: HTMLElement | null;
    /**
     * 需要显示未来几天的数据
     */
    ReviewNum: number = 10;
    /**
     * 每个时间段需要的长度
     */
    TimeSlotWidth: number = 0
    /**
     * 每个时间段又分出多少间隔,默认:24
     */
    TimeSlotInterval: number = 60;
    /**
     * 时间段中每个间隔的长度
     */
    TimeSlotIntervalWidth: number = 0;
    /**
     * 保存当前时间进度
     */
    CurrentTimeProgress: number = 0;
    /**
     * 时间轴移动的速度
     */
    TimeMoveSpeed: number = 200;
    /**
     * 保存当前的移动进度
     */
    CurrentDay: number = 0;
    /**
     * 要显示的宏观文本数组--就是时间轴中最高级的显示单位
     */
    MacroTextArray: string[] = []
    /**
     * 要显示的微观数组
     */
    DateTextArray: number[] = []
    /**
     * 时间轴与鼠标交互时的元素
     */
    time_progressbar_tooltip: Element | null = null;
    /**
     * 覆盖在时间轴元素上方
     */
    t_progressbar: Element | null = null;
    /**
     * 自动播放按钮的点击状态回调
     * @param {*} flag  点击状态 播放:true 停止:false
     * @param {*} target 当前的元素
     */
    autoPlayClickStatus: Function | null = null;
    /**
     * @description tooltip更换显示dom
     * @description tip:请返回string形式的dom
     * @param {object} e macro:最宏观的显示单位 day:中等显示单位 date:最小显示单位
     * 
     */
    setTooltipElementFn: Function | null = null;
    /**
     * 自定义时间轴底部的显示内容
     * @param {object} macro 当前的宏远显示单位
     * @param {object} secondary 当前二级显示单位
     */
    CustomReMindLabelfn: Function | null = null;
    /**
     * 生成时间轴
     * @param eleId 最外层的id 
     * @param reviewNum 要显示的天数
     * @param level 可选参数- 显示的层级
     * @returns 
     */
    constructor(eleId: string, param:any) {
        this.ParentNode = this.getById(eleId);
        
        if(!this.ParentNode) return this;
        
        this.ReviewNum = param.reviewNum ? param.reviewNum : 0;

        (param.CustomReMindLabelfn instanceof Function ) && (this.CustomReMindLabelfn = param.CustomReMindLabelfn);

        (param.level != void 0) && (this.TimeLevel = param.level);

        // 根据层级编辑展示信息数组
        this.useTimeShowTextByLevel()

        this.init();

        return this;
    }
    init() {

        // 如果不是第一次加载--先销毁先前添加的事件
        !this.isInitLoad && this.DestoryTimeLine()
        // 往父元素中添加内容
        this.ParentNode?.appendChild(this.createElement())
        // 添加时间轴底部的信息展示框
        this.MakeReMindElement()
        // 添加鼠标事件
        this.addEventMouse()
        // 开关添加点击事件
        this.SwitchAddClick();
        
        this.ParentNode && this.ElementAddProxy(this.ParentNode)
        
        // 改变是否初次加载的值,变为false
        this.isInitLoad = false;
    }
    /**
     * 按照显示级别来给时间轴设置显示文本
     */
    useTimeShowTextByLevel(level?: number) {
        let l = level ? level : this.TimeLevel;
        
        switch (l) {
            case 0:
                this.setDateTextArrayZore()
                // 一个小时有60分钟
                this.TimeSlotInterval = 60;
                break;
            case 1:
                this.setDateTextArrayOne()
                // 一天有24小时
                this.TimeSlotInterval = 24;
                break;
            case 2:
                this.setDateTextArrayTwo()
                // 一月有30天,但是每个月有特殊性
                this.TimeSlotInterval = 30;
                break;
            case 3:
                this.setDateTextArrayThree()
                // 一月有30天,但是每个月有特殊性
                this.TimeSlotInterval = 12;
                break;
            default:
                throw new Error("当前显示设置级别不合理")
                break;
        }
    }
    ElementAddProxy(target: HTMLElement) {
        if (!target) return false;
        let self = this;
        // let observer = new MutationObserver((mutationList) => {
        // 	observer.disconnect()
        // 	console.log("html元素尺寸发生变化:", mutationList)
        //     self.init()

        // })
        // observer.observe(target, { attributes: true, subtree:true,characterData:true, attributeFilter: ['style'], attributeOldValue: true })
        // // 观察目标节点的变化
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                if (mutation.attributeName === 'style') {
                    observer.disconnect()
                    self.init()
                }
            });
        });
        const config = {
            attributes: true,  //监听元素的属性有没有变化
            attributeFilter: ['style'],
            characterData: true,
        };
        observer.observe(target, config);
    }
    /**
     * @description 鼠标是否可以拖动时间轴的条件
     * @description true:允许
     * @description false:禁止
     */
    TimeMouseDownFlag:boolean=false;
    // 为时间轴添加鼠标相应的事件
    addEventMouse() {
        let time_progressbar = this.getByClass("time_progressbar")
        if(!time_progressbar) return false;
        // 因为使用addEventListener来绑定事件,所以使用bind来更改this的指向
        time_progressbar[0].addEventListener("mousedown",this.Time_MouseDown.bind(this))
        window.addEventListener("mousemove",this.WindowMouseMove_time.bind(this))
        window.addEventListener("mouseup",this.WindowMouseUp_time.bind(this))
        return true;
    }
    /**
     * window上的鼠标移动事件
     */
    WindowMouseMove_time(e:any){
        let self = this;
        if (!self.TimeMouseDownFlag) return false;
        self.updataElementStatus(e.offsetX)
    }
    /**
     * window上的鼠标松开事件
     */
    WindowMouseUp_time(e:any){
        this.TimeMouseDownFlag = false;
    }
    /**
     * 时间轴上的鼠标按下事件
     */
    Time_MouseDown(e:any){
        let self = this;
        self.TimeMouseDownFlag = true;
        self.updataElementStatus(e.offsetX)
    }
    /**
     * 销毁时间线中实例和绑定的事件
     */
    DestoryTimeLine(){
        let time_progressbar = this.getByClass("time_progressbar")
        if(!time_progressbar || time_progressbar?.length == 0) return false;
        time_progressbar[0].removeEventListener("mousedown",this.Time_MouseDown)
        window.removeEventListener("mousemove",this.WindowMouseMove_time)
        window.removeEventListener("mouseup",this.WindowMouseUp_time)
        // 清空父元素
        this.ParentNode && (this.ParentNode.innerHTML = "");
    }
    // 给时间轴的开关添加点击事件
    SwitchAddClick() {
        let self = this;
        let time_switch = this.getByClass("time_switch")[0];
        if (!time_switch) {
            setTimeout(() => {
                self.SwitchAddClick()
            }, 1000)
            return false;
        }
        if (time_switch == null) return false;
        let autoClickStatus = true
        time_switch.onclick = function () {
            self.autoUpdateTimeStatus(autoClickStatus);
            self.autoPlayClickStatus instanceof Function && self.autoPlayClickStatus(autoClickStatus, time_switch)
            autoClickStatus = !autoClickStatus;
        }
        return true;
    }
    // 自动播放的定时器id,方便清除定时器
    _setInterval: any;
    /**
     * 自动修改时间轴状态
     * @param flag 
     */
    autoUpdateTimeStatus(flag: boolean) {
        let self = this;

        if (flag) {

            // 某个时间段内的进一步的值
            let IntervalWidth = this.TimeSlotIntervalWidth;
            if (this.TimeLevel == 2) {
                let curr_monthDay = this.getDaysByMonth(this.CurrentDay);

                IntervalWidth = curr_monthDay / this.TimeSlotWidth;
            }
            let len = self.CurrentTimeProgress + self.TimeSlotIntervalWidth;
            let totalLen = self.TimeSlotWidth * self.ReviewNum;
            if (len >= totalLen) len = 0;

            self.updataElementStatus(len)

            self._setInterval && clearInterval(self._setInterval);
            self._setInterval = setInterval(() => {
                self.autoUpdateTimeStatus(flag);
            }, self.TimeMoveSpeed)
        } else {
            clearInterval(self._setInterval)
        }
    }
    /**
     * 修改时间轴的位置-进度
     * @param PosiX 
     * @returns 
     */
    updataElementStatus(PosiX: number) {
        if (this.t_progressbar == null || this.time_progressbar_tooltip == null) return false;
        let offsetX = PosiX;
        this.CurrentTimeProgress = offsetX;
        this.t_progressbar.style.cssText = `width:${offsetX}px;`
        this.updataShowTooltip(PosiX)
    }
    /**
     * 定时器的id,
     * 控制tip元素的消失
     */
    _setTimeoutId: any;
    /**
     * 更改tip元素样式
     * @param PosiX
     * @returns 
     */
    updataShowTooltip(PosiX: number) {

        let self = this;

        let offsetX = PosiX;

        if (this.time_progressbar_tooltip == null) return false;

        let rect = self.getDateByOffsetX(offsetX);

        this.time_progressbar_tooltip.innerHTML = (self.setTooltipElementFn instanceof Function && self.setTooltipElementFn(rect)) || `
            周:${rect.week}<br />
            日期:${rect.day}<br />
            时间:${rect.date}
        `

        let tooltip_rect = this.getElementRectById("time_progressbar_tooltip");

        this.time_progressbar_tooltip.style.cssText = `z-index:999;opacity:1;left:${offsetX}px;top:-${(tooltip_rect.height) - 10}px`

        if (this._setTimeoutId != 0) clearTimeout(self._setTimeoutId);

        this._setTimeoutId = setTimeout(() => {
            clearTimeout(self._setTimeoutId)
            if (self.time_progressbar_tooltip == null) return false;
            self.time_progressbar_tooltip.style.cssText = "opacity:0,z-index:-1;"
        }, 2000)
    }
    /**
     * @description 将长度转换为时间
     * @description 本长度的起点是指时间线0点距离,不是屏幕的0点
     */
    getDateByOffsetX(len: number) {
        // 跨越的时间段
        let timeSolt = Math.floor(len / this.TimeSlotWidth);
        // 剩余的时间段距离
        let _timeSolt = Math.floor(len % this.TimeSlotWidth);

        let macro = this.MacroTextArray[Math.floor(timeSolt)];

        let day = this.DateTextArray[timeSolt] + 1;

        // 记录下当前走到哪里了
        this.CurrentDay = day;
        // 具体时间
        let date = Math.floor(_timeSolt / this.TimeSlotIntervalWidth);

        // 如果时间轴的层级是2,则因为月份会有不同,因此要使用另一种计算方法
        if (this.TimeLevel == 2) {

            let a = this.getDaysByMonth(this.DateTextArray[Math.floor(timeSolt)])
            let b = a / this.TimeSlotWidth;

            date = (Math.ceil(_timeSolt * b) % a) + 1;
        }
        return {
            macro,
            day,
            date
        }
    }
    /**
     * 添加时间提示dom
     * @returns 
     */
    MakeReMindElement() {
        let rect = this.getElementRectById("time_content");

        if (rect == null) return false;

        this.TimeSlotWidth = Math.floor(rect?.width / this.ReviewNum);

        this.TimeSlotIntervalWidth = Number((this.TimeSlotWidth / this.TimeSlotInterval).toFixed(2))

        let time_progressbar_remind = this.getByClass("time_progressbar_remind")[0];

        for (let i = 0; i < this.ReviewNum; i++) {
            let span = document.createElement("span");
            span.innerText = (this.CustomReMindLabelfn instanceof Function) ? 
                            this.CustomReMindLabelfn(this.MacroTextArray[i], this.DateTextArray[i]) : 
                            this.MacroTextArray[i] + this.UnitArray[this.TimeLevel] + (this.DateTextArray[i] ? this.DateTextArray[i] : '')
            span.style.cssText = `width:${this.TimeSlotWidth}px;`
            time_progressbar_remind?.appendChild(span)
        }
        return true
    }
    /**
     * 设置显示的时间文本数组
     * 0级
     */
    setDateTextArrayZore() {

        let date = new Date();
        // 月份
        let month = date.getMonth() + 1;
        // 小时
        let hours = date.getHours();

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(month + '')
            this.DateTextArray.push(hours)
            hours += 1;
            if (hours == 24) {
                month++
                hours = 0;
            }
        }

    }
    /**
     * 设置显示的时间文本数组
     * 1级
     */
    setDateTextArrayOne() {

        let date = new Date();
        // 周几
        let week = date.getDay();
        // 几号 ,因为下面会+1 因此这里要 -1
        let day = date.getDate() - 1;
        // 月份
        let month = date.getMonth() + 1;
        // 本月的天数
        let monthDay = this.getDaysByMonth(month);
        let _monthDay = 0;

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(((week + i) % 7) + '')

            _monthDay++;
            // 避免月份出错
            if (_monthDay > monthDay) {
                month++
                monthDay = this.getDaysByMonth(month);
                _monthDay = 0;
            }
            this.DateTextArray.push(((day + i) % (monthDay)) + 1)
        }
    }
    /**
     * 设置显示的时间文本数组
     * 2级
     */
    setDateTextArrayTwo() {
        let date = new Date();
        // 月份
        let month = date.getMonth() + 1;
        // 年份
        let year = date.getFullYear();

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(year + '')
            this.DateTextArray.push(month)
            month += 1
            if (month == 13) {
                year++
                month = 1
            }
        }
    }
    /**
     * 设置显示的时间文本数组
     * 3级
     */
    setDateTextArrayThree() {
        let date = new Date();
        // 年份
        let year = date.getFullYear();

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(year + '')
            this.DateTextArray.push(0)
            year++
        }

    }
    /**
     * 通过月份来返回本月的时间
     * @param month 月份
     * @returns 本月的时间
     */
    getDaysByMonth(month: number) {
        let a = [1, 3, 5, 7, 8, 10, 12];
        let b = [2]
        if (a.includes(month)) {
            return 31
        } else if (b.includes(month)) {
            return 28
        } else {
            return 30
        }
    }
    // 创建指定的元素
    createElement() {
        // 最外层的元素
        let timeLine = document.createElement("div");
        timeLine.classList.add('timeLine')
        // 控制时间轴的开关
        let time_switch = document.createElement("div");
        time_switch.classList.add('time_switch')
        // 时间轴线的外层元素
        let time_content = document.createElement("div");
        time_content.classList.add('time_content')
        // 最下面的问题提示元素--周一--周二
        let time_progressbar_remind = document.createElement("div");
        time_progressbar_remind.classList.add('time_progressbar_remind')
        // 时间轴与鼠标交互时的元素
        this.time_progressbar_tooltip = document.createElement("div");
        this.time_progressbar_tooltip.classList.add('time_progressbar_tooltip')
        // 时间轴线
        let time_progressbar = document.createElement("div");
        time_progressbar.classList.add('time_progressbar')
        // 覆盖在时间轴元素上方
        this.t_progressbar = document.createElement("div");
        this.t_progressbar.classList.add('t_progressbar')

        timeLine.appendChild(time_switch)
        timeLine.appendChild(time_content)
        time_content.appendChild(this.time_progressbar_tooltip)
        time_content.appendChild(time_progressbar)
        time_content.appendChild(time_progressbar_remind)
        time_progressbar.appendChild(this.t_progressbar)

        return timeLine;
    }
    /**
     * 通过id查找元素
     * @param id 
     * @param flag 是否往时间轴最外层元素中找元素
     * @returns 
     */
    getById(id: string,flag=true) {
        let n = flag ? document : this.ParentNode;
        return n && n.getElementById(id);
    }
    /**
     * 通过class查找元素
     * @param className 
     * @returns 
     */
    getByClass(className: string) {
        if(this.ParentNode==null) return null;
        return this.ParentNode.getElementsByClassName(className);
    }
    /**
     * 通过id获取元素的具体信息
     * @param id 
     * @returns 
     */
    getElementRectById(id: string) {
        let e = this.getByClass(id);
        return e && e[0].getBoundingClientRect();
    }
}

3、调用方式

使用import来引入相应的ts方法,创建一个最外层的盒子,定义好相应的样式,具体使用方式如下:

<div id="wtimeLine"></div>
#wtimeLine{
  width: 80%;
  height: 50px;
  position: absolute;
  left: 10%;
  bottom: 50px;
}
let timeLine:useTimeLine;
onMounted(async () => {

  timeLine = new useTimeLine("wtimeLine",{
    reviewNum:10,
    CustomReMindLabelfn:(macro:any,secondary:any)=>{
      // 自定义展示标签
      return "周"+(Number(macro)+1)+'--'+secondary+"号"
    }
  })

  // 设置跟随元素的样式和显示内容
  timeLine.setTooltipElementFn = (e:any)=>{
    return `
      <div class='too' style='width:100px'>
        ${e.date}--${e.macro}--${e.day-1}
      </div>
    `
  }
})
 最后总结

该实例在使用和业务构思时可能存在不足,在功能上可能也会存在缺陷,但目前还未发现,我会持续改进,也非常欢迎有建议的伙伴们来提出有意义的建议,我们共同进步。

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vite是一个用于快速构建现代化的Web项目的构建工具,它专注于开发阶段的快速热重载,并使用ES模块作为原生的开发模式。Vue3是Vue.js的最新版本,它在性能、开发体验和可维护性上都有所提升。 针对你提到的具体的库和框架: - Vue Router是Vue.js官方的路由管理器,用于实现页面之间的导航和路由控制。你可以通过npm安装vue-router,并在项目中进行配置和使用。 - Pinia是Vue.js的状态管理库,它提供了一种简单而强大的方式来管理应用程序的状态。你可以集成Pinia到你的Vue项目中,以便更好地组织和共享你的应用程序状态。 - Axios是一个基于Promise的HTTP客户端,用于通过网络发送异步请求。你可以使用Axios来处理与服务器的通信,并获取数据来更新你的Vue应用程序。 - Element Plus是一套基于Vue3的组件库,包含了丰富的UI组件,可以帮助你快速搭建漂亮的用户界面。你可以在项目中安装和使用Element Plus来实现各种交互效果和用户界面。 如果你想使用Vite、Vue3和以上提到的库和框架来创建一个项目,你可以按照以下步骤进行: 1. 安装Vite:通过npm全局安装Vite,然后使用Vite命令初始化一个新的项目。 2. 配置Vite:根据你的项目需求,在Vite的配置文件中添加Vue Router、Pinia、Axios和Element Plus的相关配置。 3. 安装和配置Vue Router:通过npm安装Vue Router,并在项目中配置和使用Vue Router来管理应用程序的路由。 4. 集成Pinia:通过npm安装Pinia,并在项目中引入和配置Pinia,以便在应用程序中使用Pinia来管理状态。 5. 使用Axios:通过npm安装Axios,并在项目中引入和配置Axios,以便进行网络请求和数据获取。 6. 引入Element Plus:通过npm安装Element Plus,并在项目中按需引入和使用Element Plus的组件,以搭建漂亮的用户界面。 希望以上信息对你有帮助,祝你在使用Vite、Vue3和这些库和框架时取得成功!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vite-vue-ts精简模版集成pinia+svg+router+@src](https://download.csdn.net/download/ldy889/85018930)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [vite+vue3+ts+vue-router+pinia+axios+element-plus](https://blog.csdn.net/zy_080400/article/details/127125359)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值