diff --git a/common/changes/@visactor/vrender-components/fix-brush-problem_2025-05-31-14-30.json b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-05-31-14-30.json new file mode 100644 index 000000000..ba64f9de6 --- /dev/null +++ b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-05-31-14-30.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vrender-components", + "comment": "fix: brush active problem. fix visactor/vchart#4017", + "type": "none" + } + ], + "packageName": "@visactor/vrender-components" +} \ No newline at end of file diff --git a/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-02-36.json b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-02-36.json new file mode 100644 index 000000000..504ec2dcc --- /dev/null +++ b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-02-36.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vrender-components", + "comment": "fix: brush event pos problem when stage scale", + "type": "none" + } + ], + "packageName": "@visactor/vrender-components" +} \ No newline at end of file diff --git a/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-02-53.json b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-02-53.json new file mode 100644 index 000000000..cce01dfea --- /dev/null +++ b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-02-53.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vrender-components", + "comment": "fix: use removeAllChild to remove brush mask. fix visactor/vchart#4017", + "type": "none" + } + ], + "packageName": "@visactor/vrender-components" +} \ No newline at end of file diff --git a/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-03-56.json b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-03-56.json new file mode 100644 index 000000000..f33830f10 --- /dev/null +++ b/common/changes/@visactor/vrender-components/fix-brush-problem_2025-06-06-03-56.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vrender-components", + "comment": "fix: datazoom text render error. fix visactor/vchart#4018", + "type": "none" + } + ], + "packageName": "@visactor/vrender-components" +} \ No newline at end of file diff --git a/packages/vrender-components/src/brush/brush.ts b/packages/vrender-components/src/brush/brush.ts index 4da78c53c..b71dee060 100644 --- a/packages/vrender-components/src/brush/brush.ts +++ b/packages/vrender-components/src/brush/brush.ts @@ -28,6 +28,7 @@ export class Brush extends AbstractComponent> { private _container!: IGroup; // 绘制mask时的相关属性 + private _activeBrushState = false; // 用于标记激活状态 private _activeDrawState = false; // 用于标记绘制状态 private _cacheDrawPoints: IPointLike[] = []; // 用于维护鼠标走过的路径,主要用于绘制mask的点 // 移动mask时的相关属性 @@ -67,18 +68,10 @@ export class Brush extends AbstractComponent> { * @description * 1. 判断状态: 如果在brushMask中,则属于移动状态; 否则属于绘制状态 *(移动状态和绘制状态互斥, 且移动状态考虑brushMoved配置, 如果在brush点内但brushMoved为false, 则走绘制状态, 而非两个状态都不响应, 此效果与echarts保持一致) - * 2. 判断坐标是否在有效交互范围内 * 2. 如果是移动状态: 标记移动状态 & 标记正在移动的mask & 初始化mask的dx和dy * 3. 如果是绘制状态: 标记绘制状态 & 标记正在绘制的mask & 清除之前的mask & 添加新的mask */ private _onBrushStart = (e: FederatedPointerEvent) => { - if (this._outOfInteractiveRange(e)) { - if (!this._isEmptyMask()) { - this._clearMask(); - this._dispatchBrushEvent(IOperateType.brushClear, e); - } - return; - } const { updateTrigger = DEFAULT_BRUSH_ATTRIBUTES.updateTrigger, endTrigger = DEFAULT_BRUSH_ATTRIBUTES.endTrigger, @@ -157,12 +150,7 @@ export class Brush extends AbstractComponent> { brushMode === 'single' && this._clearMask(); this._addBrushMask(); this._dispatchBrushEvent(IOperateType.drawStart, e); - // 无论是多选,还是单选 - // 如果这是第一个brush mask - // 证明这第一次绘制, 则触发brushActive事件 - if (Object.keys(this._brushMaskAABBBoundsDict).length === 1) { - this._dispatchBrushEvent(IOperateType.brushActive, e); - } + this._activeBrushState = false; } /** @@ -198,7 +186,7 @@ export class Brush extends AbstractComponent> { */ private _drawing(e: FederatedPointerEvent) { const pos = this.eventPosToStagePos(e); - const { brushType } = this.attribute as BrushAttributes; + const { brushType, sizeThreshold = DEFAULT_SIZE_THRESHOLD } = this.attribute as BrushAttributes; const cacheLength = this._cacheDrawPoints.length; @@ -218,7 +206,20 @@ export class Brush extends AbstractComponent> { // 更新mask形状 const maskPoints = this._computeMaskPoints(); this._operatingMask.setAttribute('points', maskPoints); - this._dispatchBrushEvent(IOperateType.drawing, e); + const { x: x1, y: y1 } = this._startPos; + const { x: x2, y: y2 } = this.eventPosToStagePos(e); + // 绘制大小超过阈值, 才激活brush + if (Math.abs(x2 - x1) > sizeThreshold || Math.abs(y1 - y2) > sizeThreshold) { + // 无论是多选,还是单选 + // 如果这是第一个brush mask + // 证明这第一次绘制, 则触发brushActive事件 + if (Object.keys(this._brushMaskAABBBoundsDict).length === 1 && !this._activeBrushState) { + this._activeBrushState = true; + this._dispatchBrushEvent(IOperateType.brushActive, e); + } else { + this._dispatchBrushEvent(IOperateType.drawing, e); + } + } } /** @@ -441,13 +442,6 @@ export class Brush extends AbstractComponent> { return false; } - /** - * 事件系统坐标转换为stage坐标 - */ - protected eventPosToStagePos(e: FederatedPointerEvent) { - return this.stage.eventPointTransform(e); - } - /** * 根据操作类型触发对应的事件 */ @@ -464,7 +458,7 @@ export class Brush extends AbstractComponent> { */ private _clearMask() { this._brushMaskAABBBoundsDict = {}; - this._container.incrementalClearChild(); + this._container.removeAllChild(); this._operatingMask = null; } diff --git a/packages/vrender-components/src/core/base.ts b/packages/vrender-components/src/core/base.ts index 9a6908329..863a4e431 100644 --- a/packages/vrender-components/src/core/base.ts +++ b/packages/vrender-components/src/core/base.ts @@ -1,7 +1,7 @@ /** * @description 组件基类 */ -import type { IGroupGraphicAttribute, ISetAttributeContext } from '@visactor/vrender-core'; +import type { FederatedPointerEvent, IGroupGraphicAttribute, ISetAttributeContext } from '@visactor/vrender-core'; import { Group, CustomEvent } from '@visactor/vrender-core'; import type { Dict } from '@visactor/vutils'; import { merge, isFunction, isPlainObject, isNil } from '@visactor/vutils'; @@ -166,4 +166,14 @@ export abstract class AbstractComponent 内部坐标 + const stagePoints = this.stage?.eventPointTransform(e as any) ?? { x: 0, y: 0 }; // updateSpec过程中交互的话, stage可能为空 + // 2. 内部坐标 -> 组件坐标 (比如: 给layer设置 scale / x / y) + this.globalTransMatrix.transformPoint(stagePoints, result); + return result; + } } diff --git a/packages/vrender-components/src/data-zoom/data-zoom.ts b/packages/vrender-components/src/data-zoom/data-zoom.ts index eb97228b3..7dbdb8134 100644 --- a/packages/vrender-components/src/data-zoom/data-zoom.ts +++ b/packages/vrender-components/src/data-zoom/data-zoom.ts @@ -232,12 +232,6 @@ export class DataZoom extends AbstractComponent> { shouldRender && this.setAttributes({ start, end }); } - /** 事件系统坐标转换为stage坐标 */ - protected eventPosToStagePos(e: FederatedPointerEvent) { - // updateSpec过程中交互的话, stage可能为空 - return this.stage?.eventPointTransform(e) ?? { x: 0, y: 0 }; - } - private _clearDragEvents() { const evtTarget = vglobal.env === 'browser' ? vglobal : this.stage; const triggers = getEndTriggersOfDrag(); @@ -613,10 +607,13 @@ export class DataZoom extends AbstractComponent> { // 第三次绘制: 避免startText和endText重叠, 如果重叠了, 对startText做位置调整(考虑到调整的最小化,只单独调整startText而不调整endText) if (new Bounds().set(x1, y1, x2, y2).intersects(endTextBounds)) { const direction = this.attribute.orient === 'bottom' || this.attribute.orient === 'right' ? -1 : 1; + if (this._isHorizontal) { - this._startText.setAttribute('dy', startTextDy + direction * Math.abs(endTextBounds.y1 - endTextBounds.y2)); + const boundsYDiff = Math.abs(endTextBounds.y1 - endTextBounds.y2); // visible: false时, bounds可能是非法的 + this._startText.setAttribute('dy', startTextDy + direction * (Number.isFinite(boundsYDiff) ? boundsYDiff : 0)); } else { - this._startText.setAttribute('dx', startTextDx + direction * Math.abs(endTextBounds.x1 - endTextBounds.x2)); + const boundsXDiff = Math.abs(endTextBounds.x1 - endTextBounds.x2); // visible: false时, bounds可能是非法的 + this._startText.setAttribute('dx', startTextDx + direction * (Number.isFinite(boundsXDiff) ? boundsXDiff : 0)); } } else { if (this._isHorizontal) { @@ -736,7 +733,7 @@ export class DataZoom extends AbstractComponent> { height, cursor: brushSelect ? 'crosshair' : 'auto', ...backgroundStyle, - pickable: zoomLock ? false : backgroundStyle.pickable ?? true + pickable: zoomLock ? false : (backgroundStyle.pickable ?? true) }, 'rect' ) as IRect; @@ -760,7 +757,7 @@ export class DataZoom extends AbstractComponent> { height: height, cursor: brushSelect ? 'crosshair' : 'move', ...selectedBackgroundStyle, - pickable: zoomLock ? false : (selectedBackgroundChartStyle as any).pickable ?? true + pickable: zoomLock ? false : ((selectedBackgroundChartStyle as any).pickable ?? true) }, 'rect' ) as IRect; @@ -775,7 +772,7 @@ export class DataZoom extends AbstractComponent> { height: (end - start) * height, cursor: brushSelect ? 'crosshair' : 'move', ...selectedBackgroundStyle, - pickable: zoomLock ? false : selectedBackgroundStyle.pickable ?? true + pickable: zoomLock ? false : (selectedBackgroundStyle.pickable ?? true) }, 'rect' ) as IRect; @@ -797,7 +794,7 @@ export class DataZoom extends AbstractComponent> { width: (end - start) * width, height: middleHandlerBackgroundSize, ...middleHandlerStyle.background?.style, - pickable: zoomLock ? false : middleHandlerStyle.background?.style?.pickable ?? true + pickable: zoomLock ? false : (middleHandlerStyle.background?.style?.pickable ?? true) }, 'rect' ) as IRect; @@ -810,7 +807,7 @@ export class DataZoom extends AbstractComponent> { angle: 0, symbolType: middleHandlerStyle.icon?.symbolType ?? 'square', ...middleHandlerStyle.icon, - pickable: zoomLock ? false : middleHandlerStyle.icon.pickable ?? true + pickable: zoomLock ? false : (middleHandlerStyle.icon.pickable ?? true) }, 'symbol' ) as ISymbol; @@ -824,7 +821,7 @@ export class DataZoom extends AbstractComponent> { symbolType: startHandlerStyle.symbolType ?? 'square', ...(DEFAULT_HANDLER_ATTR_MAP.horizontal as any), ...startHandlerStyle, - pickable: zoomLock ? false : startHandlerStyle.pickable ?? true + pickable: zoomLock ? false : (startHandlerStyle.pickable ?? true) }, 'symbol' ) as ISymbol; @@ -837,7 +834,7 @@ export class DataZoom extends AbstractComponent> { symbolType: endHandlerStyle.symbolType ?? 'square', ...(DEFAULT_HANDLER_ATTR_MAP.horizontal as any), ...endHandlerStyle, - pickable: zoomLock ? false : endHandlerStyle.pickable ?? true + pickable: zoomLock ? false : (endHandlerStyle.pickable ?? true) }, 'symbol' ) as ISymbol; @@ -890,7 +887,7 @@ export class DataZoom extends AbstractComponent> { width: middleHandlerBackgroundSize, height: (end - start) * height, ...middleHandlerStyle.background?.style, - pickable: zoomLock ? false : middleHandlerStyle.background?.style?.pickable ?? true + pickable: zoomLock ? false : (middleHandlerStyle.background?.style?.pickable ?? true) }, 'rect' ) as IRect; @@ -907,7 +904,7 @@ export class DataZoom extends AbstractComponent> { symbolType: middleHandlerStyle.icon?.symbolType ?? 'square', strokeBoundsBuffer: 0, ...middleHandlerStyle.icon, - pickable: zoomLock ? false : middleHandlerStyle.icon?.pickable ?? true + pickable: zoomLock ? false : (middleHandlerStyle.icon?.pickable ?? true) }, 'symbol' ) as ISymbol; @@ -921,7 +918,7 @@ export class DataZoom extends AbstractComponent> { symbolType: startHandlerStyle.symbolType ?? 'square', ...(DEFAULT_HANDLER_ATTR_MAP.vertical as any), ...startHandlerStyle, - pickable: zoomLock ? false : startHandlerStyle.pickable ?? true + pickable: zoomLock ? false : (startHandlerStyle.pickable ?? true) }, 'symbol' ) as ISymbol; @@ -935,7 +932,7 @@ export class DataZoom extends AbstractComponent> { symbolType: endHandlerStyle.symbolType ?? 'square', ...(DEFAULT_HANDLER_ATTR_MAP.vertical as any), ...endHandlerStyle, - pickable: zoomLock ? false : endHandlerStyle.pickable ?? true + pickable: zoomLock ? false : (endHandlerStyle.pickable ?? true) }, 'symbol' ) as ISymbol;