strokes-js 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/glyphs/glyphs.js +54 -52
- package/dist/glyphs/space.d.ts +2 -0
- package/dist/glyphs/space.js +5 -0
- package/dist/glyphs/types.d.ts +1 -0
- package/dist/renderer.js +39 -19
- package/package.json +1 -1
package/dist/glyphs/glyphs.js
CHANGED
|
@@ -66,59 +66,61 @@ import { punctExclamation } from "./punctuation/punct-exclamation.js";
|
|
|
66
66
|
import { punctQuestion } from "./punctuation/punct-question.js";
|
|
67
67
|
import { punctApostrophe } from "./punctuation/punct-apostrophe.js";
|
|
68
68
|
import { punctHyphen } from "./punctuation/punct-hyphen.js";
|
|
69
|
+
import { space } from "./space.js";
|
|
69
70
|
export const glyphs = {
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
71
|
+
" ": space,
|
|
72
|
+
A: letterAUpper,
|
|
73
|
+
a: letterA,
|
|
74
|
+
B: letterBUpper,
|
|
75
|
+
b: letterB,
|
|
76
|
+
C: letterCUpper,
|
|
77
|
+
c: letterC,
|
|
78
|
+
D: letterDUpper,
|
|
79
|
+
d: letterD,
|
|
80
|
+
E: letterEUpper,
|
|
81
|
+
e: letterE,
|
|
82
|
+
F: letterFUpper,
|
|
83
|
+
f: letterF,
|
|
84
|
+
G: letterGUpper,
|
|
85
|
+
g: letterG,
|
|
86
|
+
H: letterHUpper,
|
|
87
|
+
h: letterH,
|
|
88
|
+
I: letterIUpper,
|
|
89
|
+
i: letterI,
|
|
90
|
+
J: letterJUpper,
|
|
91
|
+
j: letterJ,
|
|
92
|
+
K: letterKUpper,
|
|
93
|
+
k: letterK,
|
|
94
|
+
L: letterLUpper,
|
|
95
|
+
l: letterL,
|
|
96
|
+
M: letterMUpper,
|
|
97
|
+
m: letterM,
|
|
98
|
+
N: letterNUpper,
|
|
99
|
+
n: letterN,
|
|
100
|
+
O: letterOUpper,
|
|
101
|
+
o: letterO,
|
|
102
|
+
P: letterPUpper,
|
|
103
|
+
p: letterP,
|
|
104
|
+
Q: letterQUpper,
|
|
105
|
+
q: letterQ,
|
|
106
|
+
R: letterRUpper,
|
|
107
|
+
r: letterR,
|
|
108
|
+
S: letterSUpper,
|
|
109
|
+
s: letterS,
|
|
110
|
+
T: letterTUpper,
|
|
111
|
+
t: letterT,
|
|
112
|
+
U: letterUUpper,
|
|
113
|
+
u: letterU,
|
|
114
|
+
V: letterVUpper,
|
|
115
|
+
v: letterV,
|
|
116
|
+
W: letterWUpper,
|
|
117
|
+
w: letterW,
|
|
118
|
+
X: letterXUpper,
|
|
119
|
+
x: letterX,
|
|
120
|
+
Y: letterYUpper,
|
|
121
|
+
y: letterY,
|
|
122
|
+
Z: letterZUpper,
|
|
123
|
+
z: letterZ,
|
|
122
124
|
"0": digit0,
|
|
123
125
|
"1": digit1,
|
|
124
126
|
"2": digit2,
|
package/dist/glyphs/types.d.ts
CHANGED
package/dist/renderer.js
CHANGED
|
@@ -45,25 +45,44 @@ function renderWord(word, delayOffset, animate) {
|
|
|
45
45
|
continue;
|
|
46
46
|
const variantIndex = pickVariant(char, entry);
|
|
47
47
|
const d = entry.variants[variantIndex];
|
|
48
|
+
const width = entry.variantWidths?.[variantIndex] ?? entry.width;
|
|
48
49
|
const style = animate
|
|
49
50
|
? `stroke-dasharray:1;stroke-dashoffset:1;transition:stroke-dashoffset ${STROKE_MS}ms ease ${(delayOffset + letterIndex) * STAGGER_MS}ms;`
|
|
50
51
|
: "";
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
if (d) {
|
|
53
|
+
const { rotate, dy, scale } = randomJitter();
|
|
54
|
+
const cx = width / 2;
|
|
55
|
+
const cy = LETTER_HEIGHT / 2;
|
|
56
|
+
// Position first, then rotate/scale around the letter's own center (not the SVG origin)
|
|
57
|
+
// so the jitter can't nudge later letters in long words out of position.
|
|
58
|
+
const transform = `translate(${x} ${dy.toFixed(2)}) translate(${cx} ${cy}) ` +
|
|
59
|
+
`rotate(${rotate.toFixed(1)}) scale(${scale.toFixed(3)}) translate(${-cx} ${-cy})`;
|
|
60
|
+
paths.push(`<path d="${d}" transform="${transform}" stroke="var(--hw-color, #000)" fill="none" ` +
|
|
61
|
+
`stroke-width="2" stroke-linecap="round" pathLength="1" style="${style}" class="hw-letter" />`);
|
|
62
|
+
}
|
|
63
|
+
x += width;
|
|
61
64
|
letterIndex++;
|
|
62
65
|
}
|
|
63
66
|
const svg = `<svg class="hw-word" xmlns="http://www.w3.org/2000/svg" width="${x}" height="${LETTER_HEIGHT}" ` +
|
|
64
67
|
`viewBox="0 0 ${x} ${LETTER_HEIGHT}" aria-hidden="true">${paths.join("")}</svg>`;
|
|
65
68
|
return { svg, letterCount: letterIndex };
|
|
66
69
|
}
|
|
70
|
+
function renderWhitespace(whitespace, delayOffset, animate) {
|
|
71
|
+
let letterCount = 0;
|
|
72
|
+
const markup = [];
|
|
73
|
+
for (const chunk of whitespace.split(/(\r\n|\r|\n)/)) {
|
|
74
|
+
if (!chunk)
|
|
75
|
+
continue;
|
|
76
|
+
if (/^(\r\n|\r|\n)$/.test(chunk)) {
|
|
77
|
+
markup.push("<br />");
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const { svg, letterCount: count } = renderWord(chunk.replace(/\s/g, " "), delayOffset + letterCount, animate);
|
|
81
|
+
markup.push(svg);
|
|
82
|
+
letterCount += count;
|
|
83
|
+
}
|
|
84
|
+
return { markup: markup.join(""), letterCount };
|
|
85
|
+
}
|
|
67
86
|
/**
|
|
68
87
|
* Renders text as a sentence wrapper containing one <svg> per word.
|
|
69
88
|
* When animate (default), each word's letters draw in via stroke-dashoffset once `.hw-visible`
|
|
@@ -74,15 +93,22 @@ export function renderText(text, options = {}) {
|
|
|
74
93
|
const parts = splitText(text);
|
|
75
94
|
let delayOffset = 0;
|
|
76
95
|
const markup = [];
|
|
96
|
+
let pendingWhitespace = "";
|
|
77
97
|
for (const part of parts) {
|
|
78
98
|
if (/^\s+$/.test(part)) {
|
|
79
|
-
|
|
99
|
+
pendingWhitespace += part;
|
|
80
100
|
continue;
|
|
81
101
|
}
|
|
82
102
|
const word = part;
|
|
83
103
|
const { svg, letterCount } = renderWord(word, delayOffset, animate);
|
|
84
|
-
markup
|
|
85
|
-
|
|
104
|
+
const { markup: space, letterCount: spaceCount } = renderWhitespace(pendingWhitespace, delayOffset, animate);
|
|
105
|
+
markup.push(`<span class="hw-token" style="display:inline-flex;align-items:baseline">${space}${svg}</span>`);
|
|
106
|
+
pendingWhitespace = "";
|
|
107
|
+
delayOffset += spaceCount + letterCount;
|
|
108
|
+
}
|
|
109
|
+
if (pendingWhitespace) {
|
|
110
|
+
const { markup: space } = renderWhitespace(pendingWhitespace, delayOffset, animate);
|
|
111
|
+
markup.push(`<span class="hw-token" style="display:inline-flex;align-items:baseline">${space}</span>`);
|
|
86
112
|
}
|
|
87
113
|
return `<span class="hw-sentence" aria-label="${escapeAttr(text)}">${markup.join("")}</span>`;
|
|
88
114
|
}
|
|
@@ -93,12 +119,6 @@ function escapeAttr(value) {
|
|
|
93
119
|
.replace(/</g, "<")
|
|
94
120
|
.replace(/>/g, ">");
|
|
95
121
|
}
|
|
96
|
-
function escapeHtml(value) {
|
|
97
|
-
return value
|
|
98
|
-
.replace(/&/g, "&")
|
|
99
|
-
.replace(/</g, "<")
|
|
100
|
-
.replace(/>/g, ">");
|
|
101
|
-
}
|
|
102
122
|
/** Toggles the class that starts the draw-in transition for a rendered word's strokes. */
|
|
103
123
|
export function animateWriting(el) {
|
|
104
124
|
el.classList.add("hw-visible");
|