| import { h } from "../h"; | ||
| import dayjs from "dayjs"; | ||
| import { mergeObjs, isPath } from "../utils/utils"; | ||
| import Group from "./group"; | ||
| import LineOption from "./lineOption"; | ||
| import ItemWithSize from "./itemWithSize"; | ||
| import Menu from "./menu"; | ||
| class Timeline { | ||
| /** | ||
| * 传入的父容器 | ||
| */ | ||
| private container: HTMLElement; | ||
| /**时间轴容器 */ | ||
| private timeline: HTMLElement; | ||
| /**开始时间 */ | ||
| private startDate: Date; | ||
| /**结束时间 */ | ||
| private endDate: Date; | ||
| /**缩放比例 */ | ||
| private zoomLevel: number; | ||
| /**偏移量 */ | ||
| private offset: number; | ||
| /**鼠标x轴位置 */ | ||
| private x: number; | ||
| /**鼠标y轴位置 */ | ||
| private y: number = 0; | ||
| /**提示框 */ | ||
| private tipDiv: HTMLElement; | ||
| /**提示框线 */ | ||
| private tipLineDiv: HTMLElement; | ||
| /**x轴刻度 */ | ||
| private xAxisScaleDiv: HTMLElement; | ||
| /**x轴线 */ | ||
| private xAxisLineDiv: HTMLElement; | ||
| /**x轴线高度 */ | ||
| private xAxisLineHeight: number = 30; | ||
| /**y轴线 */ | ||
| private yAxisLineDiv: HTMLElement; | ||
| /**y轴刻度 */ | ||
| private yAxisScaleDiv: HTMLElement; | ||
| /**数据集(y轴) */ | ||
| private data: Array<Group>; | ||
| /**数据块占用分组空间的比例 取值范围(0-1) */ | ||
| private dataInGroupScale: number = 0.5; | ||
| /**时间轴上边距 */ | ||
| private lineTop: number = 5; | ||
| /**时间轴左边距 */ | ||
| private lineLeft: number = 20; | ||
| /**时间轴样式参数 */ | ||
| private lineOptions: LineOption = new LineOption(); | ||
| /**选中区域 */ | ||
| private selectedDataDiv: HTMLElement; | ||
| /** | ||
| * 组件尺寸修改监视器 | ||
| */ | ||
| private resizeObserver: ResizeObserver; | ||
| constructor( | ||
| containerId: string, | ||
| startDate: Date, | ||
| endDate: Date, | ||
| lineOptions?: LineOption | ||
| ) { | ||
| requestAnimationFrame(() => { | ||
| if (lineOptions) { | ||
| mergeObjs(this.lineOptions, lineOptions); | ||
| } | ||
| this.container = document.getElementById(containerId)!; | ||
| this.container.style.position = "relative"; | ||
| this.startDate = startDate; | ||
| this.endDate = endDate; | ||
| this.zoomLevel = 1; | ||
| this.offset = this.lineOptions.textWidth!; | ||
| const timelineContainer = this.createTimelineElement(); | ||
| this.container.appendChild(timelineContainer); | ||
| this.init(); | ||
| }); | ||
| } | ||
| private createTimelineElement(): HTMLElement { | ||
| this.timeline = h("div", { | ||
| className: "timeline-zoom", | ||
| style: "width:100%;height:100%;display:flex;", | ||
| }); | ||
| const timelineContainer = h( | ||
| "div", | ||
| { | ||
| style: `width:calc(100% - ${this.lineLeft * 2}px);height:calc(100% - ${ | ||
| this.lineTop * 2 | ||
| }px);position:absolute;top:${this.lineTop}px;left:${ | ||
| this.lineLeft | ||
| }px;overflow:hidden;display:flex`, | ||
| }, | ||
| this.timeline | ||
| ); | ||
| return timelineContainer; | ||
| } | ||
| /** | ||
| * 关闭时注意回调 | ||
| */ | ||
| public remove() { | ||
| this.resizeObserver?.disconnect(); | ||
| } | ||
| private init() { | ||
| // this.timeline.addEventListener("click", this.onClick.bind(this)); | ||
| this.timeline.addEventListener("dblclick", this.onDbClick.bind(this)); | ||
| this.timeline.addEventListener("wheel", this.onWheel.bind(this)); | ||
| this.timeline.addEventListener("mousedown", this.onMouseDown.bind(this)); | ||
| this.timeline.addEventListener("mousemove", this.onMouseMove.bind(this)); | ||
| this.timeline.addEventListener("mouseup", this.onMouseUp.bind(this)); | ||
| this.timeline.addEventListener("mouseleave", this.onMouseUp.bind(this)); | ||
| window.addEventListener("resize", this.onResize.bind(this)); | ||
| this.timeline.addEventListener("resize", this.onResize.bind(this)); | ||
| this.resizeObserver = new ResizeObserver((t) => { | ||
| this.onResize(); | ||
| }); | ||
| this.resizeObserver.observe(this.timeline); | ||
| this.drawTimeline(); | ||
| } | ||
| /**设置时间轴时间范围 */ | ||
| public setDate(startDate: Date, endDate: Date) { | ||
| this.startDate = startDate; | ||
| this.endDate = endDate; | ||
| this.drawTimeline(); | ||
| } | ||
| /**设置数据 */ | ||
| public setData(data: any) { | ||
| this.data = data; | ||
| this.drawTimeline(); | ||
| } | ||
| /**绘制时间轴 */ | ||
| private drawTimeline() { | ||
| if (!this.timeline || this.timeline.clientWidth == 0) { | ||
| setTimeout(() => { | ||
| this.drawTimeline(); | ||
| }, 10); | ||
| return; | ||
| } | ||
| this.timeline.innerHTML = ""; | ||
| this.timeline.style.transform = ` translateX(${this.offset}px)`; | ||
| let timelineWidth = this.timeline.clientWidth * this.zoomLevel; | ||
| this.drawXAxis(timelineWidth); | ||
| this.drawYAxisLine(); | ||
| this.drawTimeData(timelineWidth); | ||
| this.drawSelectArea(); | ||
| this.drawTipArea(); | ||
| } | ||
| /** | ||
| * 绘制x轴 | ||
| */ | ||
| private drawXAxis(timelineWidth) { | ||
| this.xAxisLineDiv = h("div", { | ||
| style: `position:absolute;width:${timelineWidth}px;height:1px;background-color:${this.lineOptions.xAxisColor};bottom:30px;left:${this.lineOptions.textWidth}px;`, | ||
| }); | ||
| this.timeline.appendChild(this.xAxisLineDiv); | ||
| let lastDate; | ||
| const length = 180; | ||
| let range = this.endDate.getTime() - this.startDate.getTime(); | ||
| let pixelsPerMs = timelineWidth / range; | ||
| for (let i = 0; i <= timelineWidth; ) { | ||
| const date = new Date(this.startDate.getTime() + i / pixelsPerMs); | ||
| if (!lastDate || date.getDate() != lastDate.getDate()) { | ||
| this.xAxisScaleDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-x-scale", | ||
| style: `width:${length}px;font-size:14px;flex:0 0 auto;user-select:none;display:flex;justify-content:flex-end;flex-direction:column;position:relative;left:${this.lineOptions.textWidth}px;z-index:1`, | ||
| }, | ||
| h("div", { | ||
| style: `background:${this.lineOptions.xAxisColor};width:1px;height:10px;position:relative;bottom:10px;`, | ||
| }), | ||
| h( | ||
| "div", | ||
| { | ||
| style: `background-color:${this.lineOptions.xAxisBgColor};color:${this.lineOptions.xAxisColor};position:relative;top:0px;left:-50%;text-align:center;height:20px`, | ||
| }, | ||
| dayjs(date).format("YYYY-MM-DD HH:mm:ss") | ||
| ) | ||
| ); | ||
| lastDate = date; | ||
| i += length; | ||
| } else { | ||
| this.xAxisScaleDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-x-scale", | ||
| style: `width:${length}px;font-size:14px;flex:0 0 auto;user-select:none;display:flex;justify-content:flex-end;flex-direction:column;position:relative;left:${this.lineOptions.textWidth}px;z-index:1`, | ||
| }, | ||
| h("div", { | ||
| style: `background:${this.lineOptions.xAxisColor};width:1px;height:10px;position:relative;bottom:10px;`, | ||
| }), | ||
| h( | ||
| "div", | ||
| { | ||
| style: `background-color:${this.lineOptions.xAxisBgColor};color:${this.lineOptions.xAxisColor};position:relative;top:0px;left:-50%;text-align:center;height:20px`, | ||
| }, | ||
| dayjs(date).format("HH:mm:ss") | ||
| ) | ||
| ); | ||
| i += length; | ||
| } | ||
| this.timeline.appendChild(this.xAxisScaleDiv); | ||
| } | ||
| } | ||
| /** | ||
| * 绘制y轴 | ||
| */ | ||
| private drawYAxisLine() { | ||
| if (this.data) { | ||
| this.yAxisLineDiv = h("div", { | ||
| className: "timeline-y-line", | ||
| style: `position:absolute;width:1px;height:calc(100% - 30px);background-color:${ | ||
| this.lineOptions.yAxisColor | ||
| };bottom:30px;left:${ | ||
| this.offset > 0 | ||
| ? 0 + this.lineOptions.textWidth! | ||
| : -this.offset + this.lineOptions.textWidth! | ||
| }px;z-index:1`, | ||
| }); | ||
| this.timeline.appendChild(this.yAxisLineDiv); | ||
| } else { | ||
| this.yAxisLineDiv = h("div", { | ||
| className: "timeline-y-line", | ||
| style: `position:absolute;width:1px;height:calc(100% - 30px);background-color:${ | ||
| this.lineOptions.yAxisColor | ||
| };bottom:30px;left:${0 + this.lineOptions.textWidth!}px;z-index:1`, | ||
| }); | ||
| this.timeline.appendChild(this.yAxisLineDiv); | ||
| } | ||
| } | ||
| /** | ||
| * 计算时间轴工作区高度以及分组高度 | ||
| * @returns | ||
| */ | ||
| private getHeightPerGroup() { | ||
| const height = this.timeline.clientHeight - this.xAxisLineHeight; | ||
| let heightPerGroup = 0; | ||
| if (this.data) { | ||
| heightPerGroup = height / this.data.length; | ||
| } | ||
| return { height, heightPerGroup }; | ||
| } | ||
| /** | ||
| * 获取分组下标序号 | ||
| * @param heightPerGroup | ||
| * @returns | ||
| */ | ||
| private getGroupIndex(heightPerGroup) { | ||
| const y = this.y; | ||
| const index = Math.ceil(y / heightPerGroup); | ||
| return index; | ||
| } | ||
| /** | ||
| * 绘制数据 | ||
| * @param timelineWidth | ||
| */ | ||
| private drawTimeData(timelineWidth: number) { | ||
| if (this.data) { | ||
| const { height, heightPerGroup } = this.getHeightPerGroup(); | ||
| let index = 0; | ||
| this.yAxisScaleDiv = h("div", { | ||
| className: "timeline-y-scale", | ||
| style: `width:${ | ||
| this.lineOptions.textWidth | ||
| }px;height:${height}px;position:absolute;left:${ | ||
| this.offset > 0 ? 0 : -this.offset | ||
| }px;top:0;display:flex;flex-direction:column;justify-content:flex-end;border-top:1px solid #666;`, | ||
| }); | ||
| this.timeline.appendChild(this.yAxisScaleDiv); | ||
| this.data.forEach((group) => { | ||
| const scaleLineDiv = h("div", { | ||
| style: `background:#666;width:${timelineWidth}px;height:1px;position:absolute;left:${ | ||
| this.lineOptions.textWidth | ||
| }px;top:${index * heightPerGroup}px`, | ||
| }); | ||
| this.timeline.appendChild(scaleLineDiv); | ||
| const groupDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-group", | ||
| style: `width:100%;height:${heightPerGroup}px;display:flex;position:relative;`, | ||
| }, | ||
| h( | ||
| "div", | ||
| { | ||
| style: `color:${this.lineOptions.yAxisColor};overflow-wrap:break-word;flex:0 1 ${this.lineOptions.textWidth}px;padding-right:10px;background-color:${this.lineOptions.yAxisBgColor};height:100%;z-index:1;display:flex;justify-content:center;align-items:center;flex-wrap:wrap;`, | ||
| }, | ||
| group.groupName | ||
| ) | ||
| ); | ||
| this.yAxisScaleDiv.appendChild(groupDiv); | ||
| group.items.forEach((item) => { | ||
| let start = item.startTime.getTime() - this.startDate.getTime(); | ||
| let end = item.endTime.getTime() - this.startDate.getTime(); | ||
| let width = | ||
| ((end - start) / | ||
| (this.endDate.getTime() - this.startDate.getTime())) * | ||
| timelineWidth; | ||
| let left = | ||
| (start / (this.endDate.getTime() - this.startDate.getTime())) * | ||
| timelineWidth; | ||
| //由于offset小于0会发生位置偏移,因此需要对绘制的数据进行同等偏移 | ||
| if (this.offset < 0) { | ||
| left = left + this.offset; | ||
| } | ||
| left += this.lineOptions.textWidth!; | ||
| (item as ItemWithSize).width = width; | ||
| (item as ItemWithSize).height = | ||
| heightPerGroup * this.dataInGroupScale; | ||
| (item as ItemWithSize).left = left; | ||
| (item as ItemWithSize).top = | ||
| (heightPerGroup - heightPerGroup * this.dataInGroupScale) / 2; | ||
| const itemDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-item", | ||
| style: `width:${width}px;height:${ | ||
| heightPerGroup * this.dataInGroupScale | ||
| }px;background-color:${ | ||
| group.groupOptions.backgroundColor | ||
| };position:absolute;left:${left}px;top:${ | ||
| (heightPerGroup - heightPerGroup * this.dataInGroupScale) / 2 | ||
| }px`, | ||
| // };position:absolute;left:${left}px;display:flex;justify-content:center;flex-direction:column;top:50%;transform:translate(0,-50%);`, | ||
| }, | ||
| h( | ||
| "div", | ||
| { | ||
| style: `color:${group.groupOptions.textColor};font-size:${group.groupOptions.textSize}px;`, | ||
| }, | ||
| item.itemName | ||
| ) | ||
| ); | ||
| groupDiv.appendChild(itemDiv); | ||
| }); | ||
| index++; | ||
| }); | ||
| } | ||
| } | ||
| /** | ||
| * 绘制tip提示区域 | ||
| */ | ||
| private drawTipArea() { | ||
| const { height, heightPerGroup } = this.getHeightPerGroup(); | ||
| if (this.x) { | ||
| let index = 0; | ||
| if (this.data) { | ||
| index = this.getGroupIndex(heightPerGroup); | ||
| } | ||
| this.tipDiv = h( | ||
| "div", | ||
| { | ||
| style: `position:absolute;color:${ | ||
| this.lineOptions.tipColor | ||
| };background-color:${this.lineOptions.tipBackground};font-size:${ | ||
| this.lineOptions.tipTextSize | ||
| }px;top:${this.y}px;left:${this.x + 10}px;z-index:1`, | ||
| }, | ||
| h( | ||
| "div", | ||
| {}, | ||
| dayjs(this.getTimeByPx(this.x)).format("YYYY-MM-DD HH:mm:ss") | ||
| ), | ||
| h( | ||
| "div", | ||
| {}, | ||
| this.data && index <= this.data.length && index > 0 | ||
| ? this.data[index - 1].groupName | ||
| : "" | ||
| ) | ||
| ); | ||
| this.tipLineDiv = h("div", { | ||
| style: `position:absolute;width:1px;height:calc(100% - 30px);background-color:${this.lineOptions.tipLineColor};left:${this.x}px;bottom:30px;z-index:1`, | ||
| }); | ||
| this.timeline.appendChild(this.tipLineDiv); | ||
| this.timeline.appendChild(this.tipDiv); | ||
| } | ||
| } | ||
| /** | ||
| * 绘制选中区域 | ||
| */ | ||
| private drawSelectArea() { | ||
| if (this.isSelecting) { | ||
| //绘制选中区域 | ||
| let start = Math.min(this.lastX, this.endX); | ||
| let end = Math.max(this.lastX, this.endX); | ||
| if (end - start > 1) { | ||
| //由于offset小于0会发生位置偏移,因此需要对绘制的数据进行同等偏移 | ||
| const crect = this.container.getBoundingClientRect(); | ||
| let left = start - this.offset - this.lineLeft - crect.left; | ||
| let menus = [] as HTMLElement[]; | ||
| if (this.lineOptions.menus) { | ||
| const defaultMenu = { | ||
| text: "", | ||
| icon: "", | ||
| iconSize: 14, | ||
| textSize: 14, | ||
| clickEvent: (e: MouseEvent) => {}, | ||
| }; | ||
| menus = this.lineOptions.menus.map((t) => { | ||
| const menu = new Menu({ ...defaultMenu, ...t }); | ||
| return h( | ||
| "div", | ||
| { | ||
| style: `z-index:2;display:flex;justify-content:center;flex-direction:column;cursor:pointer;padding:0 5px;align-items:center`, | ||
| onmousedown: (e: MouseEvent) => { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| }, | ||
| onclick: (e: MouseEvent) => { | ||
| let startPx = Math.min(this.lastX, this.endX); | ||
| let endPx = Math.max(this.lastX, this.endX); | ||
| const startTime = this.getTimeByPx(startPx); | ||
| const endTime = this.getTimeByPx(endPx); | ||
| const { heightPerGroup } = this.getHeightPerGroup(); | ||
| const index = this.getGroupIndex(heightPerGroup); | ||
| menu.clickEvent(e, startTime, endTime, this.data[index - 1]); | ||
| }, | ||
| }, | ||
| isPath(menu.icon) | ||
| ? h("img", { | ||
| src: menu.icon, | ||
| style: `width:${menu.iconSize + 2}px;height:${ | ||
| menu.iconSize | ||
| }px;color:${menu.iconColor}`, | ||
| }) | ||
| : h("i", { | ||
| class: `${menu.icon}`, | ||
| style: `font-size:${menu.iconSize}px`, | ||
| }), | ||
| h( | ||
| "div", | ||
| { | ||
| style: `font-size:${menu.textSize}px;color:${menu.textColor}`, | ||
| }, | ||
| menu.text | ||
| ) | ||
| ); | ||
| }); | ||
| } | ||
| this.selectedDataDiv = h( | ||
| "div", | ||
| { | ||
| class: "select-item", | ||
| style: `position:absolute;top:${this.selectTop}px;left:${left}px;display:flex`, | ||
| }, | ||
| [ | ||
| h("div", { | ||
| style: `background-color:${ | ||
| this.lineOptions.selectedColor | ||
| };height:${this.selectHeight}px;width:${ | ||
| end - start | ||
| }px;z-index:2;`, | ||
| }), | ||
| ...(this.lineOptions.menus ? menus : ""), | ||
| ] | ||
| ); | ||
| this.timeline.appendChild(this.selectedDataDiv); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * 判断是否处于块数据中 | ||
| * @returns | ||
| */ | ||
| private isInItems() { | ||
| let flag = false; | ||
| let height = 0; | ||
| let curItem; | ||
| let top = 0; | ||
| const crect = this.container.getBoundingClientRect(); | ||
| const rect = this.timeline.getBoundingClientRect(); | ||
| const { heightPerGroup } = this.getHeightPerGroup(); | ||
| const index = this.getGroupIndex(heightPerGroup); | ||
| if (index <= 0 || index > this.data.length) flag = false; | ||
| else { | ||
| const x = this.x; | ||
| const y = this.y; | ||
| for (let i = 0; i < this.data[index - 1].items.length; i++) { | ||
| const t = this.data[index - 1].items[i]; | ||
| const item = t as ItemWithSize; | ||
| let left = 0; | ||
| if (this.offset < 0) { | ||
| left = item.left - this.offset; | ||
| } else { | ||
| left = item.left; | ||
| } | ||
| if ( | ||
| x >= left && | ||
| x <= left + item.width && | ||
| y >= | ||
| item.top + //自身顶点 | ||
| heightPerGroup * (index - 1) + //上一组的高度 | ||
| this.lineTop - //时间轴边距 | ||
| (rect.top - crect.top) && //timeline对象与父容器的距离 | ||
| y <= | ||
| item.top + | ||
| heightPerGroup * (index - 1) + | ||
| this.lineTop - | ||
| (rect.top - crect.top) + | ||
| item.height | ||
| ) { | ||
| top = item.top + heightPerGroup * (index - 1); | ||
| height = item.height; | ||
| curItem = item; | ||
| flag = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return { flag, top, height, item: curItem, group: this.data[index - 1] }; | ||
| } | ||
| private onDbClick(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| if (this.lineOptions.dblClickEvent) { | ||
| const curDate = this.getTimeByPx(this.x); | ||
| const { item, group } = this.isInItems(); | ||
| this.lineOptions.dblClickEvent( | ||
| event, | ||
| curDate, | ||
| Object.assign({}, item), | ||
| Object.assign({}, group) | ||
| ); | ||
| } | ||
| } | ||
| private onResize() { | ||
| this.isSelecting = false; | ||
| this.drawTimeline(); | ||
| } | ||
| private onWheel(event: WheelEvent) { | ||
| event.preventDefault(); | ||
| this.zoomLevel -= event.deltaY * 0.001; | ||
| if (this.zoomLevel < 0.1) this.zoomLevel = 0.1; | ||
| if (this.zoomLevel > 10) this.zoomLevel = 10; | ||
| this.isSelecting = false; | ||
| this.drawTimeline(); | ||
| } | ||
| //是否处于拖拽状态 | ||
| private isDragging = false; | ||
| //是否处于选中状态,即当鼠标处于数据块中时不再触发拖动时间轴而是开始选中,当未处于数据块中则为拖动 | ||
| private isSelecting = false; | ||
| private lastX = 0; | ||
| private endX = 0; | ||
| private selectTop = 0; | ||
| private selectHeight = 0; | ||
| private onMouseDown(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| this.isDragging = true; | ||
| this.lastX = event.clientX; | ||
| this.endX = event.clientX; | ||
| if (this.data) { | ||
| const { flag, top, height } = this.isInItems(); | ||
| this.isSelecting = flag; | ||
| this.selectTop = top; | ||
| this.selectHeight = height; | ||
| } | ||
| } | ||
| private onMouseMove(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| if (this.isDragging) { | ||
| if (!this.isSelecting) { | ||
| const dx = event.clientX - this.lastX; | ||
| this.offset += dx; | ||
| this.lastX = event.clientX; | ||
| } else { | ||
| //处理选中显示 | ||
| this.endX = event.clientX; | ||
| } | ||
| } | ||
| const rect = this.timeline.getBoundingClientRect(); | ||
| this.y = event.clientY - rect.top; | ||
| this.x = event.clientX - rect.left; | ||
| this.drawTimeline(); | ||
| } | ||
| private onMouseUp(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| this.isDragging = false; | ||
| } | ||
| private getTimeByPx(x) { | ||
| let timelineWidth = this.timeline.clientWidth * this.zoomLevel; | ||
| let range = this.endDate.getTime() - this.startDate.getTime(); | ||
| let pixelsPerMs = timelineWidth / range; | ||
| if (x < this.lineOptions.textWidth!) x = this.lineOptions.textWidth!; | ||
| const curDate = new Date( | ||
| this.startDate.getTime() + (x - this.lineOptions.textWidth!) / pixelsPerMs | ||
| ); | ||
| return curDate; | ||
| } | ||
| } | ||
| export default Timeline; |
+6
-633
@@ -1,637 +0,10 @@ | ||
| import { h } from "./h"; | ||
| import dayjs from "dayjs"; | ||
| import { mergeObjs, isPath } from "./utils/utils"; | ||
| import TimeLine from "./models/timeline"; | ||
| import Group from "./models/group"; | ||
| import GroupOption from "./models/groupOption"; | ||
| import Item from "./models/item"; | ||
| import LineOption from "./models/lineOption"; | ||
| import ItemWithSize from "./models/itemWithSize"; | ||
| import Menu from "./models/menu"; | ||
| import { h } from "./h"; | ||
| class Timeline { | ||
| /** | ||
| * 传入的父容器 | ||
| */ | ||
| private container: HTMLElement; | ||
| /**时间轴容器 */ | ||
| private timeline: HTMLElement; | ||
| /**开始时间 */ | ||
| private startDate: Date; | ||
| /**结束时间 */ | ||
| private endDate: Date; | ||
| /**缩放比例 */ | ||
| private zoomLevel: number; | ||
| /**偏移量 */ | ||
| private offset: number; | ||
| /**当前时间 */ | ||
| private curTime: Date; | ||
| /**鼠标x轴位置 */ | ||
| private x: number; | ||
| /**鼠标y轴位置 */ | ||
| private y: number = 0; | ||
| /**提示框 */ | ||
| private tipDiv: HTMLElement; | ||
| /**提示框线 */ | ||
| private tipLineDiv: HTMLElement; | ||
| /**x轴刻度 */ | ||
| private xAxisScaleDiv: HTMLElement; | ||
| /**x轴线 */ | ||
| private xAxisLineDiv: HTMLElement; | ||
| /**x轴线高度 */ | ||
| private xAxisLineHeight: number = 30; | ||
| /**y轴线 */ | ||
| private yAxisLineDiv: HTMLElement; | ||
| /**y轴刻度 */ | ||
| private yAxisScaleDiv: HTMLElement; | ||
| /**数据集(y轴) */ | ||
| private data: Array<Group>; | ||
| /**数据块占用分组空间的比例 取值范围(0-1) */ | ||
| private dataInGroupScale: number = 0.5; | ||
| /**时间轴上边距 */ | ||
| private lineTop: number = 5; | ||
| /**时间轴左边距 */ | ||
| private lineLeft: number = 20; | ||
| /**时间轴样式参数 */ | ||
| private lineOptions: LineOption = new LineOption(); | ||
| /**选中区域 */ | ||
| private selectedDataDiv: HTMLElement; | ||
| /** | ||
| * 组件尺寸修改监视器 | ||
| */ | ||
| private resizeObserver: ResizeObserver; | ||
| constructor( | ||
| containerId: string, | ||
| startDate: Date, | ||
| endDate: Date, | ||
| lineOptions?: LineOption | ||
| ) { | ||
| requestAnimationFrame(() => { | ||
| if (lineOptions) { | ||
| mergeObjs(this.lineOptions, lineOptions); | ||
| } | ||
| this.container = document.getElementById(containerId)!; | ||
| this.container.style.position = "relative"; | ||
| this.startDate = startDate; | ||
| this.endDate = endDate; | ||
| this.zoomLevel = 1; | ||
| this.offset = this.lineOptions.textWidth!; | ||
| const timelineContainer = this.createTimelineElement(); | ||
| this.container.appendChild(timelineContainer); | ||
| this.init(); | ||
| }); | ||
| } | ||
| private createTimelineElement(): HTMLElement { | ||
| this.timeline = h("div", { | ||
| className: "timeline-zoom", | ||
| style: "width:100%;height:100%;display:flex;", | ||
| }); | ||
| const timelineContainer = h( | ||
| "div", | ||
| { | ||
| style: `width:calc(100% - ${this.lineLeft * 2}px);height:calc(100% - ${ | ||
| this.lineTop * 2 | ||
| }px);position:absolute;top:${this.lineTop}px;left:${ | ||
| this.lineLeft | ||
| }px;overflow:hidden;display:flex`, | ||
| }, | ||
| this.timeline | ||
| ); | ||
| return timelineContainer; | ||
| } | ||
| /** | ||
| * 关闭时注意回调 | ||
| */ | ||
| public remove() { | ||
| this.resizeObserver?.disconnect(); | ||
| } | ||
| private init() { | ||
| // this.timeline.addEventListener("click", this.onClick.bind(this)); | ||
| this.timeline.addEventListener("dblclick", this.onDbClick.bind(this)); | ||
| this.timeline.addEventListener("wheel", this.onWheel.bind(this)); | ||
| this.timeline.addEventListener("mousedown", this.onMouseDown.bind(this)); | ||
| this.timeline.addEventListener("mousemove", this.onMouseMove.bind(this)); | ||
| this.timeline.addEventListener("mouseup", this.onMouseUp.bind(this)); | ||
| this.timeline.addEventListener("mouseleave", this.onMouseUp.bind(this)); | ||
| window.addEventListener("resize", this.onResize.bind(this)); | ||
| this.timeline.addEventListener("resize", this.onResize.bind(this)); | ||
| this.resizeObserver = new ResizeObserver((t) => { | ||
| this.onResize(); | ||
| }); | ||
| this.resizeObserver.observe(this.timeline); | ||
| this.drawTimeline(); | ||
| } | ||
| /**设置时间轴时间范围 */ | ||
| public setDate(startDate: Date, endDate: Date) { | ||
| this.startDate = startDate; | ||
| this.endDate = endDate; | ||
| this.drawTimeline(); | ||
| } | ||
| /**设置数据 */ | ||
| public setData(data: any) { | ||
| this.data = data; | ||
| this.drawTimeline(); | ||
| } | ||
| /**绘制时间轴 */ | ||
| private drawTimeline() { | ||
| if (!this.timeline || this.timeline.clientWidth == 0) { | ||
| setTimeout(() => { | ||
| this.drawTimeline(); | ||
| }, 10); | ||
| return; | ||
| } | ||
| this.timeline.innerHTML = ""; | ||
| this.timeline.style.transform = ` translateX(${this.offset}px)`; | ||
| let timelineWidth = this.timeline.clientWidth * this.zoomLevel; | ||
| this.drawXAxis(timelineWidth); | ||
| this.drawYAxisLine(); | ||
| this.drawTimeData(timelineWidth); | ||
| this.drawSelectArea(); | ||
| this.drawTipArea(); | ||
| } | ||
| /** | ||
| * 绘制x轴 | ||
| */ | ||
| private drawXAxis(timelineWidth) { | ||
| this.xAxisLineDiv = h("div", { | ||
| style: `position:absolute;width:${timelineWidth}px;height:1px;background-color:${this.lineOptions.xAxisColor};bottom:30px;left:${this.lineOptions.textWidth}px;`, | ||
| }); | ||
| this.timeline.appendChild(this.xAxisLineDiv); | ||
| let lastDate; | ||
| const length = 180; | ||
| let range = this.endDate.getTime() - this.startDate.getTime(); | ||
| let pixelsPerMs = timelineWidth / range; | ||
| for (let i = 0; i <= timelineWidth; ) { | ||
| const date = new Date(this.startDate.getTime() + i / pixelsPerMs); | ||
| if (!lastDate || date.getDate() != lastDate.getDate()) { | ||
| this.xAxisScaleDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-x-scale", | ||
| style: `width:${length}px;font-size:14px;flex:0 0 auto;user-select:none;display:flex;justify-content:flex-end;flex-direction:column;position:relative;left:${this.lineOptions.textWidth}px;z-index:1`, | ||
| }, | ||
| h("div", { | ||
| style: `background:${this.lineOptions.xAxisColor};width:1px;height:10px;position:relative;bottom:10px;`, | ||
| }), | ||
| h( | ||
| "div", | ||
| { | ||
| style: `background-color:${this.lineOptions.xAxisBgColor};color:${this.lineOptions.xAxisColor};position:relative;top:0px;left:-50%;text-align:center;height:20px`, | ||
| }, | ||
| dayjs(date).format("YYYY-MM-DD HH:mm:ss") | ||
| ) | ||
| ); | ||
| lastDate = date; | ||
| i += length; | ||
| } else { | ||
| this.xAxisScaleDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-x-scale", | ||
| style: `width:${length}px;font-size:14px;flex:0 0 auto;user-select:none;display:flex;justify-content:flex-end;flex-direction:column;position:relative;left:${this.lineOptions.textWidth}px;z-index:1`, | ||
| }, | ||
| h("div", { | ||
| style: `background:${this.lineOptions.xAxisColor};width:1px;height:10px;position:relative;bottom:10px;`, | ||
| }), | ||
| h( | ||
| "div", | ||
| { | ||
| style: `background-color:${this.lineOptions.xAxisBgColor};color:${this.lineOptions.xAxisColor};position:relative;top:0px;left:-50%;text-align:center;height:20px`, | ||
| }, | ||
| dayjs(date).format("HH:mm:ss") | ||
| ) | ||
| ); | ||
| i += length; | ||
| } | ||
| this.timeline.appendChild(this.xAxisScaleDiv); | ||
| } | ||
| } | ||
| /** | ||
| * 绘制y轴 | ||
| */ | ||
| private drawYAxisLine() { | ||
| if (this.data) { | ||
| this.yAxisLineDiv = h("div", { | ||
| className: "timeline-y-line", | ||
| style: `position:absolute;width:1px;height:calc(100% - 30px);background-color:${ | ||
| this.lineOptions.yAxisColor | ||
| };bottom:30px;left:${ | ||
| this.offset > 0 | ||
| ? 0 + this.lineOptions.textWidth! | ||
| : -this.offset + this.lineOptions.textWidth! | ||
| }px;z-index:1`, | ||
| }); | ||
| this.timeline.appendChild(this.yAxisLineDiv); | ||
| } else { | ||
| this.yAxisLineDiv = h("div", { | ||
| className: "timeline-y-line", | ||
| style: `position:absolute;width:1px;height:calc(100% - 30px);background-color:${ | ||
| this.lineOptions.yAxisColor | ||
| };bottom:30px;left:${0 + this.lineOptions.textWidth!}px;z-index:1`, | ||
| }); | ||
| this.timeline.appendChild(this.yAxisLineDiv); | ||
| } | ||
| } | ||
| /** | ||
| * 计算时间轴工作区高度以及分组高度 | ||
| * @returns | ||
| */ | ||
| private getHeightPerGroup() { | ||
| const height = this.timeline.clientHeight - this.xAxisLineHeight; | ||
| let heightPerGroup = 0; | ||
| if (this.data) { | ||
| heightPerGroup = height / this.data.length; | ||
| } | ||
| return { height, heightPerGroup }; | ||
| } | ||
| /** | ||
| * 获取分组下标序号 | ||
| * @param heightPerGroup | ||
| * @returns | ||
| */ | ||
| private getGroupIndex(heightPerGroup) { | ||
| const y = this.y; | ||
| const index = Math.ceil(y / heightPerGroup); | ||
| return index; | ||
| } | ||
| /** | ||
| * 绘制数据 | ||
| * @param timelineWidth | ||
| */ | ||
| private drawTimeData(timelineWidth: number) { | ||
| if (this.data) { | ||
| const { height, heightPerGroup } = this.getHeightPerGroup(); | ||
| let index = 0; | ||
| this.yAxisScaleDiv = h("div", { | ||
| className: "timeline-y-scale", | ||
| style: `width:${ | ||
| this.lineOptions.textWidth | ||
| }px;height:${height}px;position:absolute;left:${ | ||
| this.offset > 0 ? 0 : -this.offset | ||
| }px;top:0;display:flex;flex-direction:column;justify-content:flex-end;border-top:1px solid #666;`, | ||
| }); | ||
| this.timeline.appendChild(this.yAxisScaleDiv); | ||
| this.data.forEach((group) => { | ||
| const scaleLineDiv = h("div", { | ||
| style: `background:#666;width:${timelineWidth}px;height:1px;position:absolute;left:${ | ||
| this.lineOptions.textWidth | ||
| }px;top:${index * heightPerGroup}px`, | ||
| }); | ||
| this.timeline.appendChild(scaleLineDiv); | ||
| const groupDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-group", | ||
| style: `width:100%;height:${heightPerGroup}px;display:flex;position:relative;`, | ||
| }, | ||
| h( | ||
| "div", | ||
| { | ||
| style: `color:${this.lineOptions.yAxisColor};overflow-wrap:break-word;flex:0 1 ${this.lineOptions.textWidth}px;padding-right:10px;background-color:${this.lineOptions.yAxisBgColor};height:100%;z-index:1;display:flex;justify-content:center;align-items:center;flex-wrap:wrap;`, | ||
| }, | ||
| group.groupName | ||
| ) | ||
| ); | ||
| this.yAxisScaleDiv.appendChild(groupDiv); | ||
| group.items.forEach((item) => { | ||
| let start = item.startTime.getTime() - this.startDate.getTime(); | ||
| let end = item.endTime.getTime() - this.startDate.getTime(); | ||
| let width = | ||
| ((end - start) / | ||
| (this.endDate.getTime() - this.startDate.getTime())) * | ||
| timelineWidth; | ||
| let left = | ||
| (start / (this.endDate.getTime() - this.startDate.getTime())) * | ||
| timelineWidth; | ||
| //由于offset小于0会发生位置偏移,因此需要对绘制的数据进行同等偏移 | ||
| if (this.offset < 0) { | ||
| left = left + this.offset; | ||
| } | ||
| left += this.lineOptions.textWidth!; | ||
| (item as ItemWithSize).width = width; | ||
| (item as ItemWithSize).height = | ||
| heightPerGroup * this.dataInGroupScale; | ||
| (item as ItemWithSize).left = left; | ||
| (item as ItemWithSize).top = | ||
| (heightPerGroup - heightPerGroup * this.dataInGroupScale) / 2; | ||
| const itemDiv = h( | ||
| "div", | ||
| { | ||
| className: "timeline-item", | ||
| style: `width:${width}px;height:${ | ||
| heightPerGroup * this.dataInGroupScale | ||
| }px;background-color:${ | ||
| group.groupOptions.backgroundColor | ||
| };position:absolute;left:${left}px;top:${ | ||
| (heightPerGroup - heightPerGroup * this.dataInGroupScale) / 2 | ||
| }px`, | ||
| // };position:absolute;left:${left}px;display:flex;justify-content:center;flex-direction:column;top:50%;transform:translate(0,-50%);`, | ||
| }, | ||
| h( | ||
| "div", | ||
| { | ||
| style: `color:${group.groupOptions.textColor};font-size:${group.groupOptions.textSize}px;`, | ||
| }, | ||
| item.itemName | ||
| ) | ||
| ); | ||
| groupDiv.appendChild(itemDiv); | ||
| }); | ||
| index++; | ||
| }); | ||
| } | ||
| } | ||
| /** | ||
| * 绘制tip提示区域 | ||
| */ | ||
| private drawTipArea() { | ||
| const { height, heightPerGroup } = this.getHeightPerGroup(); | ||
| if (this.x) { | ||
| let index = 0; | ||
| if (this.data) { | ||
| index = this.getGroupIndex(heightPerGroup); | ||
| } | ||
| this.tipDiv = h( | ||
| "div", | ||
| { | ||
| style: `position:absolute;color:${ | ||
| this.lineOptions.tipColor | ||
| };background-color:${this.lineOptions.tipBackground};font-size:${ | ||
| this.lineOptions.tipTextSize | ||
| }px;top:${this.y}px;left:${this.x + 10}px;z-index:1`, | ||
| }, | ||
| h( | ||
| "div", | ||
| {}, | ||
| dayjs(this.getTimeByPx(this.x)).format("YYYY-MM-DD HH:mm:ss") | ||
| ), | ||
| h( | ||
| "div", | ||
| {}, | ||
| this.data && index <= this.data.length && index > 0 | ||
| ? this.data[index - 1].groupName | ||
| : "" | ||
| ) | ||
| ); | ||
| this.tipLineDiv = h("div", { | ||
| style: `position:absolute;width:1px;height:calc(100% - 30px);background-color:${this.lineOptions.tipLineColor};left:${this.x}px;bottom:30px;z-index:1`, | ||
| }); | ||
| this.timeline.appendChild(this.tipLineDiv); | ||
| this.timeline.appendChild(this.tipDiv); | ||
| } | ||
| } | ||
| /** | ||
| * 绘制选中区域 | ||
| */ | ||
| private drawSelectArea() { | ||
| if (this.isSelecting) { | ||
| //绘制选中区域 | ||
| let start = Math.min(this.lastX, this.endX); | ||
| let end = Math.max(this.lastX, this.endX); | ||
| if (end - start > 1) { | ||
| //由于offset小于0会发生位置偏移,因此需要对绘制的数据进行同等偏移 | ||
| const crect = this.container.getBoundingClientRect(); | ||
| let left = start - this.offset - this.lineLeft - crect.left; | ||
| let menus = [] as HTMLElement[]; | ||
| if (this.lineOptions.menus) { | ||
| const defaultMenu = { | ||
| text: "", | ||
| icon: "", | ||
| iconSize: 14, | ||
| textSize: 14, | ||
| clickEvent: (e: MouseEvent) => {}, | ||
| }; | ||
| menus = this.lineOptions.menus.map((t) => { | ||
| const menu = new Menu({ ...defaultMenu, ...t }); | ||
| return h( | ||
| "div", | ||
| { | ||
| style: `z-index:2;display:flex;justify-content:center;flex-direction:column;cursor:pointer;padding:0 5px;align-items:center`, | ||
| onmousedown: (e: MouseEvent) => { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| }, | ||
| onclick: (e: MouseEvent) => { | ||
| let startPx = Math.min(this.lastX, this.endX); | ||
| let endPx = Math.max(this.lastX, this.endX); | ||
| const startTime = this.getTimeByPx(startPx); | ||
| const endTime = this.getTimeByPx(endPx); | ||
| const { heightPerGroup } = this.getHeightPerGroup(); | ||
| const index = this.getGroupIndex(heightPerGroup); | ||
| menu.clickEvent(e, startTime, endTime, this.data[index - 1]); | ||
| }, | ||
| }, | ||
| isPath(menu.icon) | ||
| ? h("img", { | ||
| src: menu.icon, | ||
| style: `width:${menu.iconSize + 2}px;height:${ | ||
| menu.iconSize | ||
| }px;color:${menu.iconColor}`, | ||
| }) | ||
| : h("i", { | ||
| class: `${menu.icon}`, | ||
| style: `font-size:${menu.iconSize}px`, | ||
| }), | ||
| h( | ||
| "div", | ||
| { | ||
| style: `font-size:${menu.textSize}px;color:${menu.textColor}`, | ||
| }, | ||
| menu.text | ||
| ) | ||
| ); | ||
| }); | ||
| } | ||
| this.selectedDataDiv = h( | ||
| "div", | ||
| { | ||
| class: "select-item", | ||
| style: `position:absolute;top:${this.selectTop}px;left:${left}px;display:flex`, | ||
| }, | ||
| [ | ||
| h("div", { | ||
| style: `background-color:${ | ||
| this.lineOptions.selectedColor | ||
| };height:${this.selectHeight}px;width:${ | ||
| end - start | ||
| }px;z-index:2;`, | ||
| }), | ||
| ...(this.lineOptions.menus ? menus : ""), | ||
| ] | ||
| ); | ||
| this.timeline.appendChild(this.selectedDataDiv); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * 判断是否处于块数据中 | ||
| * @returns | ||
| */ | ||
| private isInItems() { | ||
| let flag = false; | ||
| let height = 0; | ||
| let curItem; | ||
| let top = 0; | ||
| const crect = this.container.getBoundingClientRect(); | ||
| const rect = this.timeline.getBoundingClientRect(); | ||
| const { heightPerGroup } = this.getHeightPerGroup(); | ||
| const index = this.getGroupIndex(heightPerGroup); | ||
| if (index <= 0 || index > this.data.length) flag = false; | ||
| else { | ||
| const x = this.x; | ||
| const y = this.y; | ||
| for (let i = 0; i < this.data[index - 1].items.length; i++) { | ||
| const t = this.data[index - 1].items[i]; | ||
| const item = t as ItemWithSize; | ||
| let left = 0; | ||
| if (this.offset < 0) { | ||
| left = item.left - this.offset; | ||
| } else { | ||
| left = item.left; | ||
| } | ||
| if ( | ||
| x >= left && | ||
| x <= left + item.width && | ||
| y >= | ||
| item.top + //自身顶点 | ||
| heightPerGroup * (index - 1) + //上一组的高度 | ||
| this.lineTop - //时间轴边距 | ||
| (rect.top - crect.top) && //timeline对象与父容器的距离 | ||
| y <= | ||
| item.top + | ||
| heightPerGroup * (index - 1) + | ||
| this.lineTop - | ||
| (rect.top - crect.top) + | ||
| item.height | ||
| ) { | ||
| top = item.top + heightPerGroup * (index - 1); | ||
| height = item.height; | ||
| curItem = item; | ||
| flag = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return { flag, top, height, item: curItem, group: this.data[index - 1] }; | ||
| } | ||
| private onDbClick(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| if (this.lineOptions.dblClickEvent) { | ||
| const curDate = this.getTimeByPx(this.x); | ||
| const { item, group } = this.isInItems(); | ||
| this.lineOptions.dblClickEvent( | ||
| event, | ||
| curDate, | ||
| Object.assign({}, item), | ||
| Object.assign({}, group) | ||
| ); | ||
| } | ||
| } | ||
| private onResize() { | ||
| this.isSelecting = false; | ||
| this.drawTimeline(); | ||
| } | ||
| private onWheel(event: WheelEvent) { | ||
| event.preventDefault(); | ||
| this.zoomLevel -= event.deltaY * 0.001; | ||
| if (this.zoomLevel < 0.1) this.zoomLevel = 0.1; | ||
| if (this.zoomLevel > 10) this.zoomLevel = 10; | ||
| this.isSelecting = false; | ||
| this.drawTimeline(); | ||
| } | ||
| //是否处于拖拽状态 | ||
| private isDragging = false; | ||
| //是否处于选中状态,即当鼠标处于数据块中时不再触发拖动时间轴而是开始选中,当未处于数据块中则为拖动 | ||
| private isSelecting = false; | ||
| private lastX = 0; | ||
| private endX = 0; | ||
| private selectTop = 0; | ||
| private selectHeight = 0; | ||
| private onMouseDown(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| this.isDragging = true; | ||
| this.lastX = event.clientX; | ||
| this.endX = event.clientX; | ||
| if (this.data) { | ||
| const { flag, top, height } = this.isInItems(); | ||
| this.isSelecting = flag; | ||
| this.selectTop = top; | ||
| this.selectHeight = height; | ||
| } | ||
| } | ||
| private onMouseMove(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| if (this.isDragging) { | ||
| if (!this.isSelecting) { | ||
| const dx = event.clientX - this.lastX; | ||
| this.offset += dx; | ||
| this.lastX = event.clientX; | ||
| } else { | ||
| //处理选中显示 | ||
| this.endX = event.clientX; | ||
| } | ||
| } | ||
| const rect = this.timeline.getBoundingClientRect(); | ||
| this.y = event.clientY - rect.top; | ||
| this.x = event.clientX - rect.left; | ||
| this.drawTimeline(); | ||
| } | ||
| private onMouseUp(event: MouseEvent) { | ||
| event.preventDefault(); | ||
| this.isDragging = false; | ||
| } | ||
| private getTimeByPx(x) { | ||
| let timelineWidth = this.timeline.clientWidth * this.zoomLevel; | ||
| let range = this.endDate.getTime() - this.startDate.getTime(); | ||
| let pixelsPerMs = timelineWidth / range; | ||
| if (x < this.lineOptions.textWidth!) x = this.lineOptions.textWidth!; | ||
| const curDate = new Date( | ||
| this.startDate.getTime() + (x - this.lineOptions.textWidth!) / pixelsPerMs | ||
| ); | ||
| return curDate; | ||
| } | ||
| } | ||
| export default Timeline; | ||
| export default TimeLine; | ||
| export { Group, GroupOption, Item, LineOption, Menu, h }; |
+4
-6
| import GroupOption from "./groupOption"; | ||
| import item from "./item"; | ||
| class Group { | ||
| import Item from "./item"; | ||
| export default class Group { | ||
| public groupId: number; | ||
| public groupName: string; | ||
| public items: Array<item>; | ||
| public items: Array<Item>; | ||
| public groupOptions: GroupOption = new GroupOption(); | ||
@@ -12,3 +12,3 @@ | ||
| groupName: string, | ||
| items: Array<item>, | ||
| items: Array<Item>, | ||
| groupOptions?: GroupOption | ||
@@ -22,3 +22,1 @@ ) { | ||
| } | ||
| export default Group; |
@@ -1,2 +0,2 @@ | ||
| class GroupOption { | ||
| export default class GroupOption { | ||
| public textColor?; | ||
@@ -6,3 +6,7 @@ public backgroundColor?; | ||
| constructor(textColor: string = "#fff", backgroundColor: string = "#999", textSize: number = 14) { | ||
| constructor( | ||
| textColor: string = "#fff", | ||
| backgroundColor: string = "#999", | ||
| textSize: number = 14 | ||
| ) { | ||
| this.textColor = textColor; | ||
@@ -13,3 +17,1 @@ this.backgroundColor = backgroundColor; | ||
| } | ||
| export default GroupOption; |
+1
-3
@@ -1,2 +0,2 @@ | ||
| class Item { | ||
| export default class Item { | ||
| public itemId: number; | ||
@@ -19,3 +19,1 @@ public itemName: string; | ||
| } | ||
| export default Item; |
| import Item from "./item"; | ||
| class ItemWithSize extends Item { | ||
| public width:number; | ||
| public height:number; | ||
| public top:number; | ||
| public left:number; | ||
| export default class ItemWithSize extends Item { | ||
| public width: number; | ||
| public height: number; | ||
| public top: number; | ||
| public left: number; | ||
| } | ||
| export default ItemWithSize; |
@@ -5,3 +5,3 @@ import Group from "./group"; | ||
| class LineOption { | ||
| export default class LineOption { | ||
| public xAxisColor?; | ||
@@ -29,3 +29,3 @@ public xAxisBgColor?; | ||
| yAxisColor: string = "#fff", | ||
| yAxisBgColor: string = "#666", | ||
| yAxisBgColor: string = "transparent", | ||
| textWidth: number = 60, | ||
@@ -64,3 +64,1 @@ tipTextSize: number = 14, | ||
| } | ||
| export default LineOption; |
+1
-3
| import Group from "./group"; | ||
| class Menu { | ||
| export default class Menu { | ||
| public text: string = ""; | ||
@@ -42,3 +42,1 @@ public icon: string = ""; | ||
| } | ||
| export default Menu; |
+1
-1
| { | ||
| "name": "glibline", | ||
| "version": "0.0.4", | ||
| "version": "0.0.5", | ||
| "description": "时间轴(timeline),用于实现根据时间对数据事件操作。", | ||
@@ -5,0 +5,0 @@ "main": "index.ts", |
+7
-2
@@ -9,2 +9,6 @@ <!-- [](LICENSE) --> | ||
| ### 提醒 | ||
| ##### 1. 选中菜单支持图片类型路径,以及fontawesome等样式,但是需要调用本组件项目已经完成对fontawesome的样式支持,否则无法展示 | ||
| ##### 2. 当为图片类型路径时,iconColor无效 | ||
| ### 使用 | ||
@@ -15,4 +19,5 @@ 定义容器 | ||
| ~~~ | ||
| 初始化对象。LineOption参考引入:import LineOption from "glibline/models/lineOption" | ||
| 初始化对象。LineOption参考引入:import {LineOption} from "glibline" | ||
| ~~~ | ||
| import GlibLine,{LineOption,GroupOption} from 'glibline' | ||
| const gline = new GlibLine( | ||
@@ -49,3 +54,3 @@ "container", | ||
| ~~~ | ||
| 设置组对象。GroupOption参考引入:import GroupOption from "glibline/models/groupOption" | ||
| 设置组对象。GroupOption参考引入:import {GroupOption} from "glibline" | ||
| ~~~ | ||
@@ -52,0 +57,0 @@ const groups = [ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
32199
1.61%16
6.67%838
0.6%96
5.49%1
Infinity%