|
|
|
import moment from "moment";
|
|
|
|
import {h} from "vue";
|
|
|
|
import {urlRE} from "vite/dist/node/utils/cssUtils";
|
|
|
|
|
|
|
|
export class BoxDrawer{
|
|
|
|
|
|
|
|
private canvas: any = null;
|
|
|
|
private canvasContext: any = null;
|
|
|
|
private targetCanvas: any = null;
|
|
|
|
private targetCanvasContext: any = null;
|
|
|
|
// private canvasContext3d: any = null;
|
|
|
|
|
|
|
|
private borderWidth: number = 0;
|
|
|
|
private borderHeight: number = 0;
|
|
|
|
|
|
|
|
private readonly width: number = null;
|
|
|
|
private readonly height: number = null;
|
|
|
|
private readonly paddingLeft: number = 100;
|
|
|
|
private readonly paddingRight: number = 100;
|
|
|
|
private readonly paddingTop: number = 50;
|
|
|
|
private readonly paddingBottom: number = 50;
|
|
|
|
private readonly colorChart: ColorChart = null;
|
|
|
|
private readonly values: any = null;
|
|
|
|
private readonly verticalScaleSpecialCount: number = 6;
|
|
|
|
private readonly horizontalScaleSpecialCount: number = 6;
|
|
|
|
|
|
|
|
// 垂直刻度
|
|
|
|
private readonly verticalScaleLine: number = 18;
|
|
|
|
// 水平刻度
|
|
|
|
private readonly horizontalScaleLine: number = 20;
|
|
|
|
public base64Image: string = null;
|
|
|
|
|
|
|
|
private verticalScaleIntervalLength: number = 0;
|
|
|
|
private horizontalScaleIntervalLength: number = 0;
|
|
|
|
|
|
|
|
private paddingColorLeft: number = 50;
|
|
|
|
private defaultColorWidth: number = 30;
|
|
|
|
|
|
|
|
// private boxes: any = [];
|
|
|
|
private calc: Calc = null;
|
|
|
|
private box: Box = null;
|
|
|
|
|
|
|
|
constructor(width: number, height: number, colorChart: ColorChart, values: any, id: string) {
|
|
|
|
if (id == null) return;
|
|
|
|
|
|
|
|
this.width = width;
|
|
|
|
this.height = height;
|
|
|
|
this.colorChart = colorChart;
|
|
|
|
this.values = values;
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
this.drawCoordinate();
|
|
|
|
this.drawColorChart();
|
|
|
|
this.draw();
|
|
|
|
|
|
|
|
this.setTargetCanvas(id);
|
|
|
|
this.base64Image = this.canvas.toDataURL();
|
|
|
|
}
|
|
|
|
|
|
|
|
private drawColorChart(): void {
|
|
|
|
let startX = this.paddingLeft + this.horizontalScaleLine + this.borderWidth + this.paddingColorLeft;
|
|
|
|
let heightInterval = 0;
|
|
|
|
let startY = this.paddingTop + this.verticalScaleLine + heightInterval;
|
|
|
|
let interval = (this.borderHeight - heightInterval) / (this.colorChart.colors.length)
|
|
|
|
|
|
|
|
this.canvasContext.font = "normal 18px Verdana";
|
|
|
|
this.canvasContext.fillStyle = "#000000";
|
|
|
|
|
|
|
|
for(let lastLen = this.colorChart.colors.length - 1, index = lastLen; index >= 0; index --){
|
|
|
|
const targetIndex = lastLen - index;
|
|
|
|
const _y = startY + index * interval;
|
|
|
|
|
|
|
|
this.canvasContext.fillStyle = this.colorChart.colors[targetIndex];
|
|
|
|
this.canvasContext.fillRect(startX, _y, this.defaultColorWidth, interval);
|
|
|
|
|
|
|
|
this.canvasContext.fillStyle = "#000000";
|
|
|
|
if (index == 0){
|
|
|
|
if (this.colorChart.showStartValue){
|
|
|
|
const text = this.colorChart.values[targetIndex + 1];
|
|
|
|
this.canvasContext.fillText(text, startX + this.defaultColorWidth, _y + 9);
|
|
|
|
}
|
|
|
|
|
|
|
|
const text = this.colorChart.values[targetIndex];
|
|
|
|
this.canvasContext.fillText(text, startX + this.defaultColorWidth, _y + interval + 9);
|
|
|
|
}else if (index == lastLen){
|
|
|
|
if (this.colorChart.showEndValue){
|
|
|
|
const text = this.colorChart.values[targetIndex];
|
|
|
|
this.canvasContext.fillText(text, startX + this.defaultColorWidth, _y + interval + 9);
|
|
|
|
}
|
|
|
|
|
|
|
|
const text = this.colorChart.values[targetIndex + 1];
|
|
|
|
this.canvasContext.fillText(text, startX + this.defaultColorWidth, _y + 9);
|
|
|
|
}else{
|
|
|
|
const text = this.colorChart.values[targetIndex];
|
|
|
|
this.canvasContext.fillText(text, startX + this.defaultColorWidth, _y + interval + 9);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private draw(): void{
|
|
|
|
let startX = this.paddingLeft + this.horizontalScaleLine - this.verticalScaleIntervalLength / 2;
|
|
|
|
let startY = this.paddingTop + this.verticalScaleLine - this.horizontalScaleIntervalLength / 2;
|
|
|
|
|
|
|
|
let boxes = [];
|
|
|
|
for(let dataIndex = 0, len = this.values.radar_data.length, lastDataLen = len - 1; dataIndex < len; dataIndex++){
|
|
|
|
let value = this.values.radar_data[dataIndex];
|
|
|
|
|
|
|
|
let rows = [];
|
|
|
|
for(let lastInfoLen = this.values.radar_info.length - 1, infoIndex = lastInfoLen; infoIndex >= 0; infoIndex--){
|
|
|
|
let x = startX + dataIndex * this.verticalScaleIntervalLength;
|
|
|
|
let y = startY + (this.borderHeight - infoIndex * this.horizontalScaleIntervalLength);
|
|
|
|
let width = this.verticalScaleIntervalLength;
|
|
|
|
let height = this.horizontalScaleIntervalLength;
|
|
|
|
if (dataIndex == 0) {
|
|
|
|
x = x + this.verticalScaleIntervalLength / 2;
|
|
|
|
width = width / 2;
|
|
|
|
}
|
|
|
|
if (infoIndex == lastInfoLen){
|
|
|
|
y = y + this.horizontalScaleIntervalLength / 2;
|
|
|
|
height = height / 2;
|
|
|
|
}
|
|
|
|
if (dataIndex == lastDataLen){
|
|
|
|
width = width / 2;
|
|
|
|
}
|
|
|
|
if (infoIndex == 0){
|
|
|
|
height = height / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
let radarInfo = this.values.radar_info[infoIndex];
|
|
|
|
let targetValue = value[radarInfo.col_name];
|
|
|
|
let color = this.colorChart.getColor(targetValue);
|
|
|
|
if (color == null) continue;
|
|
|
|
this.canvasContext.fillStyle = color;
|
|
|
|
this.canvasContext.fillRect(x, y, width, height);
|
|
|
|
rows.push(new Box(x, y, width, height, targetValue, radarInfo.col_factor, value.data_time, radarInfo.col_unit));
|
|
|
|
}
|
|
|
|
boxes.push(rows);
|
|
|
|
}
|
|
|
|
this.createCalc(boxes);
|
|
|
|
}
|
|
|
|
|
|
|
|
private createCalc(boxes: any): void{
|
|
|
|
this.calc = new Calc(this.width, this.height, this.paddingLeft + this.verticalScaleLine, this.paddingTop + this.horizontalScaleLine,
|
|
|
|
this.paddingRight + this.horizontalScaleLine, this.paddingBottom + this.verticalScaleLine, boxes);
|
|
|
|
}
|
|
|
|
|
|
|
|
private resetValues() : void{
|
|
|
|
this.values.radar_data.sort((a, b) => {
|
|
|
|
return moment(a.data_time).isBefore(b.data_time, 'second');
|
|
|
|
});
|
|
|
|
|
|
|
|
this.values.radar_info.sort((a, b) => {
|
|
|
|
return a.col_factor - b.col_factor;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private init(): void{
|
|
|
|
// 创建画布
|
|
|
|
this.canvas = document.createElement('canvas');
|
|
|
|
this.canvas.width = this.width;
|
|
|
|
this.canvas.height = this.height;
|
|
|
|
this.canvasContext = this.canvas.getContext('2d');
|
|
|
|
|
|
|
|
this.fillBackground();
|
|
|
|
this.resetValues();
|
|
|
|
}
|
|
|
|
|
|
|
|
private setTargetCanvas(id): void{
|
|
|
|
this.targetCanvas = document.getElementById(id);
|
|
|
|
this.targetCanvas.width = this.width;
|
|
|
|
this.targetCanvas.height = this.height;
|
|
|
|
this.targetCanvasContext = this.targetCanvas.getContext('2d');
|
|
|
|
// canvasContext.drawImage(this.canvas, 0, 0, 1500, 500);
|
|
|
|
this.copyCanvas();
|
|
|
|
this.bindMoveEvent();
|
|
|
|
}
|
|
|
|
|
|
|
|
private copyCanvas(): void{
|
|
|
|
const imgData= this.canvasContext.getImageData(0,0, this.width, this.height);
|
|
|
|
this.targetCanvasContext.putImageData(imgData, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private bindMoveEvent(): void{
|
|
|
|
this.targetCanvas.addEventListener('mousemove', (e) => {
|
|
|
|
let box = this.calc.calc(e.offsetX, e.offsetY);
|
|
|
|
if (box == null) {
|
|
|
|
if (this.box != null){
|
|
|
|
this.copyCanvas();
|
|
|
|
this.box = null;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.box != null){
|
|
|
|
if (this.box.id == box.id) return;
|
|
|
|
this.copyCanvas();
|
|
|
|
this.box = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
box.show(this.targetCanvasContext);
|
|
|
|
this.box = box;
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
private fillBackground(): void{
|
|
|
|
this.canvasContext.fillStyle = 'white'
|
|
|
|
this.canvasContext.fillRect(0,0, this.width,this.height)
|
|
|
|
}
|
|
|
|
|
|
|
|
private drawCoordinate(): void{
|
|
|
|
this.drawBorder();
|
|
|
|
this.drawVerticalScale();
|
|
|
|
this.drawHorizontalScale();
|
|
|
|
}
|
|
|
|
|
|
|
|
private drawVerticalScale(): void{
|
|
|
|
let scaleCount = this.values.radar_data.length;
|
|
|
|
this.verticalScaleIntervalLength = this.borderWidth / (scaleCount - 1);
|
|
|
|
let scaleSpecial = parseInt((scaleCount * 1.0 / this.verticalScaleSpecialCount).toString());
|
|
|
|
|
|
|
|
let startX = this.paddingLeft + this.horizontalScaleLine;
|
|
|
|
let startY = this.paddingTop + this.verticalScaleLine;
|
|
|
|
for(let index = 0, len = scaleCount, lastLen = scaleCount - 1; index < len; index++){
|
|
|
|
if (index == 0 || index == lastLen) continue;
|
|
|
|
|
|
|
|
const flag = index % scaleSpecial == 0;
|
|
|
|
this.setScaleStyle(flag);
|
|
|
|
const yInterval = flag ? this.verticalScaleLine : this.verticalScaleLine / 2;
|
|
|
|
|
|
|
|
const _x = startX + index * this.verticalScaleIntervalLength;
|
|
|
|
this.canvasContext.beginPath();
|
|
|
|
this.canvasContext.moveTo(_x, startY);
|
|
|
|
this.canvasContext.lineTo(_x,startY - yInterval);
|
|
|
|
this.canvasContext.moveTo(_x, startY + this.borderHeight);
|
|
|
|
this.canvasContext.lineTo(_x,startY + yInterval + this.borderHeight);
|
|
|
|
this.canvasContext.stroke();
|
|
|
|
|
|
|
|
if (flag){
|
|
|
|
this.canvasContext.font = "normal 20px Verdana";
|
|
|
|
this.canvasContext.fillStyle = "#000000";
|
|
|
|
const text = moment(this.values.radar_data[index].data_time).format("HH:mm");
|
|
|
|
this.canvasContext.fillText(text, _x - this.canvasContext.measureText(text).width / 2, startY + yInterval + this.borderHeight + 36);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private drawHorizontalScale(): void{
|
|
|
|
let scaleCount = this.values.radar_info.length;
|
|
|
|
this.horizontalScaleIntervalLength = this.borderHeight / (scaleCount - 1);
|
|
|
|
let scaleSpecial = parseInt((scaleCount * 1.0 / this.horizontalScaleSpecialCount).toString());
|
|
|
|
let startX = this.paddingLeft + this.horizontalScaleLine;
|
|
|
|
let startY = this.paddingTop + this.verticalScaleLine;
|
|
|
|
const lastLen = scaleCount - 1;
|
|
|
|
for(let index = lastLen; index >= 0; index--){
|
|
|
|
const flag = index % scaleSpecial == 0;
|
|
|
|
this.setScaleStyle(flag);
|
|
|
|
const xInterval = flag? this.horizontalScaleLine : this.horizontalScaleLine / 2;
|
|
|
|
|
|
|
|
const _y = startY + (this.borderHeight - index * this.horizontalScaleIntervalLength);
|
|
|
|
this.canvasContext.beginPath();
|
|
|
|
this.canvasContext.moveTo(startX, _y);
|
|
|
|
this.canvasContext.lineTo(startX - xInterval, _y);
|
|
|
|
this.canvasContext.moveTo(this.borderWidth + startX, _y);
|
|
|
|
this.canvasContext.lineTo(this.borderWidth + startX + xInterval, _y);
|
|
|
|
this.canvasContext.stroke();
|
|
|
|
|
|
|
|
if (flag){
|
|
|
|
this.canvasContext.font = "normal 20px Verdana";
|
|
|
|
this.canvasContext.fillStyle = "#000000";
|
|
|
|
const text = this.values.radar_info[index].col_factor;
|
|
|
|
this.canvasContext.fillText(text, startX - this.canvasContext.measureText(text).width - this.horizontalScaleLine, _y + 10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private setScaleStyle(isSpecial: boolean): void{
|
|
|
|
if (isSpecial){
|
|
|
|
this.canvasContext.lineWidth = 3;
|
|
|
|
this.canvasContext.strokeStyle = '#101010';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.canvasContext.lineWidth = 2;
|
|
|
|
this.canvasContext.strokeStyle = '#5e5e5e';
|
|
|
|
}
|
|
|
|
|
|
|
|
private drawBorder(): void{
|
|
|
|
const x = this.horizontalScaleLine + this.paddingLeft;
|
|
|
|
const y = this.verticalScaleLine + this.paddingTop;
|
|
|
|
const _x = this.width - this.horizontalScaleLine - this.paddingRight;
|
|
|
|
const _y = this.height - this.verticalScaleLine - this.paddingBottom;
|
|
|
|
|
|
|
|
this.canvasContext.beginPath();
|
|
|
|
this.canvasContext.lineWidth = "2";
|
|
|
|
this.canvasContext.strokeStyle = "black";
|
|
|
|
|
|
|
|
this.borderWidth = _x - x;
|
|
|
|
this.borderHeight = _y - y;
|
|
|
|
this.canvasContext.rect(x, y, this.borderWidth, this.borderHeight);
|
|
|
|
this.canvasContext.stroke();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ColorChart{
|
|
|
|
|
|
|
|
public colors: any = null;
|
|
|
|
public values: any = null;
|
|
|
|
public showStartValue: boolean = false;
|
|
|
|
public showEndValue: boolean = false;
|
|
|
|
|
|
|
|
private readonly valueLength: number = 0;
|
|
|
|
|
|
|
|
constructor(colors: any, values: any, showStartValue: boolean = false, showEndValue: boolean = false) {
|
|
|
|
this.verify(colors, values);
|
|
|
|
this.colors = colors;
|
|
|
|
this.values = values;
|
|
|
|
this.showStartValue = showStartValue;
|
|
|
|
this.showEndValue = showEndValue;
|
|
|
|
this.valueLength = this.values.length;
|
|
|
|
|
|
|
|
this.resetValues();
|
|
|
|
}
|
|
|
|
|
|
|
|
private resetValues(): void{
|
|
|
|
if (this.values[0] < this.values[1]) return;
|
|
|
|
this.values = this.values.reverse();
|
|
|
|
this.colors = this.colors.reverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
private verify(colors: any, values: any): void{
|
|
|
|
if (values.length <= 2)
|
|
|
|
throw new Error("色标数据至少包含两个数据");
|
|
|
|
|
|
|
|
if (colors.length != values.length - 1)
|
|
|
|
throw new Error("色标数据不正确,数据个数应为颜色个数 + 1");
|
|
|
|
}
|
|
|
|
|
|
|
|
public getColor(value: number): string{
|
|
|
|
for(let index = 1; index < this.valueLength; index ++){
|
|
|
|
if (this.values[index -1] <= value && this.values[index] > value){
|
|
|
|
return this.colors[index - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
interface IBox {
|
|
|
|
show(canvasContext: any);
|
|
|
|
}
|
|
|
|
|
|
|
|
class Box implements IBox{
|
|
|
|
id: string = null;
|
|
|
|
|
|
|
|
x: number;
|
|
|
|
y: number;
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
|
|
|
|
value: number;
|
|
|
|
dataHeight: number;
|
|
|
|
time: string;
|
|
|
|
util: string;
|
|
|
|
|
|
|
|
rectWidth: number = 180;
|
|
|
|
rectHeight: number = 80;
|
|
|
|
rectX: number;
|
|
|
|
rectY: number;
|
|
|
|
|
|
|
|
constructor(x: number, y: number, width: number, height: number, value: number, dataHeight: number, time: string, util: string) {
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.width = width;
|
|
|
|
this.height = height;
|
|
|
|
this.value = value;
|
|
|
|
|
|
|
|
this.dataHeight = dataHeight;
|
|
|
|
this.time = time;
|
|
|
|
this.util = util;
|
|
|
|
|
|
|
|
this.id = this.uuid();
|
|
|
|
}
|
|
|
|
|
|
|
|
show(canvasContext: any) {
|
|
|
|
this.setSelectStyle(canvasContext);
|
|
|
|
this.setTip(canvasContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
private setTip(canvasContext: any): void{
|
|
|
|
this.setBackground(canvasContext);
|
|
|
|
this.setText(canvasContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
private setBackground(canvasContext: any): void{
|
|
|
|
canvasContext.fillStyle = 'rgba(59, 55, 55, 0.8)';
|
|
|
|
this.rectX = this.x + this.width / 2 - this.rectWidth / 2;
|
|
|
|
this.rectY = this.y - this.height / 2 - this.rectHeight - 10;
|
|
|
|
canvasContext.fillRect(this.rectX, this.rectY, this.rectWidth, this.rectHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
private setText(canvasContext: any) : void{
|
|
|
|
canvasContext.font = "normal 14px Arial";
|
|
|
|
canvasContext.fillStyle = "#fafafa";
|
|
|
|
|
|
|
|
const centerX = this.rectX + this.rectWidth / 2;
|
|
|
|
const timeText = "time:" + this.time;
|
|
|
|
canvasContext.fillText(timeText, centerX - canvasContext.measureText(timeText).width / 2, this.rectY + 22);
|
|
|
|
const valueText = 'value:' + this.value;
|
|
|
|
canvasContext.fillText(valueText, centerX - canvasContext.measureText(valueText).width / 2, this.rectY + 18 + 26);
|
|
|
|
const heightText = 'height:' + this.dataHeight + ' ' + this.util;
|
|
|
|
canvasContext.fillText(heightText, centerX - canvasContext.measureText(heightText).width / 2, this.rectY + 18 + 18 + 30);
|
|
|
|
}
|
|
|
|
|
|
|
|
private setSelectStyle(canvasContext: any): void{
|
|
|
|
canvasContext.lineWidth = 1;
|
|
|
|
canvasContext.strokeStyle = "#ff0000";
|
|
|
|
canvasContext.strokeRect(this.x, this.y, this.width, this.height);
|
|
|
|
}
|
|
|
|
|
|
|
|
uuid(): string{
|
|
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
|
|
let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
|
|
|
return v.toString(16);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Calc{
|
|
|
|
private readonly width: number;
|
|
|
|
private readonly height: number;
|
|
|
|
|
|
|
|
private readonly paddingLeft: number;
|
|
|
|
private readonly paddingTop: number;
|
|
|
|
private readonly paddingRight: number;
|
|
|
|
private readonly paddingBottom: number;
|
|
|
|
|
|
|
|
private readonly boxWidth: number;
|
|
|
|
private readonly boxHeight: number;
|
|
|
|
|
|
|
|
private xMax: number = null;
|
|
|
|
private yMax: number = null;
|
|
|
|
|
|
|
|
private startX: number = null;
|
|
|
|
private startY: number = null;
|
|
|
|
|
|
|
|
private readonly boxes: any = null;
|
|
|
|
|
|
|
|
constructor(width: number, height: number,
|
|
|
|
paddingLeft: number, paddingTop: number, paddingRight: number, paddingBottom: number, boxes: any) {
|
|
|
|
this.width = width;
|
|
|
|
this.height = height;
|
|
|
|
this.paddingLeft = paddingLeft;
|
|
|
|
this.paddingTop = paddingTop;
|
|
|
|
this.paddingRight = paddingRight;
|
|
|
|
this.paddingBottom = paddingBottom;
|
|
|
|
this.boxes = boxes;
|
|
|
|
this.boxWidth = this.boxes[1][1].width;
|
|
|
|
this.boxHeight = this.boxes[1][1].height;
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
}
|
|
|
|
|
|
|
|
private init(): void{
|
|
|
|
this.xMax = this.width - this.paddingRight;
|
|
|
|
this.yMax = this.height - this.paddingBottom;
|
|
|
|
|
|
|
|
this.startX = this.paddingLeft - this.boxWidth / 2;
|
|
|
|
this.startY = this.paddingTop - this.boxHeight / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
public calc(x: number, y: number): Box{
|
|
|
|
if (x <= this.paddingLeft || x >= this.xMax || y <= this.paddingTop || y >= this.yMax){
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const indexX = parseInt(((x - this.startX) * 1.0 / this.boxWidth).toString());
|
|
|
|
const indexY = parseInt(((y - this.startY) * 1.0 / this.boxHeight).toString())
|
|
|
|
return this.boxes[indexX][indexY];
|
|
|
|
}
|
|
|
|
}
|