@5even7/dlc-ui

DLC 星云胶囊、流体进度、动态背景、Lottie 表情、本地头像创建器、Icon Motion 图标与任意形状盒子组件库,支持 H5 / Vue2 / Vue3。
4,897
Install
npm install @5even7/dlc-ui
Latest Version:0.2.29
Vue:2 · 3
License:MIT
Last Updated:Oct 8, 2026
Links: Gitee  ·  npm
Maintainer: 5even7

@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 插槽(颜色区):占剩余区域(文字区右侧到右边缘)。

容器默认完全透明、无内容:不传插槽等于没有任何文字/覆盖层。容器只负责几何定位,对插槽内容施加的样式影响只有:

  1. box-sizing: border-box 会作用到插槽内所有元素(组件根节点统一规则);
  2. pointer-events: none 会继承(组件要把点击/拖拽留给进度条和事件系统);需要交互时在你的内容上写 pointer-events: auto;
  3. 根节点 overflow: hidden + border-radius 会裁剪超出区域的内容;文字区容器自身也带 overflow: hidden,设置 --hj-text-radius 时内容按圆角裁剪;
  4. 不继承组件任何文字样式:颜色、字体、字距、大小写、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

vue2-svg-icon

A solution for svg icon in vue2.0

3,471
svg-vuejs

SVG VueJS - Vue 2.0 SVG图标组件库,用于管理和使用SVG资源

1,754
@shader-core/vue

Vue components for Shader Core: with layers as children.

1,342
vue-cryptocurrency-icons

A Vue.js component for the project 'cryptocurrency-icons: 6000 icons – almost 40...

1,375 8
glyphcast

Video as colored glyphs: a complete WebGL2 player with controls and PiP, and a g...

4,980 0

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