paint-brush
Како да увезете икони од Figma на кул начинод страна на@smileek
Нова историја

Како да увезете икони од Figma на кул начин

од страна на Andrei Sieedugin8m2025/03/04
Read on Terminal Reader

Премногу долго; Да чита

Овој водич покажува како лесно да внесувате икони од Figma во проект React/Vue и да креирате компонента што овозможува нивно користење во различни големини и бои.
featured image - Како да увезете икони од Figma на кул начин
Andrei Sieedugin HackerNoon profile picture

Кажете дека имате збир на икони во Figma и проект во React/Vue/што и да било друго. Не е изненадување, ќе треба да ги користите тие икони. Како можеме да ги увеземе?


Ако сте доволно тврдоглави, можете да ги преземете еден по еден и да ги додавате како средства. Но, тоа е тажен начин. Нема слично.


Ајде да престанеме да бидеме тажни и наместо тоа да бидеме прекрасни. Еве што ќе ни треба:

  • Убава компонента што овозможува користење на тие икони во различни големини и бои.
  • Автоматско комплетирање со имиња на икони.
  • Едноставно ажурирање кога ќе се додаде нова икона.


*не е обезбедено дополнително време*

Започнете овде

Ќе ни треба токен Figma. Мислам дека не е неопходно да се објасни како да се направи ова, затоа наместо тоа, еве дел за помош: https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens .


Стандардните поставки се доволно добри: потребен ни е пристап само за читање до содржината на датотеката.



Дај ми лична карта

Како пример ќе ги користам иконите за дизајн на материјали (Заедница) .


Ќе ни треба клуч за датотека и идентификација на главниот јазол:


Сега кога имаме доволно предуслови, да започнеме со кодирање. Ми се допаѓаат моите скрипти одвоени од главниот код на проектот, па затоа ќе создадам папка scripts во root и fetch-icons.ts во неа. Нема потреба да се прави во TypeScript, но тоа го прави овој напис некако фенси, па јас ќе одам со него.


Сепак, ова ќе бара уште неколку зависности:

 yarn add -D tsx dotenv axios @figma/rest-api-spec


dotenv е тука за да ги користи променливите .env , tsx — за да ја изврши TS скриптата (фенси, се сеќавате?), @figma/rest-api-spec е за безбедност на типот, а axios е само затоа што сум навикнат на тоа. Потребно е само dotenv (освен ако не сте во ред со додавањето на клучот API на кодот), а јас ќе ја додадам „чистата“ верзија на крајот. Засега, ќе го имаме ова поставување, а еве со што започнуваме:

 import * as dotenv from "dotenv"; dotenv.config(); const PERSONAL_ACCESS_TOKEN = String( process.env.VITE_FIGMA_PERSONAL_ACCESS_TOKEN ); // I've added my Figma token to the .env file, and so should you const FIGMA_API_URL = "https://api.figma.com/v1"; const FILE_KEY = "v50KJO82W9bBJUppE8intT"; // this one is from the URL const NODE_ID = "2402-2207"; // node-id query param, also from the URL


Донеси!

Најпрво, ќе ги добиеме метаподатоците за главната рамка:

 import { GetFileNodesResponse, GetImagesResponse, HasChildrenTrait, } from "@figma/rest-api-spec"; // ... const fetchFrameData = async ( nodeId: string ): Promise<HasChildrenTrait> => { const { data } = await axios.get<GetFileNodesResponse>( `${FIGMA_API_URL}/files/${FILE_KEY}/nodes?ids=${nodeId}`, getConfig() ); return data.nodes[nodeId].document as HasChildrenTrait; };


Ќе ги врати информациите за своите деца. Тоа може да бидат слики (нивниот тип ќе биде INSTANCE ) или други рамки што ќе ги анализираме рекурзивно:

 const getImageNodes = ( frameData: HasChildrenTrait ): Record<string, string> => { const nodes: Record<string, string> = {}; for (const image of frameData.children.filter( (node) => node.type === 'INSTANCE' )) { // normalizeName simply converts 'check-box' to 'CheckBox' const name = normalizeName(image.name); const id = image.id; nodes[id] = name; } for (const frame of frameData.children.filter( (node) => node.type === 'FRAME' )) { Object.assign(nodes, getImageNodes(frame)); } return nodes; };


Конечно, кога ќе имаме куп ID на слики, ќе преземеме URL-адреси на датотеки SVG:

 const fetchImageUrls = async ( nodeIds: string[] ): Promise<GetImagesResponse> => { const { data } = await axios.get<GetImagesResponse>( `${FIGMA_API_URL}/images/${FILE_KEY}?ids=${nodeIds.join(",")}&format=svg`, getConfig() ); return data; };


И ќе завршиме со низа од такви објекти:

 { name: 'CheckBox', url: 'https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/7dd5bc31-4f26-4701-b155-7bf7dea45824' }


Како што веќе погодивте, ќе ги преземеме сите, но тоа не е најинтересниот дел.

Нивната анатомија

Секој пат кога ќе преземеме SVG, добиваме нешто слично:

 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17 6H7C3.69 6 1 8.69 [...] 9 17 9Z" fill="black" /> </svg>


Ќе ги отстраниме ознаките svg бидејќи тие се идентични за секоја икона (претпоставувам дека се со иста големина заради едноставност). Но, она што е поинтересно е што ќе го замениме целото fill=”black” со место за место во буквален шаблон, и како резултат на тоа, ќе имаме таква функција:

 export const IconToggleOn = (color: string) => `<path d="M17 6H7C3.69 [...] 9 17 9Z" fill="${color}"/>`;


Еве како го добиваме:

 const cleanupSvg = (data: string): string => { const svgTags = /<\/?svg.*?>/g; const colorFills = /fill=["'].+?["']/g; return data .replace(svgTags, "") .replace(colorFills, 'fill="${color}"') .replace(/\n/g, ""); };


Очигледно, нема да треба да ги замените боите ако треба да внесете некои логоа, но правењето поинаква логика за нив ќе биде вашата домашна задача.

Ајде да започнеме

Добро, еве го нашиот план:

  1. Отстранете ја добиената датотека ако постои.
  2. Преземете јазли на икони од Figma.
  3. Преземете URL-адреси на иконите за тие јазли.
  4. Преземете SVG.
  5. Создадете нова датотека со нашите функции за икони.


Нема да ја рецитирам целата датотека - можете да ја проверите на GitHub . Ајде само да го погледнеме самото преземање:

 const FILE_BATCH_SIZE = 10; const fileContent: string[] = []; const getPromise = async (item: IconData) => { try { const { data } = await axios.get<string>( item.url, getConfig("image/svg+xml") // returns necessary options and headers ); return { name: item.name, data }; } catch (err) { console.error(err); } }; for (let i = 0; i < iconsData.length; i += FILE_BATCH_SIZE) { const batch = await Promise.allSettled( iconsData.slice(i, i + FILE_BATCH_SIZE).map(getPromise) ); for (const icon of batch) { if (icon?.status === "fulfilled" && icon.value) { const iconName = `Icon${icon.value.name}`; const svgData = cleanupSvg(icon.value.data); fileContent.push( `export const ${iconName} = (color: string) =>\n \`${svgData}\`;\n` ); } } }


Партијата е важна овде бидејќи во спротивно, Figma нема да ви се допадне ако одлучите да преземете неколку илјади датотеки паралелно.


Ако треба да добиете икона по името што го добивате од задниот дел, можете да креирате и речник:

 const iconArray: string[] = []; // ... for (const icon of batch) { if (icon?.status === "fulfilled" && icon.value) { const iconName = `Icon${icon.value.name}`; const svgData = cleanupSvg(icon.value.data); fileContent.push( `export const ${iconName} = (color: string) =>\n \`${svgData}\`;\n` ); iconArray.push(iconName); // <-- we added this } } // ... when all files are processed fileContent.push( `\r\nexport const iconDictionary: Record<string, (color: string) => string> = {\r\n ${iconArray.join( ",\r\n " )}\r\n};\r\n` );


Овој пристап е полош бидејќи целиот сет ќе биде додаден во пакетот дури и ако користите само една икона преку речник, но понекогаш мора да направите нешто непријатно. Исто така, сите \r\n и празни места се тука за да го израдуваат мојот ESLint: можеби ќе треба да ја прилагодите нивната сума.


Еве го последниот чекор; ајде да направиме нова датотека:

 import path from "path"; import { fileURLToPath } from "url"; // ... const PATH_TO_ICONS = "../src/assets/icons"; const currentPath = fileURLToPath(import.meta.url); const currentDirectory = path.dirname(currentPath); const fileName = "index.ts"; const filePath = path.join( path.resolve(currentDirectory, PATH_TO_ICONS), fileName ); // ... fs.writeFileSync(filePath, fileContent.join(""));


Една мала работа

Добра идеја е да ги сортирате иконите по преземањето на URL-адресите, што е едноставно како ова:

 iconsData.sort((a, b) => a.name.localeCompare(b.name));


Подредената листа ќе биде полесна за читање и, што е уште поважно, ќе имаме читлива разлика секогаш кога ќе добиеме нови икони. Во спротивно, нарачката не е загарантирана, а додавањето на една икона може да збрка сè.

Ја рекреираме датотеката, но редоследот на иконите останува недопрен


Сега што?

Потребна ни е компонентата што ги прифаќа низата, бојата и големината на иконата како реквизити и црта нешто слатко:

 import React, { useMemo } from "react"; import { IconSvg } from "../types/IconSvg"; interface IconProps { svg: IconSvg; // type IconSvg = (color: string) => string; color?: string; size?: number | string; } const refine = (x: string | number) => { return typeof x === "number" || !/\D+/.test(x) ? `${x}px` : x; }; const MyIcon: React.FC<IconProps> = ({ svg, color, size, }) => { const iconColor = useMemo(() => color ?? "black", [color]); const refinedSize = useMemo(() => refine(size ?? 24), [size]); const currentIcon = useMemo(() => svg(currentColor), [svg, currentColor]); return ( <svg viewBox="0 0 24 24" // make sure it's the same as in Figma dangerouslySetInnerHTML={{ __html: currentIcon }} style={{ width: refinedSize, minWidth: refinedSize, height: refinedSize, minHeight: refinedSize, }} ></svg> ); }; export default MyIcon;


Верувам дека е прилично јасен, освен за рафинирање на големината: кога го имплементирав за прв пат, Chrome прифати бројки како ширина/висина на CSS својства, но Firefox не, па додадов px . Дури и да не е веќе случај, да го задржиме како што треба според стандардите.


И еве го автоматското комплетирање што го ветив:

Тоа е сè луѓе

Јас вака го готвам. Се надевам дека ви е корисно.


Недостаток на овој пристап е што иконите ќе бидат вметнати и нема да се кешираат поради тоа. Сепак, мислам дека не е голема работа за неколку бајти. Покрај тоа, можете да додадете ротирачки, менувајќи ја бојата при лебдење, што било друго, и ќе имате мал швајцарски армиски нож од тие икони.


Исто така, ќе бара организирана датотека во Figma. Но, еј, ако можете да го напишете целиот овој код, можете исто така да ги убедите вашите дизајнери да го средат.


Можете да го најдете целосниот код, вклучувајќи ги компонентите React и Vue, како и верзијата Node.js со помалку зависности овде: https://github.com/Smileek/fetch-icons

L O A D I N G
. . . comments & more!

About Author

Andrei Sieedugin HackerNoon profile picture
Andrei Sieedugin@smileek
Senior frontend developer with product management experience

ВИСЕТЕ ТАГОВИ

ОВОЈ СТАТИЈА БЕШЕ ПРЕТСТАВЕН ВО...