fix(图表): 修复图表极值显示错位的问题

This commit is contained in:
jianneng-fit2cloud
2025-09-18 10:54:43 +08:00
parent f88df9a13d
commit fdd0cdfc85
6 changed files with 71 additions and 75 deletions

View File

@@ -14,7 +14,7 @@
"preview": "vite preview"
},
"dependencies": {
"@antv/g2": "^5.3.5",
"@antv/g2": "^5.4.0",
"@antv/l7": "^2.22.0",
"@antv/l7plot": "^0.5.5",
"@antv/s2": "^2.4.5",

View File

@@ -90,7 +90,7 @@ export const extremumEvt = (
// 这里获取 y 字段
// 部分图表传过来的是options包含 children 的数组
// 部分图表是children数组中的对象line or bar
const { y: yField } = options.encode
const { y: yField } = options.encode ? options.encode : options.children[0].encode
const chartData = options.children ? options.children : [options]
// 遍历所有 series为标签注入 HTML 和样式
chartData
@@ -119,35 +119,37 @@ export const extremumEvt = (
pointSize = Math.max(pointSize, item.encode?.size || 0)
})
const parentRect = parent?.getBoundingClientRect()
// 渲染后调整极值标签位置,防止溢出
newChart.on('afterrender', () => {
document.querySelectorAll('.extremum-' + chart.container).forEach(item => {
const setExtremumPosition = () => {
document.querySelectorAll('.extremum-' + chart.container)?.forEach(item => {
item.style.display = 'block'
item?.parentElement?.parentElement
const itemRect = item.getBoundingClientRect()
const childNode = item.childNodes[1] as HTMLElement
const itemParentRect = item.parentElement?.getBoundingClientRect()
const spanElement = item.parentElement.querySelector('span' as string) as HTMLElement
// 判断是否顶部溢出
if (itemRect.top < parentRect.top) {
item.style.transform = `translate(-50%) translateY(${pointSize / scale + 10}px)`
childNode.style.cssText += 'transform: translateX(-50%) rotate(180deg); top: -5px;'
const itemParentParentRect = item.parentElement?.parentElement?.getBoundingClientRect()
// 顶部有足够空间
if (itemParentRect.top - itemParentParentRect.top > itemParentRect.height) {
item.style.transform = `translateY(-${itemRect.height + 5}px)`
spanElement.style.cssText += 'transform: rotate(0deg);top: -6px;'
} else {
item.style.transform = `translateY(${pointSize / scale + 5 * 2}px)`
spanElement.style.cssText += `transform: rotate(180deg);top: ${pointSize / scale + 5}px;`
}
// 判断是否右侧溢出
if (itemRect.right > parentRect.right) {
const currentLeft = parseFloat(window.getComputedStyle(item).left) || 0
const newLeft = currentLeft - (itemRect.right - parentRect.right)
item.style.left = `${newLeft}px`
// childNode 反向偏移,保持始终指向在数据点上
childNode.style.left = itemRect.width / 2 + Math.abs(newLeft) + 'px'
}
// 判断是否左侧溢出
if (itemRect.left < parentRect.left) {
const currentLeft = parseFloat(window.getComputedStyle(item).left) || 0
const newLeft = currentLeft + (parentRect.left - itemRect.left)
item.style.left = `${newLeft}px`
// childNode 反向偏移,保持始终指向在数据点上
childNode.style.left = itemRect.width / 2 - Math.abs(newLeft) + 'px'
// 判断右侧溢出
const overflowRight = itemParentRect.right - itemParentParentRect.right
let newRight = itemRect.width / 2
if (overflowRight > itemRect.width * 0.5) {
newRight = overflowRight
}
item.style.right = newRight + 'px'
})
}
newChart.on('afterchangesize', () => {
setExtremumPosition()
})
newChart.on('afterrender', () => {
setExtremumPosition()
})
}
@@ -244,18 +246,16 @@ const extremumHtml = (chart, yField, isSeriesLabel) => {
background: rgba(${color});
">
${textContent}
<span style="
</div>
<span style="
position: absolute;
bottom: -4.8px;
left: 50%;
transform: translateX(-50%);
top: -6px;
left: -4px;
width: 0; height: 0;
border-top: 5px solid rgba(${color});
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 0;
"></span>
</div>
`
}
}

View File

@@ -7,7 +7,7 @@ import {
} from '@/views/chart/components/js/panel/charts/g2/bar/common'
import { useI18n } from '@/hooks/web/useI18n'
import { flow, hexColorToRGBA, hexToRgba, parseJson } from '@/views/chart/components/js/util'
import { cloneDeep, isEmpty } from 'lodash-es'
import { cloneDeep, defaultsDeep, isEmpty } from 'lodash-es'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import {
getLineDash,
@@ -83,8 +83,7 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
background: true
}
},
transform: [{ type: 'dodgeX' } as Transform],
data: []
transform: [{ type: 'dodgeX' } as Transform]
} as ViewSpec
async drawChart(drawOptions: G2DrawOptions<G2Column>): Promise<G2Column> {
@@ -96,14 +95,11 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
const data = cloneDeep(drawOptions.chart.data?.data)
const initOptions: ViewSpec = {
type: 'view',
data: {
value: data
},
data: data,
children: [
{
...this.intervalOptions,
transform: [].concat(this.intervalOptions.transform),
data
transform: [].concat(this.intervalOptions.transform)
}
]
}
@@ -120,17 +116,6 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
scale,
this.name === 'bar' || this.name === 'bar-group'
)
newChart.afterRender = (c?: any) => {
extremumEvt(
c,
chart,
c.options(),
container,
scale,
this.name === 'bar' || this.name === 'bar-group'
)
c.render()
}
return newChart
}
@@ -155,12 +140,9 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
const position = {
position: l.position === 'middle' ? 'inside' : l.position,
textAlign: 'center',
dy: l.position === 'top' ? -10 : 0,
dy: l.position === 'top' ? -15 : 0,
dx: 0
}
const transform = {
transform: [{ type: 'exceedAdjust' }, { type: 'overlapHide' }]
}
// 配置标签样式
const newLabel = {
text: 'value',
@@ -195,8 +177,10 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
return ''
}
return valueFormatter(value, labelCfg.formatterCfg)
},
...(l.fullDisplay ? { transform: [{ type: 'exceedAdjust' }] } : transform)
}
}
if (!l.fullDisplay) {
newLabel.transform = [{ type: 'overlapHide' }]
}
return {
...options,
@@ -712,6 +696,29 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
}
}
protected configSlider(chart: Chart, options: ViewSpec): ViewSpec {
const { functionCfg } = parseJson(chart.senior)
if (!functionCfg?.sliderShow) {
return options
}
const lineMark = options.children[0]
const sliderOpt = {
slider: {
x: {
values: [functionCfg.sliderRange[0] / 100, functionCfg.sliderRange[1] / 100],
style: {
trackFill: functionCfg.sliderBg,
selectionFill: functionCfg.sliderFillBg,
handleLabelFill: functionCfg.sliderTextColor,
sparklineLineStrokeOpacity: 0
}
}
}
}
defaultsDeep(lineMark, sliderOpt)
return options
}
protected setupOptions(chart: Chart, options: ViewSpec): ViewSpec {
return flow(
this.configTheme,
@@ -722,7 +729,8 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
this.configXAxis,
this.configYAxis,
this.configAnalyse,
this.configBarConditions
this.configBarConditions,
this.configSlider
)(chart, options, {}, this)
}

View File

@@ -97,10 +97,7 @@ export class Area extends G2ChartView {
// 开始渲染
newChart.options(options)
newChart.on('point:click', action)
newChart.afterRender = (c?: any) => {
extremumEvt(c, chart, c.options(), container, scale, this.name === 'area')
c.render()
}
extremumEvt(newChart, chart, options, container, scale)
// configPlotTooltipEvent(chart, newChart)
return newChart
}
@@ -258,9 +255,7 @@ export class Area extends G2ChartView {
}
return labelCfg.position === 'top' ? 'bottom' : 'top'
},
transform: labelAttr.fullDisplay
? [{ type: 'exceedAdjust' }]
: [{ type: 'exceedAdjust' }, { type: 'overlapHide' }],
...(labelAttr.fullDisplay ? {} : { transform: [{ type: 'overlapHide' }] }),
fontFamily: chart.fontFamily
}
]
@@ -736,9 +731,7 @@ export class StackArea extends Area {
textBaseline: () => {
return labelAttr.position === 'top' ? 'bottom' : 'top'
},
transform: labelAttr.fullDisplay
? [{ type: 'exceedAdjust' }]
: [{ type: 'exceedAdjust' }, { type: 'overlapHide' }],
...(labelAttr.fullDisplay ? {} : { transform: [{ type: 'overlapHide' }] }),
fontFamily: chart.fontFamily
}
]

View File

@@ -95,10 +95,7 @@ export class Line extends G2ChartView {
const newChart = new G2Chart({ container })
newChart.options(options)
newChart.on('point:click', action)
newChart.afterRender = (c?: any) => {
extremumEvt(c, chart, c.options(), container, scale)
c.render()
}
extremumEvt(newChart, chart, options, container, scale)
// configPlotTooltipEvent(chart, newChart)
return newChart
}
@@ -257,9 +254,7 @@ export class Line extends G2ChartView {
}
return labelCfg.position === 'top' ? 'bottom' : 'top'
},
transform: labelAttr.fullDisplay
? [{ type: 'exceedAdjust' }]
: [{ type: 'exceedAdjust' }, { type: 'overlapHide' }],
...(labelAttr.fullDisplay ? {} : { transform: [{ type: 'overlapHide' }] }),
fontFamily: chart.fontFamily
}
]

View File

@@ -1,4 +1,4 @@
import { useI18n } from '@/hooks/web/useI18n'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()