Day 10 - Design System x 实作 — Icon 元件

https://ithelp.ithome.com.tw/upload/images/20210925/201207549Gd7QN7Xfv.png
今天就要来实作 Icon 啦!事不宜迟直接开始!

想先看 Code 或是 Demo 的由此去:
Github Repo: ithelp-ui-demo
Live Demo:Storybook

在 Icon 这边首先要建立一个观念,就是 「网页中的 Icon 基本上都会是以 SVG 的格式来呈现」
使用 SVG 的原因主要是用来减少图片的使用率,透过向量图形把 Icon 画出来并当成文字来调整大小 (font-size)、和颜色 (color)。

更详细的理由可以看 MDN - 为何使用 SVG ?

而在这边能使用 font-size 来改变大小的原因是因为我在宽高是使用 1em 来设定,em 的意思是以父元素的倍数来呈现,因此在父元素指定 font-size 就能改变 Icon 大小啦,而没给的话就是吃到最顶层预设的值。
参考 一次搞懂 CSS 字体单位:px、em、rem 和 %

那既然都是用 SVG 来 Render 的话,其实就可以把 SVG 会用到的属性统一出一个规格,再透过框架各自的语法把它 Render 出来。

顺着这个逻辑,以下会来逐步介绍 IconDefinition、Icon interface 和 Icon Component,带大家一探 Icon 是如何被元件化的!

IconDefinition

首先来看看能如何定义一个 IconDefinition 的介面 ,让日後新引入的 Icon 都能遵循这个格式来绘制。

export interface IconDefinition {
  name: string;
  definition: {
    svg?: {
      viewBox?: string; // default: 0 0 24 24
    };
    path?: {
      d?: string;
      fill?: string;
      fillRule?: 'nonzero' | 'evenodd' | 'inherit';
      stroke?: string;
      strokeWidth?: string | number;
      transform?: string;
    };
  };
}

接着直接来看看一个 Icon 能怎麽透过 SVG 画出来,以 check 为例:

https://ithelp.ithome.com.tw/upload/images/20210925/201207549rnXBUSON6.png

import { IconDefinition } from "./typings";

export const CheckIcon: IconDefinition = {
  name: "check",
  definition: {
    svg: {
      viewBox: "0 0 24 24",
    },
    path: {
      fill: "currentColor",
      fillRule: "evenodd",
      stroke: "none",
      strokeWidth: 1,
      d:
        "M17.993 8.768l-7.625 7.625L6.4 12.425l1.061-1.061 2.908 2.907 6.564-6.563z",
    },
  },
};

以此就可以知道其实要用 SVG 画出一个 Icon 的话只需要像上面这样定义,主要就是 viewBox 确认比例,然後设定一下 fill、fillRule、stroke、strokeWidth,接着运用 d 这个属性把线都画出来。

那我之前的经验是可以请 UI 设计师遵从 viewBox: "0 0 24 24" 这样的比例把 SVG 切出来给我,如此统一规格之後,未来就可以无痛新增 Icon 了。

想看看其他 Icon 的 Definition 请参照 Github

这边在简短总结一下 IconDefinition :「藉由制定一套 Icon 的 SVG 介面,日後想新增客制化的 Icon 只要照着这介面去绘制即可。」

既然统一了 Icon 的 SVG 规格之後,我们就可以接着来看看 Icon 这个元件的 Interface 和在 React 上如何实作它了!

介面

最大的重点在 IconDefinition 都说完了,剩余的两个属性挺白话的,就直接看 Storybook 的 Description 吧!

https://ithelp.ithome.com.tw/upload/images/20210925/20120754oqPC7B3cBv.png

元件实作

最後在实作这边就只是在用 React JSX 的语法把 IconDefinition 丢下去 Render 出对应的 Icon 而已。

export const Icon: React.FC<IconProps> = (props) => {
  const { className, color = "black", icon, spin = false } = props;
  const { definition } = icon;

  return (
    <i
      aria-hidden
      className={`
        inline-block flex-shrink-0 select-none w-em h-em
        ${spin ? "animate-spin" : ""}
        ${color ? Color[color] : ""}
        ${className ? className : ""}
      `}
      data-icon-name={icon.name}
    >
      <svg {...definition.svg} focusable={false}>
        <path {...definition.path} />
      </svg>
    </i>
  );
};

加码:看看 Angular 如何运用同一套 IconDefinition 实作 Icon 元件

上面讲完大家可能还是没感受到 IconDefinition 的力量,因此这边直接运用同一套 Definition 来用 Angular 的 Template 语法上去呈现。

template: `
    <svg
      [attr.focusable]="false"
      [attr.viewBox]="svg?.viewBox"
    >
      <svg:path
        *ngIf="path"
        [attr.d]="path.d"
        [attr.fill]="path.fill"
        [attr.fill-rule]="path.fillRule"
        [attr.stroke]="path.stroke"
        [attr.stroke-width]="path.strokeWidth"
        [attr.transform]="path.transform"
      >
      </svg:path>
    </svg>
  `,

get svg() {
  return this.icon.definition.svg;
}

get path() {
  return this.icon.definition.path;
}

由此可知,有时候介面定义出来,不同框架就只是在用不同的语法去实作而已。

小结

相信介绍完 Icon 之後,大家会对介面的定义能更有感触,我第一次知道 Icon 能用这样的方式来产出来的时候,也是惊为天人呢,没想到可以帮一个个 Icon 抽象化成这样的 SVG 格式!

明天要介绍的事在 Design System 的 System 实作上的最後一个元素 — 元件动画(Motion)的过渡(Transition)。

如果觉得对你有帮助的话,希望大家可以不吝在 Github Repo 上给个 Star ><

那就明天见罗!


<<:  Day 12 :阵列(array)与链结串列(linked list)

>>:  [Day24] Tableau 轻松学 - TabPy 使用方法 1

[Day6] 自我必备沟通力:Content & Context

发挥影响力 随时必备的两个元素:Content & Context 自觉、找镜子、了解与掌握...

Unity与Photon的新手相遇旅途 | Day5-灯光介绍、粒子效果

今天天内容为灯光、粒子效果的基本介绍! Duration 粒子发射的时间 Looping 设定粒子是...

Day6: IAM简介

上一篇我们最後讲到了AWS针对NIST所发展的资安五大面向。今天我们来了解第一个面向IAM。 Ide...

Day01:时间复杂度

刚开始,我想说点什麽 看过市面上许多解释演算法的资料,有些书搭配图片,有些影片浅显易懂,为了挑战自己...

Day 9— 物品借用纪录系统 (1) 基础建构

今天我们要来制作新的专题:物品借用纪录微服务! 在学校,尤其是行政处室,最常出现的状况应该就是「借物...