Skip to content

Commit

Permalink
Remedy inaccurate tooltip size approximation
Browse files Browse the repository at this point in the history
  • Loading branch information
EthanRutherford committed Oct 19, 2022
1 parent 272649b commit 4a89b0b
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 16 deletions.
100 changes: 84 additions & 16 deletions packages/victory-core/src/victory-util/textsize.ts
@@ -1,6 +1,6 @@
// http://www.pearsonified.com/2012/01/characters-per-line.php
/* eslint-disable no-magic-numbers */
import { assign, defaults } from "lodash";
import { assign, defaults, memoize } from "lodash";

// Based on measuring specific character widths
// as in the following example https://bl.ocks.org/tophtucker/62f93a4658387bb61e4510c37e2e97cf
Expand Down Expand Up @@ -243,6 +243,77 @@ const _approximateTextHeightInternal = (text: string | string[], style) => {
}, 0);
};

const _approximateDimensionsInternal = (
text: string | string[],
style?: TextSizeStyleInterface,
) => {
const angle = Array.isArray(style)
? style[0] && style[0].angle
: style && style.angle;
const height = _approximateTextHeightInternal(text, style);
const width = _approximateTextWidthInternal(text, style);
const widthWithRotate = angle
? _getSizeWithRotate(width, height, angle)
: width;
const heightWithRotate = angle
? _getSizeWithRotate(height, width, angle)
: height;
return {
width: widthWithRotate,
height: heightWithRotate * coefficients.heightOverlapCoef,
};
};

const _getMeasurementContainer = memoize(() => {
const element = document.createElementNS("http://www.w3.org/2000/svg", "svg");
element.setAttribute("xlink", "http://www.w3.org/1999/xlink");

const containerElement = document.createElementNS(
"http://www.w3.org/2000/svg",
"text",
);
element.appendChild(containerElement);

element.style.position = "fixed";
element.style.top = "-9999px";
element.style.left = "-9999px";

document.body.appendChild(element);

return containerElement;
});

const _measureDimensionsInternal = (
text: string | string[],
style?: TextSizeStyleInterface,
) => {
const containerElement = _getMeasurementContainer();

const lines = _splitToLines(text);
for (const [i, line] of lines.entries()) {
const textElement = document.createElementNS(
"http://www.w3.org/2000/svg",
"tspan",
);
const params = _prepareParams(style, i);
textElement.style.fontFamily = params.fontFamily;
textElement.style.transform = `rotate(${params.angle})`;
textElement.style.fontSize = `${params.fontSize}px`;
textElement.style.lineHeight = params.lineHeight;
textElement.style.fontFamily = params.fontFamily;
textElement.style.letterSpacing = params.letterSpacing;
textElement.textContent = line;

containerElement.appendChild(textElement);
}

const { width, height } = containerElement.getBoundingClientRect();

containerElement.innerHTML = "";

return { width, height };
};

export interface TextSizeStyleInterface {
angle?: number;
characterConstant?: string;
Expand All @@ -255,21 +326,18 @@ export interface TextSizeStyleInterface {
// Stubbable implementation.
export const _approximateTextSizeInternal = {
impl: (text: string | string[], style?: TextSizeStyleInterface) => {
const angle = Array.isArray(style)
? style[0] && style[0].angle
: style && style.angle;
const height = _approximateTextHeightInternal(text, style);
const width = _approximateTextWidthInternal(text, style);
const widthWithRotate = angle
? _getSizeWithRotate(width, height, angle)
: width;
const heightWithRotate = angle
? _getSizeWithRotate(height, width, angle)
: height;
return {
width: widthWithRotate,
height: heightWithRotate * coefficients.heightOverlapCoef,
};
// Attempt to first measure the element in DOM. If there is no DOM, fallback
// to the less accurate approximation algorithm.
const isClient =
typeof window !== "undefined" &&
typeof window.document !== "undefined" &&
typeof window.document.createElement !== "undefined";

if (!isClient) {
return _approximateDimensionsInternal(text, style);
}

return _measureDimensionsInternal(text, style);
},
};

Expand Down
24 changes: 24 additions & 0 deletions stories/victory-label.stories.js
Expand Up @@ -415,6 +415,30 @@ export const LineHeight = () => {
/>
}
/>
<VictoryScatter
{...defaultScatterProps}
labelComponent={
<VictoryLabel
lineHeight={[2, 1, 3]}
text={["测试汉字", "不在正常的 ASCII 范围内", "最后一行"]}
backgroundStyle={[{ stroke: "blue", fill: "none" }]}
/>
}
/>
<VictoryScatter
{...defaultScatterProps}
labelComponent={
<VictoryLabel
lineHeight={[2, 1, 3]}
text={[
"اختبار اللغات التي تُقرأ من اليمين إلى اليسار",
"مثل العربية",
"هناك أكثر من ذلك بكثير",
]}
backgroundStyle={[{ stroke: "blue", fill: "none" }]}
/>
}
/>
</div>
);
};
Expand Down

0 comments on commit 4a89b0b

Please sign in to comment.