iter(g54): 新增 watermark 水印组件
This commit is contained in:
+53
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T04:17:05.094Z",
|
"generatedAt": "2026-10-26T04:18:05.467Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -195,7 +195,8 @@
|
|||||||
"datePicker(props):日期选择,单月网格 + 翻月 / 今天快捷键,value 为 YYYY-MM-DD 信号,min/max 闭区间限制,方向键逐日逐周可导航",
|
"datePicker(props):日期选择,单月网格 + 翻月 / 今天快捷键,value 为 YYYY-MM-DD 信号,min/max 闭区间限制,方向键逐日逐周可导航",
|
||||||
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
||||||
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
||||||
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose"
|
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose",
|
||||||
|
"watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -2410,6 +2411,56 @@
|
|||||||
"卡片优先落在目标下方,空间不足自动翻到上方,左右夹在视口内;滚动 / 缩放会实时重算",
|
"卡片优先落在目标下方,空间不足自动翻到上方,左右夹在视口内;滚动 / 缩放会实时重算",
|
||||||
"末步按钮文案变「完成」并触发 onFinish;× 或 Esc 触发 onClose,焦点归还开启前的元素"
|
"末步按钮文案变「完成」并触发 onFinish;× 或 Esc 触发 onClose,焦点归还开启前的元素"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "watermark",
|
||||||
|
"title": "水印 Watermark",
|
||||||
|
"demo": "sec-watermark",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "text",
|
||||||
|
"t": "string",
|
||||||
|
"r": 1,
|
||||||
|
"d": "水印文字,支持 \\n 多行"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "opacity",
|
||||||
|
"t": "number",
|
||||||
|
"d": "透明度;缺省 0.12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "rotate",
|
||||||
|
"t": "number",
|
||||||
|
"d": "旋转角度(度);缺省 -20"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "gap",
|
||||||
|
"t": "number",
|
||||||
|
"d": "平铺间距像素;缺省 140"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "fontSize",
|
||||||
|
"t": "number",
|
||||||
|
"d": "字号像素;缺省 14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "color",
|
||||||
|
"t": "string",
|
||||||
|
"d": "颜色(CSS 串或 var(--ua-*) 令牌);缺省取 --ua-color-text-muted 的实色"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "children",
|
||||||
|
"t": "...unknown[]",
|
||||||
|
"d": "被水印覆盖的内容,作为第二个及之后的实参传入"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "watermark({ text: '内部资料\\n仅限内网', gap: 160 }, card({ title: '受保护内容' }, h('p', null, '正文')))",
|
||||||
|
"steps": [
|
||||||
|
"第一个参数是配置,其后所有实参都被当作内容渲染在水印层下方",
|
||||||
|
"组件用一块 gap 见方的 canvas 画单个旋转单元,再 background-repeat 铺满,因此不需要监听尺寸变化",
|
||||||
|
"color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随",
|
||||||
|
"覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
- `packages/core/table-core`(@ui-arch/table-core):Headless 表格逻辑内核
|
- `packages/core/table-core`(@ui-arch/table-core):Headless 表格逻辑内核
|
||||||
- `packages/core/frame`(@ui-arch/frame):signal + hyperscript 微框架
|
- `packages/core/frame`(@ui-arch/frame):signal + hyperscript 微框架
|
||||||
- `packages/skin/ui`(@ui-arch/ui):52 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌
|
- `packages/skin/ui`(@ui-arch/ui):53 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌
|
||||||
- `packages/design/tokens`(@ui-arch/tokens):设计令牌
|
- `packages/design/tokens`(@ui-arch/tokens):设计令牌
|
||||||
- `packages/tools/cli`(@ui-arch/cli):`ua` 命令行 + MCP 注册表服务(private)
|
- `packages/tools/cli`(@ui-arch/cli):`ua` 命令行 + MCP 注册表服务(private)
|
||||||
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ ui-arch-mvp/
|
|||||||
│ │ ├── table-core/ # 表格状态内核
|
│ │ ├── table-core/ # 表格状态内核
|
||||||
│ │ └── frame/ # 信号 + hyperscript 微框架
|
│ │ └── frame/ # 信号 + hyperscript 微框架
|
||||||
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
||||||
│ │ └── ui/ # 52 个组件 + ui.css
|
│ │ └── ui/ # 53 个组件 + ui.css
|
||||||
│ ├── design/ # 资产层(可发布)
|
│ ├── design/ # 资产层(可发布)
|
||||||
│ │ └── tokens/ # --ua-* 设计令牌
|
│ │ └── tokens/ # --ua-* 设计令牌
|
||||||
│ └── tools/ # 工程层(private,不发布)
|
│ └── tools/ # 工程层(private,不发布)
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ npm run demo # 打开演示:apps/demo/dist/demo.html
|
|||||||
npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全量文档 llms-full.txt
|
npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全量文档 llms-full.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
## 组件(共 52 个)
|
## 组件(共 53 个)
|
||||||
|
|
||||||
<!-- ua:components -->
|
<!-- ua:components -->
|
||||||
accordion · alert · autoComplete · avatar · backTop · badge · breadcrumb · button · carousel · card · cascader · checkbox · colorPicker · dataTable · datePicker · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · notification · pagination · popover · progress · radioGroup · rate · result · segmented · select · skeleton · slider · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tour · transfer · tree · upload
|
accordion · alert · autoComplete · avatar · backTop · badge · breadcrumb · button · carousel · card · cascader · checkbox · colorPicker · dataTable · datePicker · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · notification · pagination · popover · progress · radioGroup · rate · result · segmented · select · skeleton · slider · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tour · transfer · tree · upload · watermark
|
||||||
<!-- /ua:components -->
|
<!-- /ua:components -->
|
||||||
|
|
||||||
分组:表单(button / textField / inputNumber / textarea / checkbox / colorPicker / switchButton / select / radioGroup / slider / rate / autoComplete / datePicker / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / toast / notification / result / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree)、数据展示(dataTable / descriptions / card / list / timeline / tag / avatar / image / carousel)、其他(divider / splitter / backTop / tour)。
|
分组:表单(button / textField / inputNumber / textarea / checkbox / colorPicker / switchButton / select / radioGroup / slider / rate / autoComplete / datePicker / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / toast / notification / result / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree)、数据展示(dataTable / descriptions / card / list / timeline / tag / avatar / image / carousel)、其他(divider / splitter / backTop / tour / watermark)。
|
||||||
|
|
||||||
## 可访问性约定
|
## 可访问性约定
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ accordion · alert · autoComplete · avatar · backTop · badge · breadcrumb
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) |
|
| `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) |
|
||||||
| `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) |
|
| `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) |
|
||||||
| `packages/skin/ui` | @ui-arch/ui:52 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
| `packages/skin/ui` | @ui-arch/ui:53 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
||||||
| `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) |
|
| `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) |
|
||||||
| `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server |
|
| `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server |
|
||||||
| `apps/demo` | 交互演示(esbuild 打包成单文件) |
|
| `apps/demo` | 交互演示(esbuild 打包成单文件) |
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
notification,
|
notification,
|
||||||
result,
|
result,
|
||||||
upload,
|
upload,
|
||||||
|
watermark,
|
||||||
menu,
|
menu,
|
||||||
modal,
|
modal,
|
||||||
pagination,
|
pagination,
|
||||||
@@ -689,6 +690,23 @@ const content = h(
|
|||||||
button({ label: '错误(可关)', variant: 'danger', onClick: () => notification({ title: '保存失败', description: '服务端返回 500,请稍后重试', variant: 'danger' }) }),
|
button({ label: '错误(可关)', variant: 'danger', onClick: () => notification({ title: '保存失败', description: '服务端返回 500,请稍后重试', variant: 'danger' }) }),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
section(
|
||||||
|
'sec-watermark',
|
||||||
|
'水印',
|
||||||
|
'watermark:canvas 生成平铺单元,颜色从 --ua-* 令牌解析成实色,覆盖层 pointer-events:none 不挡交互。',
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{ class: 'row' },
|
||||||
|
watermark(
|
||||||
|
{ text: 'UI-ARCH · 内部资料\n仅限内网查看', gap: 160, fontSize: 13 },
|
||||||
|
card(
|
||||||
|
{ title: '受保护内容' },
|
||||||
|
h('p', { class: 'desc' }, '水印覆盖在内容之上、交互穿透到下方,适合内部系统防截图外传。颜色缺省取 --ua-color-text-muted,暗色主题下自动变浅。'),
|
||||||
|
h('div', { class: 'row' }, button({ label: '可点击(验证穿透)', variant: 'primary', onClick: () => toast.success('水印层没有挡住点击') })),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
section(
|
section(
|
||||||
'sec-tour',
|
'sec-tour',
|
||||||
'引导漫游',
|
'引导漫游',
|
||||||
|
|||||||
+29
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T04:17:05.094Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T04:18:05.467Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -1385,6 +1385,33 @@ const t = tour({ steps: [{ target: '#sec-tour', title: '引导漫游', descripti
|
|||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-tour
|
现场演示:apps/docs/demo.html#sec-tour
|
||||||
|
|
||||||
|
### watermark — 水印 Watermark
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- text(必填):string —— 水印文字,支持 \n 多行
|
||||||
|
- opacity:number —— 透明度;缺省 0.12
|
||||||
|
- rotate:number —— 旋转角度(度);缺省 -20
|
||||||
|
- gap:number —— 平铺间距像素;缺省 140
|
||||||
|
- fontSize:number —— 字号像素;缺省 14
|
||||||
|
- color:string —— 颜色(CSS 串或 var(--ua-*) 令牌);缺省取 --ua-color-text-muted 的实色
|
||||||
|
- children:...unknown[] —— 被水印覆盖的内容,作为第二个及之后的实参传入
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
watermark({ text: '内部资料\n仅限内网', gap: 160 }, card({ title: '受保护内容' }, h('p', null, '正文')))
|
||||||
|
```
|
||||||
|
|
||||||
|
手把手:
|
||||||
|
|
||||||
|
1. 第一个参数是配置,其后所有实参都被当作内容渲染在水印层下方
|
||||||
|
2. 组件用一块 gap 见方的 canvas 画单个旋转单元,再 background-repeat 铺满,因此不需要监听尺寸变化
|
||||||
|
3. color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随
|
||||||
|
4. 覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪
|
||||||
|
|
||||||
|
现场演示:apps/docs/demo.html#sec-watermark
|
||||||
|
|
||||||
API 速览:
|
API 速览:
|
||||||
|
|
||||||
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)
|
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)
|
||||||
@@ -1441,6 +1468,7 @@ API 速览:
|
|||||||
- backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶
|
- backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶
|
||||||
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
||||||
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
||||||
|
- watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互
|
||||||
|
|
||||||
一行用法:
|
一行用法:
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,7 @@
|
|||||||
- backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶
|
- backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶
|
||||||
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
||||||
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
||||||
|
- watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互
|
||||||
- 用法: `const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')`
|
- 用法: `const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')`
|
||||||
- 约定: 皮肤不持有业务状态,一律委托 headless 内核或调用方信号
|
- 约定: 皮肤不持有业务状态,一律委托 headless 内核或调用方信号
|
||||||
- 约定: 表单类组件统一约定:*Props.value/checked/active 为 Signal 双向绑定,onChange/onInput 为回调
|
- 约定: 表单类组件统一约定:*Props.value/checked/active 为 Signal 双向绑定,onChange/onInput 为回调
|
||||||
|
|||||||
@@ -52,3 +52,4 @@ export { datePicker } from './datePicker.js';
|
|||||||
export { backTop } from './backTop.js';
|
export { backTop } from './backTop.js';
|
||||||
export { colorPicker } from './colorPicker.js';
|
export { colorPicker } from './colorPicker.js';
|
||||||
export { tour } from './tour.js';
|
export { tour } from './tour.js';
|
||||||
|
export { watermark } from './watermark.js';
|
||||||
|
|||||||
@@ -590,4 +590,14 @@ export interface TourHandle {
|
|||||||
close: () => void; // 立即关闭
|
close: () => void; // 立即关闭
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 水印入参 ***/
|
||||||
|
export interface WatermarkProps {
|
||||||
|
text: string; // 水印文字,支持 \n 多行
|
||||||
|
opacity?: number; // 透明度;缺省 0.12
|
||||||
|
rotate?: number; // 旋转角度(度);缺省 -20
|
||||||
|
gap?: number; // 平铺间距像素;缺省 140
|
||||||
|
fontSize?: number; // 字号像素;缺省 14
|
||||||
|
color?: string; // 颜色(CSS 串或令牌名);缺省取 --ua-color-text-muted 的实色
|
||||||
|
}
|
||||||
|
|
||||||
export type { ColumnDef, RowKey }; // 再导出常用内核类型,方便调用方
|
export type { ColumnDef, RowKey }; // 再导出常用内核类型,方便调用方
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { h } from '@ui-arch/frame';
|
||||||
|
import type { WatermarkProps } from './types.js';
|
||||||
|
|
||||||
|
/*** 取令牌实色:canvas 不认 var(--ua-*),先解析成计算值,解析不到再退回中性灰 ***/
|
||||||
|
function tokenColor(expr: string): string {
|
||||||
|
const name = /^var\((--[\w-]+)\)$/.exec(expr.trim())?.[1]; // 形如 var(--ua-color-text-muted)
|
||||||
|
if (!name) return expr; // 已经是实际色值,直接用
|
||||||
|
const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
||||||
|
return v || 'rgb(128 128 128)'; // 兜底:中性灰,避免透明水印
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 生成平铺单元:一块 gap 见方的画布,居中画一行旋转文字,靠 background-repeat 铺满 ***/
|
||||||
|
function tile(props: WatermarkProps): string {
|
||||||
|
const gap = props.gap ?? 140; // 平铺单元尺寸
|
||||||
|
const size = props.fontSize ?? 14; // 字号
|
||||||
|
const ratio = window.devicePixelRatio || 1; // 高分屏按 DPR 放大,避免糊
|
||||||
|
const cv = document.createElement('canvas');
|
||||||
|
cv.width = gap * ratio;
|
||||||
|
cv.height = gap * ratio;
|
||||||
|
const ctx = cv.getContext('2d');
|
||||||
|
if (!ctx) return ''; // 卫语句:环境不支持 canvas 时不出图
|
||||||
|
ctx.scale(ratio, ratio);
|
||||||
|
ctx.translate(gap / 2, gap / 2);
|
||||||
|
ctx.rotate(((props.rotate ?? -20) * Math.PI) / 180);
|
||||||
|
ctx.font = `${size}px system-ui, sans-serif`;
|
||||||
|
ctx.fillStyle = tokenColor(props.color ?? 'var(--ua-color-text-muted)');
|
||||||
|
ctx.globalAlpha = props.opacity ?? 0.12;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
props.text.split('\n').forEach((line, i, arr) => {
|
||||||
|
ctx.fillText(line, 0, (i - (arr.length - 1) / 2) * (size + 4)); // 多行按行距垂直居中
|
||||||
|
});
|
||||||
|
return cv.toDataURL('image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 水印:内容容器 + 覆盖层,覆盖层用 repeat 背景平铺,pointer-events:none 不挡交互 ***/
|
||||||
|
export function watermark(props: WatermarkProps, ...children: unknown[]): HTMLElement {
|
||||||
|
const img = tile(props);
|
||||||
|
const layer = h('div', { class: 'ua-watermark-layer', 'aria-hidden': 'true' }) as HTMLElement;
|
||||||
|
if (img) layer.style.backgroundImage = `url(${img})`; // 画布不可用时降级为无水印,不影响内容
|
||||||
|
return h('div', { class: 'ua-watermark' }, h('div', { class: 'ua-watermark-body' }, ...children), layer) as HTMLElement;
|
||||||
|
}
|
||||||
@@ -66,7 +66,8 @@
|
|||||||
"datePicker(props):日期选择,单月网格 + 翻月 / 今天快捷键,value 为 YYYY-MM-DD 信号,min/max 闭区间限制,方向键逐日逐周可导航",
|
"datePicker(props):日期选择,单月网格 + 翻月 / 今天快捷键,value 为 YYYY-MM-DD 信号,min/max 闭区间限制,方向键逐日逐周可导航",
|
||||||
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
||||||
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
||||||
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose"
|
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose",
|
||||||
|
"watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -2281,6 +2282,56 @@
|
|||||||
"卡片优先落在目标下方,空间不足自动翻到上方,左右夹在视口内;滚动 / 缩放会实时重算",
|
"卡片优先落在目标下方,空间不足自动翻到上方,左右夹在视口内;滚动 / 缩放会实时重算",
|
||||||
"末步按钮文案变「完成」并触发 onFinish;× 或 Esc 触发 onClose,焦点归还开启前的元素"
|
"末步按钮文案变「完成」并触发 onFinish;× 或 Esc 触发 onClose,焦点归还开启前的元素"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "watermark",
|
||||||
|
"title": "水印 Watermark",
|
||||||
|
"demo": "sec-watermark",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "text",
|
||||||
|
"t": "string",
|
||||||
|
"r": 1,
|
||||||
|
"d": "水印文字,支持 \\n 多行"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "opacity",
|
||||||
|
"t": "number",
|
||||||
|
"d": "透明度;缺省 0.12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "rotate",
|
||||||
|
"t": "number",
|
||||||
|
"d": "旋转角度(度);缺省 -20"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "gap",
|
||||||
|
"t": "number",
|
||||||
|
"d": "平铺间距像素;缺省 140"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "fontSize",
|
||||||
|
"t": "number",
|
||||||
|
"d": "字号像素;缺省 14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "color",
|
||||||
|
"t": "string",
|
||||||
|
"d": "颜色(CSS 串或 var(--ua-*) 令牌);缺省取 --ua-color-text-muted 的实色"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "children",
|
||||||
|
"t": "...unknown[]",
|
||||||
|
"d": "被水印覆盖的内容,作为第二个及之后的实参传入"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "watermark({ text: '内部资料\\n仅限内网', gap: 160 }, card({ title: '受保护内容' }, h('p', null, '正文')))",
|
||||||
|
"steps": [
|
||||||
|
"第一个参数是配置,其后所有实参都被当作内容渲染在水印层下方",
|
||||||
|
"组件用一块 gap 见方的 canvas 画单个旋转单元,再 background-repeat 铺满,因此不需要监听尺寸变化",
|
||||||
|
"color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随",
|
||||||
|
"覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3222,3 +3222,20 @@ button.ua-step-dot:hover {
|
|||||||
.ua-tour-card { animation: none; }
|
.ua-tour-card { animation: none; }
|
||||||
.ua-tour-spot { transition: none; }
|
.ua-tour-spot { transition: none; }
|
||||||
}
|
}
|
||||||
|
.ua-watermark {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--ua-radius-md);
|
||||||
|
}
|
||||||
|
.ua-watermark-body {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.ua-watermark-layer {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2;
|
||||||
|
pointer-events: none;
|
||||||
|
background-repeat: repeat;
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user