構建互動無花果小部件
Figma一直鼓勵開發者和設計師之間的協作,並憑藉其豐富的社區插件庫而蓬勃發展。需要3D元素?有插件!需要抽象的SVG?也有插件!
然而,Figma的設計部分一直相對靜態——始終使用不可移動的矩形,通過預定義的用戶交互連接在一起。但如果我說你的設計可以突然栩栩如生——可以動畫化、交互式,甚至是有狀態的,你會怎麼想?那麼,概念與實現之間還有什麼區別呢?
Figma在6月份宣布將推出基於JavaScript的小部件。現在,設計師可以直接在Figma中瀏覽和實現邏輯驅動的組件!
讓我們一起了解一下Widgets API!想知道它是什麼以及如何使用它嗎?這正是我們將在本文中一起探討的內容。
Figma小部件開啟無限可能
想像一下,你和你的合作夥伴日夜合作設計一個大型餐廳應用程序。你們都在同一個Figma畫板上協作;你們共享完全相同的文檔,更改實時發生。
當然,你已經知道協作不僅僅包括設計過程:
- 項目管理,
- 舉辦投票,
- 導入和可視化模擬數據,
- 也許甚至玩一個多人遊戲來放鬆一下長時間的工作。
只需要一個人管理所有內容並向小組的其他成員發送鏈接。但是,這效率不高,對吧?
這就是小部件發揮作用的地方。我們可以想像做到所有這些——是的,所有這些——無需離開Figma。
以下是一些你可能希望在Figma中使用小部件的方式:
- 為Jira和Asana創建任務
- 在GitHub中創建問題
- 顯示動態數據
- 錄製語音備忘錄
- 創建任務列表
-
浪費時間玩井字遊戲 - 追踪活動
- 創建計時器
等等等等。正如你所看到的,已經有大量的小部件可以自由地用於你的文檔中。事實上,你可以直接從“小部件”菜單(Shift I)將小部件添加到你的畫板中。
但我們不是來學習如何使用小部件的,因為這很容易。讓我們做我們最擅長的事情:我們將創建我們自己的Figma小部件!這個小部件將以Chris Coyier的設計報價網站為靈感。我們將獲取API,將其饋送到小部件,然後直接在Figma中顯示隨機的設計報價。
我們需要什麼
我不喜歡當壞消息的傳播者,但是為了開發小部件,你必須在Windows或Mac上。 Linux用戶,對不起,你運氣不好。 (如果你想繼續學習,你仍然可以使用虛擬機。)
我們將下載Figma桌面應用程序。最簡單的入門方法是從應用程序中直接生成小部件模板。
讓我們通過打開小部件菜單(Shift I)、切換到開發選項卡並創建一個新項目來創建一個新的畫板。
之後,Figma會提示你命名新的小部件,並決定它是否更適合設計畫板或FigJam畫板。對於本文的目的,前一個選項就足夠了。
定制並沒有就此結束;Figma還會讓你選擇從預製計數器小部件或啟用iFrame的替代方案開始,該方案還允許你訪問Canvas和Fetch API(以及所有其他瀏覽器API)。我們將選擇簡單的“空”選項,但我們最終會自己修改它以使用Fetch API。
然後,系統會提示你將新的Widget項目保存到系統中的特殊目錄。完成後,啟動你的終端並將其定向到該文件夾。現在還不要運行任何命令——我們稍後會這樣做,並故意出現錯誤,目的是學習更多關於Widgets API的知識。
設計小部件
我們直接從Chris Coyier的設計報價網站獲取設計。所以,讓我們去那裡,通過啟動DevTools來深入研究。
我在這裡使用的兩個關鍵快捷鍵是Ctrl Shift C(或Cmd Shift C)來切換“拾取元素”工具,以及Shift 單擊來將顏色格式更改為HEX代碼。我們這樣做是為了了解Chris網站中使用的顏色、字體、字體粗細和字體大小。所有這些信息對於在Figma中構建一個非常相似的Widget至關重要,這將是我們的下一步!你可以抓取設計好的組件並將其用於你自己的畫布。
我在這裡不會詳細介紹,因為本文的主題是通過編寫代碼來構建小部件。但我必須強調,精心設計小部件的樣式非常重要……CSS-Tricks已經有大量面向設計的Figma教程;你不會後悔將它們添加到你的閱讀列表中。
為我們的Widget創建佈局
設計完成後,是時候拿出我們的編程手指,開始構建我們小部件的齒輪了。
Figma如何將其設計構建塊轉換為類似React的組件非常有趣。例如,具有自動佈局功能的框架元素在代碼中表示為<autolayout></autolayout>
組件。除此之外,我們還將使用另外兩個組件:<text></text>
和<svg></svg>
。
看看我的Figma畫板……我正是要求你關注對象樹。這是我們需要能夠將我們的Widget設計轉換為JSX代碼的關鍵。
如你所見,我們的設計報價小部件需要導入三個組件。考慮到完整的API只包含八個基於圖層的節點,這是一個相當數量的組件。但正如你很快就會看到的,這些模塊足以製作各種佈局。
<code>// code.tsx const { widget } = figma; const { AutoLayout, Text, SVG } = widget;</code>
有了這個,我們就可以像在React中一樣構建我們小部件的骨架了:
<code>function QuotesWidget() { const quote = `...`; const author = `...`; return (<autolayout></autolayout></code><svg></svg><autolayout><text> {quote}</text><text> — {author}</text></autolayout><svg></svg> ); } widget.register(QuotesWidget);
這段代碼至少可以說是非常混亂的。現在,我們無法區分設計圖層。幸運的是,我們可以通過使用name
屬性輕鬆解決這個問題。
<code><autolayout name="{" quote></autolayout></code> <svg name="{" leftquotationmark></svg><autolayout name="{" quotecontent><text name="{" quotetext> {quote}</text><text name="{" quoteauthor> — {author}</text></autolayout><svg name="{" rightquotationmark></svg> ;
當然,我們仍然看不到我們的引號SVG,所以讓我們著手解決這個問題。<svg></svg>
組件接受一個src
屬性,該屬性採用SVG元素的源代碼。對此沒有太多可說的,所以讓我們保持簡單,直接跳回代碼:
<code>const leftQuotationSvgSrc = `</code> // 為簡潔起見,已縮短`; const rightQuotationSvgSrc = ` <svg fill="none" height="103" viewbox="0 0 118 103" width="118" xmlns="<http://www.w3.org/2000/svg>"> // 為簡潔起見,已縮短</svg>`; function QuotesWidget() { return ( <svg name="{" leftquotationmark src="%7BleftQuotationSvgSrc%7D"></svg><svg name="{" rightquotationmark src="%7BrightQuotationSvgSrc%7D"></svg> ); }
我認為我們都可以同意現在一切都清楚多了!當我們命名事物時,它們的目的突然變得對我們代碼的讀者更加明顯。
實時預覽我們的Widget
Figma在構建小部件時提供了良好的開發體驗,包括(但不限於)熱重載。使用此功能,我們可以實時編碼和預覽對小部件的更改。
首先打開小部件菜單(Shift I),切換到開發選項卡,然後單擊或拖動你的新小部件到畫板。找不到你的小部件?別擔心,只需單擊三點菜單並導入你的小部件的manifest.json
文件即可。是的,這就是讓它恢復存在的全部步驟!
等等,你的屏幕底部是否出現錯誤消息?
如果是這樣,讓我們調查一下。單擊“打開控制台”並閱讀它的內容。如果“打開控制台”按鈕消失了,則有一種替代方法可以打開調試控制台。單擊Figma徽標,跳轉到“小部件”類別並顯示開髮菜單。
該錯誤可能是因為我們尚未將TypeScript編譯為JavaScript。我們可以通過運行npm install
和npm run watch
(或yarn
和yarn watch
)在命令行中執行此操作。這次沒有錯誤!
你可能會遇到的另一個障礙是,每次代碼更改時,小部件都無法重新渲染。我們可以使用以下上下文菜單命令輕鬆強制小部件更新:小部件→重新渲染小部件。
樣式設置小部件
就目前而言,我們小部件的外觀仍然與我們的最終目標相差甚遠。
那麼我們如何從代碼中設置Figma組件的樣式呢?也許像在React項目中那樣使用CSS?錯誤。對於Figma小部件,所有樣式都是通過一套完善的屬性來實現的。幸運的是,這些項目的名稱與Figma中的對應項目幾乎完全相同。
我們將首先配置我們的兩個<autolayout></autolayout>
組件。如上圖所示,屬性名稱對它們的目的進行了非常清晰的描述。這使我們能夠直接跳轉到代碼並開始進行一些更改。我不會再次顯示整個代碼,所以請依靠組件名稱來指導你代碼片段的位置。
<code><autolayout direction="{" horizontal horizontal:="" horizontalalignitems="{" center name="{" quote padding="{{" spacing="{54}" vertical:="" verticalalignitems="{" start> <autolayout direction="{" vertical horizontal:="" horizontalalignitems="{" start name="{" quotecontent padding="{{" spacing="{10}" vertical:="" verticalalignitems="{" end></autolayout></autolayout> ;</code>
我們取得了很大的進展!讓我們保存並跳回到Figma以查看我們的Widget的外觀。還記得新更改後Figma如何自動重新加載Widget嗎?
但它還不夠。我們還必須向根組件添加背景顏色:
<code><autolayout fill="{" name="{" quote></autolayout></code>
同樣,查看你的Figma畫板,注意更改如何幾乎立即反映到Widget中。
讓我們繼續本指南並設置<text></text>
組件的樣式。
查看Widgets API文檔後,再次清楚地看到屬性名稱與其在Figma應用程序中的對應項幾乎相同,如上圖所示。我們還將使用上一節中檢查Chris網站的值。
<code><text fill="{'#545454'}" fontfamily="{'Lora'}" fontsize="{36}" fontweight="{'normal'}" name="{'QuoteText'}" width="{700}"> {quote}</text> <text fill="{'#16B6DF'}" fontfamily="{'Raleway'}" fontsize="{26}" fontweight="{'bold'}" name="{'QuoteAuthor'}" textcase="{'upper'}" width="{700}"> — {author}</text></code>
向Widget添加狀態
我們的小部件目前顯示相同的報價,但我們希望隨機從整個報價池中提取。我們必須向我們的Widget添加狀態,所有React開發人員都知道這是一個變量,它的更改會觸發我們組件的重新渲染。
在Figma中,狀態是使用useSyncedState
鉤子創建的;它幾乎就是React的useState
,但它要求程序員指定一個唯一的key
。此要求源於這樣一個事實,即Figma必須同步我們Widget的狀態,這些狀態跨越可能正在查看同一設計畫板的所有客戶端,但通過不同的計算機。
<code>const { useSyncedState } = widget; function QuotesWidget() { const [quote, setQuote] = useSyncedState("quote-text", ""); const [author, setAuthor] = useSyncedState("quote-author", ""); }</code>
目前,這就是我們需要做的全部更改。在下一節中,我們將弄清楚如何從互聯網獲取數據。劇透警告:這並不像看起來那麼簡單。
從網絡獲取數據
回想一下Figma讓我們選擇從啟用iFrame的小部件開始。雖然我們沒有選擇該選項,但我們仍然必須實現其一些功能。讓我解釋一下為什麼我們不能簡單地在小部件代碼中調用fetch()
。
當你使用小部件時,你正在你的計算機上運行由其他人編寫的JavaScript代碼。雖然所有小部件都經過Figma員工的徹底審查,但這仍然是一個巨大的安全漏洞,因為我們都知道即使是一行JavaScript也會造成多大的損害。
因此,Figma不能簡單地eval()
匿名程序員編寫的任何小部件代碼。長話短說,團隊決定最佳解決方案是在嚴密保護的沙箱環境中運行第三方代碼。正如你可能猜到的那樣,瀏覽器API在這種環境中是不可用的。
但不要擔心,Figma對此第二個問題的解決方案是<iframe></iframe>
。我們在文件中編寫的任何HTML代碼(最好稱為ui.html
)都可以訪問所有瀏覽器API。你可能想知道我們如何從Widget觸發此代碼,但我們稍後會研究這個問題。現在,讓我們回到代碼中:
<code>// manifest.json { "ui": "ui.html" }</code>
<code>window.onmessage = async (event) => { if (event.data.pluginMessage.type === 'networkRequest') { // TODO: 从服务器获取数据window.parent.postMessage({ pluginMessage: { // TODO: 返回获取的数据} }, '*') } }</code>
這是Widget到iFrame通信的通用模板。讓我們用它從服務器獲取數據:
<code>window.onmessage = async (event) => { if (event.data.pluginMessage.type === 'networkRequest') { // 获取从0到100的随机数const randomPage = Math.round(Math.random() * 100) // 从Design Quotes API获取随机报价const res = await fetch(`https://quotesondesign.com/wp-json/wp/v2/posts/?orderby=rand&per_page=1&page=${randomPage}&_fields=title,yoast_head_json`) const data = await res.json() // 从响应中提取作者姓名和报价内容const authorName = data[0].title.rendered const quoteContent = data[0].yoast_head_json.og_description window.parent.postMessage({ pluginMessage: { authorName, quoteContent } }, '*') } }</code>
為了保持簡單明了,我們省略了錯誤處理。讓我們回到小部件代碼中,看看我們如何訪問<iframe></iframe>
中定義的函數:
<code>function fetchData() { return new Promise<void> (resolve => { figma.showUI(__html__, {visible: false}) figma.ui.postMessage({type: 'networkRequest'}) figma.ui.onmessage = async ({authorName, quoteContent}) => { setAuthor(authorName) setQuote(quoteContent) resolve() } }) }</void></code>
如你所見,我們首先告訴Figma公開訪問我們隱藏的<iframe></iframe>
並觸發名為“networkRequest”的事件。我們通過檢查event.data.pluginMessage.type === 'networkRequest'
來處理ui.html
文件中的此事件,然後將數據發布回小部件。
但是現在還沒有發生任何事情……我們仍然沒有調用fetchData()
函數。如果我們直接在組件函數中調用它,則控制台中會出現以下錯誤:
<code>在小部件渲染期间无法使用showUI。</code>
Figma告訴我們不要直接在函數體中調用showUI
……那麼我們應該把它放在哪裡呢?答案是一個新的鉤子和一個新的函數: useEffect
和waitForTask
。如果你是一位React開發人員,你可能已經熟悉useEffect
了,但我們將在這裡使用它來在小部件組件掛載時從服務器獲取數據。
<code>const { useEffect, waitForTask } = widget; function QuotesWidget() { useEffect(() => { waitForTask(fetchData()); }); }</code>
但這會導致另一個“錯誤”,我們的Widget將永遠使用新的報價不斷重新渲染。發生這種情況是因為useEffect
根據定義,每當Widget的狀態發生更改時,或者當我們調用fetchData
時,它都會再次觸發。雖然有一種技術可以在React中只調用一次useEffect
,但它不適用於Figma的實現。來自Figma文檔:
由於小部件的運行方式,useEffect應該處理使用相同狀態多次被調用。
幸運的是,我們可以利用一個簡單的解決方法,只在組件第一次掛載時調用一次useEffect
,方法是檢查狀態的值是否仍然為空:
<code>function QuotesWidget() { useEffect(() => { if (!author.length & !quote.length) { waitForTask(fetchData()); } }); }</code>
你可能會遇到一個可怕的“內存訪問越界”錯誤。這在插件和小部件開發中非常常見。只需重新啟動Figma,它就不會再出現了。
你可能已經註意到,有時報價文本包含奇怪的字符。
這些是Unicode字符,我們必須在代碼中正確格式化它們:
<code>window.onmessage = async (event) => { // ... const quoteContent = decodeEntities(data[0].yoast_head_json.og_description); }; //<https:> var decodeEntities = (function () { // this prevents any overhead from creating the object each time var element = document.createElement("div"); function decodeHTMLEntities(str) { if (str && typeof str === "string") { // strip script/html tags str = str.replace(/]*>([\\\\S\\\\s]*?)/gim, ""); str = str.replace(/]|"[^"]*"|'[^']*')*>/gim, ""); element.innerHTML = str; str = element.textContent; element.textContent = ""; } return str; } return decodeHTMLEntities; })();</https:></code>
瞧,我們的Widget每次添加到設計畫板時都會獲取一個全新的設計報價。
向我們的Widget添加屬性菜單
雖然我們的Widget在實例化時會獲取新的報價,但如果我們能夠再次執行此過程而無需刪除它,則會更實用。本節將簡短介紹,因為解決方案非常出色。使用屬性菜單,我們可以通過一次調用usePropertyMenu
鉤子來向我們的Widget添加交互性。
<code>const { usePropertyMenu } = widget; function QuotesWidget() { usePropertyMenu( [ { itemType: "action", propertyName: "generate", tooltip: "Generate", icon: ` <svg fill="none" height="15" viewbox="0 0 22 15" width="22" xmlns="<http://www.w3.org/2000/svg>"></svg></code> `, }, ], () => fetchData() ); }
使用一個簡單的鉤子,我們就可以創建一個按鈕,當選中我們的Widget時,該按鈕會出現在我們的Widget附近。這是我們需要添加的最後一個部分才能完成這個項目。
將我們的Widget發佈到公共
如果沒有人使用它,那麼構建Widget就沒有多大用處。雖然Figma允許組織啟動用於內部使用的私有Widget,但將這些小程序發佈到世界各地更為常見。
Figma有一個細緻的小部件審查流程,可能需要5到10個工作日。雖然我們一起構建的設計報價小部件已經位於小部件庫中,但我仍然會演示它是如何到達那裡的。請不要嘗試再次發布此小部件,因為這只會導致刪除。但是,如果你對其進行了一些重大更改,請繼續與社區分享你自己的小部件!
首先單擊小部件菜單(Shift I),然後切換到開發選項卡以查看我們的Widget。單擊三點菜單並按發布。
Figma會提示你輸入有關你的Widget的一些詳細信息,例如標題、描述和一些標籤。我們還需要一個128×128的圖標圖像和一個1920×960的橫幅圖像。
導入所有這些資源後,我們仍然需要Widget的屏幕截圖。關閉發布模式(別擔心,你不會丟失數據),然後右鍵單擊Widget以顯示一個有趣的上下文菜單。找到複製/粘貼為類別並選擇複製為PNG 。
完成此操作後,讓我們回到發布模式並將Widget的屏幕截圖粘貼進去:
向下滾動並最終發布你的模式。慶祝! ?
Figma會在幾天后與你聯繫,告知你的模式審查狀態。如果被拒絕,你將有機會進行更改並再次提交。
結論
我們剛剛從頭開始構建了一個Figma小部件!這裡有很多內容沒有涵蓋,例如單擊事件、輸入表單等等。你可以在這個GitHub存儲庫中深入了解Widget的完整源代碼。
對於那些渴望將他們的Figma技能提升到更高水平的人,我建議探索Widgets社區,並使用你感興趣的內容作為靈感。繼續構建更多的小部件,繼續磨練你的React技能,在你意識到之前,你將教我如何做到這一切。
更多資源
在製作這個Widget時,我不得不參考大量的文檔。我認為我會分享我發現最有幫助的內容。
構建更多小部件:
- 構建小部件的最佳實踐
- 官方Figma小部件示例,包含代碼
更深入地學習小部件:
- 所有小部件鉤子
- 所有小部件組件
- 小部件在幕後如何運行
小部件與插件
- 小部件與插件
- Figma插件簡介
- 插件在幕後如何運行
以上是構建互動無花果小部件的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

您是否曾經在項目上需要一個倒計時計時器?對於這樣的東西,可以自然訪問插件,但實際上更多

在元素個數不固定的情況下如何通過CSS選擇第一個指定類名的子元素在處理HTML結構時,常常會遇到元素個數不�...

關於Flex佈局中紫色斜線區域的疑問在使用Flex佈局時,你可能會遇到一些令人困惑的現象,比如在開發者工具(d...

格子呢是一塊圖案布,通常與蘇格蘭有關,尤其是他們時尚的蘇格蘭語。在Tartanify.com上,我們收集了5,000多個格子呢
