Skip to content

Commit

Permalink
fix: Apply temporary logic to apply missing fonts in the current exca…
Browse files Browse the repository at this point in the history
…lidraw package (#56)

* fix: Apply missing fonts and logic to adopt new fonts in svg loader

* fix: Remove accidentally added tailing comma

* fix: Gracefully check textElement's fontFamily in case it's missing

* fix: Remove tailing comma

* fix: Apply prettier

* fix: replace textPath to text element to draw the same animation

* fix: Update elements check to by group
  • Loading branch information
kei95 authored Sep 2, 2024
1 parent 31a4b98 commit dfc5a01
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 9 deletions.
24 changes: 24 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Excalidraw Animate</title>
<!-- Links for Lilita One font. Remove these links once Excalidraw is updated to the next version (v0.17.6 as of now) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lilita+One&display=swap" rel="stylesheet">
<style>
/* Styles for additional fonts that are not part of Excalidraw package in the current version.
Remove them all once Excalidraw is updated (v0.17.6 as of now) */
@font-face {
font-family: 'Excalifont';
src: url('https://excalidraw.nyc3.cdn.digitaloceanspaces.com/fonts/oss/Excalifont-Regular-C9eKQy_N.woff2');
}
@font-face {
font-family: 'Comic Shanns';
src: url('https://excalidraw.nyc3.cdn.digitaloceanspaces.com/fonts/oss/ComicShanns-Regular-D0c8wzsC.woff2');
}
@font-face {
font-family: 'Nunito';
src: url('https://fonts.gstatic.com/s/nunito/v26/XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2');
}
@font-face {
font-family: 'Lilita One';
src: url('https://fonts.googleapis.com/css2?family=Lilita+One&display=swap');
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
20 changes: 11 additions & 9 deletions src/animate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,20 +290,22 @@ const animateText = (
pathForTextIndex += 1;
const path = svg.ownerDocument.createElementNS(SVG_NS, "path");
path.setAttribute("id", "pathForText" + pathForTextIndex);

const textPath = svg.ownerDocument.createTextNode(ele.textContent ?? "");
ele.textContent = " "; // HACK for Firebox as `null` does not work
ele.appendChild(textPath);
ele.setAttribute("opacity", "0.0");

const animate = svg.ownerDocument.createElementNS(SVG_NS, "animate");
animate.setAttribute("attributeName", "d");
animate.setAttribute("from", `m${x} ${y} h0`);
animate.setAttribute("to", `m${x} ${y} h${width}`);
animate.setAttribute("attributeName", "opacity");
animate.setAttribute("from", `0.0`);
animate.setAttribute("to", `1.0`);
animate.setAttribute("begin", `${currentMs}ms`);
animate.setAttribute("dur", `${durationMs}ms`);
animate.setAttribute("fill", "freeze");
path.appendChild(animate);
const textPath = svg.ownerDocument.createElementNS(SVG_NS, "textPath");
textPath.setAttribute("href", "#pathForText" + pathForTextIndex);
textPath.textContent = ele.textContent;
ele.textContent = " "; // HACK for Firebox as `null` does not work
ele.appendChild(animate);

findNode(svg, "defs")?.appendChild(path);
ele.appendChild(textPath);
animatePointer(
svg,
ele,
Expand Down
100 changes: 100 additions & 0 deletions src/useLoadSvg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import type { BinaryFiles } from "@excalidraw/excalidraw/types/types";
import type {
ExcalidrawElement,
ExcalidrawTextElement,
NonDeletedExcalidrawElement,
} from "@excalidraw/excalidraw/types/element/types";

Expand Down Expand Up @@ -71,6 +72,11 @@ export const useLoadSvg = () => {
appState: data.appState,
exportPadding: 30,
});

// This is a patch up function to apply new fonts that are not part of Excalidraw package
// Remove this function once Excalidraw package is updated (v0.17.6 as of now)
applyNewFontsToSvg(svg, elements);

const result = animateSvg(svg, elements, options);
console.log(svg);
if (inSequence) {
Expand Down Expand Up @@ -122,3 +128,97 @@ export const useLoadSvg = () => {

return { loading, loadedSvgList, loadDataList };
};

// Change below are to apply new fonts that are not part of current version of Excalidraw package
// Remove them all below once Excalidraw is updated (v0.17.6 as of now)
// ================================================
const DEFAULT_FONT = "Segoe UI Emoji";
/** Up to date version of font family. It's brought from the latest version of Excalidraw repo */
export const FONT_FAMILY = {
Virgil: 1,
Helvetica: 2,
Cascadia: 3,
// leave 4 unused as it was historically used for Assistant (which we don't use anymore) or custom font (Obsidian)
Excalifont: 5,
Nunito: 6,
"Lilita One": 7,
"Comic Shanns": 8,
"Liberation Sans": 9,
} as const;

/**
* Recursively apply new fonts to all text elements in the given SVG.
* `exportToSvg()` is not compatible with new fonts due to a discrepancy between package and release excalidraw.
* This function patches up the fonts resulting in a default font family.
*
* issue link: https://github.com/dai-shi/excalidraw-animate/issues/55
* */
function applyNewFontsToSvg(svg: SVGSVGElement, elements: ExcalidrawElement[]) {
const textElements: ExcalidrawTextElement[] = elements.filter(
(element): element is ExcalidrawTextElement =>
element.type === "text" && !!element.fontFamily
) as ExcalidrawTextElement[];

/** index to keep track of block of text elements */
let currentTextElementIndex = 0;

// Since text element is represented in a group in given svg
// apply font family based on the group that contains the text elements
svg.querySelectorAll("g").forEach((svgGroup) => {
// It indicates the group is not for text - thus skip it
if (svgGroup.hasAttribute("stroke-linecap")) return;

const fontFamily = textElements[currentTextElementIndex]?.fontFamily;
svgGroup.querySelectorAll("text").forEach((svgText) => {
convertFontFamily(svgText, fontFamily);
});

currentTextElementIndex += 1;
});
}

function convertFontFamily(
textElement: SVGTextElement,
fontFamilyNumber: number | undefined
) {
switch (fontFamilyNumber) {
case FONT_FAMILY.Virgil:
textElement.setAttribute("font-family", `Virgil, ${DEFAULT_FONT}`);
break;

case FONT_FAMILY.Helvetica:
textElement.setAttribute("font-family", `Helvetica, ${DEFAULT_FONT}`);
break;

case FONT_FAMILY.Cascadia:
textElement.setAttribute("font-family", `Cascadia, ${DEFAULT_FONT}`);
break;

case FONT_FAMILY.Excalifont:
textElement.setAttribute("font-family", `Excalifont, ${DEFAULT_FONT}`);
break;

case FONT_FAMILY.Nunito:
textElement.setAttribute("font-family", `Nunito, ${DEFAULT_FONT}`);
break;

case FONT_FAMILY["Lilita One"]:
textElement.setAttribute("font-family", `Lilita One, ${DEFAULT_FONT}`);
break;

case FONT_FAMILY["Comic Shanns"]:
textElement.setAttribute("font-family", `Comic Shanns, ${DEFAULT_FONT}`);
break;

case FONT_FAMILY["Liberation Sans"]:
textElement.setAttribute(
"font-family",
`Liberation Sans, ${DEFAULT_FONT}`
);
break;

default:
textElement.setAttribute("font-family", DEFAULT_FONT);
break;
}
}

0 comments on commit dfc5a01

Please sign in to comment.