比 PSD.js 更强的下一代 PSD 解析器,支持 WebAssembly
1.什么是 @webtoon/ps
@webtoon/ps 是 Typescript 中轻量级 Adobe Photoshop .psd/.psb 文件解析器,对 Web 浏览器和 NodeJS 环境提供支持,且做到零依赖。
Fast zero-dependency PSD parser for the web and Node.js
@webtoon/ps 使用标准 ES2015+ 功能,因为零依赖性使其比其他 PSD 解析器(ag-psd:200 KiB,PSD.js:443 KiB)更小,压缩后只有大约 100 KiB,同时使用 WebAssembly 来加速图像数据的解码。
目前 Chrome>=57、Firefox>=52、Safari>=11、Edge>=79、Node>=12 版本都已经支持。@webtoon/ps 在 Github 通过 MIT 协议开源,有超过 1.1k 的 star,是一个值得关注的前端开源项目。
2.如何使用 @webtoon/ps
浏览器环境使用
@webtoon/psd 作为纯 ECMAScript 模块提供,但是必须与 Webpack 或 Rollup 等捆绑器捆绑。 @webtoon/psd 将 PSD 文件读取为 ArrayBuffer,开发者可以使用 FileReader 或 File 加载 PSD 文件:
import Psd from "@webtoon/psd";
const inputEl: HTMLInputElement = document.querySelector("input[type='file']");
inputEl.addEventListener("change", async () => {
const file = inputEl.files[0];
const result = await file.arrayBuffer();
const psdFile = Psd.parse(result);
// 获取包含 PSD 或 PSB 文件的 ArrayBuffer
// 并返回一个新的 Psd 对象
const canvasElement = document.createElement("canvas");
const context = canvasElement.getContext("2d");
const compositeBuffer = await psdFile.composite();
const imageData = new ImageData(
compositeBuffer,
psdFile.width,
psdFile.height
);
canvasElement.width = psdFile.width;
canvasElement.height = psdFile.height;
context.putImageData(imageData, 0, 0);
document.body.append(canvasElement);
});
为了提高性能,非常建议在 Web Worker 而不是主线程中解析 PSD 文件。
NodeJS 环境使用
@webtoon/psd 不支持 Node.js 缓冲区,开发者必须显式提供底层 ArrayBuffer。
import * as fs from "fs";
import Psd from "@webtoon/psd";
const psdData = fs.readFileSync("./my-file.psd");
// 在 Buffer 中传递 ArrayBuffer 实例
const psdFile = Psd.parse(psdData.buffer);
由于 @webtoon/psd 是作为 ES 模块提供的,因此开发者必须使用动态 import() 或捆绑器在 CommonJS 代码中运行:
const Psd = await import("@webtoon/psd");
3.图层遍历
Psd 对象是包含图层和组(即图层组)对象的树,其提供了一个 Children 属性,是顶级图层和组的数组。
每个 Group 对象都提供一个 Children 属性,是直接属于当前图层组的图层和组的数组。Psd、Group 和 Layer 对象提供了一个类型字段,可用于区分每种类型:
import Psd, {Node} from "@webtoon/psd";
// 递归遍历图层和组
function traverseNode(node: Node) {
if (node.type === "Layer") {
// 图层
} else if (node.type === "Group") {
// 组
} else if (node.type === "Psd") {
// PSD 类型
} else {
throw new Error("Invalid node type");
}
node.children?.forEach((child) => traverseNode(child));
}
traverseNode(psdFile);
Psd 对象还提供了 layers 属性,是图像中所有 Layer(包括嵌套的)的数组。
for (const layer of psdFile.layers) {
doSomething(layer);
}
4.解码图像数据
开发者可以使用 Psd.prototype.composite() 和 Layer.prototype.composite() 解码整个图像或单个图层的像素信息。
请注意,要使 Psd.prototype.composite() 正常工作,PSD/PSB 文件需要以 “最大化兼容性” 模式保存。 否则,将不返回任何数据。
// 解码整个图像
pixelData = await psd.composite();
// 提取图层的像素数据,并应用所有图层和图层组效果
// (currently, only the opacity is supported)
layerPixelData = await layer.composite();
// 提取图层的像素数据,仅应用图层自身的效果
layerPixelData = await layer.composite(true, false);
// 提取图层的像素数据,无任何效果
layerPixelData = await layer.composite(false);