diff --git a/.trae/specs/add-interactInvertType-background/checklist.md b/.trae/specs/add-interactInvertType-background/checklist.md new file mode 100644 index 000000000..240ee38b7 --- /dev/null +++ b/.trae/specs/add-interactInvertType-background/checklist.md @@ -0,0 +1,6 @@ +- [x] SmartInvertAttrs.interactInvertType 已包含 'background' +- [x] 相交且 'background' 时设置 label.stroke=false +- [x] 相交且 'background' 时设置 label.background=baseMark.fill(有填充时) +- [x] 其他类型(none/stroked/inside)行为无回归 +- [x] 注释更新为“四种处理方式”并包含 background 描述 +- [x] 示例验证通过(IText 与 IRichText 场景) diff --git a/.trae/specs/add-interactInvertType-background/spec.md b/.trae/specs/add-interactInvertType-background/spec.md new file mode 100644 index 000000000..4603baa79 --- /dev/null +++ b/.trae/specs/add-interactInvertType-background/spec.md @@ -0,0 +1,38 @@ +# 标签相交背景模式 Spec + +## Why +当前标签与 mark 相交时仅支持 none/stroked/inside 三种处理方式,无法满足关闭描边并以底层 mark 的填充色作为文字背景的视觉需求,导致相交场景下对比度与观感不可控。 + +## What Changes +- 在 SmartInvertAttrs.interactInvertType 中新增 'background' 选项 +- 在相交场景下(label 与 mark 相交且不完全在内部),当 interactInvertType 为 'background': + - 将标签描边 stroke 设置为 false(禁用描边) + - 将标签背景 background 设置为关联 mark 的填充色 +- 更新 SmartInvert 相关注释文档,将“三种处理方式”修改为“四种”,并补充 background 描述 +- 默认行为不变(未配置时保持现状),无破坏性变更 + +## Impact +- Affected specs: 标签智能反色/相交处理能力 +- Affected code: + - packages/vrender-components/src/label/type.ts(类型与注释) + - packages/vrender-components/src/label/base.ts(_smartInvert 相交分支逻辑) + +## ADDED Requirements +### Requirement: 新增 interactInvertType 'background' +系统应在标签与 mark 相交且配置 interactInvertType 为 'background' 时: +- 将标签的 stroke 设为 false,关闭描边; +- 将标签的 background 颜色设置为关联 mark 的填充色(优先取 mark.attribute.fill;若缺省则不改动 background)。 +- 保持标签 fill 不做变更。 + +#### Scenario: Success case +- WHEN 标签与 mark 相交,且 smartInvert.interactInvertType === 'background' +- THEN label.stroke === false,且 label.background === baseMark.fill(存在时) + +## MODIFIED Requirements +### Requirement: 更新交互反色类型文档 +将注释由“支持三种处理方式:none/stroked/inside”更新为“四种处理方式:none/stroked/inside/background”,其中: +- background:关闭描边,并以 mark 的填充色作为文本背景参与渲染。 + +## REMOVED Requirements +无 + diff --git a/.trae/specs/add-interactInvertType-background/tasks.md b/.trae/specs/add-interactInvertType-background/tasks.md new file mode 100644 index 000000000..b53c327ae --- /dev/null +++ b/.trae/specs/add-interactInvertType-background/tasks.md @@ -0,0 +1,9 @@ +# Tasks +- [x] 更新类型定义与注释:为 SmartInvertAttrs.interactInvertType 添加 'background',并改注释为“四种处理方式” +- [x] 修改 _smartInvert 相交分支:实现 'background' 行为(stroke=false;background=baseMark.fill;保持 fill 不变;跳过描边缺省早退) +- [x] 添加验证用例:构造 label 与 mark 相交示例,确认背景色与填充色一致且描边关闭(覆盖 IText 与 IRichText) +- [x] 自检与清理:校验边界(mark 无 fill、stroke 初始为数组/布尔的情况)、类型约束与代码风格 + +# Task Dependencies +- [修改 _smartInvert 相交分支] 依赖于 [更新类型定义与注释] +- [添加验证用例] 依赖于 [修改 _smartInvert 相交分支] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index f36c83bee..2927f3536 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -22,7 +22,7 @@ importers: specifier: ~0.5.7 version: 0.5.7 '@visactor/vrender': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../packages/vrender '@visactor/vutils': specifier: ~1.0.12 @@ -95,7 +95,7 @@ importers: ../../packages/react-vrender: dependencies: '@visactor/vrender': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender '@visactor/vutils': specifier: ~1.0.12 @@ -153,10 +153,10 @@ importers: ../../packages/react-vrender-utils: dependencies: '@visactor/react-vrender': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../react-vrender '@visactor/vrender': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender '@visactor/vutils': specifier: ~1.0.12 @@ -211,16 +211,16 @@ importers: ../../packages/vrender: dependencies: '@visactor/vrender-animate': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-animate '@visactor/vrender-components': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-components '@visactor/vrender-core': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-core '@visactor/vrender-kits': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-kits devDependencies: '@internal/bundler': @@ -287,7 +287,7 @@ importers: ../../packages/vrender-animate: dependencies: '@visactor/vrender-core': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-core '@visactor/vutils': specifier: ~1.0.12 @@ -345,13 +345,13 @@ importers: ../../packages/vrender-components: dependencies: '@visactor/vrender-animate': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-animate '@visactor/vrender-core': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-core '@visactor/vrender-kits': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-kits '@visactor/vscale': specifier: ~1.0.12 @@ -470,7 +470,7 @@ importers: specifier: 2.4.1 version: 2.4.1 '@visactor/vrender-core': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../vrender-core '@visactor/vutils': specifier: ~1.0.12 @@ -586,19 +586,19 @@ importers: ../../tools/bugserver-trigger: dependencies: '@visactor/vrender': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../../packages/vrender '@visactor/vrender-animate': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../../packages/vrender-animate '@visactor/vrender-components': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../../packages/vrender-components '@visactor/vrender-core': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../../packages/vrender-core '@visactor/vrender-kits': - specifier: workspace:1.0.41 + specifier: workspace:1.0.42 version: link:../../packages/vrender-kits devDependencies: '@internal/bundler': diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 122354b8b..d1deddbaa 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vrenderMain","version":"1.0.41","nextBump":"patch"}] +[{"definitionName":"lockStepVersion","policyName":"vrenderMain","version":"1.0.42","nextBump":"patch"}] diff --git a/docs/package.json b/docs/package.json index 11be8105b..d6d805ba5 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,7 +13,7 @@ "@visactor/vchart": "1.3.0", "@visactor/vutils": "~1.0.12", "@visactor/vgrammar": "~0.5.7", - "@visactor/vrender": "workspace:1.0.41", + "@visactor/vrender": "workspace:1.0.42", "markdown-it": "^13.0.0", "highlight.js": "^11.8.0", "axios": "^1.4.0", diff --git a/packages/react-vrender-utils/CHANGELOG.json b/packages/react-vrender-utils/CHANGELOG.json index bd4061017..d7bb088d4 100644 --- a/packages/react-vrender-utils/CHANGELOG.json +++ b/packages/react-vrender-utils/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/react-vrender-utils", "entries": [ + { + "version": "1.0.42", + "tag": "@visactor/react-vrender-utils_v1.0.42", + "date": "Mon, 23 Mar 2026 08:08:45 GMT", + "comments": {} + }, { "version": "1.0.41", "tag": "@visactor/react-vrender-utils_v1.0.41", diff --git a/packages/react-vrender-utils/CHANGELOG.md b/packages/react-vrender-utils/CHANGELOG.md index 3ae51de68..968197e87 100644 --- a/packages/react-vrender-utils/CHANGELOG.md +++ b/packages/react-vrender-utils/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/react-vrender-utils -This log was last generated on Tue, 03 Mar 2026 11:54:26 GMT and should not be manually modified. +This log was last generated on Mon, 23 Mar 2026 08:08:45 GMT and should not be manually modified. + +## 1.0.42 +Mon, 23 Mar 2026 08:08:45 GMT + +_Version update only_ ## 1.0.41 Tue, 03 Mar 2026 11:54:26 GMT diff --git a/packages/react-vrender-utils/package.json b/packages/react-vrender-utils/package.json index e3a3d960e..2adc43e74 100644 --- a/packages/react-vrender-utils/package.json +++ b/packages/react-vrender-utils/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vrender-utils", - "version": "1.0.41", + "version": "1.0.42", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -24,8 +24,8 @@ "react-dom": "^18.2.0" }, "dependencies": { - "@visactor/vrender": "workspace:1.0.41", - "@visactor/react-vrender": "workspace:1.0.41", + "@visactor/vrender": "workspace:1.0.42", + "@visactor/react-vrender": "workspace:1.0.42", "@visactor/vutils": "~1.0.12", "react-reconciler": "^0.29.0", "tslib": "^2.3.1" diff --git a/packages/react-vrender/CHANGELOG.json b/packages/react-vrender/CHANGELOG.json index 6b86a1d48..f4add14c7 100644 --- a/packages/react-vrender/CHANGELOG.json +++ b/packages/react-vrender/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/react-vrender", "entries": [ + { + "version": "1.0.42", + "tag": "@visactor/react-vrender_v1.0.42", + "date": "Mon, 23 Mar 2026 08:08:45 GMT", + "comments": {} + }, { "version": "1.0.41", "tag": "@visactor/react-vrender_v1.0.41", diff --git a/packages/react-vrender/CHANGELOG.md b/packages/react-vrender/CHANGELOG.md index 2d4a72114..2f56392b3 100644 --- a/packages/react-vrender/CHANGELOG.md +++ b/packages/react-vrender/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/react-vrender -This log was last generated on Tue, 03 Mar 2026 11:54:26 GMT and should not be manually modified. +This log was last generated on Mon, 23 Mar 2026 08:08:45 GMT and should not be manually modified. + +## 1.0.42 +Mon, 23 Mar 2026 08:08:45 GMT + +_Version update only_ ## 1.0.41 Tue, 03 Mar 2026 11:54:26 GMT diff --git a/packages/react-vrender/package.json b/packages/react-vrender/package.json index 967c100d5..3a69f2a3c 100644 --- a/packages/react-vrender/package.json +++ b/packages/react-vrender/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vrender", - "version": "1.0.41", + "version": "1.0.42", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -23,7 +23,7 @@ "react": "^18.2.0" }, "dependencies": { - "@visactor/vrender": "workspace:1.0.41", + "@visactor/vrender": "workspace:1.0.42", "@visactor/vutils": "~1.0.12", "react-reconciler": "^0.29.0", "tslib": "^2.3.1" diff --git a/packages/vrender-animate/CHANGELOG.json b/packages/vrender-animate/CHANGELOG.json index c7d32592e..036663e7f 100644 --- a/packages/vrender-animate/CHANGELOG.json +++ b/packages/vrender-animate/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender-animate", "entries": [ + { + "version": "1.0.42", + "tag": "@visactor/vrender-animate_v1.0.42", + "date": "Mon, 23 Mar 2026 08:08:45 GMT", + "comments": {} + }, { "version": "1.0.41", "tag": "@visactor/vrender-animate_v1.0.41", diff --git a/packages/vrender-animate/CHANGELOG.md b/packages/vrender-animate/CHANGELOG.md index 35060220a..e0373f3a6 100644 --- a/packages/vrender-animate/CHANGELOG.md +++ b/packages/vrender-animate/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-animate -This log was last generated on Tue, 03 Mar 2026 11:54:26 GMT and should not be manually modified. +This log was last generated on Mon, 23 Mar 2026 08:08:45 GMT and should not be manually modified. + +## 1.0.42 +Mon, 23 Mar 2026 08:08:45 GMT + +_Version update only_ ## 1.0.41 Tue, 03 Mar 2026 11:54:26 GMT diff --git a/packages/vrender-animate/package.json b/packages/vrender-animate/package.json index 7de39baf8..1f5a4bfed 100644 --- a/packages/vrender-animate/package.json +++ b/packages/vrender-animate/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-animate", - "version": "1.0.41", + "version": "1.0.42", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -21,7 +21,7 @@ }, "dependencies": { "@visactor/vutils": "~1.0.12", - "@visactor/vrender-core": "workspace:1.0.41" + "@visactor/vrender-core": "workspace:1.0.42" }, "devDependencies": { "@internal/bundler": "workspace:*", diff --git a/packages/vrender-components/CHANGELOG.json b/packages/vrender-components/CHANGELOG.json index 4b51eb4e1..565cc17a9 100644 --- a/packages/vrender-components/CHANGELOG.json +++ b/packages/vrender-components/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender-components", "entries": [ + { + "version": "1.0.42", + "tag": "@visactor/vrender-components_v1.0.42", + "date": "Mon, 23 Mar 2026 08:08:45 GMT", + "comments": {} + }, { "version": "1.0.41", "tag": "@visactor/vrender-components_v1.0.41", diff --git a/packages/vrender-components/CHANGELOG.md b/packages/vrender-components/CHANGELOG.md index 87a4ce528..079cbab9d 100644 --- a/packages/vrender-components/CHANGELOG.md +++ b/packages/vrender-components/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-components -This log was last generated on Tue, 03 Mar 2026 11:54:26 GMT and should not be manually modified. +This log was last generated on Mon, 23 Mar 2026 08:08:45 GMT and should not be manually modified. + +## 1.0.42 +Mon, 23 Mar 2026 08:08:45 GMT + +_Version update only_ ## 1.0.41 Tue, 03 Mar 2026 11:54:26 GMT diff --git a/packages/vrender-components/__tests__/browser/examples/label-smart-inverse-background.ts b/packages/vrender-components/__tests__/browser/examples/label-smart-inverse-background.ts new file mode 100644 index 000000000..51f5354dc --- /dev/null +++ b/packages/vrender-components/__tests__/browser/examples/label-smart-inverse-background.ts @@ -0,0 +1,45 @@ +import '@visactor/vrender'; +import { createRect } from '@visactor/vrender'; +import { createRenderer } from '../../util/render'; +import { RectLabel } from '../../../src'; + +export const run = () => { + const stage = createRenderer('main'); + + const rect = createRect({ + x: 180, + y: 180, + width: 160, + height: 100, + fill: '#2E62F1' + }); + stage.defaultLayer.add(rect); + + const label = new RectLabel({ + data: [{ text: 'Intersect Background' }], + // 放置在图形边缘附近以制造相交(根据文本尺寸可能略有不同) + position: 'top', + offset: -10, + textStyle: { + fill: '#000', + stroke: '#fff', + lineWidth: 1 + }, + smartInvert: { + interactInvertType: 'background' + } + }); + + // 关联 label 到 rect + // @ts-ignore + label.setRelatedGraphic(rect); + stage.defaultLayer.add(label); + + stage.render(); + + // 输出验证信息 + // eslint-disable-next-line no-console + console.log('label.stroke:', label.attribute?.stroke); + // eslint-disable-next-line no-console + console.log('label.background:', label.attribute?.background); +}; diff --git a/packages/vrender-components/__tests__/browser/main.ts b/packages/vrender-components/__tests__/browser/main.ts index 38c82fdb8..0352cec9d 100644 --- a/packages/vrender-components/__tests__/browser/main.ts +++ b/packages/vrender-components/__tests__/browser/main.ts @@ -146,6 +146,10 @@ const specs = [ path: 'label-smart-inverse', name: '标签智能反色' }, + { + path: 'label-smart-inverse-background', + name: '标签相交背景模式' + }, { path: 'scrollbar', name: '滚动条' diff --git a/packages/vrender-components/__tests__/unit/legend/continuous.test.ts b/packages/vrender-components/__tests__/unit/legend/continuous.test.ts new file mode 100644 index 000000000..587990ff6 --- /dev/null +++ b/packages/vrender-components/__tests__/unit/legend/continuous.test.ts @@ -0,0 +1,183 @@ +import type { IGraphic, IText, Stage } from '@visactor/vrender-core'; +import { ColorContinuousLegend, SizeContinuousLegend, SLIDER_ELEMENT_NAME } from '../../../src'; +import { createCanvas } from '../../util/dom'; +import { createStage } from '../../util/vrender'; +import { initBrowserEnv } from '@visactor/vrender-kits'; + +initBrowserEnv(); + +describe('ContinuousLegend handlerText style callback', () => { + let stage: Stage; + + beforeAll(() => { + createCanvas(document.body, 'continuous-legend'); + stage = createStage('continuous-legend'); + }); + + afterAll(() => { + stage.release(); + }); + + it('ColorContinuousLegend should evaluate handlerText style callback on render and update', () => { + const style = jest.fn((value: number, position: 'start' | 'end') => ({ + fill: value >= 50 ? 'red' : 'blue', + dx: position === 'start' ? -6 : 6 + })); + const legend = new ColorContinuousLegend({ + layout: 'horizontal', + align: 'bottom', + min: 0, + max: 100, + value: [20, 80], + colors: ['#1664FF', '#B2CFFF', '#FEE3A2', '#FF8A00'], + railWidth: 200, + railHeight: 8, + handlerText: { + visible: true, + style + } + }); + + stage.defaultLayer.add(legend as unknown as IGraphic); + stage.render(); + + let startHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.startHandlerText)[0] as IText; + let endHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.endHandlerText)[0] as IText; + + expect(style).toHaveBeenCalledWith( + 20, + 'start', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 200, + railHeight: 8 + }) + ); + expect(style).toHaveBeenCalledWith( + 80, + 'end', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 200, + railHeight: 8 + }) + ); + expect(startHandlerText.attribute.fill).toBe('blue'); + expect(startHandlerText.attribute.dx).toBe(-6); + expect(endHandlerText.attribute.fill).toBe('red'); + expect(endHandlerText.attribute.dx).toBe(6); + + legend.setSelected([55, 90]); + + startHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.startHandlerText)[0] as IText; + endHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.endHandlerText)[0] as IText; + + expect(style).toHaveBeenCalledWith( + 55, + 'start', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 200, + railHeight: 8 + }) + ); + expect(style).toHaveBeenCalledWith( + 90, + 'end', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 200, + railHeight: 8 + }) + ); + expect(startHandlerText.attribute.fill).toBe('red'); + expect(startHandlerText.attribute.dx).toBe(-6); + expect(endHandlerText.attribute.fill).toBe('red'); + expect(endHandlerText.attribute.dx).toBe(6); + }); + + it('SizeContinuousLegend should evaluate handlerText style callback on render and update', () => { + const style = jest.fn((value: number, position: 'start' | 'end') => ({ + fill: value >= 40 ? 'green' : 'gray', + dy: position === 'start' ? -4 : 4 + })); + const legend = new SizeContinuousLegend({ + layout: 'horizontal', + align: 'bottom', + min: 0, + max: 100, + value: [10, 70], + railWidth: 180, + railHeight: 8, + handlerText: { + visible: true, + style + } + }); + + stage.defaultLayer.add(legend as unknown as IGraphic); + stage.render(); + + let startHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.startHandlerText)[0] as IText; + let endHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.endHandlerText)[0] as IText; + + expect(style).toHaveBeenCalledWith( + 10, + 'start', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 180, + railHeight: 8 + }) + ); + expect(style).toHaveBeenCalledWith( + 70, + 'end', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 180, + railHeight: 8 + }) + ); + expect(startHandlerText.attribute.fill).toBe('gray'); + expect(startHandlerText.attribute.dy).toBe(-4); + expect(endHandlerText.attribute.fill).toBe('green'); + expect(endHandlerText.attribute.dy).toBe(4); + + legend.setSelected([45, 90]); + + startHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.startHandlerText)[0] as IText; + endHandlerText = legend.getElementsByName(SLIDER_ELEMENT_NAME.endHandlerText)[0] as IText; + + expect(style).toHaveBeenCalledWith( + 45, + 'start', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 180, + railHeight: 8 + }) + ); + expect(style).toHaveBeenCalledWith( + 90, + 'end', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 180, + railHeight: 8 + }) + ); + expect(startHandlerText.attribute.fill).toBe('green'); + expect(startHandlerText.attribute.dy).toBe(-4); + expect(endHandlerText.attribute.fill).toBe('green'); + expect(endHandlerText.attribute.dy).toBe(4); + }); +}); diff --git a/packages/vrender-components/__tests__/unit/slider.test.ts b/packages/vrender-components/__tests__/unit/slider.test.ts index a74723a39..ef2a288f6 100644 --- a/packages/vrender-components/__tests__/unit/slider.test.ts +++ b/packages/vrender-components/__tests__/unit/slider.test.ts @@ -154,6 +154,61 @@ describe('Slider', () => { startHandlerText = slider.getElementsByName(SLIDER_ELEMENT_NAME.startHandlerText)[0] as IText; expect(startHandlerText.attribute.x).toBe(16); }); + + it('should evaluate handlerText style callback on render and update', () => { + const style = jest.fn((value: number, position: 'start' | 'end') => ({ + fill: value > 50 ? 'red' : 'blue', + dx: position === 'start' ? -6 : 6 + })); + const slider = new Slider({ + x: 100, + y: 100, + layout: 'horizontal', + align: 'bottom', + railWidth: 200, + railHeight: 10, + min: 0, + max: 100, + value: 34, + handlerText: { + visible: true, + style + } + }); + + stage.defaultLayer.add(slider as unknown as IGraphic); + stage.render(); + + let startHandlerText = slider.getElementsByName(SLIDER_ELEMENT_NAME.startHandlerText)[0] as IText; + expect(style).toHaveBeenCalledWith( + 34, + 'end', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 200, + railHeight: 10 + }) + ); + expect(startHandlerText.attribute.fill).toBe('blue'); + expect(startHandlerText.attribute.dx).toBe(6); + + slider.setValue(78); + + startHandlerText = slider.getElementsByName(SLIDER_ELEMENT_NAME.startHandlerText)[0] as IText; + expect(style).toHaveBeenLastCalledWith( + 78, + 'end', + expect.objectContaining({ + layout: 'horizontal', + align: 'bottom', + railWidth: 200, + railHeight: 10 + }) + ); + expect(startHandlerText.attribute.fill).toBe('red'); + expect(startHandlerText.attribute.dx).toBe(6); + }); }); describe('Slider with two handlers', () => { diff --git a/packages/vrender-components/package.json b/packages/vrender-components/package.json index 6260b6220..967be5090 100644 --- a/packages/vrender-components/package.json +++ b/packages/vrender-components/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-components", - "version": "1.0.41", + "version": "1.0.42", "description": "components library for dp visualization", "sideEffects": false, "main": "cjs/index.js", @@ -27,9 +27,9 @@ "dependencies": { "@visactor/vutils": "~1.0.12", "@visactor/vscale": "~1.0.12", - "@visactor/vrender-core": "workspace:1.0.41", - "@visactor/vrender-kits": "workspace:1.0.41", - "@visactor/vrender-animate": "workspace:1.0.41" + "@visactor/vrender-core": "workspace:1.0.42", + "@visactor/vrender-kits": "workspace:1.0.42", + "@visactor/vrender-animate": "workspace:1.0.42" }, "devDependencies": { "@internal/bundler": "workspace:*", diff --git a/packages/vrender-components/src/label/base.ts b/packages/vrender-components/src/label/base.ts index e9201f4ed..29efe39c0 100644 --- a/packages/vrender-components/src/label/base.ts +++ b/packages/vrender-components/src/label/base.ts @@ -1057,6 +1057,7 @@ export class LabelBase extends AnimateComponent { * null(不执行智能反色,保持fill设置的颜色) * */ let backgroundColor = baseMark.getAttributes(true).fill as IColor; + const backgroundOpacity = baseMark.getAttributes(true).fillOpacity as number; let foregroundColor = label.attribute.fill as IColor; if (isObject(backgroundColor) && backgroundColor.gradient) { @@ -1089,11 +1090,21 @@ export class LabelBase extends AnimateComponent { if (label.attribute.lineWidth === 0 || label.attribute.strokeOpacity === 0) { continue; } - - const stroke = smartInvertStrategy(strokeStrategy, backgroundColor, invertColor, similarColor); - stroke && label.setAttributes({ stroke }); + if (interactInvertType === 'background') { + label.setAttributes({ stroke: false }); + } else { + const stroke = smartInvertStrategy(strokeStrategy, backgroundColor, invertColor, similarColor); + stroke && label.setAttributes({ stroke }); + } } else if (isIntersect && interactInvertType !== 'none') { // 存在相交的情况 + if (interactInvertType === 'background') { + // 按照标签展示在柱子内部的情况,执行反色逻辑 + const fill = smartInvertStrategy(fillStrategy, backgroundColor, invertColor, similarColor); + fill && label.setAttributes({ fill }); + label.setAttributes({ stroke: false, background: backgroundColor as string, backgroundOpacity }); + continue; + } /** 当label无法设置stroke时,不进行反色计算(容易反色为白色与白色背景混合不可见) */ if (label.attribute.lineWidth === 0 || label.attribute.strokeOpacity === 0) { continue; @@ -1120,6 +1131,9 @@ export class LabelBase extends AnimateComponent { const stroke = smartInvertStrategy(strokeStrategy, backgroundColor, invertColor, similarColor); stroke && label.setAttributes({ stroke }); } + if (isInside === false && interactInvertType === 'background') { + label.setAttributes({ background: null }); + } } } diff --git a/packages/vrender-components/src/label/type.ts b/packages/vrender-components/src/label/type.ts index 65e3ed068..ef172b13c 100644 --- a/packages/vrender-components/src/label/type.ts +++ b/packages/vrender-components/src/label/type.ts @@ -307,13 +307,14 @@ export interface SmartInvertAttrs { */ outsideEnable?: boolean; /** - * 当标签和mark相交,但是没有完全在mark内部的时候,支持三种处理方式: + * 当标签和mark相交,但是没有完全在mark内部的时候,支持四种处理方式: * * * none:不做任何处理 * * stroked:标签存在描边的时候,根据描边进行处理 * * inside: 和标签完全在mark内部一样处理 + * * background:关闭标签描边,并将标签背景色设置为 mark 的填充色 */ - interactInvertType?: 'none' | 'stroked' | 'inside'; + interactInvertType?: 'none' | 'stroked' | 'inside' | 'background'; } export type ShiftYStrategy = { diff --git a/packages/vrender-components/src/slider/slider.ts b/packages/vrender-components/src/slider/slider.ts index 1e59bff2a..4565f8dfe 100644 --- a/packages/vrender-components/src/slider/slider.ts +++ b/packages/vrender-components/src/slider/slider.ts @@ -14,7 +14,18 @@ import type { FederatedPointerEvent, Cursor } from '@visactor/vrender-core'; -import { isNil, merge, clamp, isValid, array, isObject, isArray, clampRange, debounce } from '@visactor/vutils'; +import { + isNil, + merge, + clamp, + isValid, + array, + isObject, + isArray, + clampRange, + debounce, + isFunction +} from '@visactor/vutils'; import { graphicCreator, vglobal, CustomEvent } from '@visactor/vrender-core'; import { AbstractComponent } from '../core/base'; import { SLIDER_ELEMENT_NAME } from './constant'; @@ -475,6 +486,15 @@ export class Slider extends AbstractComponent> { } private _renderHandlerText(value: number, position: 'start' | 'end') { + const textShape = graphicCreator.text(this._getHandlerTextAttributes(value, position)); + return textShape; + } + + private _getHandlerPosition(isStart: boolean): 'start' | 'end' { + return this.attribute.range ? (isStart ? 'start' : 'end') : 'end'; + } + + private _getHandlerTextStyle(value: number, position: 'start' | 'end') { const { align, handlerSize = 14, @@ -484,13 +504,37 @@ export class Slider extends AbstractComponent> { slidable } = this.attribute as SliderAttributes; - const isHorizontal = this._isHorizontal; + if (isFunction(handlerText.style)) { + return handlerText.style(value, position, { + layout: this.attribute.layout, + align, + railWidth, + railHeight, + handlerSize, + slidable + }); + } + + return handlerText.style; + } + private _getHandlerTextAttributes(value: number, position: 'start' | 'end'): ITextGraphicAttribute { + const { + align, + handlerSize = 14, + handlerText = {}, + railHeight, + railWidth, + slidable + } = this.attribute as SliderAttributes; + + const isHorizontal = this._isHorizontal; const pos = this.calculatePosByValue(value, position); const textSpace = handlerText.space ?? 4; + const handlerTextStyle = this._getHandlerTextStyle(value, position); const textStyle: ITextGraphicAttribute = { text: handlerText.formatter ? handlerText.formatter(value) : value.toFixed(handlerText.precision ?? 0), - lineHeight: handlerText.style?.lineHeight, + lineHeight: handlerTextStyle?.lineHeight, cursor: slidable === false ? 'default' : getDefaultCursor(isHorizontal) }; if (isHorizontal) { @@ -523,12 +567,10 @@ export class Slider extends AbstractComponent> { } } - // 展示 handler 当前所在的数值 - const textShape = graphicCreator.text({ + return { ...textStyle, - ...handlerText.style - }); - return textShape; + ...handlerTextStyle + }; } private _renderTooltip() { @@ -1016,11 +1058,8 @@ export class Slider extends AbstractComponent> { const updateHandlerText = handler.name === SLIDER_ELEMENT_NAME.startHandler ? this._startHandlerText : this._endHandlerText; if (updateHandlerText) { - const { handlerText = {} } = this.attribute as SliderAttributes; - updateHandlerText.setAttributes({ - text: handlerText.formatter ? handlerText.formatter(value) : value.toFixed(handlerText.precision ?? 0), - [isHorizontal ? 'x' : 'y']: position - }); + const handlerPosition = this._getHandlerPosition(handler.name === SLIDER_ELEMENT_NAME.startHandler); + updateHandlerText.setAttributes(this._getHandlerTextAttributes(value, handlerPosition)); } if (handler.name === SLIDER_ELEMENT_NAME.startHandler) { @@ -1035,11 +1074,8 @@ export class Slider extends AbstractComponent> { // 更新 handler 以及对应 text private _updateHandlerText(handlerText: IText, position: number, value: number) { const isHorizontal = this._isHorizontal; - const { handlerText: handlerTextAttr = {} } = this.attribute as SliderAttributes; - handlerText.setAttributes({ - [isHorizontal ? 'x' : 'y']: position, - text: handlerTextAttr.formatter ? handlerTextAttr.formatter(value) : value.toFixed(handlerTextAttr.precision ?? 0) - }); + const handlerPosition = this._getHandlerPosition(handlerText.name === SLIDER_ELEMENT_NAME.startHandlerText); + handlerText.setAttributes(this._getHandlerTextAttributes(value, handlerPosition)); const updateHandler = handlerText.name === SLIDER_ELEMENT_NAME.startHandlerText ? this._startHandler : this._endHandler; if (updateHandler) { diff --git a/packages/vrender-components/src/slider/type.ts b/packages/vrender-components/src/slider/type.ts index f06cf319a..2cb23beb2 100644 --- a/packages/vrender-components/src/slider/type.ts +++ b/packages/vrender-components/src/slider/type.ts @@ -6,6 +6,22 @@ import type { } from '@visactor/vrender-core'; type Text = string | number; +type SliderLayout = 'horizontal' | 'vertical'; +type SliderAlign = 'top' | 'bottom' | 'left' | 'right'; + +export type HandlerTextStyleContext = { + layout?: SliderLayout; + align?: SliderAlign; + railWidth: number; + railHeight: number; + handlerSize?: number; + slidable?: boolean; +}; +export type HandlerTextStyleCallback = ( + value: Text, + position: 'start' | 'end', + context: HandlerTextStyleContext +) => Omit, 'text'> | undefined; export type TextAttribute = { /** 是否展示 */ @@ -38,7 +54,7 @@ export type HandlerTextAttribute = { /** * 文本样式 */ - style?: Omit, 'text'>; + style?: Omit, 'text'> | HandlerTextStyleCallback; }; export type TooltipConfig = { @@ -100,7 +116,7 @@ export type SliderAttributes = { * - 'horizontal' 水平布局 * - 'vertical' 垂直布局 */ - layout?: 'horizontal' | 'vertical' | string; + layout?: SliderLayout; /** * 指定组件中手柄和文字的摆放位置,可选值为: * 'left' 手柄和label在滑块左侧,layout 为 vertical 时有效。 @@ -108,7 +124,7 @@ export type SliderAttributes = { * 'top' 手柄和label在滑块上侧,layout 为 horizontal 时有效。 * 'bottom' 手柄和label在滑块下侧,layout 为 horizontal 时有效。 */ - align?: 'top' | 'bottom' | 'left' | 'right'; + align?: SliderAlign; /** * 双滑块模式,默认为 false,单滑块模式 */ diff --git a/packages/vrender-core/CHANGELOG.json b/packages/vrender-core/CHANGELOG.json index 5c593761f..b40e41b39 100644 --- a/packages/vrender-core/CHANGELOG.json +++ b/packages/vrender-core/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender-core", "entries": [ + { + "version": "1.0.42", + "tag": "@visactor/vrender-core_v1.0.42", + "date": "Mon, 23 Mar 2026 08:08:45 GMT", + "comments": {} + }, { "version": "1.0.41", "tag": "@visactor/vrender-core_v1.0.41", diff --git a/packages/vrender-core/CHANGELOG.md b/packages/vrender-core/CHANGELOG.md index 6985cfa8b..9e16b3d82 100644 --- a/packages/vrender-core/CHANGELOG.md +++ b/packages/vrender-core/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-core -This log was last generated on Tue, 03 Mar 2026 11:54:26 GMT and should not be manually modified. +This log was last generated on Mon, 23 Mar 2026 08:08:45 GMT and should not be manually modified. + +## 1.0.42 +Mon, 23 Mar 2026 08:08:45 GMT + +_Version update only_ ## 1.0.41 Tue, 03 Mar 2026 11:54:26 GMT diff --git a/packages/vrender-core/package.json b/packages/vrender-core/package.json index 87755ea7d..4c258d918 100644 --- a/packages/vrender-core/package.json +++ b/packages/vrender-core/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-core", - "version": "1.0.41", + "version": "1.0.42", "description": "", "sideEffects": [ "./src/modules.ts", diff --git a/packages/vrender-core/src/render/contributions/render/contributions/text-contribution-render.ts b/packages/vrender-core/src/render/contributions/render/contributions/text-contribution-render.ts index 091e7b57e..677c42866 100644 --- a/packages/vrender-core/src/render/contributions/render/contributions/text-contribution-render.ts +++ b/packages/vrender-core/src/render/contributions/render/contributions/text-contribution-render.ts @@ -110,9 +110,10 @@ export class DefaultTextBackgroundRenderContribution context.highPerformanceRestore(); context.setTransformForCurrent(); } else { - const { backgroundCornerRadius } = graphic.attribute; + const { backgroundCornerRadius, backgroundOpacity = 1 } = graphic.attribute; context.highPerformanceSave(); context.setCommonStyle(graphic, graphic.attribute, x, y, graphicAttribute); + context.globalAlpha = backgroundOpacity; context.fillStyle = background as string; if (backgroundCornerRadius) { // 测试后,cache对于重绘性能提升不大,但是在首屏有一定性能损耗,因此rect不再使用cache diff --git a/packages/vrender-kits/CHANGELOG.json b/packages/vrender-kits/CHANGELOG.json index 2f8222e43..6c07c9516 100644 --- a/packages/vrender-kits/CHANGELOG.json +++ b/packages/vrender-kits/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender-kits", "entries": [ + { + "version": "1.0.42", + "tag": "@visactor/vrender-kits_v1.0.42", + "date": "Mon, 23 Mar 2026 08:08:45 GMT", + "comments": {} + }, { "version": "1.0.41", "tag": "@visactor/vrender-kits_v1.0.41", diff --git a/packages/vrender-kits/CHANGELOG.md b/packages/vrender-kits/CHANGELOG.md index 51a09c7e9..82c4362e7 100644 --- a/packages/vrender-kits/CHANGELOG.md +++ b/packages/vrender-kits/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-kits -This log was last generated on Tue, 03 Mar 2026 11:54:26 GMT and should not be manually modified. +This log was last generated on Mon, 23 Mar 2026 08:08:45 GMT and should not be manually modified. + +## 1.0.42 +Mon, 23 Mar 2026 08:08:45 GMT + +_Version update only_ ## 1.0.41 Tue, 03 Mar 2026 11:54:26 GMT diff --git a/packages/vrender-kits/package.json b/packages/vrender-kits/package.json index 3333d1c07..12ac54710 100644 --- a/packages/vrender-kits/package.json +++ b/packages/vrender-kits/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-kits", - "version": "1.0.41", + "version": "1.0.42", "description": "", "sideEffects": false, "main": "cjs/index-node.js", @@ -21,7 +21,7 @@ }, "dependencies": { "@visactor/vutils": "~1.0.12", - "@visactor/vrender-core": "workspace:1.0.41", + "@visactor/vrender-core": "workspace:1.0.42", "@resvg/resvg-js": "2.4.1", "roughjs": "4.6.6", "gifuct-js": "2.1.2", diff --git a/packages/vrender/CHANGELOG.json b/packages/vrender/CHANGELOG.json index ff6ea6389..7c304030f 100644 --- a/packages/vrender/CHANGELOG.json +++ b/packages/vrender/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender", "entries": [ + { + "version": "1.0.42", + "tag": "@visactor/vrender_v1.0.42", + "date": "Mon, 23 Mar 2026 08:08:45 GMT", + "comments": {} + }, { "version": "1.0.41", "tag": "@visactor/vrender_v1.0.41", diff --git a/packages/vrender/CHANGELOG.md b/packages/vrender/CHANGELOG.md index bb16df370..df91dfa17 100644 --- a/packages/vrender/CHANGELOG.md +++ b/packages/vrender/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender -This log was last generated on Tue, 03 Mar 2026 11:54:26 GMT and should not be manually modified. +This log was last generated on Mon, 23 Mar 2026 08:08:45 GMT and should not be manually modified. + +## 1.0.42 +Mon, 23 Mar 2026 08:08:45 GMT + +_Version update only_ ## 1.0.41 Tue, 03 Mar 2026 11:54:26 GMT diff --git a/packages/vrender/package.json b/packages/vrender/package.json index ba2227586..b5893688c 100644 --- a/packages/vrender/package.json +++ b/packages/vrender/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender", - "version": "1.0.41", + "version": "1.0.42", "description": "", "sideEffects": true, "main": "cjs/index.js", @@ -24,10 +24,10 @@ "test-watch": "cross-env DEBUG_MODE=1 jest --watch" }, "dependencies": { - "@visactor/vrender-core": "workspace:1.0.41", - "@visactor/vrender-kits": "workspace:1.0.41", - "@visactor/vrender-animate": "workspace:1.0.41", - "@visactor/vrender-components": "workspace:1.0.41" + "@visactor/vrender-core": "workspace:1.0.42", + "@visactor/vrender-kits": "workspace:1.0.42", + "@visactor/vrender-animate": "workspace:1.0.42", + "@visactor/vrender-components": "workspace:1.0.42" }, "devDependencies": { "@internal/bundler": "workspace:*", diff --git a/tools/bugserver-trigger/package.json b/tools/bugserver-trigger/package.json index 51f59a440..0703d9597 100644 --- a/tools/bugserver-trigger/package.json +++ b/tools/bugserver-trigger/package.json @@ -8,11 +8,11 @@ "ci": "ts-node --transpileOnly --skipProject ./scripts/trigger-test.ts" }, "dependencies": { - "@visactor/vrender": "workspace:1.0.41", - "@visactor/vrender-core": "workspace:1.0.41", - "@visactor/vrender-kits": "workspace:1.0.41", - "@visactor/vrender-components": "workspace:1.0.41", - "@visactor/vrender-animate": "workspace:1.0.41" + "@visactor/vrender": "workspace:1.0.42", + "@visactor/vrender-core": "workspace:1.0.42", + "@visactor/vrender-kits": "workspace:1.0.42", + "@visactor/vrender-components": "workspace:1.0.42", + "@visactor/vrender-animate": "workspace:1.0.42" }, "devDependencies": { "@rushstack/eslint-patch": "~1.1.4",