時間軸版本-2.0

文章簡述

這是本人自己封裝的時間軸2.0版本的代碼,用到了TypeScript+JavaScript

這篇文章只有代碼和具體的使用方式,如果想看具體的講解可以參考本人寫的時間軸1.0版本的,在1.0版本中可能計算時間線的邏輯略有不同,但是大致的計算邏輯都一樣
時間軸1.0

主要內容
具體代碼
/*** 時間軸*/
type TimeLineParamsType = {reviewNum: number,MouseDownfn: Function,MouseUpfn: Function,CustomReMindLabelfn: Function,level: number,stepLength: number,timeSwitchContent: Function,CanvasConfigParams: Partial<CanvasConfigParamsType>,baseDate: Date,isCanvas: boolean
}
type GradientModeType = 'linear' | 'radial' | 'conic'
type CanvasConfigParamsType = {fontSize: string,scale_fontColor: string,scale_strokeColor: string,scale_lineWidth: number,scale_lineLength: number,tl_Height: number,tl_strokeColor: string,tl_strokeWidth: number,tl_fillColor: string,pg_fillColor: string,pg_gradientMode: GradientModeType,pg_gradientColors: string[],isGradient: boolean,tl_radius: number
}export class useTimeLine {/*** 基時間* @description 如果沒有則使用當前時間 */baseDate: Date | null = null/*** 時間數組*/DateArray: string[] = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]/*** 記錄單位的數組 ["號","周","月","年"]*/UnitArray: string[] = ["號", "周", "年", "年"]/*** 是否使用吸附效果*/isAdsorb = false/*** 記錄是否是初次加載 默認:是*/#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;/*** 時間軸的步長*/TimeStepLength = 1;/*** 時間軸移動的速度*/TimeMoveSpeed: number = 2000;/*** 保存當前的移動進度*/CurrentDay: number = 0;/*** 要顯示的宏觀文本數組--就是時間軸中最高級的顯示單位*/MacroTextArray: string[] = []/*** 要顯示的微觀數組*/DateTextArray: number[] = []/*** 當前的canvas對象*/_CanvasContent: CanvasRenderingContext2D | null = null/*** 是否使用canvas做時間軸*/isCanvas = false;/*** 計算進度條時左邊距-不用動*/#CanvasMarginLeft = 60;/*** 時間軸的路徑對象*/TimeLinePath: Path2D | null = null;/*** 參數配置對象*  fontSize: 時間軸下方提示標尺的字體大小,*  scale_fontColor: 時間軸下方提示標尺的字體顏色scale_strokeColor: 標尺線的顏色,scale_lineWidth: 標尺線的寬度,scale_lineLength: 標尺線的高度,tl_Height: 時間軸的高度--進度條背景板,tl_strokeColor: 繪制時間軸時的線顏色 - 此屬性必須配合tl_strokeWidth屬性一起使用,tl_strokeWidth: 繪制時間軸時的線寬,tl_fillColor: 繪制時間軸時的填充顏色,pg_fillColor: 進度條的顏色pg_gradientMode: 進度條的漸變色的模式,pg_gradientColors: ['#cd1919', '#1977cd', '#f70da2'],isGradient: 是否使用漸變色tl_radius: 時間軸的邊緣弧度*/CanvasConfigParams: CanvasConfigParamsType = {fontSize: '',scale_fontColor: '#fff',scale_strokeColor: '',scale_lineWidth: 0,scale_lineLength: 5,tl_Height: 10,tl_strokeColor: '',tl_strokeWidth: 0,tl_fillColor: '',pg_fillColor: '',pg_gradientMode: 'radial',pg_gradientColors: ['#cd1919', '#1977cd', '#f70da2'],isGradient: false,tl_radius: 20}/*** 時間軸與鼠標交互時的元素*/time_progressbar_tooltip: HTMLElement | null = null;/*** 覆蓋在時間軸元素上方*/t_progressbar: HTMLElement | null = null;/*** 自動播放按鈕的點擊狀態回調* @param {*} flag  點擊狀態 播放:true 停止:false* @param {*} target 當前的元素*/autoPlayClickStatus: Function | null = null;/*** 點擊的狀態*/autoClickStatus: boolean = true;/*** 往自動播放按鈕元素中添加元素的方法*/timeSwitchContent: 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;/*** 鼠標點下的回調方法*/MouseDownfn: Function | null = null;/*** 鼠標松開的回調方法*/MouseUpfn: Function | null = null/*** 生成時間軸* @param eleId 最外層的id * @param reviewNum 要顯示的天數* @param level 可選參數- 顯示的層級* @returns useTimeLine*/constructor(eleId: string, params: Partial<TimeLineParamsType>) {this.ParentNode = this.getById(eleId);if (!this.ParentNode) return this;this.init(params);return this;}init(params?: Partial<TimeLineParamsType>) {this.setGlobalConfigs(params)// 根據層級編輯展示信息數組this.useTimeShowTextByLevel()// 如果不是第一次加載--先銷毀先前添加的事件this.DestoryTimeLine(this.#isInitLoad)// 往父元素中添加內容this.ParentNode?.appendChild(this.createElement() as HTMLDivElement)// 添加時間軸底部的信息展示框this.MakeReMindElement()// 添加鼠標事件this.addEventMouse()// 開關添加點擊事件this.SwitchAddClick();this.ParentNode && this.ElementAddProxy(this.ParentNode)// 改變是否初次加載的值,變為falsethis.#isInitLoad = false;}/*** @param {*} params*/setGlobalConfigs(params?: Partial<TimeLineParamsType>) {if (!params) return false;let { reviewNum, MouseDownfn, MouseUpfn, CustomReMindLabelfn, level, stepLength, timeSwitchContent, CanvasConfigParams, baseDate, isCanvas } = params;this.ReviewNum = reviewNum || this.ReviewNum;this.TimeLevel = level !== void 0 ? level : this.TimeLevelthis.TimeStepLength = stepLength || this.TimeStepLengththis.CustomReMindLabelfn = CustomReMindLabelfn instanceof Function ? CustomReMindLabelfn : nullthis.timeSwitchContent = timeSwitchContent instanceof Function ? timeSwitchContent : null;this.MouseDownfn = MouseDownfn instanceof Function ? MouseDownfn : null;this.MouseUpfn = MouseUpfn instanceof Function ? MouseUpfn : null;this.CanvasConfigParams = { ...this.CanvasConfigParams, ...CanvasConfigParams }this.baseDate = baseDate || this.baseDate || new Date();this.isCanvas = typeof isCanvas === "boolean" ? isCanvas : this.isCanvas || 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 = false;// 為時間軸添加鼠標相應的事件addEventMouse() {let time_progressbar = this.getByClass("TimeLineProgressbar")if (!time_progressbar || time_progressbar.length == 0) return false;// 因為使用addEventListener來綁定事件,所以使用bind來更改this的指向(time_progressbar[0] as HTMLElement).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: MouseEvent) {let self = this;if (!self.TimeMouseDownFlag) return false;e.preventDefault()this.#currentTargetOffset && self.updataElementStatus(e.pageX - this.#currentTargetOffset.left)}/*** window上的鼠標松開事件*/WindowMouseUp_time(e: MouseEvent) {this.MouseUpfn && this.MouseUpfn(e)this.TimeMouseDownFlag = false;}#currentTargetOffset: { width: number, height: number, top: number, left: number } | null = null;/*** 時間軸上的鼠標按下事件*/Time_MouseDown(e: MouseEvent) {let self = this;// 判斷點擊位置是否在路徑內if (!this.isPointInPath(this.TimeLinePath, e.offsetX, e.offsetY)) return false;this.MouseDownfn && this.MouseDownfn(e)self.TimeMouseDownFlag = true;self.#currentTargetOffset = (e.currentTarget as HTMLElement)?.getBoundingClientRect();e.preventDefault()self.updataElementStatus(e.offsetX)}// 給時間軸的開關添加點擊事件SwitchAddClick() {let self = this;let time_switch = this.getByClass("time_switch")[0] as HTMLDivElement;if (!time_switch) {setTimeout(() => {self.SwitchAddClick()}, 1000)return false;}if (time_switch == null) return false;time_switch.addEventListener("click", this.timeSwitchClickHandle.bind(this))return true;}timeSwitchClickHandle(e: MouseEvent) {this.autoUpdateTimeStatus(this.autoClickStatus);this.autoPlayClickStatus instanceof Function && this.autoPlayClickStatus(this.autoClickStatus, e.currentTarget, e)this.autoClickStatus = !this.autoClickStatus;}/*** 創建指定的元素*/createElement() {if (!this.ParentNode) return false;let { width, height } = this.ParentNode.getBoundingClientRect();// 最外層的元素let timeLine = document.createElement("div");timeLine.classList.add('timeLine')// 控制時間軸的開關let time_switch = document.createElement("div");time_switch.classList.add('time_switch')this.timeSwitchContent && (time_switch.innerHTML = this.timeSwitchContent(time_switch))timeLine.appendChild(time_switch)// 時間軸線的外層元素let time_content = document.createElement("div");time_content.classList.add('time_content')!this.isCanvas && time_content.classList.add('time_content_flex')timeLine.appendChild(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;if (this.isCanvas) {time_progressbar = document.createElement("canvas");this._CanvasContent = time_progressbar.getContext("2d");time_progressbar.classList.add('TimeLineProgressbar')time_progressbar.width = width - 60;time_progressbar.height = height;this.drawTimeLineByCanvas();} else {time_progressbar = document.createElement("div");time_progressbar.classList.add('time_progressbar')time_progressbar.classList.add('TimeLineProgressbar')// 覆蓋在時間軸元素上方this.t_progressbar = document.createElement("div");this.t_progressbar.classList.add('t_progressbar')time_progressbar.appendChild(this.t_progressbar)}time_content.appendChild(this.time_progressbar_tooltip)time_content.appendChild(time_progressbar)time_content.appendChild(time_progressbar_remind)return timeLine;}/*** 繪制canvas時間軸*/drawTimeLineByCanvas(progress = 0) {let { tl_Height, tl_strokeColor, tl_strokeWidth, tl_radius, tl_fillColor, pg_fillColor, isGradient } = this.CanvasConfigParams;// 繪制進度條if (!this._CanvasContent || !this.ParentNode) return false;let { width, height } = this.ParentNode.getBoundingClientRect();width = Math.floor(width - this.#CanvasMarginLeft);this.TimeLinePath = new Path2D();let roundLeft = 0;let ctx = this._CanvasContent;ctx.clearRect(roundLeft, (height / 2) - tl_Height / 2, width, tl_Height)ctx.save()ctx.beginPath();ctx.strokeStyle = tl_strokeColor || "red"ctx.lineWidth = tl_strokeWidth || 0;ctx.fillStyle = tl_fillColor || "#666"this.TimeLinePath.roundRect(roundLeft, (height / 2) - tl_Height / 2, width, tl_Height, tl_radius)ctx.closePath();ctx.fill(this.TimeLinePath);tl_strokeWidth && ctx.stroke(this.TimeLinePath);ctx.restore()// 繪制進度ctx.save()ctx.fillStyle = isGradient ? this.createGradient(roundLeft, (height / 2) - tl_Height / 2, progress, tl_Height) as CanvasGradient : pg_fillColor || "red"ctx.beginPath();ctx.roundRect(roundLeft, (height / 2) - tl_Height / 2, progress, tl_Height, tl_radius)ctx.fill();ctx.restore()}/*** 添加時間提示dom* @returns */MakeReMindElement() {if (this.isCanvas) {this.#_MakeScaleCanvas()} else {this.#_MakeScaleElement()}}#_MakeScaleCanvas() {if (!this._CanvasContent || !this.ParentNode) return false;let ctx = this._CanvasContent;let { fontSize, scale_fontColor, scale_strokeColor, tl_Height, scale_lineWidth, scale_lineLength } = this.CanvasConfigParams;let { width, height } = this.ParentNode.getBoundingClientRect();width = width - this.#CanvasMarginLeft;// 繪制標尺this.TimeSlotWidth = width / this.ReviewNum;this.TimeSlotIntervalWidth = Number((this.TimeSlotWidth / this.TimeSlotInterval).toFixed(2))ctx.save()ctx.font = `${fontSize}px 微軟雅黑`;for (let i = 0; i < this.ReviewNum; i++) {let x = (i * this.TimeSlotWidth * 1);let y = height / 2;ctx.strokeStyle = scale_strokeColor || "red"ctx.lineWidth = scale_lineWidth || 2ctx.beginPath();ctx.moveTo(x, y + tl_Height / 2)ctx.lineTo(x, y + tl_Height / 2 + scale_lineLength)let text = (this.CustomReMindLabelfn instanceof Function) ?this.CustomReMindLabelfn(this.MacroTextArray[i], this.DateTextArray[i]) :this.UnitArray[this.TimeLevel] + (Number(this.MacroTextArray[i]) + 1) + (this.DateTextArray[i] ? this.DateTextArray[i] : '')let { width: mt_width } = ctx.measureText(text)ctx.fillStyle = scale_fontColor || "#000"ctx.fillText(text, Math.max(0, x - mt_width / 2) && Math.min(width, x - mt_width / 2), y + 20)ctx.stroke();}ctx.restore()}#_MakeScaleElement() {let rect = this.getElementRectById("time_content");if (rect == null) return false;this.TimeSlotWidth = 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.UnitArray[this.TimeLevel] + (Number(this.MacroTextArray[i]) + 1) + (this.DateTextArray[i] ? this.DateTextArray[i] : '')span.style.cssText = `width:${this.TimeSlotWidth}px;`time_progressbar_remind?.appendChild(span)}}// 自動播放的定時器id,方便清除定時器_setInterval: number = -1;/*** 自動修改時間軸狀態* @param flag */autoUpdateTimeStatus(flag: boolean) {let self = this;if (flag) {// 某個時間段內的進一步的值let IntervalWidth = this.TimeSlotIntervalWidth * this.TimeStepLength;if (this.TimeLevel == 2) {let curr_monthDay = this.getDaysByMonth(this.CurrentDay);IntervalWidth = curr_monthDay / this.TimeSlotWidth;}let len = self.CurrentTimeProgress + IntervalWidth;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(progress: number) {let _progress = progress;this.CurrentTimeProgress = _progress;this.updataShowTooltip(_progress)}/*** 定時器的id,* 控制tip元素的消失*/_setTimeoutId: number = -1;/*** 更改tip元素樣式* @param progress* @returns */updataShowTooltip(progress: number) {let self = this;let _progress = progress;if (this.time_progressbar_tooltip == null) return false;let rect = self.getDateByOffsetX(_progress);let { isNotAchieve, length } = this.getLengthByDate(rect);if (isNotAchieve && this.isAdsorb) rect = self.getDateByOffsetX(length)this.time_progressbar_tooltip.innerHTML = (self.setTooltipElementFn instanceof Function && self.setTooltipElementFn(rect)) || `日期:${rect.day - 1}<br />時間:${rect.date}`let tooltip_rect = this.getElementRectById("time_progressbar_tooltip");if(this.isCanvas){this.drawTimeLineByCanvas(length);}else{if (this.t_progressbar) this.t_progressbar.style.cssText = `width:${length}px;`}(this.time_progressbar_tooltip).style.cssText = `z-index:999;width:7%;opacity:1;left:${length}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點* @param {*} offsetX*/getDateByOffsetX(offsetX: number) {// 跨越的時間段let timeSolt = Math.floor(offsetX / this.TimeSlotWidth);// 剩余的時間段距離let _timeSolt = offsetX % 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}}/*** 將時間轉換為長度*/getLengthByDate({ macro, day, date }: { macro: string, day: number, date: number }) {// 計算宏觀時間的總長度let macroTotal = 0;const _day = Number(day) - 1;// 計算在時間數組中的位置let timeSlotIndex = 0;for(let i = 0; i < this.DateTextArray.length; i++) {if(this.DateTextArray[i] === _day && this.MacroTextArray[i] === String(macro)) {timeSlotIndex = i;break;}}// 計算宏觀長度macroTotal = timeSlotIndex * this.TimeSlotWidth;// 計算具體時間的長度let timeTotal = 0;switch(this.TimeLevel) {case 0: // 分鐘timeTotal = (date / 60) * this.TimeSlotWidth;break;case 1: // 小時timeTotal = (date / 24) * this.TimeSlotWidth;break;case 2: // 天const daysInMonth = this.getDaysByMonth(_day);timeTotal = (date / daysInMonth) * this.TimeSlotWidth;break;case 3: // 月timeTotal = (date / 12) * this.TimeSlotWidth;break;}// 計算總長度const totalLength = macroTotal + timeTotal;// 處理吸附效果if(this.isAdsorb) {const stepWidth = this.TimeSlotIntervalWidth * this.TimeStepLength;const steps = Math.round(totalLength / stepWidth);const snapLength = steps * stepWidth;return {isNotAchieve: Math.abs(totalLength - snapLength) > 1,length: snapLength};}return {isNotAchieve: false,length: totalLength};}/*** 設置顯示的時間文本數組* 0級*/setDateTextArrayZore() {let date = this.baseDate || 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 = this.baseDate || new Date();// 周幾let week = date.getDay();// 幾號 ,因為下面會+1 因此這里要 -1let 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 = this.baseDate || 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 += 1if (month == 13) {year++month = 1}}}/*** 設置顯示的時間文本數組* 3級*/setDateTextArrayThree() {let date = this.baseDate || 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 this.#isLeapYear(this.baseDate?.getFullYear()) ? 29 : 28} else {return 30}}/*** 判斷當前是否是閏年* @param year * @returns */#isLeapYear(year: number | undefined) {if (!year) return false;let _year = Number(year)return (_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0) ? true : false}/*** 判斷點是否在指定路徑內*/isPointInPath(...args: any[]) {if (!this.isCanvas || !this._CanvasContent) return true;let [path, x, y] = args;if (path instanceof Path2D) {return this._CanvasContent.isPointInPath(path, x, y);} else {return this._CanvasContent.isPointInPath(args[0], args[1]);}}/*** 生成漸變色* @param x0 起始點x* @param y0 起始點y* @param x1 結束點x* @param y1 結束點y* @returns */createGradient(x0: number, y0: number, x1: number, y1: number) {let { pg_gradientMode, pg_gradientColors } = this.CanvasConfigParams;let gradient: CanvasGradient | null = this.makeGridentObjectByMode(pg_gradientMode, x0, y0, x1, y1);if (!gradient) return ''let average = 1 / pg_gradientColors.length;pg_gradientColors.forEach((item, index) => {gradient.addColorStop(average * index, item)})return gradient;}/*** 根據模式返回相關的漸變色對象*/makeGridentObjectByMode(mode: GradientModeType, ...args: any[]): CanvasGradient | null {let [x0, y0, x1, y1] = args;let canvasGridentMode: GradientModeType[] = ['linear', 'radial', 'conic']if (!canvasGridentMode.includes(mode)) throw new Error(`漸變的模式必須是${canvasGridentMode.join(',')}的其中一種`)if (!this._CanvasContent) return null;let ctx = this._CanvasContent;// 計算中心點let center = {x: (x1 - x0) / 2,y: (y1 - y0) / 2}let result = null;switch (mode) {case "linear":result = ctx.createLinearGradient(x0, y0, x1, y1)case 'radial':result = ctx.createRadialGradient(x0, y0, 20, x1, y1, center.x)case 'conic':result = ctx.createConicGradient(0, center.x, center.y)}return result;}/*** 通過id查找元素* @param id * @param flag 是否往時間軸最外層元素中找元素* @returns */getById(id: string, flag = true) {let n = flag ? document : this.ParentNode;return n && (n as Document).getElementById(id);}/*** 通過class查找元素* @param className * @returns */getByClass(className: string) {if (this.ParentNode == null) return [];return this.ParentNode.getElementsByClassName(className);}/*** 通過id獲取元素的具體信息* @param id * @returns */getElementRectById(id: string) {let e = this.getByClass(id);return e && e[0].getBoundingClientRect();}/*** 銷毀時間線中實例和綁定的事件*/DestoryTimeLine(flag?: boolean) {if (flag) return false;let time_progressbar = this.getByClass("time_progressbar")time_progressbar[0] && (time_progressbar[0] as HTMLElement).removeEventListener("mousedown", this.Time_MouseDown)window.removeEventListener("mousemove", this.WindowMouseMove_time)window.removeEventListener("mouseup", this.WindowMouseUp_time)// 刪除自動播放開關的事件let time_switch = this.getByClass("time_switch")[0];(time_switch as HTMLDivElement).removeEventListener("click", this.timeSwitchClickHandle)this.setTooltipElementFn = null;// 將播放標識恢復初始化this.autoClickStatus = true;this.autoUpdateTimeStatus(false);// 將進度歸零this.CurrentTimeProgress = 0;// 清空父元素if (this.ParentNode) this.ParentNode.innerHTML = "";}
}
使用方式
// 引入時間軸組件代碼
import { useTimeLine } from '@/hook/TimeLine'// 使用方式
new useTimeLine(dom || 'id名',options)
options配置項
options = {reviewNum:'要顯示多少天或時間的內容',isCanvas:'是否用canvas來繪制,默認false',level:'當前的層級,0-3級,默認:1',isAdsorb:'是否使用吸附效果,當我們的步長不為1時,當前效果可用,默認:false',TimeStepLength:'時間軸的步長,有時候需要和isAdsorb參數一起使用,默認:1',baseDate:'配置基本事件,從什么時間開始計算,默認當前時間',TimeMoveSpeed:'時間軸移動的速度,單位是(秒),默認2000',CanvasConfigParams:'如果使用canvas來渲染時,通過此參數可以配置其樣式'}CanvasConfigParams = {fontSize: '時間軸下方提示標尺的字體大小',scale_fontColor: '時間軸下方提示標尺的字體顏色',scale_strokeColor: '標尺線的顏色',scale_lineWidth: '標尺線的寬度',scale_lineLength: '標尺線的高度',tl_Height: '時間軸的高度--進度條背景板',tl_strokeColor: '繪制時間軸時的線顏色 - 此屬性必須配合tl_strokeWidth屬性一起使用',tl_strokeWidth: '繪制時間軸時的線寬',tl_fillColor: '繪制時間軸時的填充顏色',pg_fillColor: '進度條的顏色',pg_gradientMode: '進度條的漸變色的模式',pg_gradientColors: ['#cd1919', '#1977cd', '#f70da2'],isGradient: '是否使用漸變色',tl_radius: '時間軸的邊緣弧度'
}
時間軸的回調方法
autoPlayClickStatus: 自動播放按鈕的點擊狀態回調,需要一個方法,參數是點擊狀態 播放:true 停止:false
// 使用方式:useTime_xxx.autoPlayClickStatus = (flag:boolean)=>{}timeSwitchContent:往自動播放按鈕中添加元素,當前組件自帶的自動播放按鈕是白色圓形,比較丑,可用用此方法重新添加元素
// 使用方式 useTime_xxx.timeSwitchContent = ()=>{ return <div>播放按鈕</div>}setTooltipElementFn:當時間軸的長度發生變化時彈出的提示框,可通過此方法修改彈出內容
// 使用方式 useTime_xxx.setTooltipElementFn = ({ day, macro, date })=>{ return `<div>當前時間是${day - 1}</div>`}CustomReMindLabelfn:時間軸底部顯示的內容
// 使用方式 useTime_xxx.CustomReMindLabelfn = (o1, o2)=>{ return o2}MouseDownfn:鼠標點擊時間軸時的回調MouseUpfn:鼠標按下的回調
簡單使用示例
// 準備一個容器
<div id="timeline"></div>
// JavaScript代碼
let useTime = new useTimeLine('timeline',{reviewNum:24,isCanvas:false,level:0,CustomReMindLabelfn: (o1, o2) => {// 時間軸底部要顯示的內容return o2 % len === 0 ? `${o2}` : ''}
})/*** 時間軸彈出框的顯示內容* { day, macro, date } 這些值會根據層級配置的不同返回不同可用的數值*/
useTime.setTooltipElementFn = function ({ day, macro, date }) {let _baseDate = new Date();_baseDate.setMonth(_baseDate.getMonth() + Number(macro) - 1)return `${macro}${day - 1}點:${date}`;
}
文章感想

當前的組件只是本人用自己所學所用的知識編寫的,主要是用來給自己做個筆記,等用到時可以直接拿來使用。功用性可能并不是很好,所以如果有哪位同行想使用這段代碼插件,并發現問題請和我溝通,我會立刻出來更改,提前感謝各位大佬的指導

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/73761.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/73761.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/73761.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大語言模型的壓縮技術

盡管人們對越來越大的語言模型一直很感興趣&#xff0c;但MistralAI 向我們表明&#xff0c;規模只是相對而言的&#xff0c;而對邊緣計算日益增長的興趣促使我們使用小型語言獲得不錯的結果。壓縮技術提供了一種替代方法。在本文中&#xff0c;我將解釋這些技術&#xff0c;并…

大華HTTP協議在智聯視頻超融合平臺中的接入方法

一. 大華HTTP協議介紹 大華HTTP協議是大華股份&#xff08;Dahua Technology&#xff09;為其安防監控設備開發的一套基于HTTP/HTTPS的通信協議&#xff0c;主要用于設備與客戶端&#xff08;如PC、手機、服務器&#xff09;之間的數據交互。該協議支持設備管理、視頻流獲取、…

Linux內核實時機制28 - RT調度器11 - RT 組調度

Linux內核實時機制28 - RT調度器11 - RT 組調度 相關數據結構 內核中通過static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq)函數來判斷實時任務運行時間是否超出帶寬限制,判斷這個運行隊列rt_rq的運行時間是否超過了額定的運行時間。而“運行時間”和“額定時間”都…

java,poi,提取ppt文件中的文字內容

注意&#xff0c;不涉及圖片處理。 先上pom依賴&#xff1a; <!-- 處理PPTX文件 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency><!--…

7、vue3做了什么

大佬認為有何優點&#xff1a; 組合式api----邏輯集中、對ts有更好的支持RFC–開放了一個討論機制&#xff0c;可以看到每一個api的提案&#xff0c;方便源碼維護&#xff0c;功能擴展&#xff0c;大家一起討論 官方rfc響應式獨立&#xff0c;new Proxy&#xff0c;天生自帶來…

多人在線聊天系統,創建群,視頻,語音,自帶帶授權碼

多人在線聊天系統&#xff0c;創建群&#xff0c;視頻&#xff0c;語音 帶授權碼&#xff0c;授權碼限制 10 個網站&#xff0c;需要下載研究吧 在線聊天&#xff0c;創建群&#xff0c;表情&#xff0c;圖片&#xff0c;文件&#xff0c;視頻&#xff0c;語音&#xff0c;自…

數據結構概覽

關鍵點&#xff1a; 數據結構是組織和存儲數據的方式&#xff0c;幫助高效訪問和操作數據。常見類型包括數組、鏈表、棧、隊列、樹和圖&#xff0c;每種都有特定用途。代碼示例和實際應用場景將幫助初學者理解這些概念。 什么是數據結構&#xff1f; 數據結構就像你整理書架或…

Android studio點擊運行按鈕在build\intermediates\apk\debug目錄下生成的apk在真機上安裝失敗,提示test only

Android studio點擊運行按鈕在build\intermediates\apk\debug目錄下生成的apk在真機上安裝失敗&#xff0c;提示test only DeepSeek R1 思考 15 秒 思考過程 針對Android Studio生成的APK在真機安裝時提示“test only”的問題&#xff0c;以下是詳細解決方案&#xff1a; 1.…

NFC 碰一碰發視頻源碼搭建,支持OEM

一、引言 NFC&#xff08;Near Field Communication&#xff09;近場通信技術&#xff0c;以其便捷、快速的數據交互特性&#xff0c;正廣泛應用于各個領域。其中&#xff0c;NFC 碰一碰發視頻這一應用場景&#xff0c;為用戶帶來了新穎且高效的視頻分享體驗。想象一下&#x…

Python基礎語法全解析:從入門到實踐

Python作為一門簡潔高效、功能強大的編程語言&#xff0c;憑借其易讀性和豐富的生態系統&#xff0c;已成為編程領域的“明星語言”。本文將系統講解Python的核心語法&#xff0c;涵蓋變量、數據類型、控制結構、函數、模塊等核心概念&#xff0c;幫助讀者快速掌握編程基礎。 一…

TypeScript中的類型斷言(type assertion),如何使用類型斷言進行類型轉換?

一、什么是類型斷言&#xff1f; 類型斷言&#xff08;Type Assertion&#xff09;是 TypeScript 中一種顯式指定變量類型的方式&#xff0c;它告訴編譯器&#xff1a;“我比編譯器更清楚這個值的類型”。?這不是運行時類型轉換&#xff0c;而是編譯階段的類型聲明輔助機制。…

分區表和分表

分區表&#xff08;Partitioning&#xff09; 定義 分區表是將單個表的數據按照某種規則&#xff08;如范圍、列表、哈希等&#xff09;劃分為多個邏輯部分&#xff0c;每個部分稱為一個分區。數據仍然存儲在一個物理表中&#xff0c;但邏輯上被分割為多個分區。 特點 邏輯…

C++從入門到入土(八)——多態的原理

目錄 前言 多態的原理 動態綁定與靜態綁定 虛函數表 小結 前言 在前面的文章中&#xff0c;我們介紹了C三大特性之一的多態&#xff0c;我們主要介紹了多態的構成條件&#xff0c;但是對于多態的原理我們探討的是不夠深入的&#xff0c;下面這這一篇文章&#xff0c;我們將…

用Maven創建只有POM文件的項目

使用 mvn 創建一個僅包含 pom.xml 文件的父項目&#xff0c;可以借助 maven-archetype-quickstart 原型&#xff0c;然后移除不必要的文件&#xff0c;或者直接通過命令生成最簡的 pom.xml 文件。以下是具體操作步驟&#xff1a; 一、方法一&#xff1a;使用原型創建后清理 1…

Linux目錄理解

前言 最近在復習linux&#xff0c;發現有些目錄總是忘記內容&#xff0c;發現有些還是得從原義和實際例子去理解會記憶深刻些。以下是個人的一些理解 Linux目錄 常見的Linux下的目錄如下&#xff1a; 1. 根目錄 / (Root Directory) 英文含義&#xff1a;/ 是文件系統的根…

gitee AI使用

gitee AI使用 gitee AI使用 gitee AI使用簡介正文開始1. 安裝openai2. 測試2.1 不使用流2.2 使用流 2.3 使用curl工具 簡介 發現gitee 推出了個ai幫助多數人使用ai&#xff0c;突破算力和模型的壁壘&#xff0c;我就遵從開源精神&#xff0c;測試了下&#xff0c;希望可以幫助…

c++領域展開第十七幕——STL(vector容器的模擬實現以及迭代器失效問題)超詳細!!!!

文章目錄 前言vector——基本模型vector——迭代器模擬實現vector——容量函數以及push_back、pop_backvector——默認成員函數vector——運算符重載vector——插入和刪除函數vector——實現過程的問題迭代器失效memcpy的淺拷貝問題 總結 前言 上篇博客我們已經詳細介紹了vecto…

WPF 開發從入門到進階(五)

一、WPF 簡介與開發環境搭建 1.1 WPF 概述 Windows Presentation Foundation&#xff08;WPF&#xff09;是微軟推出的用于構建 Windows 桌面應用程序的強大 UI 框架。它融合了矢量圖形、動畫、多媒體等多種技術&#xff0c;能讓開發者創建出具有高度視覺吸引力和交互性的應用…

DICOM醫學影像數據訪問控制與身份驗證技術應用的重要性及其實現方法詳解

DICOM醫學影像數據訪問控制與身份驗證技術應用的重要性及其實現方法詳解 在現代醫療體系中,DICOM(數字成像和通信醫學標準)作為醫學影像數據的核心標準,扮演著至關重要的角色。隨著醫療信息化的深入發展,DICOM醫學影像數據的安全性和隱私保護成為醫療機構亟需解決的關鍵問…

植物知識分享論壇畢設

1.這四個文件直接是什么關系&#xff1f;各自都是什么作用&#xff1f;他們之間是如何聯系的&#xff1f; 關系與聯系 UserController.java 負責接收外部請求&#xff0c;調用 UserService.java 里的方法來處理業務&#xff0c; 而 UserService.java 又會調用 UserMapper.jav…