[MarchingSquaresJS](marchingsquares - npm)
Marching Squares 算法的 Javascript
实现
开始
因为需要一个在WEB端进行PSD解析的需求,开始在网上寻找各种插件及功能实现.最终决定使用FabricJS来完成基本编辑器功能
在此特别感谢愚坤秦少卫 的个人主页 - 文章 - 掘金大佬的文章。以及大佬整理的中文教程快图设计资料库。再次感谢!
过程
基本的编辑器完成之后,就开始最重要的PSD解析功能了。在选择PSD解析器方面选择了@webtoon/psd 过程基本顺利,直到测试时发现在遇到蒙版智能嵌套及裁剪时遇到了问题。
问题及解决方案
第一个遇到的问题是在处理图层数据时识别图层蒙版,以及怎么处理剪切蒙版。webtoon/psd 除了基本的Layer、Group(组)以外并没有过多的类型,需要自己手动判断处理。在一次无意中点开了官方github 的 Issues。发现了果然也有与我一样有同样困惑的人

果然,下面就有大佬给出了解决办法。

剪切蒙版的识别问题解决了,同样,如何处理又是一个问题。 在搜索查询了一个多小时之后,还是无果。也不是知道是不是我查资料的方式不对。最终没办法,只好从最基本的开始,先观察PS中的图层结构,与webtoon/psd解析出来的层图数据进行一一比对。看看能有什么发现。
经过比对发现,PS中的蒙版基本都是用路径然后把图片剪切出一个想要达到的图形并嵌套进去的。这不是与Fabricjs 中的 clipPath 方法差不多么。
只要把蒙版图层的路径获取到,然后用 clipPath 在需要剪切的图片上剪切出来不就可以了。
第一版选择 potrace - npm 来实现图片转为SVG,然后通过获取SVG中的d属性值得到路径值。
js
const potrace = require('potrace');
potrace.trace(base64, (err, svg) => {
if (err) {
reject(err);
return;
}
console.log(svg);
const regex = /d="([^"]+)"/; // 匹配 d="..." 中的内容
const matchPath = svg.match(regex);
console.log(matchPath);
resolve(matchPath[1]);
}
结果发现,经过多次测试。呈现结果并不理想。当蒙版图层是纯白色的时候,因为在处理图层数据时,把这些图层都转为base64格式的图片用Fabric渲染到画布中。结果导致potrace 在处理这些白色图层图片时,获取到的SVG路径为空。且potrace 的资料确实不多。又陷入了瓶颈。
在不断的查找文档与寻找替代方案中,又有了一个新的思路。既然,蒙版图层数据渲染到画布之后大都是纯色图片形式。那为什么不直接检测图片边缘然后获取边缘路径。
说实话,当时脑子处于一种钻牛角尖的状态。根本没反应过来其实 Potrace 也是基于多边形的位图轮廓矢量化算法。满脑子想的都是如何把图片转为svg 然后获取svg 路径数据。
最终替代方案换为了marchingsquares
js
import { isoContours } from 'marchingsquares';
return new Promise((resolve) => {
const img = new Image();
img.src = base64;
img.crossOrigin = 'Anonymous';
img.onload = function oi() {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 将 ImageData 转换为二维数组
const { width } = imageData;
const { height } = imageData;
const data = new Array(height);
for (let y = 0; y < height; y++) {
data[y] = new Array(width);
for (let x = 0; x < width; x++) {
const index = (y * width + x) * 4;
const alpha = imageData.data[index + 3];
data[y][x] = alpha > 0 ? 0 : 1; // 1 表示有像素,0 表示无像素
}
}
// 使用 Marching Squares 算法生成轮廓
const contours = isoContours(data, 0.5, { noFrame: false });
// 将轮廓转换为 SVG 路径
let path = '';
contours.forEach((contour) => {
contour.forEach((point, index) => {
if (index === 0) {
path += `M${point[0]} ${point[1]}`;
} else {
path += `L${point[0]} ${point[1]}`;
}
});
path += 'Z';
});
resolve(path);
};
});
结束
作为一个前端切图仔,平时都是在网上搜索别人的文章和分享,当习惯了白嫖党。第一次尝试着记录一下自己遇到的问题及解决思路,本着就算我说地球是方的也有人信的观点。想着总会有人也许也遇到了我一样或相似的坑。记录一下发出去,有没有人看是一说,起码本人也算是对开源社区做了一点微小的回馈。(舔着个批脸强行给自己脸上贴个金)
第一次记录,写的可能不太详细,东一榔头西一棒槌的。望各位看官海涵!下班!撤呼~
迎接五一假期!!!