diff --git a/packages/vchart/src/component/tooltip/processor/base.ts b/packages/vchart/src/component/tooltip/processor/base.ts index f473dac2c0..64c52999aa 100644 --- a/packages/vchart/src/component/tooltip/processor/base.ts +++ b/packages/vchart/src/component/tooltip/processor/base.ts @@ -76,6 +76,18 @@ export abstract class BaseTooltipProcessor { return TooltipResult.failed; }; + protected _getTooltipContent = (data: TooltipData | undefined, params: TooltipHandlerParams): ITooltipActual => { + if (isNil(data)) { + return null; + } + + this.clearCache(); + + // 更新 this._cacheActiveSpec + this._updateViewSpec(data, params); + return this._cacheActiveSpec; + }; + protected _preprocessDimensionInfo(dimensionInfo?: IDimensionInfo[]): IDimensionInfo[] | undefined { const newDimensionInfo: IDimensionInfo[] = []; dimensionInfo?.forEach(info => { diff --git a/packages/vchart/src/component/tooltip/processor/dimension-tooltip.ts b/packages/vchart/src/component/tooltip/processor/dimension-tooltip.ts index 3a01d758f4..2d4da46336 100644 --- a/packages/vchart/src/component/tooltip/processor/dimension-tooltip.ts +++ b/packages/vchart/src/component/tooltip/processor/dimension-tooltip.ts @@ -90,6 +90,16 @@ export class DimensionTooltipProcessor extends BaseTooltipProcessor { return targetDimensionInfo; } + getTooltipContent(info: DimensionTooltipInfo, params: BaseEventParams, changePositionOnly: boolean) { + const newParams: TooltipHandlerParams = { + ...(params as TooltipHandlerParams), + dimensionInfo: this._preprocessDimensionInfo(info), + changePositionOnly, + tooltip: this.component + }; + return this._getTooltipContent(info, newParams); + } + /** 获取触发 tooltip 需要的信息 */ getMouseEventData(params: BaseEventParams): MouseEventData { return { diff --git a/packages/vchart/src/component/tooltip/processor/interface.ts b/packages/vchart/src/component/tooltip/processor/interface.ts index d83ec60f61..106ba828e9 100644 --- a/packages/vchart/src/component/tooltip/processor/interface.ts +++ b/packages/vchart/src/component/tooltip/processor/interface.ts @@ -4,6 +4,7 @@ import type { IDimensionInfo } from '../../../event/events/dimension/interface'; import type { Datum } from '../../../typings/common'; import type { BaseEventParams } from '../../../event/interface'; import type { ITooltip, TooltipResult } from '../interface/common'; +import type { ITooltipActual } from '../../../typings'; export type DimensionTooltipInfo = IDimensionInfo[]; @@ -29,6 +30,7 @@ export interface ITooltipProcessor { clearCache: () => void; showTooltip: (info: T, params: BaseEventParams, changePositionOnly: boolean) => TooltipResult; getMouseEventData: (params: BaseEventParams) => MouseEventData; + getTooltipContent: (info: T, params: BaseEventParams, changePositionOnly: boolean) => ITooltipActual; } export interface ITooltipProcessorConstructor { diff --git a/packages/vchart/src/component/tooltip/processor/mark-tooltip.ts b/packages/vchart/src/component/tooltip/processor/mark-tooltip.ts index beb4bcc82d..6fa0a8a726 100644 --- a/packages/vchart/src/component/tooltip/processor/mark-tooltip.ts +++ b/packages/vchart/src/component/tooltip/processor/mark-tooltip.ts @@ -70,6 +70,61 @@ export class MarkTooltipProcessor extends BaseTooltipProcessor { return this._showTooltipByHandler(tooltipData, newParams); } + getTooltipContent(info: MarkTooltipInfo, params: BaseEventParams, changePositionOnly: boolean) { + const { datum, series } = info; + const tooltipSpec = this.component.getSpec(); + const tooltipData = [{ datum: [datum], series }]; + const helper = series.tooltipHelper; + const seriesSpec = series.getSpec()?.tooltip as ITooltipSpec; + const seriesCheckOverlap = seriesSpec?.mark?.checkOverlap; + let checkOverlap = false; + + if (seriesCheckOverlap === true || (tooltipSpec.mark?.checkOverlap === true && seriesCheckOverlap !== false)) { + const activeTriggers = helper?.activeTriggerSet.mark; + + if (activeTriggers) { + checkOverlap = true; + const chart = this.component.getChart(); + // compute layer offset + const layer = chart.getCompiler().getStage().getLayer(undefined); + const point = { x: params.event.viewX, y: params.event.viewY }; + layer.globalTransMatrix.transformPoint({ x: params.event.viewX, y: params.event.viewY }, point); + + activeTriggers.forEach(mark => { + mark.getGraphics().forEach(g => { + if ( + g !== params.node && + g && + g.containsPoint(point.x, point.y, IContainPointMode.GLOBAL, g.stage.getPickerService()) + ) { + tooltipData[0].datum.push(getDatumOfGraphic(g)); + } + }); + }); + } + } + + const newParams: TooltipHandlerParams = { + ...(params as any), + model: series, // 在 label 支持 mark tooltip 后,eventParam.model 可能是 label 组件,而 tooltip 中需要的是 series + changePositionOnly, + tooltip: this.component + }; + if (changePositionOnly && checkOverlap) { + const cacheData = this._cacheActiveSpec && this._cacheActiveSpec.data; + + if ( + !cacheData || + (cacheData as IDimensionData[])[0].series !== tooltipData[0].series || + (cacheData as IDimensionData[])[0].datum.length !== tooltipData[0].datum.length || + (cacheData as IDimensionData[])[0].datum.some((d, index) => d !== tooltipData[0].datum[index]) + ) { + newParams.changePositionOnly = false; + } + } + return this._getTooltipContent(tooltipData, newParams); + } + /** 获取触发 tooltip 需要的信息 */ getMouseEventData(params: BaseEventParams): MouseEventData { let info: MarkTooltipInfo | undefined; diff --git a/packages/vchart/src/component/tooltip/tooltip.ts b/packages/vchart/src/component/tooltip/tooltip.ts index 60a93d7017..2168f3ec08 100644 --- a/packages/vchart/src/component/tooltip/tooltip.ts +++ b/packages/vchart/src/component/tooltip/tooltip.ts @@ -611,10 +611,10 @@ export class Tooltip extends BaseComponent implements ITooltip { } const result = showTooltip(datum, options, this); - if (result !== 'none') { + if (result !== null) { this._alwaysShow = !!options?.alwaysShow; } - return result; + return result === null ? 'none' : result; } /** 手动隐藏 tooltip,返回是否成功 */ diff --git a/packages/vchart/src/component/tooltip/utils/show-tooltip.ts b/packages/vchart/src/component/tooltip/utils/show-tooltip.ts index 698dad65f3..3b31ec839d 100644 --- a/packages/vchart/src/component/tooltip/utils/show-tooltip.ts +++ b/packages/vchart/src/component/tooltip/utils/show-tooltip.ts @@ -11,6 +11,7 @@ import type { Tooltip } from '../tooltip'; import type { IComponentOption } from '../../interface'; import { isDiscrete } from '@visactor/vscale'; import type { TooltipHandlerParams } from '../interface/common'; +import type { DimensionTooltipInfo, MarkTooltipInfo } from '../processor'; const getDataArrayFromFieldArray = (fields: string[], datum?: Datum) => isValid(datum) ? fields.map(f => datum[f]) : undefined; @@ -43,7 +44,38 @@ type MarkInfo = { dimType?: string; }; -export function showTooltip(datum: Datum, options: IShowTooltipOption, component: Tooltip): TooltipActiveType | 'none' { +export function showTooltip(datum: Datum, options: IShowTooltipOption, component: Tooltip): TooltipActiveType | null { + const mockInfo = getTooltipMockInfo(datum, options, component); + if (mockInfo === null) { + return null; + } + const componentOptions = component.getOption() as IComponentOption; + const { info, params, activeType } = mockInfo; + + if (activeType === 'dimension') { + component.processor.dimension.showTooltip(info as DimensionTooltipInfo, params, false); + } else if (activeType === 'mark') { + component.processor.mark.showTooltip(info as MarkTooltipInfo, params, false); + } + + // 全局唯一 tooltip + const vchart = componentOptions.globalInstance; + if (VChart.globalConfig.uniqueTooltip) { + VChart.hideTooltip(vchart.id); + } + + return activeType; +} + +export function getTooltipMockInfo( + datum: Datum, + options: IShowTooltipOption, + component: Tooltip +): { + info: DimensionTooltipInfo | MarkTooltipInfo; + params: TooltipHandlerParams; + activeType: TooltipActiveType; +} | null { const opt: IShowTooltipOption = { regionIndex: 0, ...options @@ -56,7 +88,7 @@ export function showTooltip(datum: Datum, options: IShowTooltipOption, component isValid(opt.regionIndex) ? [opt.regionIndex] : undefined )[0]; if (!region) { - return 'none'; + return null; } // 查询图元信息 @@ -99,11 +131,11 @@ export function showTooltip(datum: Datum, options: IShowTooltipOption, component }; }; - // 显示tooltip + // 获取 tooltip 内容 if (activeType === 'dimension') { const firstInfo = markInfoList[0]; if (!firstInfo) { - return 'none'; + return null; } // 将markInfoList按系列分组 @@ -128,7 +160,7 @@ export function showTooltip(datum: Datum, options: IShowTooltipOption, component ]; if (isValid(firstInfo.dimType)) { - mockDimensionInfo[0].position = firstInfo.pos[firstInfo.dimType]; + mockDimensionInfo[0].position = firstInfo.pos[firstInfo.dimType as keyof IPoint]; mockDimensionInfo[0].dimType = firstInfo.dimType; } @@ -147,20 +179,15 @@ export function showTooltip(datum: Datum, options: IShowTooltipOption, component }), item: undefined }; - - component.processor.dimension.showTooltip(mockDimensionInfo, mockParams, false); - - // 全局唯一 tooltip - const vchart = componentOptions.globalInstance; - if (VChart.globalConfig.uniqueTooltip) { - VChart.hideTooltip(vchart.id); - } - - return activeType; + return { + info: mockDimensionInfo, + params: mockParams, + activeType + }; } else if (activeType === 'mark') { const info = markInfoList[0]; if (!info) { - return 'none'; + return null; } const mockDatum = { ...getOriginDatum(info), @@ -191,24 +218,35 @@ export function showTooltip(datum: Datum, options: IShowTooltipOption, component item: undefined } as any; - component.processor.mark.showTooltip( - { + return { + info: { datum: mockDatum, mark: null, series: info.series }, - mockParams, - false - ); - - // 全局唯一 tooltip - const vchart = componentOptions.globalInstance; - if (VChart.globalConfig.uniqueTooltip) { - VChart.hideTooltip(vchart.id); - } - return activeType; + params: mockParams, + activeType + }; + } + return null; +} + +export function getTooltipContent(datum: Datum, options: IShowTooltipOption, component: Tooltip) { + const mockInfo = getTooltipMockInfo(datum, options, component); + if (mockInfo === null) { + return null; + } + const { info, params, activeType } = mockInfo; + + // 需要初始化 tooltip processor + // @ts-ignore + component._initProcessor(); + if (activeType === 'dimension') { + return component.processor.dimension.getTooltipContent(info as DimensionTooltipInfo, params, false); + } else if (activeType === 'mark') { + return component.processor.mark.getTooltipContent(info as MarkTooltipInfo, params, false); } - return 'none'; + return null; } export const getMarkInfoList = (datum: Datum, region: IRegion) => { diff --git a/packages/vchart/src/core/vchart.ts b/packages/vchart/src/core/vchart.ts index bcd8e012e9..5e9d65ed8a 100644 --- a/packages/vchart/src/core/vchart.ts +++ b/packages/vchart/src/core/vchart.ts @@ -51,7 +51,7 @@ import type { IMark, IMarkGraphic, MarkConstructor } from '../mark/interface'; import { registerDataSetInstanceParser, registerDataSetInstanceTransform } from '../data/register'; import { dataToDataView } from '../data/initialize'; import { copyDataView } from '../data/transforms/copy-data-view'; -import type { ITooltipHandler } from '../typings/tooltip'; +import type { ITooltipActual, ITooltipHandler } from '../typings/tooltip'; import type { Tooltip } from '../component/tooltip'; import type { Datum, @@ -117,6 +117,7 @@ import { registerElementHighlight } from '../interaction/triggers/element-highli import { registerElementSelect } from '../interaction/triggers/element-select'; import type { IVChartPluginService } from '../plugin/vchart/interface'; import { VChartPluginService } from '../plugin/vchart/plugin-service'; +import { getTooltipContent } from '../component/tooltip/utils/show-tooltip'; export class VChart implements IVChart { readonly id = createID(); @@ -1642,6 +1643,17 @@ export class VChart implements IVChart { return (isValid(datum) && tooltip?.showTooltip(datum, options) !== 'none') ?? false; } + /** + * 获取 tooltip 内容 + * @param datum 原始数据 + * @param options + * @returns + */ + getTooltipContent(datum: Datum, options: IShowTooltipOption): ITooltipActual | null { + const tooltip = this._getTooltipComponent(); + return getTooltipContent(datum, options, tooltip); + } + /** * 手动调用,关闭 tooltip * @returns