昨天聊到小五郎叔叔脖子上的傷痕,今天要來聊日本的國民漫畫航海王,大家有看過航海王的話想必對我們的主角魯夫不陌生,魯夫的絕招就是橡膠果實帶給他的能力,伸縮自如的─橡膠火箭砲!不管拉多長,都維持一樣的解析度,就好像我們今天這篇的主角 - SVG!
先前的例子中,物體都是我們自己定義點、從無到有建構,有一個要輸入特別多點的來建構的不知道大家是否還記得 ─ Bodies.fromVertices 這個方法?
不記得的話我們看一下 API 文件 的內容
Matter.Bodies.fromVertices(x, y, vertexSets, [options], [flagInternal=false], [removeCollinear=0.01], [minimumArea=10], [removeDuplicatePoints=0.01])
在建構的部分,我們會透過 vertexSets 來傳入點座標。
為什麼會提到這個呢?
因為我們今天的模組:Svg 會協助我們拿到這個 vertexSets,我們透過這個模組,能把外部圖形直接匯入變成物體。
先和大家簡單介紹一下什麼是 Svg ,Svg 全名為 Scalable Vector Graphic,中文叫做可縮放向量圖形,特性是用向量來建構圖形,來達成圖形可依倍率任意縮放並維持不變的解析度,泛用於 icon 或其他需要可縮放圖形的場合,更多的資料大家可以上網 Google,Svg 在網頁其實也是一門學問。
大略了解 Svg 後我們回帶模組本身,Svg 模組只有一個方法,但是如何透過 Svg 來建構我們的物體其實並不簡單。
唯一一個方法是
Matter.Svg.pathToVertices(path, [sampleLength=15])
輸入一個 path,path是一個 SVGPathElement,sampleLength 預設值為 15,這個值影響的是建構 Svg 圖形的邊的長度,在製作弧形的時候邊用得越多會越貼近弧的效果,也就是長度越短越貼近。
最後這個方法會回傳建構出傳入 path 的點陣列,再將這個點陣列傳入 fromVertices 中對應點集合的參數位置就能創造出對應的物體。
第一個問題是要怎麼匯入 path 這個參數?
筆者會推薦找到一個由 Svg 中 path 類別構成的圖形後把他裝在一個 Svg 容器中,同時為 path 加上 id,外圍容器相對不重要,只要有基本的宣告就好,配合上display : none,來讓它不要顯示,畢竟我們要的只是 targetPath 這個節點。
範例中我們拿 twitter 的 icon 來做示範,廣為人知且有弧型這個特徵。
<svg id="svgTarget" xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path id="targetPath" d="M302.973,57.388c-4.87,2.16-9.877,3.983-14.993,5.463c6.057-6.85,10.675-14.91,13.494-23.73 c0.632-1.977-0.023-4.141-1.648-5.434c-1.623-1.294-3.878-1.449-5.665-0.39c-10.865,6.444-22.587,11.075-34.878,13.783 c-12.381-12.098-29.197-18.983-46.581-18.983c-36.695,0-66.549,29.853-66.549,66.547c0,2.89,0.183,5.764,0.545,8.598 C101.163,99.244,58.83,76.863,29.76,41.204c-1.036-1.271-2.632-1.956-4.266-1.825c-1.635,0.128-3.104,1.05-3.93,2.467 c-5.896,10.117-9.013,21.688-9.013,33.461c0,16.035,5.725,31.249,15.838,43.137c-3.075-1.065-6.059-2.396-8.907-3.977 c-1.529-0.851-3.395-0.838-4.914,0.033c-1.52,0.871-2.473,2.473-2.513,4.224c-0.007,0.295-0.007,0.59-0.007,0.889 c0,23.935,12.882,45.484,32.577,57.229c-1.692-0.169-3.383-0.414-5.063-0.735c-1.732-0.331-3.513,0.276-4.681,1.597 c-1.17,1.32-1.557,3.16-1.018,4.84c7.29,22.76,26.059,39.501,48.749,44.605c-18.819,11.787-40.34,17.961-62.932,17.961 c-4.714,0-9.455-0.277-14.095-0.826c-2.305-0.274-4.509,1.087-5.294,3.279c-0.785,2.193,0.047,4.638,2.008,5.895 c29.023,18.609,62.582,28.445,97.047,28.445c67.754,0,110.139-31.95,133.764-58.753c29.46-33.421,46.356-77.658,46.356-121.367 c0-1.826-0.028-3.67-0.084-5.508c11.623-8.757,21.63-19.355,29.773-31.536c1.237-1.85,1.103-4.295-0.33-5.998 C307.394,57.037,305.009,56.486,302.973,57.388z"/>
</svg>
再來就是呼叫 Svg 模組的方法,使用 document.getElementById 配合 Id 來拿到 path 節點,並依照精細度需求來給予 sampleLength的值。
因為照顧到弧型顯示, sampleLength 值可以給小一點,可以比對一下範例中左右的圖,左邊的圖 sampleLength 值為 5,右邊的圖 sampleLength 值為 100,會明顯看到弧型的顯示左邊效果好得多。
var vertexSetsA = Svg.pathToVertices(document.getElementById("targetPath"), sampleLength=5);
var vertexsA = Bodies.fromVertices(200, 300, vertexSetsA);
這時候如果什麼都不做,只呼叫這個方法來嘗試建構物體,你會發現,瀏覽器報錯了。
難道我們哪裡做錯了嗎?
不,我們只是少做了一些事情,仔細看 API 文件可以發現他有提醒我們, pathseg.js 對這個方法是必須的。
我們用 cdn 來引入這個 library,主要協助我們對 path 上各個節點做段落(segment)式管理,詳細內容我們不走,我們就先引入它。
<script src="https://cdn.jsdelivr.net/npm/pathseg@1.2.0/pathseg.js"></script>
位置要放在引入 matter.js 的地方之上,因為 matter.js 會相依這個 lib。
引入後我們的 twitter icon 就能顯現出來了!
騙人!這不是肯德基,喔不是,這跟 twitter icon 一點也不像!
沒錯,因為我們還缺了最後一個碎片!
在 Bodies 模組中 fromVertices 的方法裡,下面有提到,當點集合是要表示為一個凹圖形,做圖形分解的時候如果有引入 poly-decomp 的話會讓做圖形分解的時候能處理得更為細緻。
<script src="https://cdn.jsdelivr.net/npm/poly-decomp@0.2.1/build/decomp.min.js"></script>
我們一樣在引入 matter.js 的地方之上引入這個 lib。
這時候重看畫面:
哇!我們的鳥兒出來了!
透過以上這些步驟,我們就能在 Svg 模組的協助下,以 path element 來組成物體,儘管精度上可能有些差距,但仍是建構物體時一個相當不錯的選項。