mcpField
M
morphing-icons

morphing-icons

github.com/raphaelsalaja设计与 UI42362 次安装今天更新
代码仓库

关于 morphing-icons

morphing-icons 是用于设计和审查形状变换图标的技能,图标通过形状变化而非交叉淡化来切换。它规定统一的三线 SVG 结构,并提供转场一致性与无障碍实现规则。

主要功能

  1. 01每个图标固定使用三条 SVG 线
  2. 02用折叠线表示未使用的形状
  3. 03通过分组实现旋转变体动画
  4. 04规定 viewBox 和动画行为
  5. 05包含减少动态效果与无障碍规则

适用场景

  1. 01创建状态间形变的图标
  2. 02按规则审查图标实现
  3. 03保持旋转图标变体一致
  4. 04遵循减少动态效果偏好

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/raphaelsalaja/skill --skill morphing-icons

安装后在 Claude Code 中输入/morphing-icons确认已加载。

详细介绍

图标变形

构建通过实际形状变换而非交叉淡化来变形的图标。任意图标都可以变形成其他任意图标,因为它们共享相同的底层 3 线结构。

核心概念

每个图标都恰好由 三条 SVG 线 组成。需要较少线条的图标会将多余的线条折叠为不可见的中心点。这一限制使任意两个图标之间都能无缝变形。

架构

1. 线条定义

每条线都有坐标和可选的不透明度:

interface IconLine {
  x1: number;
  y1: number;
  x2: number;
  y2: number;
  opacity?: number;
}
2. 折叠线条

需要少于 3 条线的图标会使用折叠线条——位于中心的零长度线:

const CENTER = 7; // Center of 14x14 viewbox

const collapsed: IconLine = {
  x1: CENTER,
  y1: CENTER,
  x2: CENTER,
  y2: CENTER,
  opacity: 0,
};
3. 图标定义

每个图标都恰好有 3 条线、可选的旋转角度和可选的分组:

interface IconDefinition {
  lines: [IconLine, IconLine, IconLine];
  rotation?: number;
  group?: string;
}
4. 旋转分组

共享同一个 group 的图标在彼此转换时会进行旋转动画。没有相同分组的图标会立即跳转到新的旋转角度:

// These rotate smoothly between each other
{ lines: plusLines, rotation: 0, group: "plus-cross" }   // plus
{ lines: plusLines, rotation: 45, group: "plus-cross" }  // cross

// These rotate smoothly between each other
{ lines: arrowLines, rotation: 0, group: "arrow" }       // arrow-right
{ lines: arrowLines, rotation: 90, group: "arrow" }      // arrow-down
{ lines: arrowLines, rotation: 180, group: "arrow" }     // arrow-left
{ lines: arrowLines, rotation: -90, group: "arrow" }     // arrow-up

实现规则

morphing-three-lines

每个图标必须恰好使用 3 条线,不能多也不能少。

错误:

const checkIcon = {
  lines: [
    { x1: 2, y1: 7.5, x2: 5.5, y2: 11 },
    { x1: 5.5, y1: 11, x2: 12, y2: 3 },
  ], // Only 2 lines
};

正确:

const checkIcon = {
  lines: [
    { x1: 2, y1: 7.5, x2: 5.5, y2: 11 },
    { x1: 5.5, y1: 11, x2: 12, y2: 3 },
    collapsed, // Third line collapsed
  ],
};
morphing-use-collapsed

未使用的线条必须使用折叠常量,不能省略或设为 null。

错误:

const minusIcon = {
  lines: [
    { x1: 2, y1: 7, x2: 12, y2: 7 },
    null,
    null,
  ],
};

正确:

const minusIcon = {
  lines: [
    { x1: 2, y1: 7, x2: 12, y2: 7 },
    collapsed,
    collapsed,
  ],
};
morphing-consistent-viewbox

所有图标必须使用相同的 viewBox(推荐 14x14)。

错误:

// Mixing viewbox scales
const icon1 = { lines: [{ x1: 2, y1: 7, x2: 12, y2: 7 }, ...] }; // 14x14
const icon2 = { lines: [{ x1: 4, y1: 14, x2: 24, y2: 14 }, ...] }; // 28x28

正确:

const VIEWBOX_SIZE = 14;
const CENTER = 7;
// All coordinates within 0-14 range
morphing-group-variants

具有旋转变体的图标必须共享同一个分组和基础线条。

错误:

// Different line definitions for arrows
const arrowRight = { lines: [{ x1: 2, y1: 7, x2: 12, y2: 7 }, ...] };
const arrowDown = { lines: [{ x1: 7, y1: 2, x2: 7, y2: 12 }, ...] }; // Different!

正确:

const arrowLines: [IconLine, IconLine, IconLine] = [
  { x1: 2, y1: 7, x2: 12, y2: 7 },
  { x1: 7.5, y1: 2.5, x2: 12, y2: 7 },
  { x1: 7.5, y1: 11.5, x2: 12, y2: 7 },
];

const icons = {
  "arrow-right": { lines: arrowLines, rotation: 0, group: "arrow" },
  "arrow-down": { lines: arrowLines, rotation: 90, group: "arrow" },
  "arrow-left": { lines: arrowLines, rotation: 180, group: "arrow" },
  "arrow-up": { lines: arrowLines, rotation: -90, group: "arrow" },
};
morphing-spring-rotation

分组图标之间的旋转应使用弹簧物理效果,实现自然的动感。

错误:

<motion.g animate={{ rotate: rotation }} transition={{ duration: 0.3 }} />

正确:

const rotation = useSpring(definition.rotation ?? 0, activeTransition);

<motion.g style={{ rotate: rotation, transformOrigin: "center" }} />
morphing-reduced-motion

通过禁用动画来遵循 prefers-reduced-motion 设置。

错误:

function MorphingIcon({ icon }: Props) {
  return <motion.line animate={...} transition={{ duration: 0.4 }} />;
}

正确:

function MorphingIcon({ icon }: Props) {
  const reducedMotion = useReducedMotion() ?? false;
  const activeTransition = reducedMotion ? { duration: 0 } : transition;
  
  return <motion.line animate={...} transition={activeTransition} />;
}
morphing-jump-non-grouped

在不属于同一分组的图标之间转换时,旋转角度应立即跳转。

错误:

// Always animating rotation regardless of group
useEffect(() => {
  rotation.set(definition.rotation ?? 0);
}, [definition]);

正确:

useEffect(() => {
  if (shouldRotate) {
    rotation.set(definition.rotation ?? 0); // Animate
  } else {
    rotation.jump(definition.rotation ?? 0); // Instant
  }
}, [definition, shouldRotate]);
morphing-strokelinecap-round

线条应使用 strokeLinecap="round",让端点更精致。

错误:

<motion.line strokeLinecap="butt" />

正确:

<motion.line strokeLinecap="round" />
morphing-aria-hidden

图标 SVG 是装饰性内容,因此应设置为 aria-hidden。

错误:

<svg width={size} height={size}>...</svg>

正确:

<svg width={size} height={size} aria-hidden="true">...</svg>

常见图标模式

双线图标(勾号、减号、等号、V 形箭头)

使用一条或两条折叠线:

const check = {
  lines: [
    { x1: 2, y1: 7.5, x2: 5.5, y2: 11 },
    { x1: 5.5, y1: 11, x2: 12, y2: 3 },
    collapsed,
  ],
};
三线图标(菜单、星号、播放)

使用全部三条线:

const menu = {
  lines: [
    { x1: 2, y1: 3.5, x2: 12, y2: 3.5 },
    { x1: 2, y1: 7, x2: 12, y2: 7 },
    { x1: 2, y1: 10.5, x2: 12, y2: 10.5 },
  ],
};
点状图标(更多、抓握)

使用零长度线作为圆点:

const more = {
  lines: [
    { x1: 3, y1: 7, x2: 3, y2: 7 },
    { x1: 7, y1: 7, x2: 7, y2: 7 },
    { x1: 11, y1: 7, x2: 11, y2: 7 },
  ],
};

推荐的过渡效果

使用指数缓出实现平滑变形:

const defaultTransition: Transition = {
  ease: [0.19, 1, 0.22, 1],
  duration: 0.4,
};

输出格式

审查变形图标实现时,请按以下格式输出发现的问题:

file:line - [rule-id] description of issue

Example:
components/icon/index.tsx:45 - [morphing-three-lines] Icon "check" has only 2 lines, needs collapsed third
components/icon/index.tsx:78 - [morphing-group-variants] arrow-down uses different line definitions than arrow-right

汇总表

列出发现的问题后,输出汇总:

规则数量严重程度
morphing-three-lines2HIGH
morphing-group-variants1HIGH
morphing-reduced-motion1MEDIUM

参考资料

评论

支持 Markdown