rough-emoji-draw 0.1.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/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # Rough Emoji Draw
2
+
3
+ Rough Emoji Draw 是一个把 Unicode 国旗 emoji 绘制成 Rough.js 手写风格 Canvas 图像的轻量前端库。项目使用 TypeScript 编写核心绘制逻辑,通过 Rslib 同时输出 ESM、CommonJS 与 IIFE 产物;Rough.js 会随库一并打包,安装或引用构建产物即可使用,无需额外引入 CDN。
4
+
5
+ ## 功能特性
6
+
7
+ - 将 Unicode 国旗 emoji 绘制到指定 `canvas`。
8
+ - 内置中国、日本、美国、澳大利亚、泰国、法国、意大利、西班牙、梵蒂冈等常见旗帜的手写模板。
9
+ - 对未单独适配的国旗使用 emoji 栅格化与像素采样流程生成统一风格的 fallback 图像。
10
+ - 支持三种接入方式:npm `import` / Node `require`、浏览器 `<script>` 全局变量 `window.RoughEmoji`。
11
+ - 提供 TypeScript 类型声明,可在 TS 项目中直接获得 API 提示。
12
+ - 提供 `index.html` 批量 QA 页面,用于检查 240+ 国家/地区旗帜绘制效果。
13
+
14
+ ## 技术栈
15
+
16
+ - TypeScript
17
+ - DOM Canvas
18
+ - Rough.js
19
+ - Rslib
20
+
21
+ ## 环境要求
22
+
23
+ - Node.js 20 及以上(可用 `.nvmrc`:`nvm use`)
24
+ - [pnpm](https://pnpm.io/) 9 及以上(推荐通过 Corepack:`corepack enable`)
25
+
26
+ ## 快速开始
27
+
28
+ 安装依赖:
29
+
30
+ ```bash
31
+ pnpm install
32
+ ```
33
+
34
+ 执行类型检查:
35
+
36
+ ```bash
37
+ pnpm run typecheck
38
+ ```
39
+
40
+ 构建产物:
41
+
42
+ ```bash
43
+ pnpm run build
44
+ ```
45
+
46
+ 构建完成后会生成:
47
+
48
+ | 文件 | 格式 | 用途 |
49
+ | --- | --- | --- |
50
+ | `dist/index.js` | ESM | `import` 引入 |
51
+ | `dist/index.cjs` | CJS | Node.js `require()` |
52
+ | `dist/index.d.ts` | 类型 | TypeScript 类型入口 |
53
+ | `dist/rough-emoji.js` | IIFE(压缩) | 浏览器 `<script>` 标签 |
54
+
55
+ 本地预览 QA 页面(不要用 `file://` 直接打开 HTML,浏览器会拦截本地 JS):
56
+
57
+ ```bash
58
+ pnpm run dev
59
+ ```
60
+
61
+ 然后在浏览器访问 [http://localhost:3000/](http://localhost:3000/)。
62
+
63
+ ## 使用方式
64
+
65
+ ### npm 包(ESM)
66
+
67
+ ```ts
68
+ import { RoughEmoji } from "rough-emoji-draw";
69
+
70
+ const canvas = document.querySelector<HTMLCanvasElement>("#flag-canvas")!;
71
+ RoughEmoji.draw(canvas, "🇨🇳");
72
+ ```
73
+
74
+ ### npm 包(CommonJS / Node)
75
+
76
+ ```js
77
+ const { RoughEmoji } = require("rough-emoji-draw");
78
+
79
+ console.log(typeof RoughEmoji.draw); // "function"
80
+ ```
81
+
82
+ > 绘制依赖 Canvas 与 DOM 环境。Node 侧可正常加载 API,实际调用 `draw` 时需要在浏览器、jsdom 或 `node-canvas` 等具备 Canvas 的运行时中使用。
83
+
84
+ ### 浏览器 `<script>` 标签
85
+
86
+ 页面只需加载 IIFE 产物,会自动挂载 `window.RoughEmoji`:
87
+
88
+ ```html
89
+ <script src="./dist/rough-emoji.js"></script>
90
+ <canvas id="flag-canvas" width="720" height="720"></canvas>
91
+ <script>
92
+ const canvas = document.querySelector("#flag-canvas");
93
+ window.RoughEmoji.draw(canvas, "🇨🇳");
94
+ </script>
95
+ ```
96
+
97
+ 若页面包含演示表单 DOM(`#rough-canvas`、`#emoji-form` 等),还会自动绑定交互与下载逻辑。
98
+
99
+ ## API
100
+
101
+ ### `RoughEmoji.draw(canvasElement, value)`
102
+
103
+ 把输入值规范化为国旗 emoji,并绘制到传入的 `HTMLCanvasElement`。非法输入会回退为中国国旗。
104
+
105
+ ### `RoughEmoji.isFlagEmoji(value)`
106
+
107
+ 判断字符串是否为由两个区域指示符组成的国旗 emoji。
108
+
109
+ ### `RoughEmoji.resolveFlag(value)`
110
+
111
+ 把任意输入转换为可绘制的国旗 emoji;当前只接受合法国旗 emoji,非法值回退为 `🇨🇳`。
112
+
113
+ ### 类型导出
114
+
115
+ ```ts
116
+ import type { RoughEmojiApi } from "rough-emoji-draw";
117
+ ```
118
+
119
+ 还可按需导入 `RoughOptions`、`Point`、`RoughCanvasLike`,以及演示页入口 `RoughEmojiApp`。
120
+
121
+ ## 项目结构
122
+
123
+ ```text
124
+ .
125
+ ├── src/
126
+ │ ├── index.ts # npm 库入口,导出 API 与类型
127
+ │ ├── browser.ts # 浏览器 IIFE 入口,挂载 window.RoughEmoji
128
+ │ ├── rough-emoji.ts # 核心绘制逻辑
129
+ │ ├── rough-emoji-app.ts # 单页演示 DOM 绑定
130
+ │ ├── flag-utils.ts # 国旗 emoji 解析与校验
131
+ │ ├── render-context.ts # Canvas 上下文切换
132
+ │ ├── constant.ts # 静态常量与模板映射
133
+ │ └── types.ts # 对外类型声明
134
+ ├── index.html # 批量视觉 QA 页面
135
+ ├── rslib.config.ts # Rslib 多格式构建配置
136
+ ├── tsconfig.json # TypeScript 配置
137
+ └── package.json # 脚本、依赖与 exports 字段
138
+ ```
139
+
140
+ ## 开发说明
141
+
142
+ - 新增旗帜优先在 `src/rough-emoji.ts` 中补充独立绘制函数,并在模板分发表中接入。
143
+ - 绘制函数按正方形画布坐标系工作,新增坐标建议基于当前 `size` 或已有旗帜区域变量推导。
144
+ - 修改绘制效果后,先运行 `pnpm run typecheck`,再运行 `pnpm run build` 并打开 `index.html` 进行视觉检查。
145
+ - `dist/` 为构建产物,通常不作为源码维护对象;发布或本地预览前执行 `pnpm run build` 即可。
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,279 @@
1
+ /** 单页演示入口使用的 DOM 选择器,QA 页面不存在这些节点时只暴露全局 API。 */
2
+ export declare const ELEMENT_SELECTORS: {
3
+ readonly canvas: "#rough-canvas";
4
+ readonly form: "#emoji-form";
5
+ readonly input: "#emoji-input";
6
+ readonly downloadButton: "#download-button";
7
+ };
8
+ /** 默认绘制国旗,供首次打开页面、prompt 默认值和非法输入回退共用。 */
9
+ export declare const DEFAULT_FLAG = "\uD83C\uDDE8\uD83C\uDDF3";
10
+ /** 下载文件名前缀,最终文件名会追加当前绘制的国旗 emoji。 */
11
+ export declare const DOWNLOAD_FILE_PREFIX = "rough-flag";
12
+ /** 首次进入单页演示时提示用户输入国旗的文案。 */
13
+ export declare const FLAG_PROMPT_MESSAGE = "\u8BF7\u8F93\u5165\u8981\u7ED8\u5236\u7684\u56FD\u65D7";
14
+ /** 已有专门手写模板的国旗 emoji,避免绘制分支散落字面量。 */
15
+ export declare const TEMPLATE_FLAGS: {
16
+ readonly ad: "🇦🇩";
17
+ readonly ae: "🇦🇪";
18
+ readonly af: "🇦🇫";
19
+ readonly ag: "🇦🇬";
20
+ readonly ai: "🇦🇮";
21
+ readonly al: "🇦🇱";
22
+ readonly am: "🇦🇲";
23
+ readonly ao: "🇦🇴";
24
+ readonly aq: "🇦🇶";
25
+ readonly ar: "🇦🇷";
26
+ readonly as: "🇦🇸";
27
+ readonly at: "🇦🇹";
28
+ readonly australia: "🇦🇺";
29
+ readonly aw: "🇦🇼";
30
+ readonly ax: "🇦🇽";
31
+ readonly az: "🇦🇿";
32
+ readonly ba: "🇧🇦";
33
+ readonly bb: "🇧🇧";
34
+ readonly bd: "🇧🇩";
35
+ readonly be: "🇧🇪";
36
+ readonly bf: "🇧🇫";
37
+ readonly bg: "🇧🇬";
38
+ readonly bh: "🇧🇭";
39
+ readonly bi: "🇧🇮";
40
+ readonly bj: "🇧🇯";
41
+ readonly bl: "🇧🇱";
42
+ readonly bm: "🇧🇲";
43
+ readonly bn: "🇧🇳";
44
+ readonly bo: "🇧🇴";
45
+ readonly bq: "🇧🇶";
46
+ readonly br: "🇧🇷";
47
+ readonly bs: "🇧🇸";
48
+ readonly bt: "🇧🇹";
49
+ readonly bv: "🇧🇻";
50
+ readonly bw: "🇧🇼";
51
+ readonly by: "🇧🇾";
52
+ readonly bz: "🇧🇿";
53
+ readonly ca: "🇨🇦";
54
+ readonly cc: "🇨🇨";
55
+ readonly cd: "🇨🇩";
56
+ readonly cf: "🇨🇫";
57
+ readonly cg: "🇨🇬";
58
+ readonly ch: "🇨🇭";
59
+ readonly ci: "🇨🇮";
60
+ readonly ck: "🇨🇰";
61
+ readonly cl: "🇨🇱";
62
+ readonly cm: "🇨🇲";
63
+ readonly china: "🇨🇳";
64
+ readonly co: "🇨🇴";
65
+ readonly cr: "🇨🇷";
66
+ readonly cu: "🇨🇺";
67
+ readonly cv: "🇨🇻";
68
+ readonly cw: "🇨🇼";
69
+ readonly cx: "🇨🇽";
70
+ readonly cy: "🇨🇾";
71
+ readonly cz: "🇨🇿";
72
+ readonly de: "🇩🇪";
73
+ readonly dj: "🇩🇯";
74
+ readonly dk: "🇩🇰";
75
+ readonly dm: "🇩🇲";
76
+ readonly do: "🇩🇴";
77
+ readonly dz: "🇩🇿";
78
+ readonly ec: "🇪🇨";
79
+ readonly ee: "🇪🇪";
80
+ readonly eg: "🇪🇬";
81
+ readonly eh: "🇪🇭";
82
+ readonly er: "🇪🇷";
83
+ readonly spain: "🇪🇸";
84
+ readonly et: "🇪🇹";
85
+ readonly fi: "🇫🇮";
86
+ readonly fj: "🇫🇯";
87
+ readonly fk: "🇫🇰";
88
+ readonly fm: "🇫🇲";
89
+ readonly fo: "🇫🇴";
90
+ readonly france: "🇫🇷";
91
+ readonly ga: "🇬🇦";
92
+ readonly gb: "🇬🇧";
93
+ readonly gd: "🇬🇩";
94
+ readonly ge: "🇬🇪";
95
+ readonly gf: "🇬🇫";
96
+ readonly gg: "🇬🇬";
97
+ readonly gh: "🇬🇭";
98
+ readonly gi: "🇬🇮";
99
+ readonly gl: "🇬🇱";
100
+ readonly gm: "🇬🇲";
101
+ readonly gn: "🇬🇳";
102
+ readonly gp: "🇬🇵";
103
+ readonly gq: "🇬🇶";
104
+ readonly gr: "🇬🇷";
105
+ readonly gs: "🇬🇸";
106
+ readonly gt: "🇬🇹";
107
+ readonly gu: "🇬🇺";
108
+ readonly gw: "🇬🇼";
109
+ readonly gy: "🇬🇾";
110
+ readonly hk: "🇭🇰";
111
+ readonly hm: "🇭🇲";
112
+ readonly hn: "🇭🇳";
113
+ readonly hr: "🇭🇷";
114
+ readonly ht: "🇭🇹";
115
+ readonly hu: "🇭🇺";
116
+ readonly id: "🇮🇩";
117
+ readonly ie: "🇮🇪";
118
+ readonly il: "🇮🇱";
119
+ readonly im: "🇮🇲";
120
+ readonly in: "🇮🇳";
121
+ readonly io: "🇮🇴";
122
+ readonly iq: "🇮🇶";
123
+ readonly ir: "🇮🇷";
124
+ readonly is: "🇮🇸";
125
+ readonly italy: "🇮🇹";
126
+ readonly je: "🇯🇪";
127
+ readonly jm: "🇯🇲";
128
+ readonly jo: "🇯🇴";
129
+ readonly japan: "🇯🇵";
130
+ readonly ke: "🇰🇪";
131
+ readonly kg: "🇰🇬";
132
+ readonly kh: "🇰🇭";
133
+ readonly ki: "🇰🇮";
134
+ readonly km: "🇰🇲";
135
+ readonly kn: "🇰🇳";
136
+ readonly kp: "🇰🇵";
137
+ readonly kr: "🇰🇷";
138
+ readonly kw: "🇰🇼";
139
+ readonly ky: "🇰🇾";
140
+ readonly kz: "🇰🇿";
141
+ readonly la: "🇱🇦";
142
+ readonly lb: "🇱🇧";
143
+ readonly lc: "🇱🇨";
144
+ readonly li: "🇱🇮";
145
+ readonly lk: "🇱🇰";
146
+ readonly lr: "🇱🇷";
147
+ readonly ls: "🇱🇸";
148
+ readonly lt: "🇱🇹";
149
+ readonly lu: "🇱🇺";
150
+ readonly lv: "🇱🇻";
151
+ readonly ly: "🇱🇾";
152
+ readonly ma: "🇲🇦";
153
+ readonly mc: "🇲🇨";
154
+ readonly md: "🇲🇩";
155
+ readonly me: "🇲🇪";
156
+ readonly mf: "🇲🇫";
157
+ readonly mg: "🇲🇬";
158
+ readonly mh: "🇲🇭";
159
+ readonly mk: "🇲🇰";
160
+ readonly ml: "🇲🇱";
161
+ readonly mm: "🇲🇲";
162
+ readonly mn: "🇲🇳";
163
+ readonly mo: "🇲🇴";
164
+ readonly mp: "🇲🇵";
165
+ readonly mq: "🇲🇶";
166
+ readonly mr: "🇲🇷";
167
+ readonly ms: "🇲🇸";
168
+ readonly mt: "🇲🇹";
169
+ readonly mu: "🇲🇺";
170
+ readonly mv: "🇲🇻";
171
+ readonly mw: "🇲🇼";
172
+ readonly mx: "🇲🇽";
173
+ readonly my: "🇲🇾";
174
+ readonly mz: "🇲🇿";
175
+ readonly na: "🇳🇦";
176
+ readonly nc: "🇳🇨";
177
+ readonly ne: "🇳🇪";
178
+ readonly nf: "🇳🇫";
179
+ readonly ng: "🇳🇬";
180
+ readonly ni: "🇳🇮";
181
+ readonly nl: "🇳🇱";
182
+ readonly no: "🇳🇴";
183
+ readonly np: "🇳🇵";
184
+ readonly nr: "🇳🇷";
185
+ readonly nu: "🇳🇺";
186
+ readonly nz: "🇳🇿";
187
+ readonly om: "🇴🇲";
188
+ readonly pa: "🇵🇦";
189
+ readonly pe: "🇵🇪";
190
+ readonly pf: "🇵🇫";
191
+ readonly pg: "🇵🇬";
192
+ readonly ph: "🇵🇭";
193
+ readonly pk: "🇵🇰";
194
+ readonly pl: "🇵🇱";
195
+ readonly pm: "🇵🇲";
196
+ readonly pn: "🇵🇳";
197
+ readonly pr: "🇵🇷";
198
+ readonly ps: "🇵🇸";
199
+ readonly pt: "🇵🇹";
200
+ readonly pw: "🇵🇼";
201
+ readonly py: "🇵🇾";
202
+ readonly qa: "🇶🇦";
203
+ readonly re: "🇷🇪";
204
+ readonly ro: "🇷🇴";
205
+ readonly rs: "🇷🇸";
206
+ readonly ru: "🇷🇺";
207
+ readonly rw: "🇷🇼";
208
+ readonly sa: "🇸🇦";
209
+ readonly sb: "🇸🇧";
210
+ readonly sc: "🇸🇨";
211
+ readonly sd: "🇸🇩";
212
+ readonly se: "🇸🇪";
213
+ readonly sg: "🇸🇬";
214
+ readonly sh: "🇸🇭";
215
+ readonly si: "🇸🇮";
216
+ readonly sj: "🇸🇯";
217
+ readonly sk: "🇸🇰";
218
+ readonly sl: "🇸🇱";
219
+ readonly sm: "🇸🇲";
220
+ readonly sn: "🇸🇳";
221
+ readonly so: "🇸🇴";
222
+ readonly sr: "🇸🇷";
223
+ readonly ss: "🇸🇸";
224
+ readonly st: "🇸🇹";
225
+ readonly sv: "🇸🇻";
226
+ readonly sx: "🇸🇽";
227
+ readonly sy: "🇸🇾";
228
+ readonly sz: "🇸🇿";
229
+ readonly tc: "🇹🇨";
230
+ readonly td: "🇹🇩";
231
+ readonly tf: "🇹🇫";
232
+ readonly tg: "🇹🇬";
233
+ readonly thailand: "🇹🇭";
234
+ readonly tj: "🇹🇯";
235
+ readonly tk: "🇹🇰";
236
+ readonly tl: "🇹🇱";
237
+ readonly tm: "🇹🇲";
238
+ readonly tn: "🇹🇳";
239
+ readonly to: "🇹🇴";
240
+ readonly tr: "🇹🇷";
241
+ readonly tt: "🇹🇹";
242
+ readonly tv: "🇹🇻";
243
+ readonly tw: "";
244
+ readonly tz: "🇹🇿";
245
+ readonly ua: "🇺🇦";
246
+ readonly ug: "🇺🇬";
247
+ readonly um: "🇺🇲";
248
+ readonly un: "🇺🇳";
249
+ readonly unitedStates: "🇺🇸";
250
+ readonly uy: "🇺🇾";
251
+ readonly uz: "🇺🇿";
252
+ readonly vatican: "🇻🇦";
253
+ readonly vc: "🇻🇨";
254
+ readonly ve: "🇻🇪";
255
+ readonly vg: "🇻🇬";
256
+ readonly vi: "🇻🇮";
257
+ readonly vn: "🇻🇳";
258
+ readonly vu: "🇻🇺";
259
+ readonly wf: "🇼🇫";
260
+ readonly ws: "🇼🇸";
261
+ readonly xk: "🇽🇰";
262
+ readonly ye: "🇾🇪";
263
+ readonly yt: "🇾🇹";
264
+ readonly za: "🇿🇦";
265
+ readonly zm: "🇿🇲";
266
+ readonly zw: "🇿🇼";
267
+ };
268
+ /** Unicode 区域指示符起始码点,两个区域指示符组成国旗 emoji。 */
269
+ export declare const REGION_INDICATOR_MIN_CODE_POINT = 127462;
270
+ /** Unicode 区域指示符结束码点,用于限制国旗 emoji 的合法范围。 */
271
+ export declare const REGION_INDICATOR_MAX_CODE_POINT = 127487;
272
+ /** 离屏 emoji 栅格化时使用的像素倍率,保证高分屏采样足够细。 */
273
+ export declare const DEVICE_PIXEL_RATIO: number;
274
+ /** 全局视觉色板:纸张、边框和投影色在多个旗帜绘制函数中复用。 */
275
+ export declare const PALETTE: {
276
+ readonly paper: "#fbfdfa";
277
+ readonly frame: "#d9e3db";
278
+ readonly shadow: "rgba(36, 49, 44, 0.08)";
279
+ };
@@ -0,0 +1,4 @@
1
+ /** 把任意输入规范化为可绘制的国旗 emoji;非法输入回退到中国国旗。 */
2
+ export declare function resolveFlag(value: unknown): string;
3
+ /** 判断字符串是否由两个区域指示符组成,这是 Unicode 国旗 emoji 的编码形式。 */
4
+ export declare function isFlagEmoji(value: string): boolean;