| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781 |
- /**
- * 坐标轴和图例绘制函数
- *
- * 从 u-charts 核心库搬迁的坐标轴和图例绘制相关函数
- * 用于处理X轴、Y轴、网格线和图例的绘制操作
- */
- // @ts-nocheck - 由于从 u-charts 搬迁,类型系统不兼容,暂时禁用类型检查
- import type { ChartOptions, UChartsConfig, SeriesItem } from '../data-processing/series-calculator';
- import { measureText } from '../utils/text';
- import { convertCoordinateOrigin } from '../utils/coordinate';
- import { hexToRgb } from '../utils/color';
- import { assign } from '../config';
- // Canvas 上下文类型(使用 any 以兼容小程序环境)
- export type CanvasContext = any;
- /**
- * 坐标点接口
- */
- export interface Point {
- x: number;
- y: number;
- }
- /**
- * 仪表盘选项接口
- */
- export interface GaugeOption {
- width?: number;
- labelOffset?: number;
- endAngle?: number;
- startAngle?: number;
- splitLine?: {
- splitNumber?: number;
- };
- endNumber?: number;
- startNumber?: number;
- formatter?: (val: number, index: number, opts: ChartOptions) => string;
- labelColor?: string;
- }
- /**
- * 雷达图选项接口
- */
- export interface RadarOption {
- labelPointShow?: boolean;
- labelPointColor?: string;
- labelPointRadius?: number;
- labelShow?: boolean;
- labelColor?: string;
- }
- /**
- * 图例数据项接口
- */
- export interface LegendItem {
- name: string;
- color: string;
- show?: boolean;
- legendShape?: string;
- legendText?: string;
- area?: number[];
- [key: string]: any;
- }
- /**
- * 图例数据接口
- */
- export interface LegendData {
- points: LegendItem[][];
- area: {
- start: Point;
- width: number;
- height: number;
- };
- widthArr: number[];
- heightArr: number[];
- }
- /**
- * 绘制X轴
- * @param categories - X轴分类数据
- * @param opts - 图表配置选项
- * @param config - uCharts配置对象
- * @param context - Canvas 渲染上下文
- */
- export function drawXAxis(
- categories: string[],
- opts: ChartOptions,
- config: UChartsConfig,
- context: CanvasContext
- ): void {
- let xAxisData = opts.chartData?.xAxisData;
- if (!xAxisData) return;
- let xAxisPoints = xAxisData.xAxisPoints;
- let startX = xAxisData.startX;
- let endX = xAxisData.endX;
- let eachSpacing = xAxisData.eachSpacing;
- let boundaryGap = 'center';
- if (opts.type == 'bar' || opts.type == 'line' || opts.type == 'area' || opts.type == 'scatter' || opts.type == 'bubble') {
- boundaryGap = opts.xAxis?.boundaryGap || 'center';
- }
- let startY = opts.height! - opts.area![2];
- let endY = opts.area![0];
- // 绘制滚动条
- if (opts.enableScroll && opts.xAxis?.scrollShow) {
- let scrollY = opts.height! - opts.area![2] + config.xAxisHeight;
- let scrollScreenWidth = endX - startX;
- let scrollTotalWidth = eachSpacing * (xAxisPoints.length - 1);
- if (opts.type == 'mount' && opts.extra?.mount && opts.extra.mount.widthRatio && opts.extra.mount.widthRatio > 1) {
- let widthRatio = opts.extra.mount.widthRatio > 2 ? 2 : opts.extra.mount.widthRatio;
- scrollTotalWidth += (widthRatio - 1) * eachSpacing;
- }
- let scrollWidth = scrollScreenWidth * scrollScreenWidth / scrollTotalWidth;
- let scrollLeft = 0;
- if (opts._scrollDistance_) {
- scrollLeft = -opts._scrollDistance_ * (scrollScreenWidth) / scrollTotalWidth;
- }
- context.beginPath();
- context.setLineCap('round');
- context.setLineWidth(6 * opts.pix);
- context.setStrokeStyle(opts.xAxis.scrollBackgroundColor || "#EFEBEF");
- context.moveTo(startX, scrollY);
- context.lineTo(endX, scrollY);
- context.stroke();
- context.closePath();
- context.beginPath();
- context.setLineCap('round');
- context.setLineWidth(6 * opts.pix);
- context.setStrokeStyle(opts.xAxis.scrollColor || "#A6A6A6");
- context.moveTo(startX + scrollLeft, scrollY);
- context.lineTo(startX + scrollLeft + scrollWidth, scrollY);
- context.stroke();
- context.closePath();
- context.setLineCap('butt');
- }
- context.save();
- if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
- context.translate(opts._scrollDistance_, 0);
- }
- // 绘制X轴刻度线
- if (opts.xAxis?.calibration === true) {
- context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
- context.setLineCap('butt');
- context.setLineWidth(1 * opts.pix);
- xAxisPoints.forEach(function (item: number, index: number) {
- if (index > 0) {
- context.beginPath();
- context.moveTo(item - eachSpacing / 2, startY);
- context.lineTo(item - eachSpacing / 2, startY + 3 * opts.pix);
- context.closePath();
- context.stroke();
- }
- });
- }
- // 绘制X轴网格
- if (opts.xAxis && opts.xAxis.disableGrid !== true) {
- context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
- context.setLineCap('butt');
- context.setLineWidth(1 * opts.pix);
- if (opts.xAxis.gridType == 'dash') {
- const dashLength = opts.xAxis.dashLength || 4;
- context.setLineDash([dashLength * opts.pix, dashLength * opts.pix]);
- }
- let gridEval = opts.xAxis.gridEval || 1;
- xAxisPoints.forEach(function (item: number, index: number) {
- if (index % gridEval == 0) {
- context.beginPath();
- context.moveTo(item, startY);
- context.lineTo(item, endY);
- context.stroke();
- }
- });
- context.setLineDash([]);
- }
- // 绘制X轴文案
- if (opts.xAxis && opts.xAxis.disabled !== true) {
- // 对X轴列表做抽稀处理
- // 默认全部显示X轴标签
- let maxXAxisListLength = categories.length;
- // 如果设置了X轴单屏数量
- if (opts.xAxis.labelCount) {
- // 如果设置X轴密度
- if (opts.xAxis.itemCount) {
- maxXAxisListLength = Math.ceil(categories.length / opts.xAxis.itemCount * opts.xAxis.labelCount);
- } else {
- maxXAxisListLength = opts.xAxis.labelCount;
- }
- maxXAxisListLength -= 1;
- }
- let ratio = Math.ceil(categories.length / maxXAxisListLength);
- let newCategories: string[] = [];
- let cgLength = categories.length;
- for (let i = 0; i < cgLength; i++) {
- if (i % ratio !== 0) {
- newCategories.push("");
- } else {
- newCategories.push(categories[i]);
- }
- }
- newCategories[cgLength - 1] = categories[cgLength - 1];
- let xAxisFontSize = (opts.xAxis!.fontSize || config.fontSize) * opts.pix;
- if (config._xAxisTextAngle_ === 0) {
- newCategories.forEach(function (item, index) {
- let xitem = opts.xAxis!.formatter ? opts.xAxis!.formatter!(item, index, opts) : item;
- let offset = -measureText(String(xitem), xAxisFontSize, context) / 2;
- if (boundaryGap == 'center') {
- offset += eachSpacing / 2;
- }
- let scrollHeight = 0;
- if (opts.xAxis!.scrollShow) {
- scrollHeight = 6 * opts.pix;
- }
- // 如果在主视图区域内
- let _scrollDistance_ = opts._scrollDistance_ || 0;
- let truePoints = boundaryGap == 'center' ? xAxisPoints[index] + eachSpacing / 2 : xAxisPoints[index];
- if ((truePoints - Math.abs(_scrollDistance_)) >= (opts.area![3] - 1) && (truePoints - Math.abs(_scrollDistance_)) <= (opts.width! - opts.area![1] + 1)) {
- context.beginPath();
- context.setFontSize(xAxisFontSize);
- context.setFillStyle(opts.xAxis!.fontColor || opts.fontColor);
- const marginTop = opts.xAxis!.marginTop || 0;
- const lineHeight = opts.xAxis!.lineHeight || config.fontSize;
- const xAxisFontSizeVal = opts.xAxis!.fontSize || config.fontSize;
- context.fillText(String(xitem), xAxisPoints[index] + offset, startY + marginTop * opts.pix + (lineHeight - xAxisFontSizeVal) * opts.pix / 2 + xAxisFontSizeVal * opts.pix);
- context.closePath();
- context.stroke();
- }
- });
- } else {
- newCategories.forEach(function (item, index) {
- let xitem = opts.xAxis!.formatter ? opts.xAxis!.formatter!(item, index, opts) : item;
- // 如果在主视图区域内
- let _scrollDistance_ = opts._scrollDistance_ || 0;
- let truePoints = boundaryGap == 'center' ? xAxisPoints[index] + eachSpacing / 2 : xAxisPoints[index];
- if ((truePoints - Math.abs(_scrollDistance_)) >= (opts.area![3] - 1) && (truePoints - Math.abs(_scrollDistance_)) <= (opts.width! - opts.area![1] + 1)) {
- context.save();
- context.beginPath();
- context.setFontSize(xAxisFontSize);
- context.setFillStyle(opts.xAxis!.fontColor || opts.fontColor);
- let textWidth = measureText(String(xitem), xAxisFontSize, context);
- let offsetX = xAxisPoints[index];
- if (boundaryGap == 'center') {
- offsetX = xAxisPoints[index] + eachSpacing / 2;
- }
- let scrollHeight = 0;
- if (opts.xAxis!.scrollShow) {
- scrollHeight = 6 * opts.pix;
- }
- const marginTop = opts.xAxis!.marginTop || 0;
- let offsetY = startY + marginTop * opts.pix + xAxisFontSize - xAxisFontSize * Math.abs(Math.sin(config._xAxisTextAngle_!));
- const rotateAngle = opts.xAxis!.rotateAngle || 0;
- if (rotateAngle < 0) {
- offsetX -= xAxisFontSize / 2;
- textWidth = 0;
- } else {
- offsetX += xAxisFontSize / 2;
- textWidth = -textWidth;
- }
- context.translate(offsetX, offsetY);
- context.rotate(-1 * config._xAxisTextAngle_!);
- context.fillText(String(xitem), textWidth, 0);
- context.closePath();
- context.stroke();
- context.restore();
- }
- });
- }
- }
- context.restore();
- // 画X轴标题
- if (opts.xAxis && opts.xAxis.title) {
- context.beginPath();
- const titleFontSize = opts.xAxis!.titleFontSize || config.fontSize;
- context.setFontSize(titleFontSize * opts.pix);
- context.setFillStyle(opts.xAxis!.titleFontColor!);
- const titleOffsetX = opts.xAxis!.titleOffsetX || 0;
- const marginTop = opts.xAxis!.marginTop || 0;
- const lineHeight = opts.xAxis!.lineHeight || titleFontSize;
- const titleOffsetY = opts.xAxis!.titleOffsetY || 0;
- context.fillText(String(opts.xAxis.title), opts.width! - opts.area![1] + titleOffsetX * opts.pix, opts.height! - opts.area![2] + marginTop * opts.pix + (lineHeight - titleFontSize) * opts.pix / 2 + (titleFontSize + titleOffsetY) * opts.pix);
- context.closePath();
- context.stroke();
- }
- // 绘制X轴轴线
- if (opts.xAxis && opts.xAxis.axisLine) {
- context.beginPath();
- context.setStrokeStyle(opts.xAxis!.axisLineColor!);
- context.setLineWidth(1 * opts.pix);
- context.moveTo(startX, opts.height! - opts.area![2]);
- context.lineTo(endX, opts.height! - opts.area![2]);
- context.stroke();
- }
- }
- /**
- * 绘制Y轴网格
- * @param categories - Y轴分类数据
- * @param opts - 图表配置选项
- * @param config - uCharts配置对象
- * @param context - Canvas 渲染上下文
- */
- export function drawYAxisGrid(
- categories: string[],
- opts: ChartOptions,
- config: UChartsConfig,
- context: CanvasContext
- ): void {
- if (opts.yAxis?.disableGrid === true) {
- return;
- }
- let spacingValid = opts.height! - opts.area![0] - opts.area![2];
- let eachSpacing = spacingValid / (opts.yAxis!.splitNumber || 5);
- let startX = opts.area![3];
- let xAxisPoints = opts.chartData?.xAxisData?.xAxisPoints || [];
- let xAxiseachSpacing = opts.chartData?.xAxisData?.eachSpacing || 0;
- let TotalWidth = xAxiseachSpacing * (xAxisPoints.length - 1);
- if (opts.type == 'mount' && opts.extra?.mount && opts.extra.mount.widthRatio && opts.extra.mount.widthRatio > 1) {
- let widthRatio = opts.extra.mount.widthRatio > 2 ? 2 : opts.extra.mount.widthRatio;
- TotalWidth += (widthRatio - 1) * xAxiseachSpacing;
- }
- let endX = startX + TotalWidth;
- let points: number[] = [];
- let startY = 1;
- if (opts.xAxis!.axisLine === false) {
- startY = 0;
- }
- for (let i = startY; i < (opts.yAxis!.splitNumber || 5) + 1; i++) {
- points.push(opts.height! - opts.area![2] - eachSpacing * i);
- }
- context.save();
- if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
- context.translate(opts._scrollDistance_, 0);
- }
- if (opts.yAxis!.gridType == 'dash') {
- context.setLineDash([(opts.yAxis!.dashLength || 4) * opts.pix, (opts.yAxis!.dashLength || 4) * opts.pix]);
- }
- context.setStrokeStyle(opts.yAxis!.gridColor || '#cccccc');
- context.setLineWidth(1 * opts.pix);
- points.forEach(function (item, index) {
- context.beginPath();
- context.moveTo(startX, item);
- context.lineTo(endX, item);
- context.stroke();
- });
- context.setLineDash([]);
- context.restore();
- }
- /**
- * 绘制Y轴
- * @param series - 系列数据数组
- * @param opts - 图表配置选项
- * @param config - uCharts配置对象
- * @param context - Canvas 渲染上下文
- */
- export function drawYAxis(
- series: SeriesItem[],
- opts: ChartOptions,
- config: UChartsConfig,
- context: CanvasContext
- ): void {
- if (opts.yAxis?.disabled === true) {
- return;
- }
- let spacingValid = opts.height! - opts.area![0] - opts.area![2];
- let eachSpacing = spacingValid / (opts.yAxis!.splitNumber || 5);
- let startX = opts.area![3];
- let endX = opts.width! - opts.area![1];
- let endY = opts.height! - opts.area![2];
- // set YAxis background
- context.beginPath();
- context.setFillStyle(opts.background || '#ffffff');
- if (opts.enableScroll == true && opts.xAxis!.scrollPosition && opts.xAxis!.scrollPosition !== 'left') {
- context.fillRect(0, 0, startX, endY + 2 * opts.pix);
- }
- if (opts.enableScroll == true && opts.xAxis!.scrollPosition && opts.xAxis!.scrollPosition !== 'right') {
- context.fillRect(endX, 0, opts.width!, endY + 2 * opts.pix);
- }
- context.closePath();
- context.stroke();
- let tStartLeft = opts.area![3];
- let tStartRight = opts.width! - opts.area![1];
- let tStartCenter = opts.area![3] + (opts.width! - opts.area![1] - opts.area![3]) / 2;
- if (opts.yAxis!.data) {
- for (let i = 0; i < opts.yAxis!.data.length; i++) {
- let yData = opts.yAxis!.data[i];
- let points: number[] = [];
- if (yData.type === 'categories') {
- for (let j = 0; j <= (yData.categories?.length || 0); j++) {
- points.push(opts.area![0] + spacingValid / (yData.categories!.length) / 2 + spacingValid / (yData.categories!.length) * j);
- }
- } else {
- for (let j = 0; j <= (opts.yAxis!.splitNumber || 5); j++) {
- points.push(opts.area![0] + eachSpacing * j);
- }
- }
- if (yData.disabled !== true) {
- let rangesFormat = opts.chartData?.yAxisData?.rangesFormat?.[i] || [];
- let yAxisFontSize = yData.fontSize ? yData.fontSize * opts.pix : config.fontSize;
- let yAxisWidth = opts.chartData?.yAxisData?.yAxisWidth?.[i];
- if (!yAxisWidth) continue;
- let textAlign = yData.textAlign || "right";
- // 画Y轴刻度及文案
- rangesFormat.forEach(function (item: any, index: any) {
- let pos = points[index];
- context.beginPath();
- context.setFontSize(yAxisFontSize);
- context.setLineWidth(1 * opts.pix);
- context.setStrokeStyle(yData.axisLineColor || '#cccccc');
- context.setFillStyle(yData.fontColor || opts.fontColor);
- let tmpstrat = 0;
- let gapwidth = 4 * opts.pix;
- if (yAxisWidth.position == 'left') {
- // 画刻度线
- if (yData.calibration == true) {
- context.moveTo(tStartLeft, pos);
- context.lineTo(tStartLeft - 3 * opts.pix, pos);
- gapwidth += 3 * opts.pix;
- }
- // 画文字
- switch (textAlign) {
- case "left":
- context.setTextAlign('left');
- tmpstrat = tStartLeft - yAxisWidth.width;
- break;
- case "right":
- context.setTextAlign('right');
- tmpstrat = tStartLeft - gapwidth;
- break;
- default:
- context.setTextAlign('center');
- tmpstrat = tStartLeft - yAxisWidth.width / 2;
- }
- context.fillText(String(item), tmpstrat, pos + yAxisFontSize / 2 - 3 * opts.pix);
- } else if (yAxisWidth.position == 'right') {
- // 画刻度线
- if (yData.calibration == true) {
- context.moveTo(tStartRight, pos);
- context.lineTo(tStartRight + 3 * opts.pix, pos);
- gapwidth += 3 * opts.pix;
- }
- switch (textAlign) {
- case "left":
- context.setTextAlign('left');
- tmpstrat = tStartRight + gapwidth;
- break;
- case "right":
- context.setTextAlign('right');
- tmpstrat = tStartRight + yAxisWidth.width;
- break;
- default:
- context.setTextAlign('center');
- tmpstrat = tStartRight + yAxisWidth.width / 2;
- }
- context.fillText(String(item), tmpstrat, pos + yAxisFontSize / 2 - 3 * opts.pix);
- } else if (yAxisWidth.position == 'center') {
- // 画刻度线
- if (yData.calibration == true) {
- context.moveTo(tStartCenter, pos);
- context.lineTo(tStartCenter - 3 * opts.pix, pos);
- gapwidth += 3 * opts.pix;
- }
- // 画文字
- switch (textAlign) {
- case "left":
- context.setTextAlign('left');
- tmpstrat = tStartCenter - yAxisWidth.width;
- break;
- case "right":
- context.setTextAlign('right');
- tmpstrat = tStartCenter - gapwidth;
- break;
- default:
- context.setTextAlign('center');
- tmpstrat = tStartCenter - yAxisWidth.width / 2;
- }
- context.fillText(String(item), tmpstrat, pos + yAxisFontSize / 2 - 3 * opts.pix);
- }
- context.closePath();
- context.stroke();
- context.setTextAlign('left');
- });
- // 画Y轴轴线
- if (yData.axisLine !== false) {
- context.beginPath();
- context.setStrokeStyle(yData.axisLineColor || '#cccccc');
- context.setLineWidth(1 * opts.pix);
- if (yAxisWidth.position == 'left') {
- context.moveTo(tStartLeft, opts.height! - opts.area![2]);
- context.lineTo(tStartLeft, opts.area![0]);
- } else if (yAxisWidth.position == 'right') {
- context.moveTo(tStartRight, opts.height! - opts.area![2]);
- context.lineTo(tStartRight, opts.area![0]);
- } else if (yAxisWidth.position == 'center') {
- context.moveTo(tStartCenter, opts.height! - opts.area![2]);
- context.lineTo(tStartCenter, opts.area![0]);
- }
- context.stroke();
- }
- // 画Y轴标题
- if (opts.yAxis!.showTitle) {
- let titleFontSize = (yData.titleFontSize || config.fontSize) * opts.pix;
- let title = yData.title || '';
- context.beginPath();
- context.setFontSize(titleFontSize);
- context.setFillStyle(yData.titleFontColor || opts.fontColor);
- if (yAxisWidth.position == 'left') {
- context.fillText(title, tStartLeft - measureText(title, titleFontSize, context) / 2 + (yData.titleOffsetX || 0), opts.area![0] - (10 - (yData.titleOffsetY || 0)) * opts.pix);
- } else if (yAxisWidth.position == 'right') {
- context.fillText(title, tStartRight - measureText(title, titleFontSize, context) / 2 + (yData.titleOffsetX || 0), opts.area![0] - (10 - (yData.titleOffsetY || 0)) * opts.pix);
- } else if (yAxisWidth.position == 'center') {
- context.fillText(title, tStartCenter - measureText(title, titleFontSize, context) / 2 + (yData.titleOffsetX || 0), opts.area![0] - (10 - (yData.titleOffsetY || 0)) * opts.pix);
- }
- context.closePath();
- context.stroke();
- }
- if (yAxisWidth.position == 'left') {
- tStartLeft -= (yAxisWidth.width + (opts.yAxis!.padding || 0) * opts.pix);
- } else {
- tStartRight += yAxisWidth.width + (opts.yAxis!.padding || 0) * opts.pix;
- }
- }
- }
- }
- }
- /**
- * 绘制图例
- * @param series - 系列数据数组
- * @param opts - 图表配置选项
- * @param config - uCharts配置对象
- * @param context - Canvas 渲染上下文
- * @param chartData - 图表数据对象
- */
- export function drawLegend(
- series: SeriesItem[],
- opts: ChartOptions,
- config: UChartsConfig,
- context: CanvasContext,
- chartData: any
- ): void {
- if (opts.legend?.show === false) {
- return;
- }
- let legendData = chartData.legendData as LegendData;
- let legendList = legendData.points;
- let legendArea = legendData.area;
- let padding = (opts.legend.padding || 5) * opts.pix;
- let fontSize = (opts.legend.fontSize || 12) * opts.pix;
- let shapeWidth = 15 * opts.pix;
- let shapeRight = 5 * opts.pix;
- let itemGap = (opts.legend.itemGap || 10) * opts.pix;
- let lineHeight = Math.max((opts.legend.lineHeight || 15) * opts.pix, fontSize);
- // 画背景及边框
- context.beginPath();
- context.setLineWidth((opts.legend.borderWidth || 0) * opts.pix);
- context.setStrokeStyle(opts.legend.borderColor || '#cccccc');
- context.setFillStyle(opts.legend.backgroundColor || '#ffffff');
- context.moveTo(legendArea.start.x, legendArea.start.y);
- context.rect(legendArea.start.x, legendArea.start.y, legendArea.width, legendArea.height);
- context.closePath();
- context.fill();
- context.stroke();
- legendList.forEach(function (itemList, listIndex) {
- let width = 0;
- let height = 0;
- width = legendData.widthArr[listIndex];
- height = legendData.heightArr[listIndex];
- let startX = 0;
- let startY = 0;
- if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
- switch (opts.legend.float) {
- case 'left':
- startX = legendArea.start.x + padding;
- break;
- case 'right':
- startX = legendArea.start.x + legendArea.width - width;
- break;
- default:
- startX = legendArea.start.x + (legendArea.width - width) / 2;
- }
- startY = legendArea.start.y + padding + listIndex * lineHeight;
- } else {
- if (listIndex == 0) {
- width = 0;
- } else {
- width = legendData.widthArr[listIndex - 1];
- }
- startX = legendArea.start.x + padding + width;
- startY = legendArea.start.y + padding + (legendArea.height - height) / 2;
- }
- context.setFontSize(config.fontSize);
- for (let i = 0; i < itemList.length; i++) {
- let item = itemList[i];
- item.area = [0, 0, 0, 0];
- item.area[0] = startX;
- item.area[1] = startY;
- item.area[3] = startY + lineHeight;
- context.beginPath();
- context.setLineWidth(1 * opts.pix);
- context.setStrokeStyle(item.show !== false ? item.color : (opts.legend.hiddenColor || '#999999'));
- context.setFillStyle(item.show !== false ? item.color : (opts.legend.hiddenColor || '#999999'));
- switch (item.legendShape) {
- case 'line':
- context.moveTo(startX, startY + 0.5 * lineHeight - 2 * opts.pix);
- context.fillRect(startX, startY + 0.5 * lineHeight - 2 * opts.pix, 15 * opts.pix, 4 * opts.pix);
- break;
- case 'triangle':
- context.moveTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
- context.lineTo(startX + 2.5 * opts.pix, startY + 0.5 * lineHeight + 5 * opts.pix);
- context.lineTo(startX + 12.5 * opts.pix, startY + 0.5 * lineHeight + 5 * opts.pix);
- context.lineTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
- break;
- case 'diamond':
- context.moveTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
- context.lineTo(startX + 2.5 * opts.pix, startY + 0.5 * lineHeight);
- context.lineTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight + 5 * opts.pix);
- context.lineTo(startX + 12.5 * opts.pix, startY + 0.5 * lineHeight);
- context.lineTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
- break;
- case 'circle':
- context.moveTo(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight);
- context.arc(startX + 7.5 * opts.pix, startY + 0.5 * lineHeight, 5 * opts.pix, 0, 2 * Math.PI);
- break;
- case 'rect':
- context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pix);
- context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pix, 15 * opts.pix, 10 * opts.pix);
- break;
- case 'square':
- context.moveTo(startX + 5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix);
- context.fillRect(startX + 5 * opts.pix, startY + 0.5 * lineHeight - 5 * opts.pix, 10 * opts.pix, 10 * opts.pix);
- break;
- case 'none':
- break;
- default:
- context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pix);
- context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pix, 15 * opts.pix, 10 * opts.pix);
- }
- context.closePath();
- context.fill();
- context.stroke();
- startX += shapeWidth + shapeRight;
- let fontTrans = 0.5 * lineHeight + 0.5 * fontSize - 2;
- const legendText = item.legendText ? item.legendText : item.name;
- context.beginPath();
- context.setFontSize(fontSize);
- context.setFillStyle(item.show !== false ? (opts.legend.fontColor || '#666666') : (opts.legend.hiddenColor || '#999999'));
- context.fillText(legendText, startX, startY + fontTrans);
- context.closePath();
- context.stroke();
- if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
- startX += measureText(legendText, fontSize, context) + itemGap;
- item.area[2] = startX;
- } else {
- item.area[2] = startX + measureText(legendText, fontSize, context) + itemGap;
- ;
- startX -= shapeWidth + shapeRight;
- startY += lineHeight;
- }
- }
- });
- }
- /**
- * 绘制仪表盘标签
- * @param gaugeOption - 仪表盘选项
- * @param radius - 半径
- * @param centerPosition - 中心点坐标
- * @param opts - 图表配置选项
- * @param config - uCharts配置对象
- * @param context - Canvas 渲染上下文
- */
- export function drawGaugeLabel(
- gaugeOption: GaugeOption,
- radius: number,
- centerPosition: Point,
- opts: ChartOptions,
- config: UChartsConfig,
- context: CanvasContext
- ): void {
- radius -= (gaugeOption.width || 0) / 2 + (gaugeOption.labelOffset || 0) * opts.pix;
- radius = radius < 10 ? 10 : radius;
- let totalAngle: number;
- if ((gaugeOption.endAngle || 0) < (gaugeOption.startAngle || 0)) {
- totalAngle = 2 + (gaugeOption.endAngle || 0) - (gaugeOption.startAngle || 0);
- } else {
- totalAngle = (gaugeOption.startAngle || 0) - (gaugeOption.endAngle || 0);
- }
- let splitAngle = totalAngle / (gaugeOption.splitLine?.splitNumber || 5);
- let totalNumber = (gaugeOption.endNumber || 10) - (gaugeOption.startNumber || 0);
- let splitNumber = totalNumber / (gaugeOption.splitLine?.splitNumber || 5);
- let nowAngle = gaugeOption.startAngle || 0;
- let nowNumber = gaugeOption.startNumber || 0;
- for (let i = 0; i < (gaugeOption.splitLine?.splitNumber || 5) + 1; i++) {
- let pos = {
- x: radius * Math.cos(nowAngle * Math.PI),
- y: radius * Math.sin(nowAngle * Math.PI)
- };
- let labelText = gaugeOption.formatter ? gaugeOption.formatter(nowNumber, i, opts) : String(nowNumber);
- pos.x += centerPosition.x - measureText(labelText, config.fontSize, context) / 2;
- pos.y += centerPosition.y;
- let startX = pos.x;
- let startY = pos.y;
- context.beginPath();
- context.setFontSize(config.fontSize);
- context.setFillStyle(gaugeOption.labelColor || opts.fontColor);
- context.fillText(labelText, startX, startY + config.fontSize / 2);
- context.closePath();
- context.stroke();
- nowAngle += splitAngle;
- if (nowAngle >= 2) {
- nowAngle = nowAngle % 2;
- }
- nowNumber += splitNumber;
- }
- }
- /**
- * 绘制雷达图标签
- * @param angleList - 角度列表
- * @param radius - 半径
- * @param centerPosition - 中心点坐标
- * @param opts - 图表配置选项
- * @param config - uCharts配置对象
- * @param context - Canvas 渲染上下文
- */
- export function drawRadarLabel(
- angleList: number[],
- radius: number,
- centerPosition: Point,
- opts: ChartOptions,
- config: UChartsConfig,
- context: CanvasContext
- ): void {
- let radarOption = opts.extra?.radar || {};
- angleList.forEach(function (angle, index) {
- if (radarOption.labelPointShow === true && opts.categories && opts.categories[index] !== '') {
- let posPoint = {
- x: radius * Math.cos(angle),
- y: radius * Math.sin(angle)
- };
- let posPointAxis = convertCoordinateOrigin(posPoint.x, posPoint.y, centerPosition);
- context.setFillStyle(radarOption.labelPointColor || opts.fontColor);
- context.beginPath();
- context.arc(posPointAxis.x, posPointAxis.y, (radarOption.labelPointRadius || 3) * opts.pix, 0, 2 * Math.PI, false);
- context.closePath();
- context.fill();
- }
- if (radarOption.labelShow === true && opts.categories) {
- let pos = {
- x: (radius + config.radarLabelTextMargin * opts.pix) * Math.cos(angle),
- y: (radius + config.radarLabelTextMargin * opts.pix) * Math.sin(angle)
- };
- let posRelativeCanvas = convertCoordinateOrigin(pos.x, pos.y, centerPosition);
- let startX = posRelativeCanvas.x;
- let startY = posRelativeCanvas.y;
- if (Math.abs(pos.x) < 1e-10) {
- startX -= measureText(opts.categories[index] || '', config.fontSize, context) / 2;
- } else if (pos.x < 0) {
- startX -= measureText(opts.categories[index] || '', config.fontSize, context);
- }
- context.beginPath();
- context.setFontSize(config.fontSize);
- context.setFillStyle(radarOption.labelColor || opts.fontColor);
- context.fillText(opts.categories[index] || '', startX, startY + config.fontSize / 2);
- context.closePath();
- context.stroke();
- }
- });
- }
|