Танд Figma-д олон тооны дүрс, React/Vue/бусад ямар нэгэн төсөл байгаа гэж хэлээрэй. Гайхах зүйлгүй, та эдгээр дүрсийг ашиглах хэрэгтэй болно. Бид тэдгээрийг хэрхэн импортлох вэ?
Хэрэв та хангалттай зөрүүд байвал тэдгээрийг нэг нэгээр нь татаж аваад хөрөнгө болгон нэмж болно. Гэхдээ энэ бол гунигтай арга юм. Таалагдахгүй.
Гуниглахаа больж оронд нь гайхалтай байцгаая. Энд бидэнд хэрэгтэй зүйл байна:
*цаашаа мэдээлэл өгөхгүй*
Бидэнд Figma токен хэрэгтэй болно. Үүнийг хэрхэн хийх талаар тайлбарлах шаардлагагүй гэж би бодож байна, тиймээс энд тусламжийн хэсэг байна: https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens .
Өгөгдмөл тохиргоонууд нь хангалттай сайн: бидэнд файлын агуулгад зөвхөн унших хандалт хэрэгтэй.
Би жишээ болгон Материалын дизайны дүрсүүдийг (Нийгэмлэг) ашиглах болно.
Бидэнд файлын түлхүүр болон үндсэн зангилааны ID хэрэгтэй болно:
Одоо бидэнд хангалттай урьдчилсан нөхцөл байгаа тул кодчилол хийж эхэлцгээе. Би төслийн үндсэн кодоос тусгаарлагдсан скриптүүддээ дуртай, тиймээс би root дотор scripts
хавтас, 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-тай болсон үед бид SVG файлын URL-уудыг дуудах болно:
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, ""); };
Мэдээжийн хэрэг, хэрэв та зарим лого импортлох шаардлагатай бол өнгө солих шаардлагагүй, гэхдээ тэдгээрт өөр логик хийх нь таны гэрийн даалгавар байх болно.
За, бидний төлөвлөгөө энд байна:
Би файлыг бүхэлд нь уншихгүй - та үүнийг 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` ); } } }
Энд багцлах нь чухал, учир нь өөрөөр хэлбэл, хэрэв та хэдэн мянган файлыг зэрэгцүүлэн татахаар шийдсэн бол Фигма танд таалагдахгүй.
Хэрэв та арын хэсгээс хүлээн авсан нэрээр дүрс авах шаардлагатай бол толь бичиг үүсгэж болно:
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` );
Та толь бичгээр дамжуулан зөвхөн нэг дүрс ашигласан ч гэсэн багцад бүхэл бүтэн багц нэмэгдэх тул энэ арга нь илүү муу юм, гэхдээ заримдаа та ямар нэгэн таагүй зүйл хийх хэрэгтэй болдог. Мөн миний ESLint-ийг баярлуулахын тулд бүх \r\n
болон зайнууд энд байна: та тэдгээрийн хэмжээг тохируулах хэрэгтэй байж магадгүй.
Энд сүүлчийн алхам ирлээ; шинэ файл хийцгээе:
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