Skip to content

Commit

Permalink
fix(event): Fix interaction with viewBox
Browse files Browse the repository at this point in the history
Fix basic interaction when scale is update with viewBox

Fix #3414
  • Loading branch information
netil authored Sep 10, 2024
1 parent e32ba57 commit 243bf3c
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 14 deletions.
22 changes: 18 additions & 4 deletions src/ChartInternal/data/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import {KEY} from "../../module/Cache";
import {
findIndex,
getScrollPosition,
getTransformCTM,
getUnique,
hasValue,
hasViewBox,
isArray,
isBoolean,
isDefined,
Expand Down Expand Up @@ -701,7 +703,11 @@ export default {
*/
getDataIndexFromEvent(event): number {
const $$ = this;
const {$el, config, state: {hasRadar, inputType, eventReceiver: {coords, rect}}} = $$;
const {
$el,
config,
state: {hasRadar, inputType, eventReceiver: {coords, rect}}
} = $$;
let index;

if (hasRadar) {
Expand All @@ -724,11 +730,19 @@ export default {
event.changedTouches[0] :
event;

let point = isRotated ?
e.clientY + scrollPos.y - rect.top :
e.clientX + scrollPos.x - rect.left;

if (hasViewBox($el.svg)) {
const pos = [point, 0];
isRotated && pos.reverse();
point = getTransformCTM($el.svg.node(), ...pos)[isRotated ? "y" : "x"];
}

index = findIndex(
coords,
isRotated ?
e.clientY + scrollPos.y - rect.top :
e.clientX + scrollPos.x - rect.left,
point,
0,
coords.length - 1,
isRotated
Expand Down
27 changes: 21 additions & 6 deletions src/ChartInternal/interactions/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import {drag as d3Drag} from "d3-drag";
import {select as d3Select} from "d3-selection";
import {$ARC, $AXIS, $COMMON, $SHAPE} from "../../config/classes";
import {KEY} from "../../module/Cache";
import {emulateEvent, getPointer, isNumber, isObject} from "../../module/util";
import {
emulateEvent,
getPointer,
getTransformCTM,
hasViewBox,
isNumber,
isObject
} from "../../module/util";
import type {IArcDataRow} from "../data/IData";

export default {
Expand Down Expand Up @@ -152,11 +159,11 @@ export default {
d3Drag()
.on("drag", function(event) {
state.event = event;
$$.drag(getPointer(event, this));
$$.drag(getPointer(event, <SVGElement>this));
})
.on("start", function(event) {
state.event = event;
$$.dragstart(getPointer(event, this));
$$.dragstart(getPointer(event, <SVGElement>this));
})
.on("end", event => {
state.event = event;
Expand All @@ -183,7 +190,7 @@ export default {
hasRadar,
hasTreemap
},
$el: {eventRect, funnel, radar, treemap}
$el: {eventRect, funnel, radar, svg, treemap}
} = $$;
let element = (
((hasFunnel || hasTreemap) && eventReceiver.rect) ||
Expand Down Expand Up @@ -211,12 +218,20 @@ export default {
}
}

const x = left + (mouse ? mouse[0] : 0) + (
let x = left + (mouse ? mouse[0] : 0) + (
isMultipleX || isRotated ? 0 : (width / 2)
);

// value 4, is to adjust coordinate value set from: scale.ts - updateScales(): $$.getResettedPadding(1)
const y = top + (mouse ? mouse[1] : 0) + (isRotated ? 4 : 0);
let y = top + (mouse ? mouse[1] : 0) + (isRotated ? 4 : 0);

if (hasViewBox(svg)) {
const ctm = getTransformCTM($$.$el.svg.node(), x, y, false);

x = ctm.x;
y = ctm.y;
}

const params = {
screenX: x,
screenY: y,
Expand Down
56 changes: 54 additions & 2 deletions src/ChartInternal/internals/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {document} from "../../module/browser";
import {
callFn,
getPointer,
getTransformCTM,
hasViewBox,
isEmpty,
isFunction,
isObject,
Expand Down Expand Up @@ -294,7 +296,7 @@ export default {
*/
setTooltipPosition(dataToShow: IDataRow[], eventTarget: SVGElement): void {
const $$ = this;
const {config, scale, state, $el: {eventRect, tooltip}} = $$;
const {config, scale, state, $el: {eventRect, tooltip, svg}} = $$;
const {bindto} = config.tooltip_contents;
const isRotated = config.axis_rotated;
const datum = tooltip?.datum();
Expand Down Expand Up @@ -341,7 +343,11 @@ export default {
height,
eventRect?.node(),
currPos
) ?? $$.getTooltipPosition.bind($$)(width, height, currPos);
) ?? (
hasViewBox(svg) ?
$$.getTooltipPositionViewBox.bind($$)(width, height, currPos) :
$$.getTooltipPosition.bind($$)(width, height, currPos)
);

["top", "left"].forEach(v => {
const value = pos[v];
Expand All @@ -356,6 +362,51 @@ export default {
}
},

getTooltipPositionViewBox(tWidth: number, tHeight: number,
currPos: {[key: string]: number}): {top: number, left: number} {
const $$ = this;
const {$el: {eventRect, main}, config, state} = $$;

const isRotated = config.axis_rotated;
const hasArcType = $$.hasArcType(undefined, ["radar"]) || state.hasFunnel ||
state.hasTreemap;
const target = (state.hasRadar ? main : eventRect)?.node() ?? state.event.target;
const size = 38; // getTransformCTM($el.svg.node(), 10, 0, false).x;

let {x, y} = currPos;

if (state.hasAxis) {
x = isRotated ? x : currPos.xAxis;
y = isRotated ? currPos.xAxis : y;
}

// currPos는 SVG 좌표계 기준으로 전달됨
const ctm = getTransformCTM(target, x, y, false);

let top = ctm.y;
let left = ctm.x + size;

if (hasArcType) {
top += tHeight;
left -= size; // (tWidth / 2);
}

const rect = (hasArcType ? main.node() : target).getBoundingClientRect();

if (left + tWidth > rect.right) {
left = rect.right - tWidth - size;
}

if (top + tHeight > rect.bottom) {
top -= tHeight + size;
}

return {
top,
left
};
},

/**
* Returns the position of the tooltip
* @param {string} tWidth Width value of tooltip element
Expand All @@ -375,6 +426,7 @@ export default {
const hasArcType = $$.hasArcType();
const svgLeft = $$.getSvgLeft(true);
let chartRight = svgLeft + current.width - $$.getCurrentPaddingByDirection("right");

const size = 20;
let {x, y} = currPos;

Expand Down
3 changes: 2 additions & 1 deletion src/ChartInternal/internals/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
import {TYPE, TYPE_BY_CATEGORY} from "../../config/const";
import {isArray, isNumber, isString} from "../../module/util";
import type {IData} from "../data/IData";

export default {
/**
Expand Down Expand Up @@ -137,7 +138,7 @@ export default {
* @returns {boolean}
* @private
*/
hasArcType(targets, exclude): boolean {
hasArcType(targets?: IData, exclude?: string[]): boolean {
return this.hasTypeOf("Arc", targets, exclude);
},

Expand Down
32 changes: 31 additions & 1 deletion src/module/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ export {
getRange,
getRectSegList,
getScrollPosition,
getTransformCTM,
getTranslation,
getUnique,
hasValue,
hasViewBox,
isArray,
isBoolean,
isDefined,
Expand Down Expand Up @@ -268,7 +270,7 @@ function getPathBox(
* @returns {Array} [x, y] Coordinates x, y array
* @private
*/
function getPointer(event, element?: Element): number[] {
function getPointer(event, element?: SVGElement): number[] {
const touches = event &&
(event.touches || (event.sourceEvent && event.sourceEvent.touches))?.[0];
let pointer = [0, 0];
Expand Down Expand Up @@ -537,6 +539,23 @@ function getScrollPosition(node: HTMLElement) {
};
}

/**
* Get translation string from screen <--> svg point
* @param {SVGGraphicsElement} node graphics element
* @param {number} x target x point
* @param {number} y target y point
* @param {boolean} inverse inverse flag
* @returns {object}
*/
function getTransformCTM(node: SVGGraphicsElement, x = 0, y = 0, inverse = true): DOMPoint {
const point = new DOMPoint(x, y);
const screen = <DOMMatrix>node.getScreenCTM();

return point.matrixTransform(
inverse ? screen?.inverse() : screen
);
}

/**
* Gets the SVGMatrix of an SVGGElement
* @param {SVGElement} node Node element
Expand Down Expand Up @@ -787,6 +806,17 @@ function parseDate(date: Date | string | number | any): Date {
return parsedDate;
}

/**
* Check if svg element has viewBox attribute
* @param {d3Selection} svg Target svg selection
* @returns {boolean}
*/
function hasViewBox(svg: d3Selection): boolean {
const attr = svg.attr("viewBox");

return attr ? /(\d+(\.\d+)?){3}/.test(attr) : false;
}

/**
* Return if the current doc is visible or not
* @returns {boolean}
Expand Down
2 changes: 2 additions & 0 deletions test/assets/module/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ export const {
getRectSegList,
getScrollPosition,
getTranslation,
getTransformCTM,
getUnique,
hasValue,
hasViewBox,
isArray,
isBoolean,
isDefined,
Expand Down
Loading

0 comments on commit 243bf3c

Please sign in to comment.