1. 程式人生 > >告別龐大 PSD,輕鬆測量尺寸

告別龐大 PSD,輕鬆測量尺寸

起因

作為前端工程師,日常開發離不開 psd 檔案。

但是日常開發的一個小彈窗頁面,它的 psd 居然需要 30+Mb,所以經常得定期清理 psd...

對於我一個 PS 小菜雞來說,用 PSD 無非只是需要用來度量元素大小(元素間距),檢視屬性等簡單的功能。

思考,對比

相對比於 sketch,sketch 具有 sketch-measure,設計師匯出成靜態資源給前端即可。

對於 PSD 來說,市面上已經有如 pxcook / lanhuapp,體驗也很不錯,但是需要下載 U 同學提供的 (龐大的) psd 才能進行標註體驗。

而且有時候還是需要 U 同學給(龐大的) PSD 檔案,我們才能在 pxcook / lanhuapp 中自動標註。

於是鑑於以上,考慮做一個開源專案,類似於 sketch-measure, 定位為 psd-measure。

效果展示

命令列

我們也可以使用命令列來匯出頁面標註

bash

npm i measure-export-cli -g
# 開啟服務,線上預覽 `path/to/psdDir` 下的 psd
measure-export start path/to/psdDir
# 構建 `path/to/psdDir` 下的 psd 至 `dist` 檔案目錄
measure-export build path/to/psdDir

Chrome 外掛

提供 Chrome 外掛,當我們點選 psd 連結時候跳出 Measure UI,而不是下載 PSD,當然我們也可以點選右上方的下載進行下載。

安裝

  1. 下載擴充套件,點選下載
  2. 開啟 Chrome 擴充套件頁面: chrome://extensions/
  3. 拖拽下載的包至頁面中進行安裝
  4. 出現該圖標表示安裝完成

設計與實現

流程如下:

PSD 檔案格式介紹

  • File Header(定長) 主要包括這個 psd 檔案整體的資料,如版本,尺寸大小,圖片通道數,使用的顏色類別(rgb、cmyk...)
  • Color Mode Data Section(變長) 主要是部分顏色型別圖片需要用到
  • Image Resources(變長) 放置一些外部的圖片資源
  • Layer and Mask(變長) 放置圖層和蒙層的各種資訊,大小位置,字型,描邊等等
  • Image Data(變長) 放置影象畫素資料

PSD.js

使用 psd.js 便是解析上述檔案結構,得到可讀的資料結構。其中 psd.js 使用 getter 得到懶解析資料,即如下程式碼:

const obj = Object.defineProperty({}, 'someParsedVal', {
  get: function () {
    if (!this._someParsedVal) {
      const afterMs = Date.now() + 3000
      while (true) {
        if (Date.now() >= afterMs) {
          this._someParsedVal = 'ok'
          break
        }
      }
    }
    return this._someParsedVal
  }
})

obj.someParsedVal // 3s 後出來
obj.someParsedVal // 很快

在 mobx3 中也有類似的設計(LazyInitializer)

psd-html

將 PSD 解析為 HAST,進而轉換為 HTML

HAST (HTML 抽象語法樹)

如下 html:

<a href="http://alpha.com" class="bravo" download></a>

對應 HAST 為

{
  "type": "element",
  "tagName": "a",
  "properties": {
    "href": "http://alpha.com",
    "id": "bravo",
    "className": ["bravo"],
    "download": true
  },
  "children": []
}

前後端同構

前後端同構的意思:同時執行在客戶端和服務端,具體便是同時執行在瀏覽器環境和 nodejs 環境

實現前後端同構的一些常用方式,藉助構建工具 browserify / rollup / webpack 來分別打包不同環境的 js

模擬環境
  • 在 browser 環境,則將預設的 built-in modules 打包進去,以及一些 global 變數(如 process.env / __dirname)也會進行 mock
利用 變數替換 + treeshake 區分不同環境的程式碼
  • 如 webpack 配置 DefinePlugin

    {
      plugins: [
        new webpack.DefinePlugin({
          'process.env.RUN_ENV': JSON.stringify('browser')
        })
      ]
    }
  • 在程式碼中對不同環境打包進行區分

    module.exports =
      process.env.RUN_ENV === 'browser'
        ? {
            psdToHtml,
            psdToHtmlFromBuffer,
            psdToHtmlFromURL,
            psdToHAST,
            psdToHASTFromBuffer
          }
        : {
            psdToHtml,
            psdToHtmlFromPath,
            psdToHtmlFromBuffer,
            psdToHAST,
            psdToHASTFromBuffer,
            psdToHASTFromPath
          }
  • 最終打包出來的 js 則會剔除掉 psdToHASTFromPath 相關程式碼
package.json 配置

如下:

{
  "main": "dist/psd-html.cjs.js",
  "browser": "dist/psd-html.browser.cjs.js",
  "cdn": "dist/psd-html.browser.umd.min.js",
  "unpkg": "dist/psd-html.browser.umd.min.js"
}
  • main: nodejs 環境載入的 js
  • browser: browser 環境載入的 js
  • cdn: 部分 cdn 服務載入的 js
  • unpkg: unpkg cdn 服務載入的 js (主要使用 UMD 規範打包)

html-measure 互動

佈局定位

將 psd 匯出成整個圖片,利用每一個圖層的定位和大小來自動標註。

其他

2 個 div,相對與同一個父級的絕對定位,如何判斷他們是否相交?

..........

正面直接判斷是很費力的,要考慮各種情況,這時候需要逆向思維,考慮不相交的情況。這時候就簡單了

不相交只要滿足下面四種情況之一就可以

function isIntersect(node1, node2) {
  const rect1 = node1.getBoundingClientRect()
  const rect2 = node2.getBoundingClientRect()
  return !(
    rect1.right < rect2.left ||
    rect1.left > rect2.right ||
    rect1.bottom < rect2.top ||
    rect1.top > rect2.bottom
  )
}

measure-export(-cli)

輸入 psd / html 匯出 meas-ui 靜態資源,流程如圖(區分 prod 和 dev 環境)

.svg)

Todo

  • [ ] 提供 chrome 外掛:當瀏覽器開啟 psd 時候,渲染測量尺寸 UI

相關專案

參考資料