@5even7/dlc-ui
@5even7/dlc-ui
发布 npm 包
npm login --auth-type=web --registry=https://registry.npmjs.org/ npm publish --access public
框架无关的 UI 组件库:星云胶囊、流体进度胶囊、星云背景层(dlc-color)、动态表情(dlc-emo)、 本地头像创建器、Icon Motion 图标、窗口壳子(dlc-shell) 与 任意形状盒子(dlc-shape)。 一套核心,H5 / Vue2 / Vue3 都能用。
星云组件使用 WebGL2 实时渲染,不可用时自动降级 Canvas2D;Emo 内置 type="miao"(23 个 Lottie 动作)、type="noto"(879 个 Noto Animated Emoji,dotLottie 压缩后按需解码)与 type="grok"(纯 SVG Path Morph 动态角色)。Miao / Noto 共用 lottie-web,可选 SVG / Canvas;现代浏览器加载原生 ESM,旧工具链可使用转译后的 legacy 构建。
发版规则
默认每次发布只递增补丁号(+0.01),例如 0.2.0 → 0.2.1;除非手动明确指定新版本号,否则不升次版本。
安装
npm install @5even7/dlc-ui
按需加载:怎么用(照抄就行,不用背 API)
只需要记住一句话:页面里用组件,写名字;脚本里用 import,拿东西。
① 写名字(最简单,任何时候都行)
<dlc-emo name="smile" /> <!-- 组件标签 -->
<dlc-avatar-creator avatar-style="lorelei" seed="x" /> <!-- 模板里叫 avatar-style,不是 style -->
createEmo(el, { name: 'smile' }) <!-- JS 里也一样,写名字就行 -->
createAvatarCreator(el, { style: 'lorelei', seed: 'x' })
什么都不用 import,写名字就能用。emo 和头像第一次用某个动画 / 画风时内部加载一下,都有占位、不会白屏;以后表情加到几千个,页面体积也不会变大。
② import 模块(可选,进阶:同步拿数据 / SVG、只带用到的)
想要脚本里同步拿到数据 / SVG(比如后端、工具、做缓存),就把动画 / 画风 import 进来再调用(纯函数 createAvatarSvg 必须这样;createEmo / createAvatarCreator 写名字就行):
// 头像:先引入渲染函数,再引入画风(API 里参数叫 style,直接传画风模块)
import { createAvatarSvg } from '@5even7/dlc-ui/avatar-creator'
import { lorelei } from '@5even7/dlc-ui/avatar-creator/styles'
const { svg } = createAvatarSvg(lorelei, { seed: 'x', size: 160 })
// 表情:直接引入动画数据
import smile from '@5even7/dlc-ui/emo/miao/smile'
createEmo(el, { animation: smile })
import grin from '@5even7/dlc-ui/emo/noto/1f600'
createEmo(el, { type: 'noto', animation: grin })
两种方式出的结果一模一样,只是入口不同。
路径就记这三行
| 想拿什么 | 从哪 import | 例子 |
|---|---|---|
| 头像画风(DiceBear 8 个) | @5even7/dlc-ui/avatar-creator/styles |
import { lorelei, rings } from '@5even7/dlc-ui/avatar-creator/styles' |
| humation(约 700 KB,要用才引入) | @5even7/dlc-ui/avatar-creator/styles/humation |
import { humation } from '@5even7/dlc-ui/avatar-creator/styles/humation' |
| 表情动画(23 个) | @5even7/dlc-ui/emo/miao/动画名 |
import smile from '@5even7/dlc-ui/emo/miao/smile' |
| Noto 动态 Emoji(879 个) | @5even7/dlc-ui/emo/noto/码点 |
import grin from '@5even7/dlc-ui/emo/noto/1f600' |
引入哪个就只带哪个:没 import 的画风 / 动画不会进你的项目,这是自动的,不用你操心。
做选择器 / 目录时(不用加载任何数据)
- 头像画风列表、参数控件:
getStyleDefinitions()(同步返回轻量元数据;Humation 渲染模块仍按需加载) - 表情名称、分类、可改色部位:
EMO_PRESETS/getEmoAnimationMeta('miao', 'smile')(同步) - 程序化懒加载(不 import 具体动画时):
await getEmoAnimation('miao', 'cry') - Noto 目录同样使用上述 API,例如
getEmoAnimationMeta('noto', '1f600')/await getEmoAnimation('noto', '1f600')
预置列表
| 类型 | 预置 | 说明 |
|---|---|---|
| capsule | 初光(NC-01)、沧溟(NC-02)、玄夜(NC-03)、紫霄(NC-04)、银汉(NC-05)、熔金(NC-06) |
星云材质 |
| progress | 星火(NC-10)、迁流(NC-11)、幻境(NC-12) |
流体进度 |
| progress | 汐潮(NC-13) |
潮汐边界(潮起潮落) |
| progress | 森息(NC-14) |
植物实验室配色(苔藓培养 / 生长进度) |
dlc-color 复用同一套星云渲染器,只做背景层,直接使用上表 capsule 的 6 个预置。
dlc-emo 提供三个表情家族:
type="miao":Lottie 动作表情,内置 23 个动作名:angry / cry / sad / sigh / investigate / mute / ponder / question / static / asleep / yawn / badminton / confident / laugh / leisure / music / smile / yummy / mock / shy / snigger / panic / shocked。type="noto":Google Noto Animated Emoji,内置 879 个 dotLottie 压缩动画;name使用小写 Unicode 码点序列,例如 😀 是1f600、浅肤色 👍 是1f44d_1f3fb。支持与 Miao 相同的播放、片段、fit、SVG / Canvas 能力,但素材没有稳定的语义图层,所以不支持partColors。type="grok":SVG Path Morph 动态角色,内置 13 个语义状态(分类与公开 API 一致,见GROK_CATEGORIES):情绪(idle / happy / surprised / sad / angry / confused)、Agent 状态(loading / thinking / searching / working / listening / receiving)、生命周期(sleeping)。支持shape(blob / pebble / squircle / capsule / triangle / hex / cloud / drop)与interactive(pointer 视线跟随),partColors为body/eyes两部位。
preset 只接受上表中的文学名称(如 '沧溟'),大小写不敏感、首尾空格忽略;不传时使用默认预置 初光(胶囊/背景层)或 星火(进度条)。colors、seed、speed 是独立属性,传了覆盖预置内置值,不传用预置默认值。
H5 使用
现代浏览器(ESM)
import { createCapsule, createProgressCapsule } from '@5even7/dlc-ui';
import '@5even7/dlc-ui/style.css';
const capsule = createCapsule(document.querySelector('#host-a'), {
preset: '沧溟',
width: '100%',
height: 160,
text: '<span class="cap-name">沧溟</span>', // 文字区插槽内容
colorContent: '<span class="cap-chip">DLC</span>', // 颜色区插槽内容
textRatio: 39 // 文字区宽度占比(%)
});
const progress = createProgressCapsule(document.querySelector('#host-b'), {
preset: '汐潮',
width: 454,
height: 104,
text: '<span class="prog-name">画境观屿</span>',
showValue: true
});
progress.on('change', ({ value, source }) => console.log(value, source));
动态表情(dlc-emo)
import { createEmo } from '@5even7/dlc-ui/emo';
import '@5even7/dlc-ui/emo/style.css';
const emo = createEmo(document.querySelector('#emo-host'), {
type: 'miao',
name: 'smile',
width: 240,
height: 160,
partColors: {
leftEye: '#fff8dc',
rightEye: '#fff8dc',
mouth: '#fff8dc'
},
speed: 1.5,
fit: 'contain',
renderer: 'svg'
});
emo.setName('laugh').setSegment([0, 80]);
// 宿主销毁时:emo.dispose();
Noto Animated Emoji:
const noto = createEmo(document.querySelector('#noto-host'), {
type: 'noto',
name: '1f600',
width: 220,
height: 220,
speed: 1.25,
renderer: 'svg'
});
noto.setName('1f602').setDirection('reverse').setSegment([0, 90]);
Grok 动态角色:
const grok = createEmo(document.querySelector('#grok-host'), {
type: 'grok',
name: 'thinking',
width: 229,
height: 229,
shape: 'capsule',
interactive: true,
partColors: { body: '#2f86ed', eyes: '#fffdf7' }
});
grok.setName('celebrate').setShape('hex').setInteractive(false);
// 宿主销毁时:grok.dispose();
Grok 的
name表示一个动态状态模组(表情池 + 节奏 + 眨眼 + 身体动作),而不是一张固定表情:不同状态在静态第一帧可能相似,但动态行为不同。13 个公开 name 全部有效,展示元数据见GROK_STATE_META(category/label/description/source),分类表GROK_CATEGORIES、查询函数getGrokStateMeta(name)/getGrokNamesFor(tab)均由该元数据派生。需要做 hover 预览卡片时可复用createEmoPreviewCard({ container, options, playFor, respectReducedMotion, trackViewport }):默认静态,进入时播放短预览,离开 / 移出视口 / 系统减少动态时立即暂停。
partColors 按部位替换颜色:Miao 使用素材内稳定的 paint.* 命名通道,Grok 支持 body / eyes;Noto 原始素材没有跨动画稳定的语义图层,因此传入 partColors 会被忽略。外框背景单独用 background 控制。
工厂调用用 text / colorContent 选项填插槽;Vue 组件直接用默认插槽和 #color 插槽(见下)。
插槽与 CSS 约定(重要)
两个组件都固定有两个插槽容器:
- 默认插槽(文字区):占左侧
textRatio宽度的区域; #color插槽(颜色区):占剩余区域(文字区右侧到右边缘)。
容器默认完全透明、无内容:不传插槽等于没有任何文字/覆盖层。容器只负责几何定位,对插槽内容施加的样式影响只有:
box-sizing: border-box会作用到插槽内所有元素(组件根节点统一规则);pointer-events: none会继承(组件要把点击/拖拽留给进度条和事件系统);需要交互时在你的内容上写pointer-events: auto;- 根节点
overflow: hidden+border-radius会裁剪超出区域的内容;文字区容器自身也带overflow: hidden,设置--hj-text-radius时内容按圆角裁剪; - 不继承组件任何文字样式:颜色、字体、字距、大小写、padding、背景全是页面默认——空白盒子放进去就是“干净的盒子”。
层级:canvas(1)< 颜色区插槽(3)< 文字区插槽(4)< 进度数字(5,仅进度条)。
进度条的 textRatio 建议 ≤70%:再大时右侧进度数字会压到文字区上方;需要自定义数字就 showValue=false 并在 #color 插槽里自己写。
星云背景层(dlc-color)
把星云材质铺进任意容器当背景,效果与 Capsule 完全一致:
import { createColorBackground } from '@5even7/dlc-ui/color';
import '@5even7/dlc-ui/color/style.css';
const background = createColorBackground(document.querySelector('#bg-host'), {
preset: '沧溟',
quality: 'auto'
});
background.setPreset('玄夜');
background.pause();
层级规则:宿主会被自动设为 position: relative; isolation: isolate,色层是 position: absolute; inset: 0; z-index: -1,画在宿主普通内容下面——文字、按钮、图片直接放进宿主就能显示在星云上方,一行 CSS 都不用写。色层 z-index 可用 --dlc-color-layer-z 调整;宿主内需要保证浮在星云上的复杂层级内容可加 position: relative; z-index: var(--dlc-color-content-z, 1)。
透明度与兜底色:opacity(0~1,默认 1)让宿主自己的背景透出来;fallbackColor 默认用预置底色兜底,消除 WebGL 首帧编译前的空窗闪烁,传十六进制色可自定义,传 false 可关闭。运行中可用 setOpacity(value) 热更新。mouseColor 默认开启,颜色随鼠标像油画一样旋涡,可传 { mouseColor: false } 关闭,不影响宿主上其它元素的交互。
旧浏览器 / CDN(UMD legacy)
<link rel="stylesheet" href="https://unpkg.com/@5even7/dlc-ui/styles/theme.css" />
<script src="https://unpkg.com/@5even7/dlc-ui/dist/index.umd.min.js"></script>
<script>
const { createCapsule, createProgressCapsule, createEmo } = window.CosmicCapsules;
</script>
为避免把 879 份资源全部内联进单个 legacy 文件,UMD/CJS 入口使用 type + name 时会从 Google Fonts 官方 Noto 动画地址按需请求 JSON;需要完全离线时请使用现代 ESM,或通过 @5even7/dlc-ui/emo/noto/* 子路径把指定 dotLottie source 传给 animation。
Vue3
<script setup>
import { DlcCapsule, DlcProgress, DlcColor, DlcEmo } from '@5even7/dlc-ui/vue3';
import '@5even7/dlc-ui/style.css';
function onProgressChange({ value }) {
console.log(value);
}
</script>
<template>
<dlc-capsule preset="沧溟" width="100%" height="160" :text-ratio="45" @click="...">
<span class="cap-code">NC-02</span>
<span class="cap-name">沧溟</span>
<template #color><span class="cap-chip">DLC</span></template>
</dlc-capsule>
<dlc-progress preset="汐潮" :width="454" :height="104" @change="onProgressChange">
<span class="prog-name">画境观屿</span>
</dlc-progress>
<dlc-color preset="沧溟" style="height: 200px">
这段内容显示在星云背景上方
</dlc-color>
<dlc-emo type="miao" name="smile" :width="180" :height="180" />
</template>
Vue2
import { DlcCapsule, DlcProgress, DlcEmo } from '@5even7/dlc-ui/vue2';
import '@5even7/dlc-ui/style.css';
export default {
components: { DlcCapsule, DlcProgress, DlcEmo },
template: `
<dlc-progress preset="汐潮" :width="454" :height="104" @change="onChange">
<span class="prog-name">画境观屿</span>
<template v-slot:color><span>自定义颜色区内容</span></template>
</dlc-progress>
`
};
属性(props)
两个组件共有:preset、width、height(数字=px,或 px/%/vw/vh/vmin/vmax/em/rem 字符串)、colors(4 个颜色,支持 #rgb / #rrggbb / CSS 命名色 / rgb() / rgba() / hsl() / hsla(),统一归一化为小写 6 位十六进制;rgba()/hsla() 的 alpha 会被忽略,透明请用组件级 opacity)、textRatio(文字区宽度占比 0~100,默认胶囊 39 / 进度条 54)、label(覆盖默认 aria-label,胶囊另带 role="img")、quality(auto/low/medium/high)、renderer(auto/webgl2/canvas2d)、respectReducedMotion、cssVars。
胶囊专属:seed、speed、mouseColor(颜色随鼠标旋涡,默认 true,关闭不影响 click/悬浮/长按等事件;Canvas2D 降级路径下 mouseColor 不生效)。
进度条专属:value、min、max(value 的合法区间,默认 0~100;水线归一化到 [min, max],右侧数字仍显示原始 value)、draggable、disabled(禁用交互并置灰)、readonly(禁用交互但不置灰)、edgeStyle(flow/tide)、valueSuffix(右侧数字后缀,默认 %)、showValue(是否显示右侧百分比数字,默认 true,false 只隐藏数字、不影响无障碍信息)。
插槽内容属性(H5/工厂):text、colorContent,支持 HTML 字符串或 DOM 节点(数组);Vue 组件请用默认插槽 / #color 插槽。注意:字符串走 innerHTML,不要传入不可信/用户直接输入的内容(XSS 风险)。
dlc-color 背景层(createColorBackground(host, options)):preset、colors、seed、speed、quality、renderer、mouseColor、respectReducedMotion、opacity(0~1)、fallbackColor(预置底色 / 十六进制色 / false)。控制器支持 setPreset / setColors / setSeed / setSpeed / setQuality / setOpacity / setFallbackColor / setMouseColor / randomize / pause / resume / dispose 和 ready / presetchange / contextlost / contextrestored / warning / error 事件。Vue 里可用官方组件 dlc-color(从 @5even7/dlc-ui/vue2 或 /vue3 导入),它的默认插槽内容会显示在背景层上方。
dlc-emo:type(miao / noto / grok)、name、animation、width、height、partColors(仅 Miao / Grok)、speed(0.1~4)、loop、autoplay、direction(Miao / Noto)、paused、segment(Miao / Noto 帧区间)、opacity、fit、renderer(Miao / Noto 的 svg/canvas)、background、label、instanceName、respectReducedMotion。控制器提供 play / pause / stop / setPaused / setSpeed / setDirection / setSegment / setName / setType / setPartColors / setOpacity / setBackground / setSize / setFit / setRenderer / setLoop / setAutoplay / setLabel / dispose。
胶囊控制器额外支持 setSeed(seed) / setSpeed(speed) / setMouseColor(value) / setText(content) / setColorContent(content) / setTextRatio(ratio) / setCssVars(vars) / setLabel(label) 热更新;进度控制器额外支持 setEdgeStyle('flow' | 'tide') / setValueSuffix(suffix) / setShowValue(show) 和同样的插槽热更新方法。setPreset 只接受文学名称字符串(如 '汐潮')。Vue 组件实例也暴露 setText / setColorContent / setTextRatio / setCssVars / setLabel(胶囊另有 setMouseColor,进度另有 setValueSuffix / setShowValue),与 H5 控制器对称。
进度条是完全受控组件:组件自身不再自动增长,
value由使用方驱动。要自动步进,自己写定时器加值即可:let value = 0; const timer = setInterval(() => { value = Math.min(100, value + 1); progress.setValue(value); // 或 Vue 里改绑定的 value }, 100); // 组件销毁时记得 clearInterval(timer)
API 是可扩展的:核心返回的控制器
on(event, fn)接受任意事件名;Vue wrapper 会转发$emit。ready/error在创建后异步触发,所以先创建、后监听也能收到。attrs 落点:id/data-*/title等普通属性会同步到组件内部根节点;class/style落在外层宿主.hj-vue-host上(CSS 变量可继承到内部,类选择器请写成.my-class .hj-capsule-root)。
属性 vs cssVars 边界
- 内容(文字/图片/按钮):只归插槽管,组件不施加任何样式;
- 文字区宽度:只归
textRatio属性管(唯一入口,写入--hj-text-width;0即隐藏文字区); - 区域外观(背景/内边距/圆角/左侧距):只归 cssVars 管;
- 星云颜料:只归
colors属性管(与#color插槽无关,前者改画布颜料,后者放你的覆盖内容); - 已消费的旧变量(
--hj-copy-width、--hj-copy-min-width、--hj-copy-left、--hj-copy-max-width)保留为兼容别名,只兜底不优先。
优先级统一为:textRatio > cssVars(新变量) > 旧别名 > 样式表默认。
cssVars 完整变量表
所有可调值都是 CSS 变量,直接覆盖即可:
.hj-progress-root {
--hj-radius: 24px;
--hj-value-right: 16px;
}
也可以在创建时传 cssVars:
createProgressCapsule(el, {
preset: '汐潮',
cssVars: { '--hj-radius': '12px', '--hj-text-bg': 'rgba(0,0,0,0.4)' }
});
| 变量 | 作用 | 默认 |
|---|---|---|
--hj-text-width |
文字区宽度(textRatio 写这里) |
胶囊 39% / 进度条 54% |
--hj-text-min-width |
文字区最小宽度(仅胶囊;textRatio 生效时组件强制归零,样式表兜底值才是 164px) |
0 |
--hj-text-left |
文字区左侧距离(仅进度条) | 30px |
--hj-text-bg |
文字区背景 | 透明 |
--hj-text-pad |
文字区内边距 | 0 |
--hj-text-radius |
文字区圆角(由容器应用,内容会被裁剪到圆角内) | 0 |
--hj-radius |
根节点圆角 | 999px |
--hj-bg / --hj-ink / --hj-muted |
全局令牌(可被插槽内容引用) | 浅色主题值 |
--hj-font / --hj-mono |
字体令牌(可被插槽内容引用) | Inter / 等宽栈 |
--hj-shadow |
根节点阴影 | 柔和阴影 |
--hj-value-right |
进度数字右侧距离(仅进度条) | 30px |
--dlc-color-layer-z |
色层 z-index(dlc-color,宿主 CSS 可覆盖) | -1 |
兼容别名(deprecated,只兜底):--hj-copy-width、--hj-copy-min-width、--hj-copy-left、--hj-copy-max-width。
事件
| 组件 | 事件 | 载荷 |
|---|---|---|
| capsule | ready / presetchange |
{ preset } |
| capsule | click / pointerdown / pointerup / dblclick |
{ event, preset } |
| capsule | pointerenter / pointerleave |
{ event, preset } |
| capsule | error |
{ message }(WebGL2 不可用降级时触发) |
| capsule | contextlost / contextrestored |
{} |
| progress | ready / presetchange |
{ preset } |
| progress | change |
{ value, source },source ∈ drag / keyboard / prop |
| progress | dragstart / dragend |
{ value } |
| progress | error |
{ message } |
| progress | contextlost / contextrestored |
{} |
| emo | ready |
{ preset, duration, frames }(grok 无逐帧概念:duration 为 null、不含 frames) |
| emo | complete / loopComplete / segmentStart |
播放生命周期 |
| emo | namechange / typechange |
{ preset } |
| emo | error |
{ message } |
| color | ready / presetchange / error |
{ preset } / { preset } / { message } |
| color | contextlost / contextrestored |
{} |
| avatar | ready / change / warning / error |
{ svg, width, height, metadata, state } / 渲染结果 / string / Error |
| icon | ready / iconchange |
{ type, name, source } |
| icon | animationstart / animationend / animationcancel |
{ type, name, event, motionId, source, reason? } |
| icon | click / error |
{ type, name, event, source, originalEvent? } / { type, name, event, source, error } |
click 与 dblclick 保持原生 H5 的独立事件语义:一次双击会先产生两次 click,随后产生一次 dblclick。组件不会延迟或吞掉 click;若业务需要单击与双击互斥,应由业务层根据自己的等待时长与交互后果处理。
Shell 窗口壳子
dlc-shell 分成根盒子、标题栏和内容区。根盒子的宽、高、四角圆角和溢出由公共属性管理;默认高度为 auto,由标题栏与内容共同撑开。内容区不注入 padding 或排版;控制按钮始终显示并报告点击,但不执行默认窗口行为。
<template>
<dlc-shell type="mac" header-text="照片预览" width="720px" radius="16px" :controls-anime="false">
<template #header><strong>自定义标题</strong></template>
<div class="card-body"><span>文字</span><img src="/photo.jpg" alt="照片" /></div>
</dlc-shell>
</template>
<script>
import { DlcShell } from '@5even7/dlc-ui/vue2/shell' // Vue3 改为 vue3/shell
import '@5even7/dlc-ui/shell/style.css'
export default { components: { DlcShell } }
</script>
属性:type: 'mac' | 'cmd'(默认 mac)、headerText: string(默认空字符串)、theme: 'auto' | 'light' | 'dark'(默认 auto)、controlsAnime: boolean(默认 true),以及根盒子属性 width、height、radius、overflow。数字尺寸按 px 处理,字符串接受对应 CSS 值。旧 header / setHeader() 保留为兼容别名。
Vue 的 #header 插槽只替换标题内容,不替换控制按钮;存在时 headerText 不显示。默认插槽是内容区。内容布局由使用者自己的子盒子负责:
.card-body { padding: 24px; }
H5 使用 createShell(host, options) 直接装饰宿主。带 slot="header" 的直接子元素进入标题区,其余子节点进入内容区:
<dlc-shell id="shell">
<strong slot="header">自定义标题</strong>
<div class="card-body">内容</div>
</dlc-shell>
自定义 dlc-shell 标签和普通 div 都可以作为 H5 宿主,但都必须调用 createShell() 初始化。dispose() 会移除内部窗口结构并恢复宿主子节点。根节点仍可用 class/style 设置 margin、定位、边框、背景、阴影和变换;不要在根节点设置内容 padding,内容留白写在默认插槽自己的子元素上。
内部标题栏公开四个 CSS 变量:--dlc-shell-header-height(最小高度;mac 默认 38px、cmd 默认 32px)、--dlc-shell-header-background、--dlc-shell-header-color、--dlc-shell-controls-width(mac 78px、cmd 138px)。默认 headerText 相对整个窗口居中;header 插槽保持用户布局,需要整体居中时可把插槽内容绝对定位为 inset: 0 var(--dlc-shell-controls-width),再使用 flex 居中。自定义 header 更高时,标题栏和控制按钮区会一起增高。
控制按钮只发送统一的 control-click 事件,不执行关闭或缩放。Vue 2 / Vue 3 使用 @control-click="handleControl";H5 使用 onControlClick(detail),或在宿主监听 dlc-control-click。事件数据统一为 { name, index, type, nativeEvent }。mac 的名称依次为 close / minimize / zoom,cmd 为 minimize / maximize / close,因此业务代码应按 name 分发,不要假定按钮数量或固定下标。
Shape 任意形状的盒子
dlc-shape 把任意形状变成一个能像普通 div 一样使用的盒子:曲线、挖洞、多个不连通区域都支持。
clip-path 按规范连命中区域一起裁剪,所以形状之外的指针事件会穿透到下层元素 —— 这跟"矩形盒子贴了张形状图"有本质区别。
想直接拖一个出来,去文档站的 Shape 编辑器导出 shape 属性值即可。
<template>
<dlc-shape class="shape-card" :shape="blob">
<div class="shape-card__body">任意内容</div>
</dlc-shape>
</template>
<script>
import { DlcShape } from '@5even7/dlc-ui/vue2/shape' // Vue3 改为 vue3/shape
import '@5even7/dlc-ui/shape/style.css'
export default {
components: { DlcShape },
data: () => ({
blob: 'path(M50 0 C78 0 100 22 100 50 C100 78 78 100 50 100 C22 100 0 78 0 50 C0 22 22 0 50 0 Z)'
})
}
</script>
<style>
.shape-card { width: 220px; height: 220px; background: linear-gradient(140deg, #6d5dfc, #22d3ee); }
.shape-card__body { display: grid; height: 100%; align-content: center; padding: 24px; }
</style>
H5 直接装饰现有宿主,宿主原有的内容、class、style、事件都不动:
<div id="shape-box" class="shape-card">任意内容</div>
<script type="module">
import { createShapeDiv } from '@5even7/dlc-ui/shape'
import '@5even7/dlc-ui/shape/style.css'
const controller = createShapeDiv(document.querySelector('#shape-box'), {
shape: 'path(M50 0 C78 0 100 22 100 50 C100 78 78 100 50 100 C22 100 0 78 0 50 C0 22 22 0 50 0 Z)'
})
// controller.setShape(...) / controller.setFit(...) / controller.dispose()
</script>
shape 的写法
坐标统一是 0–100 归一化空间(跟 CSS 百分比一致),同一份形状换到任意尺寸的盒子上都不用改。
| 写法 | 示例 | 组件实际使用 |
|---|---|---|
polygon(…) |
polygon(0% 0%, 100% 0%, 50% 100%) |
原生 CSS clip-path 透传,零额外 DOM |
path(…) / 裸 M…Z |
path(M0 0 L100 0 C82 92 18 92 0 62 Z) |
SVG clipPath(objectBoundingBox) |
circle() / ellipse() / inset() |
circle(50% at 50% 50%) |
原生 CSS 透传 |
| 多段子路径 | path(evenodd, M…Z M…Z) |
SVG clipPath + clip-rule: evenodd(挖洞) |
坐标写 % 按百分比换算,写数字就直接当 0–100 空间里的值。默认填充规则是 nonzero(跟 SVG / CSS 默认一致);
要挖洞就显式加 evenodd 前缀 —— 注意 CSS 的 clip-path: polygon() 不支持 fill-rule 参数,所以带 evenodd 的写法会走 SVG 而不是 CSS 透传。
因为默认是 nonzero,多条子路径的重叠部分会保留:path(大圆 小圆 小圆) 直接就是一张卡通脸,
不用把轮廓拉成一条闭合曲线。这条对画不规则物体特别省事 —— 基本形叠加 + 拖动摆位就够了。
fit
fit="stretch"(默认)把形状拉满盒子,语义跟 CSS clip-path 一致;fit="contain" 把形状等比缩进盒子并居中,
盒子外围会空出来。contain 用一个隐藏的 <path> 读出形状外接盒,再算等比缩放和居中偏移,盒子尺寸变化由 ResizeObserver 跟进。
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
shape |
string |
'' |
形状本体。留空即不裁剪,退化成普通 div。 |
fit |
'stretch' | 'contain' |
'stretch' |
拉伸填满 / 等比居中。 |
background |
string |
undefined |
快捷背景色;不传就完全交给使用方的 CSS,清空会还原宿主原本的 background。 |
H5 控制器方法:setShape(shape)、setFit(fit)、setBackground(bg)、dispose(),以及独立导出的
parseShape(input)(输入非法直接抛错,方便开发期发现问题)。dispose() 会移除 defs、clip-path、
尺寸观察器和自有 class,并还原宿主原本的内联样式。
已知限制
这些都是 clip-path 本身的语义,不是实现缺陷:
border和box-shadow会被裁掉 —— 描边请单独用一层 SVG<path stroke>绘制,投影用外层容器的filter: drop-shadow()。- 内部滚动条仍是矩形(按盒子的矩形视口绘制)。
- 布局占位仍是外接矩形:
clip-path只影响绘制和命中,不参与布局计算;需要文字环绕时用shape-outside。 - 曲线形状依赖
clip-path: url(#…)。纯直边多边形走原生 CSS 透传不受影响;曲线形状建议在目标机型上真机验证一遍(Safari 早期版本在 HTML 元素上的clip-path: url()有已知问题)。 fit="contain"靠ResizeObserver跟进盒子尺寸。没有ResizeObserver的引擎会退化成监听窗口resize:窗口变化仍然跟得上,但纯元素尺寸变化(比如侧栏折叠)不会重算。legacy CJS / UMD 入口内置了 polyfill,不受这条影响。shape.css是可选的:组件只在宿主上写clip-path和(可选的)background,不引入任何内部结构;样式文件只提供display: block这类最低默认值。
兼容性
| 目标 | 构建 | 渲染 |
|---|---|---|
| Chrome / Chromium / Edge / Android WebView 84+ | dist/*.mjs(原生 ESM) |
星云 WebGL2;Emo SVG / Canvas |
| Firefox 90+ | dist/*.mjs(原生 ESM) |
星云 WebGL2;Emo SVG / Canvas |
| Safari / iOS Safari 15+ | dist/*.mjs(原生 ESM) |
星云 WebGL2;Emo SVG / Canvas |
| Samsung Internet 14+ | dist/*.mjs(原生 ESM) |
星云 WebGL2;Emo SVG / Canvas |
IE11 不在当前官方支持范围内。legacy/UMD 仍会产出 ES5 + polyfill(PointerEvent / IntersectionObserver / ResizeObserver),作为旧工具链兼容构建使用,但不承诺 IE11 的现代浏览器渲染兼容性;这些 CJS 产物在 Node 里 require() 也是安全的。星云组件的 WebGL2 可能因显卡、驱动或浏览器策略不可用,此时自动降级 Canvas2D;Emo 的 renderer 是独立的 svg / canvas,不会走 WebGL2。
运行时开关
两个跨组件的运行时开关,从根入口或对应组件子路径导入:
import { setWebGLBudget, getWebGLBudget } from '@5even7/dlc-ui/color';
import { setNotoAssetBase, getNotoAssetBase } from '@5even7/dlc-ui/emo';
- WebGL 上下文预算:每个 Capsule / Progress / Color 实例独占一块 canvas,即一个 WebGL2 上下文。浏览器对同页上下文数量有上限(桌面 Chrome 约 16,移动端更少),超出后最早创建的上下文会被踢掉,那块画布永久空白。库内共享一个预算,默认
16,超出的实例自动改用 Canvas2D 并派发warning事件(code: 'webgl-budget-exceeded',载荷含budget)。移动端建议setWebGLBudget(6);setWebGLBudget(0)表示完全不用 WebGL,setWebGLBudget(null)表示不限制。getWebGLBudget()返回{ max, active, peak, rejected },可用于排查。默认值高于常见用量,因此低于上限的页面行为与以前完全一致。 - Noto 素材基址:现代 ESM 构建把 879 份 dotLottie 内联进包,运行时零网络请求;legacy / UMD 构建改为按需拉取,默认取「加载脚本的那个源」——
<script src="…/dist/index.umd.min.js">的用户会自动请求同源下的emo/noto-lottie/<码点>.lottie,无需配置也不依赖外部 CDN。setNotoAssetBase('https://your-cdn/dlc/')可显式换源(基址下需有emo/noto-lottie/);显式配置后不再自动回退、失败直接报错,setNotoAssetBase(null)恢复自动发现。不使用 legacy 构建时无需关心这两项。
开发
Icon Motion 图标:静态 SVG + 独立 Motion
import { createIcon } from '@5even7/dlc-ui/icon';
import bell from '@5even7/dlc-ui/icon/linemd/bell';
import '@5even7/dlc-ui/icon/style.css';
createIcon(document.querySelector('#icon'), {
icon: bell,
size: 24,
color: 'currentColor',
hoverAnm: { anm: true, speed: 1.3, count: 2 }
});
动态名称使用 type: 'linemd', name: 'bell',provider 会按需加载;固定名称推荐 @5even7/dlc-ui/icon/linemd/*。当前 1222 个公开名称中 1220 个携带原生 Motion,iconify2-static 与 iconify2-static-twotone 为静态。每个公开图标名称只携带一个原生 Motion(或保持静态):例如 alert-circle 与 alert-circle-loop 共享 geometry,但分别保留自己的 render / loop Motion。createAnm / hoverAnm / clickAnm / focusAnm 只接受 true、false、MotionDefinition 或 { anm, speed, count };true 播放该图标唯一的原生 Motion,false 是完全 no-op,不会打断其他事件正在播放或加载的 Motion;不再接受 Motion 名称或事件名字符串。animated 是全部事件动画和手动 play() 的总开关,设为 false 会取消当前播放并阻止后续 Motion,但静态 SVG 仍保留。Vue2 / Vue3 从各自入口导入同一个 DlcIcon wrapper,底层共用 H5 controller。
MotionDefinition.duration 是必填的完整编舞时长(毫秒,包含所有轨道与 delay),必须与 CSS 一致。播放优先级固定为“事件 count > 组件 count > Motion iterations”;新事件覆盖旧事件,同一 Motion 再触发会从头播放。有限 Motion 应在最后一帧恢复静态 Icon 初态,runtime 在结束、取消或切图时会移除 activation、计时器和临时 CSS 变量。外部 Motion 的每个 selector 都必须从当前 Icon 的 activation gate 开始,并且默认禁止修改 width / height / viewBox / color / fill / stroke。
来源与改造说明
dlc-icon的linemd图标与原生 Motion 基于 GitHub 上的 LINE MD 3.0.5 改造。DLC 将其整理为静态 SVG geometry、独立 CSS Motion、单图标子路径和按需加载 provider;LINE MD 由 Vjacheslav Trushkin(Iconify)维护,采用 MIT License。- 下述 Motion Studio Web 由 K_a1 基于 GitHub 上游 nolangz/pixel2motion 改造为适配
dlc-icon的 Motion-Only Web 模式;改造版仓库为 K_a1/pixel2motion。上游 Pixel2Motion 作者为 Nolan Lai,采用 MIT License。
dlc-emo 的第三方素材与授权见 THIRD_PARTY_NOTICES.md:
- Miao:
lottie-web(Airbnb,MIT)与 ESP Emote Pack 素材(来源espressif2022/esp_emote_gen_player;上游仓库 LICENSE 证据较弱,商业使用前需确认)。 - Noto:官方资源站 Google Noto Animated Emoji,授权 CC BY 4.0;页面相关静态/矢量素材来源为 googlefonts/noto-emoji。
- Grok:数据与算法参考 zhulin025/LaoA-GrokBot(MIT)。
自定义 Motion 创作工具
Pixel2Motion Motion Studio Web(K_a1 改造版) 是与 dlc-icon MotionDefinition 协议配套的 Motion-Only 本地 Web 工作台,可用静态 SVG 生成、预览并导出自定义 CSS Motion。它是独立创作工具,不是 @5even7/dlc-ui 的运行时依赖;导出的单个 *.motion.json 可传给 defineIconMotion(),再绑定到 createAnm / hoverAnm / clickAnm / focusAnm。
git clone https://gitee.com/K_a1/pixel2motion.git
cd pixel2motion
npm install
npm run dev
npm install
npm run build # 产出 dist(ESM + CJS + UMD)
npm test # node --test tests/
源码入口:src/dlc-avatar-creator/(头像创建器)、src/dlc-capsule/(星云胶囊)、src/dlc-progress/(进度胶囊)、src/dlc-color/(星云背景层)、src/dlc-emo/(动态表情)、src/dlc-icon/(Icon Motion 图标)、src/config/(预置/默认值/文案)、src/core/(调度/尺寸/质量/可见性等公共核心)。
想往 emo 加新类型、往 avatar 加新画风/供应商?看 EXTENDING.md(扩展指南:架构、接入步骤、子路径导出规则)。
示例怎么看
三个示例页都会展示全部 6 组星云胶囊与 5 组流体进度(含潮汐 TIDE 与森息),并为两类组件各提供一个控制台(可切换“全部/单个”目标、实时调节属性),属性表详细说明每个属性/方法/事件的用途。示例代码在 examples/ 下。
npm run demo:h5 # http://127.0.0.1:4173/examples/h5/
cd examples/vue3 && npm install && npm run dev # http://127.0.0.1:5173/
cd examples/vue2 && npm install && npm run dev # http://127.0.0.1:5174/
首次运行前请先执行
npm run build(示例引用的是打包产物dist/),三个示例的依赖各自独立安装。
License
MIT
Related Packages
A Vue.js component for the project 'cryptocurrency-icons: 6000 icons – almost 40...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.2.29 | >=2.7.0 | MIT | |
| 0.2.28 | >=2.7.0 | MIT | |
| 0.2.27 | >=2.7.0 | MIT | |
| 0.2.26 | >=2.7.0 | MIT | |
| 0.2.25 | >=2.7.0 | MIT | |
| 0.2.24 | >=2.7.0 | MIT | |
| 0.2.23 | >=2.7.0 | MIT | |
| 0.2.21 | >=2.7.0 | MIT | |
| 0.2.20 | >=2.7.0 | MIT | |
| 0.2.19 | >=2.7.0 | MIT | |
| 0.2.18 | >=2.7.0 | MIT | |
| 0.2.17 | >=2.7.0 | MIT | |
| 0.2.16 | >=2.7.0 | MIT | |
| 0.2.15 | >=2.7.0 | MIT | |
| 0.2.14 | >=2.7.0 | MIT | |
| 0.2.13 | >=2.7.0 | MIT | |
| 0.2.12 | >=2.7.0 | MIT | |
| 0.2.11 | >=2.7.0 | MIT | |
| 0.2.10 | >=2.7.0 | MIT | |
| 0.2.9 | >=2.7.0 | MIT | |
| 0.2.8 | >=2.7.0 | MIT | |
| 0.2.7 | >=2.7.0 | MIT | |
| 0.2.6 | >=2.7.0 | MIT | |
| 0.2.5 | >=2.7.0 | MIT | |
| 0.2.4 | >=2.7.0 | MIT | |
| 0.2.3 | >=2.7.0 | MIT | |
| 0.2.2 | >=2.7.0 | MIT | |
| 0.2.1 | >=2.7.0 | MIT | |
| 0.2.0 | >=2.7.0 | MIT | |
| 0.1.0 | >=2.7.0 | MIT |