UNPKG

@xiaomengqiang/charts

Version:

hcharts library for web visualization

473 lines (451 loc) 19.6 kB
function _createForOfIteratorHelperLoose(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (t) return (t = t.call(r)).next.bind(t); if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var o = 0; return function () { return o >= r.length ? { done: !0 } : { done: !1, value: r[o++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } import { formatDate } from '../../util/convert.js'; import defendXSS from '../../util/defendXSS.js'; import Theme from '../../feature/token/index.js'; import chartToken from './chartToken.js'; /** * Copyright (c) 2024 - present OpenTiny HUICharts Authors. * Copyright (c) 2024 - present Huawei Cloud Computing Technologies Co., Ltd. * * Use of this source code is governed by an MIT-style license. * * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. * */ var TimeLine = /*#__PURE__*/function () { function TimeLine(canvasId, currentTime, option) { // 显示元素 this.showElement = function (element) { element.style.display = 'block'; }; this.hideElement = function (element) { element.style.display = 'none'; }; this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); // 获取配置项 this.option = option; // 当前时间 this.currentTime = currentTime; // 自定义时间轴的背景色 this.canvas.style.background = this.option.background || 'transparent'; // 自定义时间轴刻度线的颜色 this.axisLineColor = this.option.axisLineStyle && this.option.axisLineStyle.color || Theme.config.xAxisTickLineColor; // 自定义时间轴文本颜色 this.labelColor = this.option.labelStyle && this.option.labelStyle.color || Theme.config.xAxisLabelColor; // 整个时间轴的时间 this.totalHours = null; // 刻度间隔(分钟) this.minuteStep = [1, 2, 5, 10, 15, 20, 30, 60, 120, 180, 240, 360, 720, 1440]; // 最小刻度间距20px this.minScaleSpacing = 20; // 允许的最小大格长度px值 如果调小 大格会变密集 this.minLargeScaleSpacing = 80; // 缩放层级 this.zoom = 24; // 鼠标选中位置 this.selectedPosition = []; // 弹窗定时器 this.setTipTimeOut = null; // 初始化时间轴 this.init(); var _this = this; this.eventListener = { mouseup: function mouseup(event) { _this.clickEvent(event); _this.hoverMove(event); }, mousemove: function mousemove(event) { _this.hoverMove(event); }, mouseleave: function mouseleave(event) { _this.init(); document.getElementsByClassName('timeline_hoverPointer')[0].style.display = 'none'; document.getElementsByClassName('timeline_hoverTooltip')[0].style.display = 'none'; } }; this.canvas.addEventListener('mousemove', this.eventListener.mousemove); this.canvas.addEventListener('mouseup', this.eventListener.mouseup); this.canvas.addEventListener('mouseleave', this.eventListener.mouseleave); } var _proto = TimeLine.prototype; _proto.init = function init() { // 更新初始时间戳 this.refreshStartTimestamp(); // 清空画布 this.clearCanvas(); // 画刻度 this.drawScale(); // 画当前时间游标 this.option.currentTime && this.drawCursor(this.option.currentTime); // 画warning事件 this.option.alarmData && this.drawAlarm(this.option); // 画状态区间背景 this.drawRoundRect(20, this.canvas.height - 40, this.canvas.width - 40, 8, [4, 4, 4, 4]); // 画状态区间 this.option.statusRangeData && this.drawStatusRange(this.option); } // 更新初始时间戳 ; _proto.refreshStartTimestamp = function refreshStartTimestamp() { if (this.option.timeRange !== undefined) { this.totalHours = (this.option.timeRange.endTime - this.option.timeRange.startTime) / (60 * 60 * 1000); } // 当currentTime改变或者整个时间轴的totalHours改变的时候 就刷新左边开始时间 this.startTimestamp = this.option.timeRange && this.option.timeRange.startTime || this.currentTime - this.totalHours * 60 * 60 * 1000; } // 清空画布 ; _proto.clearCanvas = function clearCanvas() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } // 画刻度 ; _proto.drawScale = function drawScale() { // px/分钟 var oneMinutePx = (this.canvas.width - 40) / (this.totalHours * 60); // px/毫秒 var oneMSPx = oneMinutePx / (60 * 1000); // 刻度间隔 默认20px var scaleSpacing = this.minScaleSpacing; // 每格代表多少分钟 var scaleUnit = scaleSpacing / oneMinutePx; var len = this.minuteStep.length; // 配置项刻度间隔 if (this.option.interval !== undefined) { scaleUnit = this.option.interval; scaleSpacing = oneMinutePx * scaleUnit; } // 自动根据画布长度选择合适的刻度间隔 else { for (var i = 0; i < len; i++) { if (scaleUnit < this.minuteStep[i]) { scaleUnit = this.minuteStep[i]; scaleSpacing = oneMinutePx * scaleUnit; break; } } } // 大刻度相当于多少分钟 var mediumStep = 30; if (this.option.mediumStep !== undefined) { mediumStep = this.option.mediumStep; } else { for (var _i = 0; _i < len; _i++) { if (this.minLargeScaleSpacing / oneMinutePx <= this.minuteStep[_i]) { mediumStep = this.minuteStep[_i]; break; } } } var totalScales = (this.canvas.width - 40) / scaleSpacing; // 某个刻度距离最左端的距离 var scaleLeft = 0; // 某个刻度的时间 var scaleTime = this.startTimestamp; var lineHeight; var leftOffsetMs = scaleUnit * 60 * 1000 - this.startTimestamp % (scaleUnit * 60 * 1000); // 开始时间偏移距离(px) var leftOffsetPx = leftOffsetMs * oneMSPx; // 一刻度多少毫秒 var oneScalesMS = scaleSpacing / oneMSPx; var scaleLineHeight = this.canvas.height - 30; // 画刻度线和标签 for (var _i2 = 0; _i2 < totalScales + 1; _i2++) { var date = new Date(scaleTime); if (scaleTime / (60 * 1000) % mediumStep === 0) { // 大格刻度标签长度 lineHeight = 6; var scaleText = this.createScaleText(date); this.ctx.font = 'normal 12px HarmonyOS_Sans'; // 文字颜色 this.ctx.fillStyle = this.labelColor; this.ctx.fillText(scaleText, scaleLeft + 20 - this.ctx.measureText(scaleText).width / 2, this.canvas.height - 4); } else { // 小格刻度标签长度 lineHeight = 3; } this.ctx.beginPath(); this.ctx.moveTo(scaleLeft + 20, scaleLineHeight); this.ctx.lineTo(scaleLeft + 20, scaleLineHeight + lineHeight); this.ctx.closePath(); this.ctx.lineWidth = 1; // 刻度线颜色 this.ctx.strokeStyle = this.axisLineColor; this.ctx.stroke(); // 距离 = 开始偏移距离 + 格数 * 每格得px; scaleLeft = leftOffsetPx + _i2 * scaleSpacing; // 时间 = 左侧开始时间 + 偏移时间 + 格数 * 一格多少毫秒 scaleTime = this.startTimestamp + leftOffsetMs + _i2 * oneScalesMS; } } // 画当前时间游标 ; _proto.drawCursor = function drawCursor(currentTime) { this.selectedPosition.push(this.getTimePos(currentTime), formatDate(currentTime, 'hh:mm')); this.drawSelectedPointer(this.selectedPosition); } // 叠加warning事件圆点 ; _proto.drawAlarm = function drawAlarm(option) { var alarmData = option.alarmData; var background = option.customBackground; for (var i = 0; i < alarmData.length; i++) { var x = this.getTimePos(alarmData[i].time); var type = alarmData[i].type; this.ctx.beginPath(); this.ctx.arc(x, this.canvas.height - 60, 7, 0, 2 * Math.PI); this.ctx.closePath(); this.ctx.fillStyle = background[type] || Theme.config.colorState.colorNone; this.ctx.fill(); } } // 叠加状态区间 ; _proto.drawStatusRange = function drawStatusRange(option) { var statusRangeData = option.statusRangeData; var timeRange = option.timeRange; var background = option.customBackground; for (var i = 0; i < statusRangeData.length; i++) { var startPosition = this.getTimePos(statusRangeData[i].time[0]); var endPosition = this.getTimePos(statusRangeData[i].time[1]); var type = statusRangeData[i].type; this.ctx.beginPath(); if (statusRangeData[i].time[0] === timeRange.startTime && statusRangeData[i].time[1] === timeRange.endTime) { this.roundRect(startPosition, this.canvas.height - 40, endPosition - startPosition, 8, [4, 4, 4, 4]); } else if (statusRangeData[i].time[0] === timeRange.startTime) { this.roundRect(startPosition, this.canvas.height - 40, endPosition - startPosition, 8, [4, 0, 0, 4]); } else if (statusRangeData[i].time[1] === timeRange.endTime) { this.roundRect(startPosition, this.canvas.height - 40, endPosition - startPosition, 8, [0, 4, 4, 0]); } else { this.ctx.rect(startPosition, this.canvas.height - 40, endPosition - startPosition, 8); } this.ctx.closePath(); this.ctx.fillStyle = background[type] || Theme.config.colorState.colorNone; this.ctx.fill(); } } // 绘制状态区间背景的圆角矩形 ; _proto.drawRoundRect = function drawRoundRect(x, y, width, height, radius) { this.ctx.beginPath(); this.roundRect(x, y, width, height, radius); this.ctx.closePath(); this.ctx.fillStyle = chartToken.roundRectBg; this.ctx.fill(); }; _proto.roundRect = function roundRect(x, y, width, height, radiusArray) { // 左上角 this.ctx.moveTo(x + radiusArray[0], y); this.ctx.lineTo(x + width - radiusArray[1], y); this.ctx.arcTo(x + width, y, x + width, y + radiusArray[1], radiusArray[1]); // 右上角 this.ctx.lineTo(x + width, y + height - radiusArray[2]); this.ctx.arcTo(x + width, y + height, x + width - radiusArray[2], y + height, radiusArray[2]); // 右下角 this.ctx.lineTo(x + radiusArray[3], y + height); this.ctx.arcTo(x, y + height, x, y + height - radiusArray[3], radiusArray[3]); // 左下角 this.ctx.lineTo(x, y + radiusArray[0]); this.ctx.arcTo(x, y, x + radiusArray[0], y, radiusArray[0]); } // 鼠标悬浮事件 ; _proto.hoverMove = function hoverMove(event) { var mouseX = this.getMouseXRelativePos(event); var hoverPointer = document.getElementsByClassName('timeline_hoverPointer')[0]; var hoverTooltip = document.getElementsByClassName('timeline_hoverTooltip')[0]; var hoverTime = this.getMousePosTime(event); if (mouseX > 0 && mouseX < this.canvas.width - 40) { this.showElement(hoverPointer); // 开启悬浮指针的时间悬浮框 if (this.option.timeTooltip === true) { this.showElement(hoverTooltip); hoverTooltip.innerHTML = "<p>" + formatDate(hoverTime, 'yy-MM-dd hh:mm') + "</p>"; if (mouseX + 142 > this.canvas.width - 40) { hoverTooltip.style.left = mouseX - 1 + 20 - 142 + 'px'; } else { hoverTooltip.style.left = mouseX - 1 + 30 + 'px'; } hoverTooltip.style.top = this.canvas.height - 30 + 'px'; } hoverPointer.style.left = mouseX - 1 + 20 + 'px'; hoverPointer.style.top = this.canvas.height - 30 + 'px'; } else { this.hideElement(hoverPointer); this.hideElement(hoverTooltip); } this.setAlarmTip(event, mouseX); } // 设置Alarm点的tip ; _proto.setAlarmTip = function setAlarmTip(event, mouseX) { var _this2 = this; var mouseY = this.getMouseYRelativePos(event); var alarmTooltip = document.getElementsByClassName('timeline_alarmTooltip')[0]; // 清除定时 alarmTooltip.addEventListener('mouseenter', function (e) { if (_this2.setTipTimeOut) { clearTimeout(_this2.setTipTimeOut); } }); // 移出悬浮框关闭 alarmTooltip.addEventListener('mouseleave', function (e) { _this2.closeAlarmTip(alarmTooltip); }); var alarmData = this.option.alarmData; if (alarmData) { this.processAlarmData(alarmData, mouseX, mouseY, alarmTooltip); } }; _proto.closeAlarmTip = function closeAlarmTip(alarmTooltip) { var _this3 = this; // 增加延时 this.setTipTimeOut = setTimeout(function () { _this3.hideElement(alarmTooltip); }, 200); }; _proto.processAlarmData = function processAlarmData(alarmData, mouseX, mouseY, alarmTooltip) { for (var _iterator = _createForOfIteratorHelperLoose(alarmData), _step; !(_step = _iterator()).done;) { var data = _step.value; var x = this.getTimePos(data.time); if (this.isMouseInCircle(mouseX + 20, mouseY, x, this.canvas.height - 60)) { if (this.setTipTimeOut) { clearTimeout(this.setTipTimeOut); } this.showElement(alarmTooltip); this.option.onCircleHover(alarmTooltip, data); if (x - alarmTooltip.offsetWidth / 2 < 0) { alarmTooltip.style.left = '0px'; } else if (x - alarmTooltip.offsetWidth / 2 > this.canvas.width - alarmTooltip.offsetWidth - 40) { alarmTooltip.style.left = this.canvas.width - alarmTooltip.offsetWidth + 'px'; } else { alarmTooltip.style.left = x - alarmTooltip.offsetWidth / 2 + 'px'; } alarmTooltip.style.top = this.canvas.height - 80 - alarmTooltip.clientHeight + 'px'; return; } // 移出圆点清除悬浮框 if (this.setTipTimeOut) { clearTimeout(this.setTipTimeOut); } this.closeAlarmTip(alarmTooltip); } } // 判断鼠标是否在圆点内 ; _proto.isMouseInCircle = function isMouseInCircle(mouseX, mouseY, x, y) { var distance = Math.sqrt(Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2)); return distance <= 8; } // 鼠标点击事件 ; _proto.clickEvent = function clickEvent(event) { // 进入选中状态 this.isSelected = true; var mouseX = this.getMouseXRelativePos(event); var time = this.getMousePosTime(event); // 选中时间 var seletedTime = time.getTime(); // 当前北京时间 var currentTime = new Date().getTime(); if (seletedTime <= currentTime) { this.option.onClick && this.option.onClick(time.getTime()); // 如果有点击时间,将时间赋值给配置项的当前时间 // this.option.currentTime = time; this.selectedPosition.splice(0, this.selectedPosition.length); this.selectedPosition.push(mouseX + 20, formatDate(time, 'hh:mm')); var selectedPointer = document.getElementsByClassName('timeline_selectedPointer')[0]; var selectedTooltip = document.getElementsByClassName('timeline_selectedTooltip')[0]; var selectedCircle = document.getElementsByClassName('timeline_selectedCircle')[0]; if (mouseX > 0 && mouseX < this.canvas.width - 40) { this.drawSelectedPointer(this.selectedPosition); } else { this.hideElement(selectedPointer); this.hideElement(selectedTooltip); this.hideElement(selectedCircle); } } } // 画选中状态的游标 ; _proto.drawSelectedPointer = function drawSelectedPointer(selectedPosition) { var selectedPointer = document.getElementsByClassName('timeline_selectedPointer')[0]; var selectedTooltip = document.getElementsByClassName('timeline_selectedTooltip')[0]; var selectedCircle = document.getElementsByClassName('timeline_selectedCircle')[0]; selectedPointer.style.left = selectedPosition[0] - 1 + 'px'; selectedPointer.style.top = this.canvas.height - 42 + 'px'; this.showElement(selectedPointer); selectedTooltip.innerHTML = "<p>" + defendXSS(selectedPosition[1]) + "</p>"; selectedTooltip.style.left = selectedPosition[0] - 20 + 'px'; selectedTooltip.style.top = this.canvas.height - 16 + 'px'; this.showElement(selectedTooltip); selectedCircle.style.left = selectedPosition[0] - 4 + 'px'; selectedCircle.style.top = this.canvas.height - 50 + 'px'; this.showElement(selectedCircle); } // 设置长短刻度标签文字 ; _proto.createScaleText = function createScaleText(time) { if (time.getTime() === this.option.timeRange.startTime) { return formatDate(time, '00:00'); } else if (time.getTime() === this.option.timeRange.endTime && (this.option.timeRange.endTime - this.option.timeRange.startTime) % (24 * 60 * 60 * 1000) === 0) { return formatDate(time, '24:00'); } return formatDate(time, 'hh:mm'); } // 获取鼠标相对于canvas元素左侧20px的相对位置 ; _proto.getMouseXRelativePos = function getMouseXRelativePos(event) { var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; var x = event.pageX || event.clientX + scrollX; // canvas元素距离窗口左侧距离 var baseLeft = this.canvas.getBoundingClientRect().x; return x - baseLeft - 20; } // 获取鼠标相对于canvas元素上侧的相对位置 ; _proto.getMouseYRelativePos = function getMouseYRelativePos(event) { var scrollY = document.documentElement.scrollTop || document.body.scrollTop; var y = event.pageY || event.clientY + scrollY; // canvas元素距离窗口左侧距离 var baseTop = this.canvas.getBoundingClientRect().y; return y - baseTop; } // 根据画布的宽度和时间计算出鼠标所在的时间点 ; _proto.getMousePosTime = function getMousePosTime(event) { var posX = this.getMouseXRelativePos(event); // 每毫秒多少像素 var onePxsMS = (this.canvas.width - 40) / (this.totalHours * 60 * 60 * 1000); var time = new Date(this.startTimestamp + posX / onePxsMS); return time; } // 根据时间点计算所在点距离canvas左侧20px的相对位置 ; _proto.getTimePos = function getTimePos(time) { // 每毫秒多少像素 var onePxsMS = (this.canvas.width - 40) / (this.totalHours * 60 * 60 * 1000); var posX = (time - this.startTimestamp) * onePxsMS + 20; return posX; }; // 设置当前时间 _proto.setCurrentTime = function setCurrentTime(time) { var newTime; if (typeof time === 'string') { newTime = new Date(time).getTime(); } else if (typeof time === 'object') { newTime = time.getTime && time.getTime(); } else if (typeof time === 'number') { newTime = time; } this.currentTime = newTime; this.init(); }; return TimeLine; }(); export { TimeLine as default };