- UI组件
- 前端
【免费下载链接】wired-elements
Collection of custom elements that appear hand drawn. Great for wireframes or a fun look.
wired-calendar是 wired-elements 组件库中一款采用手绘线框风格(hand-drawn wireframe)渲染的日历选择器,非常适合用于原型(wireframe)设计或追求趣味性的界面。本指南以官方文档 docs/wired-calendar.md 为主体,结合仓库源码 src/wired-calendar.ts 与可运行示例 examples/calendar.html,带你掌握 wired-calendar 的安装引入、全部属性与事件、CSS 变量定制方法,并深入理解其背后的 roughjs 手绘渲染与日期计算原理。
一、wired-calendar 是什么
wired-elements 是一组"看起来像手绘草图"的自定义元素(Custom Elements),官方 README 将其定位为Collection of custom elements that appear hand drawn. Great for wireframes or a fun look.。wired-calendar 正是其中用于日期选择的组件:它在一个手绘矩形卡片内呈现单月日历,支持月份切换、日期范围限制、禁用态、多语言表头以及基于红色草图圆圈的选中态高亮。
它的典型应用场景包括:
- 原型阶段快速搭建带日期选择的线框图;
- 需要视觉趣味性的个人项目或内部工具;
- 作为学习 LitElement + roughjs 手绘组件实现思路的参考案例。
二、安装与引入
2.1 通过 npm 安装
将 wired-elements 添加到 JavaScript 项目中:
npm i wired-elements安装完成后,当前仓库对应的包信息见 package.json,组件基于lit(^2.0.0-rc.1)与roughjs(^4.3.1)构建,构建产物输出到lib/目录。
2.2 按需导入模块
在代码中导入WiredCalendar类:
import { WiredCalendar } from 'wired-elements/lib/wired-calendar.js';说明:虽然 src/wired-elements.ts 提供了聚合入口(
main/module均指向lib/wired-elements.js),但从该文件的 re-export 列表可以推断,wired-calendar并未包含在聚合入口中,因此官方文档推荐直接导入单个模块文件lib/wired-calendar.js。
2.3 通过 CDN 直接加载
也可以将模块脚本直接加载到 HTML 页面中:
<script type="module" src="https://unpkg.com/wired-elements/lib/wired-calendar.js?module"></script>2.4 在 HTML 中使用
引入后在页面中放置标签即可:
<wired-calendar selected="Jul 4, 2019"> </wired-calendar>这是文档给出的最小可用用法:传入selected="Jul 4, 2019"后,日历会自动预选中该日期。仓库示例 examples/calendar.html 中还展示了更丰富的组合,例如同时设置日期范围、多语言与initials:
<wired-calendar id="calendar2" elevation="1" firstdate="Apr 15, 2019" lastdate="Jul 15, 2019" selected="Jul 4, 2019" locale="fr" initials> </wired-calendar>三、属性(Properties)详解
以下为文档列出的全部属性,并结合源码逐项深入说明。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
elevation | Number | 源码默认 3(文档标注 1) | 1–5(含)之间的数字,设置日历卡片的立体高度 |
selected | String | 无 | 可选,可被Date解析的字符串,预选并高亮某个日期 |
firstdate | String | 无 | 可选,可被Date解析的字符串,有效日期的下限 |
lastdate | String | 无 | 可选,可被Date解析的字符串,有效日期的上限 |
locale | String | 浏览器 locale | 仅用于渲染表头的 BCP 47 语言标签(如es-MX、fr、de) |
disabled | Boolean | false | 禁用日历选择器 |
initials | Boolean | false | 星期几使用首字母(如S、M)而非缩写(如Sun) |
value | Object | 无 | 包含选中Date对象及对应格式化文本的 JavaScript 对象 |
format | Function | 见下文 | 获取/设置将Date对象格式化为文本的 JavaScript 函数 |
源码中对应的属性声明位于 src/wired-calendar.ts。
3.1 elevation:手绘卡片的"厚度"
elevation控制日历外框手绘阴影的层数。源码在updated()中对取值做了钳制处理:
const elev = Math.min(Math.max(1, this.elevation), 5);即超出 1–5 范围的值会被强制收敛到合法区间。绘制时每增加 1 层,会在矩形右下侧叠加一条透明度递减的手绘线(opacity从 85% 起每层递减 10%),从而形成手绘纸片的立体感。
注意:文档标注 elevation 默认值为 1,但当前仓库源码 src/wired-calendar.ts 中声明为
elevation = 3,使用时请以实际源码版本为准。
3.2 selected:预选日期
selected接受任何能被 JavaScriptDate构造函数解析的字符串,例如"Jul 4, 2019"。组件在初始化时会:
- 用
new Date(this.selected)解析出日期; - 以该日期所在月份作为当前展示月份(
firstOfMonthDate取当月 1 日); - 在
refreshSelection()中通过字符串比较day.value === this.selected逐格标记选中状态(src/wired-calendar.ts)。
由于选中匹配是字符串级比较,因此传入的selected字符串应尽量与format的输出格式保持一致,才能保证正确高亮。默认format输出形如"Jul 4, 2019"(月份英文简称 + 日 + 年)。
3.3 firstdate / lastdate:日期范围限制
这两个属性分别限定可选日期的下界与上界,解析后保存在私有变量fDate/lDate中(src/wired-calendar.ts)。其作用体现在两个层面:
- 日期禁用:
isDateOutOfRange(day)(src/wired-calendar.ts)判断某天是否越界。同时给出上下界时,day < fDate || lDate < day即判定越界;只给出其中一侧时按单侧判断。越界的日期单元格会被标记为disabled,点击不生效; - 月份导航限制:
onPrevClick()/onNextClick()在翻页前会检查前一月/下一月是否会越过fDate/lDate所在月份,从而阻止用户翻出有效范围。
3.4 locale:仅作用于表头渲染
locale是 BCP 47 语言标签(如es-MX、fr、de)。文档特别强调:该属性只用于渲染日历表头(月份名与星期名),所有内部和外部的日期处理不受 locale 影响。
源码localizeCalendarHeaders()(src/wired-calendar.ts)的实现印证了这一点:
- 未显式设置时,依次探测
navigator.systemLanguage、navigator.browserLanguage、navigator.languages,兜底为en; - 当 locale 不是
en/en-US时,使用Date.prototype.toLocaleString(locale, { weekday: 'short' })与{ month: 'long' }重新生成本地化表头文本; - 关键的内部日期比较所用的
months_short数组(如Jan、Feb…)保持en-US不变,注释明确警告 "month shorts are used inen-USinternally. Do not change."。
因此,无论界面显示什么语言,format输出与selected匹配逻辑始终基于英文月份简称,这也是示例注释中特别说明"参数日期不受 locale 影响"的原因。
3.5 disabled:整体禁用
disabled为true时,组件添加wired-disabled类,表现为半透明(opacity: 0.5)、pointer-events: none、鼠标光标变为默认,同时tabIndex被设为 -1,从 Tab 键序中移除;恢复时还原(src/wired-calendar.ts)。需要说明的是,这与 3.3 节中单日 disabled 不同:前者禁用整个组件,后者只禁用范围外的日期。
3.6 initials:星期首字母模式
initials为true时,星期表头只显示首字母(如S、M、T…),否则显示weekdays_short数组中的短名称(如Sun、Mon)。该判断发生在渲染阶段:this.initials ? d[0] : d(src/wired-calendar.ts)。
3.7 value:选中结果对象
value是一个包含两个字段的 JavaScript 对象:
{ date: Date, // 被选中的 Date 对象 text: string // 对应的格式化文本(由 format 函数输出) }用户每次选中日期后,组件会更新value并触发selected事件,因此这是读取当前选中结果的主要途径(详见第五节)。
3.8 format:自定义日期格式化函数
format属性可读写,用于控制日期文本的呈现。默认实现为:
(d: Date) => this.months_short[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear()即输出"Jul 4, 2019"这类格式。它同时参与两件事:
- 生成每个日期单元格的
value字符串(用于选中匹配); - 作为
value.text的内容。
示例 examples/calendar.html 展示了配合使用方式:myCalendar4.format(today)先生成今天的格式化文本,再交给公开方法setSelectedDate()完成程序化选中。
3.9 公开方法 setSelectedDate()
文档未单独列出但示例中实际使用的方法是setSelectedDate(formatedDate)(src/wired-calendar.ts)。传入一个日期字符串即可:
myCalendar4.setSelectedDate('Jul 4, 2019');方法内部会更新selected、将视图定位到对应月份、重算日历并触发selected事件,适合在按钮或其他控件中做程序化日期选择。
四、自定义 CSS 变量
文档定义了 4 个可用于主题化的 CSS 变量:
| 变量 | 作用 | 默认值 |
|---|---|---|
--wired-calendar-bg | 日历背景色 | 白色 |
--wired-calendar-color | 日历手绘线条颜色 | 黑色 |
--wired-calendar-selected-color | 选中日期的草图圆圈颜色 | 红色 |
--wired-calendar-dimmed-color | 不属于当月的"灰显"日期字体颜色 | 灰色 |
此外,从源码样式(src/wired-calendar.ts)可以发现第 5 个可用变量:--wired-calendar-disabled-color(默认lightgray),用于超出firstdate/lastdate范围的禁用日期字体颜色,文档未列出但实际可用。
示例 examples/calendar.html 中给出了完整的自定义样式写法:
.custom { --wired-calendar-bg: yellow; --wired-calendar-color: red; --wired-calendar-selected-color: black; --wired-calendar-dimmed-color: brown; width: 260px; height: 260px; font-size: 18px; }注意:除颜色变量外,组件的最终渲染尺寸还受元素本身width/height与font-size影响(见 6.2 节的尺寸计算逻辑)。
五、事件(Events)
文档定义了唯一的事件:
selected:当用户(或通过setSelectedDate)选中某个日期时触发。
从源码 src/wired-calendar.ts 可以看到事件派发细节:
this.value = { date: new Date(this.selected), text: this.selected }; fireEvent(this, 'selected', { selected: this.selected });其中fireEvent来自 src/wired-base.ts,派发的是标准CustomEvent,并带有bubbles: true与composed: true,因此事件会冒泡且能穿透 Shadow DOM,在外部直接监听即可。
监听方式:
myCalendar4.addEventListener('selected', () => { const selectedObject = myCalendar4.value; // selectedObject.date 是 JavaScript Date 对象 // selectedObject.text 是格式化后的日期文本 console.log(selectedObject.text); });六、完整实战示例
仓库中的 examples/calendar.html 是官方提供的完整演示,包含三种静态用法和一组带 JavaScript 交互的用法。其中交互部分的核心逻辑如下:
<wired-calendar id="calendar4" elevation="5" firstdate="Apr 15, 2019" lastdate="Jul 15, 2019" locale="es-MX" initials> </wired-calendar> <p id="calendar4-result">Select a date in the calendar</p> <wired-button id="btn-today">Today</wired-button> <wired-button id="btn-update">Update</wired-button> <p id="calendar4-update">No updated yet</p>const myCalendar4 = document.getElementById('calendar4'); // 方式一:事件驱动 —— 监听 selected 事件读取 value myCalendar4.addEventListener('selected', () => { let selectedObject = myCalendar4.value; // selectedObject.date 为 Date 对象;selectedObject.text 为格式化文本 let formatedDate = selectedObject.text; document.getElementById('calendar4-result').innerHTML = formatedDate + ' <br><small>Note: Internal date handling not affected by locale.</small>'; }); // 方式二:非事件驱动 —— 通过 value 属性轮询读取 document.getElementById('btn-update').addEventListener('click', () => { const selectedObject = myCalendar4.value; if (selectedObject && selectedObject.date) { myCalendar4update.innerHTML = selectedObject.date.toLocaleDateString(); } else { myCalendar4update.innerHTML = 'No date selected yet.'; } }); // 方式三:程序化设置 —— 使用 format + setSelectedDate document.getElementById('btn-today').addEventListener('click', () => { let today = new Date(); let formatedDate = myCalendar4.format(today); // 传入任何 JavaScript Date 可解析的格式均可 myCalendar4.setSelectedDate(formatedDate); });该示例同时展示了三种典型交互路径:监听事件、直接读取value属性、调用公开方法程序化设值,覆盖了组件对外的主要编程接口。
七、源码实现解析:手绘日历是如何画出来的
7.1 整体渲染结构
组件基于 LitElement,内部模板由一个<table>和两个 SVG overlay 构成(src/wired-calendar.ts):
- 第一行表头:
<<上一月 / 月份+年份标题 />>下一月; - 第二行表头:七个星期名(
initials为 true 时取首字母); - 数据区:按周循环生成
<tr>,每周 7 个<td>日期单元格; - 日历外框:覆盖在表格之上的
svg.calendar,用于绘制手绘矩形边框与立体边线; - 选中态:选中单元格内部再嵌一个
svg.selected,用于绘制手绘红色椭圆。
7.2 roughjs 手绘绘制
所有"手绘感"都来自 src/wired-lib.ts 对 roughjs 的封装。以日历外框为例,updated()中(src/wired-calendar.ts)先清空 SVG,再用rectangle()绘制粗糙矩形边框,随后按elevation叠加半透明边线形成立体厚度;选中日期则用ellipse()画一个红色手绘椭圆(stroke-width: 2.5)圈住数字。每个元素都使用组件初始化时生成的随机种子(randomSeed(),见 src/wired-base.ts),保证每次刷新后线条抖动形状一致。
7.3 尺寸计算
getCalendarSize()(src/wired-calendar.ts)读取元素实际包围盒,若宽或高小于 180px 则回退为 320px,保证最小可读尺寸;computeCellsizes()(src/wired-calendar.ts)按比例分配空间:两行表头合计占高度的 25%,剩余 75% 按周数均分给每周;列宽为总宽除以 7 再减去 2px 边框间距;- 组件在
connectedCallback()中注册了 200ms 防抖的resize监听,窗口变化时自动重算(src/wired-calendar.ts)。
7.4 月份网格计算
computeCalendar()(src/wired-calendar.ts)以当月 1 日往前偏移到周日为起点,按 7 天为一周循环填充整月网格;不属于当月的日期标记为dimmed(灰显);同时为每个单元格生成value(格式化日期字符串)、text(日数字)与disabled(是否越界)标记。monthYear表头文本随firstOfMonthDate变化。
7.5 生命周期与禁用态
- 组件设置
role="dialog"角色(firstUpdated()); connectedCallback()中依次完成表头本地化、初始条件设置、日历计算与选中刷新,随后通过setTimeout(() => this.updated())触发首次手绘渲染;disconnectedCallback()中移除 resize 监听;- 禁用态通过
wired-disabled类实现(opacity: 0.5、pointer-events: none),焦点态下 SVG 线条加粗至 1.5。
八、注意事项与常见问题
- 日期字符串格式:
selected、firstdate、lastdate接受任何 JavaScriptDate可解析的格式,但selected与默认format输出做字符串级匹配,建议保持格式一致,例如"Jul 4, 2019"; - locale 只影响外观:无论
locale设为哪种语言,内部日期解析、格式化与比较始终基于固定的英文月份简称,这是设计上的刻意行为; - elevation 范围:虽然文档标注 1–5,源码会对超出范围的值自动钳制到 1–5,但传入合法值仍是推荐做法;
- 默认值差异:文档中 elevation 默认值标注为 1,而当前仓库源码默认值为 3,请以所安装版本的实际行为为准;
- 禁用语义:
disabled禁用整个组件,firstdate/lastdate只禁用范围外的日期,两者可组合使用。
九、深入阅读
- 组件官方文档:docs/wired-calendar.md
- 核心源码实现:src/wired-calendar.ts
- 可运行示例:examples/calendar.html
- 手绘绘制封装(roughjs):src/wired-lib.ts
- 事件与随机种子工具:src/wired-base.ts
- 项目构建与依赖配置:package.json
wired-calendar 以 MIT 协议开源(详见仓库 LICENSE),其文档贡献者包括 Eduardo Martinez。结合本文的属性说明、示例代码与源码解析,你可以直接在页面中嵌入一个手绘风格的日期选择器,并通过selected事件、value属性与 CSS 变量快速接入业务逻辑与视觉定制。
- UI组件
- 前端
【免费下载链接】wired-elements
Collection of custom elements that appear hand drawn. Great for wireframes or a fun look.
相关推荐
pi-subagents 社区生态:插件市场与第三方集成的展望 - 构建强大的AI代理协作平台
pi subagents 社区生态:插件市场与第三方集成的展望 构建强大的AI代理协作平台 在AI代理快速发展的今天, pi subagents 作为Pi生态中
人工智能AI Agent多智能体Agent 编排代码智能体终极wired-elements开发指南:20个手绘风格组件属性与事件详解
终极wired elements开发指南:20个手绘风格组件属性与事件详解 wired elements是一套独特的手绘风格Web组件库,通过简单的HTML标签
UI组件前端MiniMax-H3-Realism-People-LoRA训练幕后:176个精选视频片段如何塑造逼真人物
MiniMax H3 Realism People LoRA训练幕后:176个精选视频片段如何塑造逼真人物 MiniMax H3 Realism People
人工智能大模型媒体生成LoRA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考