diff --git a/geneit_app/src/utils/render_utils.ts b/geneit_app/src/utils/render_utils.ts new file mode 100644 index 0000000..0047c25 --- /dev/null +++ b/geneit_app/src/utils/render_utils.ts @@ -0,0 +1,31 @@ +let canvas: HTMLCanvasElement = document.createElement("canvas"); +let charLen: Map> = new Map(); + +/** + * Uses canvas.measureText to compute and return the width of the given text of given font in pixels. + * + * @param {String} text The text to be rendered. + * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana"). + * + * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393 + */ +function computeTextWidth(text: string, font: string) { + // re-use canvas object for better performance + const context = canvas.getContext("2d")!; + context.font = font; + const metrics = context.measureText(text); + return metrics.width; +} + +export function getTextWidth(text: string, font: string) { + if (!charLen.has(font)) charLen.set(font, new Map()); + const ref = charLen.get(font)!; + + let size = 0; + for (const c of text) { + if (!ref.has(c)) ref.set(c, computeTextWidth(c, font)); + size += ref.get(c)!; + } + console.log(text, size); + return size; +} diff --git a/geneit_app/src/widgets/simple_family_tree/SimpleFamilyTree.tsx b/geneit_app/src/widgets/simple_family_tree/SimpleFamilyTree.tsx index d8fef8e..b68a0b4 100644 --- a/geneit_app/src/widgets/simple_family_tree/SimpleFamilyTree.tsx +++ b/geneit_app/src/widgets/simple_family_tree/SimpleFamilyTree.tsx @@ -2,18 +2,20 @@ import React from "react"; import { Couple } from "../../api/CoupleApi"; import { Member } from "../../api/MemberApi"; import { FamilyTreeNode } from "../../utils/family_tree"; +import { getTextWidth } from "../../utils/render_utils"; const FACE_WIDTH = 60; const FACE_HEIGHT = 70; +const FACE_TEXT_SPACING = 2; -const CARD_HEIGHT = 110; +const CARD_HEIGHT = 103; const SPOUSE_SPACING = 10; const CARD_SPACING = 20; const SIBLINGS_SPACING = 20; -const LEVEL_SPACING = 20; +const LEVEL_SPACING = 25; -const NAME_CHAR_W = 7.1428; -const BIRTH_CHAR_W = 5; +const NAME_FONT = "13px Roboto"; +const BIRTH_FONT = "10px Roboto"; interface SimpleTreeSpouseInfo { member: Member; @@ -31,8 +33,8 @@ interface SimpleTreeNode { * Get the width of a member card */ function memberCardWidth(m: Member): number { - const nameWidth = m.fullName.length * NAME_CHAR_W; - const birthDeathWidth = m.displayBirthDeath.length * BIRTH_CHAR_W; + const nameWidth = getTextWidth(m.fullName, NAME_FONT); + const birthDeathWidth = getTextWidth(m.displayBirthDeath, BIRTH_FONT); return Math.max(FACE_WIDTH, nameWidth, birthDeathWidth); } @@ -170,7 +172,7 @@ function NodeArea(p: { node={n} /> ); - downXOffset += n.width; + downXOffset += n.width + SIBLINGS_SPACING; return el; })} @@ -203,18 +205,20 @@ function MemberCard(p: { )} {/* Member text */} - + {p.member.fullName} {p.member.displayBirthDeath}