strokes-js 0.1.3 → 0.2.0
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 +78 -52
- package/dist/glyphs/punctuation/punct-ampersand.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-ampersand.js +9 -0
- package/dist/glyphs/punctuation/punct-asterisk.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-asterisk.js +9 -0
- package/dist/glyphs/punctuation/punct-backtick.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-backtick.js +9 -0
- package/dist/glyphs/punctuation/punct-brace-close.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-brace-close.js +9 -0
- package/dist/glyphs/punctuation/punct-brace-open.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-brace-open.js +9 -0
- package/dist/glyphs/punctuation/punct-bracket-close.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-bracket-close.js +9 -0
- package/dist/glyphs/punctuation/punct-bracket-open.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-bracket-open.js +9 -0
- package/dist/glyphs/punctuation/punct-em-dash.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-em-dash.js +9 -0
- package/dist/glyphs/punctuation/punct-equals.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-equals.js +9 -0
- package/dist/glyphs/punctuation/punct-paren-close.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-paren-close.js +9 -0
- package/dist/glyphs/punctuation/punct-paren-open.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-paren-open.js +9 -0
- package/dist/glyphs/punctuation/punct-quote.d.ts +3 -0
- package/dist/glyphs/punctuation/punct-quote.js +9 -0
- 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 +34 -22
- package/package.json +1 -1
package/dist/glyphs/glyphs.js
CHANGED
|
@@ -66,59 +66,73 @@ 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 { punctAmpersand } from "./punctuation/punct-ampersand.js";
|
|
70
|
+
import { punctParenOpen } from "./punctuation/punct-paren-open.js";
|
|
71
|
+
import { punctParenClose } from "./punctuation/punct-paren-close.js";
|
|
72
|
+
import { punctEmDash } from "./punctuation/punct-em-dash.js";
|
|
73
|
+
import { punctQuote } from "./punctuation/punct-quote.js";
|
|
74
|
+
import { punctBacktick } from "./punctuation/punct-backtick.js";
|
|
75
|
+
import { punctBraceOpen } from "./punctuation/punct-brace-open.js";
|
|
76
|
+
import { punctBraceClose } from "./punctuation/punct-brace-close.js";
|
|
77
|
+
import { punctBracketOpen } from "./punctuation/punct-bracket-open.js";
|
|
78
|
+
import { punctBracketClose } from "./punctuation/punct-bracket-close.js";
|
|
79
|
+
import { punctAsterisk } from "./punctuation/punct-asterisk.js";
|
|
80
|
+
import { punctEquals } from "./punctuation/punct-equals.js";
|
|
81
|
+
import { space } from "./space.js";
|
|
69
82
|
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
|
-
|
|
83
|
+
" ": space,
|
|
84
|
+
A: letterAUpper,
|
|
85
|
+
a: letterA,
|
|
86
|
+
B: letterBUpper,
|
|
87
|
+
b: letterB,
|
|
88
|
+
C: letterCUpper,
|
|
89
|
+
c: letterC,
|
|
90
|
+
D: letterDUpper,
|
|
91
|
+
d: letterD,
|
|
92
|
+
E: letterEUpper,
|
|
93
|
+
e: letterE,
|
|
94
|
+
F: letterFUpper,
|
|
95
|
+
f: letterF,
|
|
96
|
+
G: letterGUpper,
|
|
97
|
+
g: letterG,
|
|
98
|
+
H: letterHUpper,
|
|
99
|
+
h: letterH,
|
|
100
|
+
I: letterIUpper,
|
|
101
|
+
i: letterI,
|
|
102
|
+
J: letterJUpper,
|
|
103
|
+
j: letterJ,
|
|
104
|
+
K: letterKUpper,
|
|
105
|
+
k: letterK,
|
|
106
|
+
L: letterLUpper,
|
|
107
|
+
l: letterL,
|
|
108
|
+
M: letterMUpper,
|
|
109
|
+
m: letterM,
|
|
110
|
+
N: letterNUpper,
|
|
111
|
+
n: letterN,
|
|
112
|
+
O: letterOUpper,
|
|
113
|
+
o: letterO,
|
|
114
|
+
P: letterPUpper,
|
|
115
|
+
p: letterP,
|
|
116
|
+
Q: letterQUpper,
|
|
117
|
+
q: letterQ,
|
|
118
|
+
R: letterRUpper,
|
|
119
|
+
r: letterR,
|
|
120
|
+
S: letterSUpper,
|
|
121
|
+
s: letterS,
|
|
122
|
+
T: letterTUpper,
|
|
123
|
+
t: letterT,
|
|
124
|
+
U: letterUUpper,
|
|
125
|
+
u: letterU,
|
|
126
|
+
V: letterVUpper,
|
|
127
|
+
v: letterV,
|
|
128
|
+
W: letterWUpper,
|
|
129
|
+
w: letterW,
|
|
130
|
+
X: letterXUpper,
|
|
131
|
+
x: letterX,
|
|
132
|
+
Y: letterYUpper,
|
|
133
|
+
y: letterY,
|
|
134
|
+
Z: letterZUpper,
|
|
135
|
+
z: letterZ,
|
|
122
136
|
"0": digit0,
|
|
123
137
|
"1": digit1,
|
|
124
138
|
"2": digit2,
|
|
@@ -135,4 +149,16 @@ export const glyphs = {
|
|
|
135
149
|
"?": punctQuestion,
|
|
136
150
|
"'": punctApostrophe,
|
|
137
151
|
"-": punctHyphen,
|
|
152
|
+
"&": punctAmpersand,
|
|
153
|
+
"(": punctParenOpen,
|
|
154
|
+
")": punctParenClose,
|
|
155
|
+
"—": punctEmDash,
|
|
156
|
+
'"': punctQuote,
|
|
157
|
+
"`": punctBacktick,
|
|
158
|
+
"{": punctBraceOpen,
|
|
159
|
+
"}": punctBraceClose,
|
|
160
|
+
"[": punctBracketOpen,
|
|
161
|
+
"]": punctBracketClose,
|
|
162
|
+
"*": punctAsterisk,
|
|
163
|
+
"=": punctEquals,
|
|
138
164
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Real hand-drawn glyph: "&", 3 wobble variants. */
|
|
2
|
+
export const punctAmpersand = {
|
|
3
|
+
width: 14,
|
|
4
|
+
variants: [
|
|
5
|
+
"M11 8 C 11 5.5 8.5 4 7 5.5 C 5.5 7 6.5 9 8 10.5 L 11.5 14 C 13 15.5 12.5 18.5 10 19.5 C 7 20.5 3.5 19.5 2.5 17 C 1.5 14.5 3.5 12 6.5 12 C 9 12 10.5 13.5 11.5 15.5",
|
|
6
|
+
"M11.0 7.9 C 11.2 5.4 8.6 3.9 6.9 5.6 C 5.4 7.1 6.6 9.1 8.1 10.4 C 8.1 10.4 11.6 13.9 11.6 13.9 C 13.1 15.6 12.4 18.6 9.9 19.6 C 6.9 20.6 3.6 19.4 2.6 16.9 C 1.6 14.4 3.6 11.9 6.6 12.1 C 9.1 11.9 10.6 13.6 11.6 15.4",
|
|
7
|
+
"M10.9 8.1 C 10.9 5.6 8.4 4.1 7.1 5.4 C 5.6 6.9 6.4 8.9 7.9 10.6 L 11.4 14.1 C 12.9 15.4 12.6 18.4 10.1 19.4 C 7.1 20.4 3.4 19.6 2.4 17.1 C 1.4 14.6 3.4 12.1 6.4 11.9 C 8.9 12.1 10.4 13.4 11.4 15.6",
|
|
8
|
+
],
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Real hand-drawn glyph: "*", 3 wobble variants. */
|
|
2
|
+
export const punctAsterisk = {
|
|
3
|
+
width: 10,
|
|
4
|
+
variants: [
|
|
5
|
+
"M5 5 L5 13 M1.5 7 L8.5 11 M8.5 7 L1.5 11",
|
|
6
|
+
"M4.9 4.9 L5.1 13.1 M1.4 6.9 L8.6 11.1 M8.6 6.9 L1.4 11.1",
|
|
7
|
+
"M5.1 5.1 L4.9 12.9 M1.6 7.1 L8.4 10.9 M8.4 7.1 L1.6 10.9",
|
|
8
|
+
],
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Real hand-drawn glyph: "}", 3 wobble variants. */
|
|
2
|
+
export const punctBraceClose = {
|
|
3
|
+
width: 10,
|
|
4
|
+
variants: [
|
|
5
|
+
"M3 3 C 6 3 5 7 5 9 C 5 11 7 11.5 8 12 C 7 12.5 5 13 5 15 C 5 17 6 21 3 21",
|
|
6
|
+
"M2.9 2.9 C 6.1 3.1 4.9 6.9 5.0 9.1 C 5.1 11.1 6.9 11.4 8.1 12.1 C 6.9 12.6 5.1 12.9 4.9 15.1 C 5.1 17.1 6.1 20.9 2.9 21.1",
|
|
7
|
+
"M3.1 3.1 C 5.9 2.9 5.1 7.1 5.0 8.9 C 4.9 10.9 7.1 11.6 7.9 11.9 C 7.1 12.4 4.9 13.1 5.1 14.9 C 4.9 16.9 5.9 21.1 3.1 20.9",
|
|
8
|
+
],
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Real hand-drawn glyph: "{", 3 wobble variants. */
|
|
2
|
+
export const punctBraceOpen = {
|
|
3
|
+
width: 10,
|
|
4
|
+
variants: [
|
|
5
|
+
"M7 3 C 4 3 5 7 5 9 C 5 11 3 11.5 2 12 C 3 12.5 5 13 5 15 C 5 17 4 21 7 21",
|
|
6
|
+
"M7.1 2.9 C 3.9 3.1 5.1 6.9 5.0 9.1 C 4.9 11.1 3.1 11.4 1.9 12.1 C 3.1 12.6 4.9 12.9 5.1 15.1 C 4.9 17.1 3.9 20.9 7.1 21.1",
|
|
7
|
+
"M6.9 3.1 C 4.1 2.9 4.9 7.1 5.0 8.9 C 5.1 10.9 2.9 11.6 2.1 11.9 C 2.9 12.4 5.1 13.1 4.9 14.9 C 5.1 16.9 4.1 21.1 6.9 20.9",
|
|
8
|
+
],
|
|
9
|
+
};
|
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`
|
|
@@ -82,15 +101,14 @@ export function renderText(text, options = {}) {
|
|
|
82
101
|
}
|
|
83
102
|
const word = part;
|
|
84
103
|
const { svg, letterCount } = renderWord(word, delayOffset, animate);
|
|
85
|
-
const space = pendingWhitespace
|
|
86
|
-
|
|
87
|
-
: "";
|
|
88
|
-
markup.push(`<span class="hw-token">${space}${svg}</span>`);
|
|
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>`);
|
|
89
106
|
pendingWhitespace = "";
|
|
90
|
-
delayOffset += letterCount;
|
|
107
|
+
delayOffset += spaceCount + letterCount;
|
|
91
108
|
}
|
|
92
109
|
if (pendingWhitespace) {
|
|
93
|
-
markup
|
|
110
|
+
const { markup: space } = renderWhitespace(pendingWhitespace, delayOffset, animate);
|
|
111
|
+
markup.push(`<span class="hw-token" style="display:inline-flex;align-items:baseline">${space}</span>`);
|
|
94
112
|
}
|
|
95
113
|
return `<span class="hw-sentence" aria-label="${escapeAttr(text)}">${markup.join("")}</span>`;
|
|
96
114
|
}
|
|
@@ -101,12 +119,6 @@ function escapeAttr(value) {
|
|
|
101
119
|
.replace(/</g, "<")
|
|
102
120
|
.replace(/>/g, ">");
|
|
103
121
|
}
|
|
104
|
-
function escapeHtml(value) {
|
|
105
|
-
return value
|
|
106
|
-
.replace(/&/g, "&")
|
|
107
|
-
.replace(/</g, "<")
|
|
108
|
-
.replace(/>/g, ">");
|
|
109
|
-
}
|
|
110
122
|
/** Toggles the class that starts the draw-in transition for a rendered word's strokes. */
|
|
111
123
|
export function animateWriting(el) {
|
|
112
124
|
el.classList.add("hw-visible");
|