- 作者:老汪软件技巧
- 发表时间:2024-10-13 07:01
- 浏览量:
前言
今天是国庆节前的倒数第二天,办公室已经能明显感受到一种假期的“松弛感”。身在办公桌前,但心已飘到了我将要攀登的四姑娘山大峰。是的,我已无心工作。一眼扫过书签栏的掘金图标,点开一看,不由得一声唏嘘感慨! 原来自己距离上次更新已过 9 个月有余,说好的矢志不渝,活到老,更到老呢?hh,掐指一算,今日正是更文吉日。遂,更。言归正传,今天聊聊这段时间主要在跟进的一个技术方向- 在线图片编辑器 。
图片编辑器的用途我就不做过多介绍了,相信大家或多或少都接触过。在国外知名的有 Adobe Spark 、Fotor 等设计平台,专注于专业的在线化图像制作工具领域,还有的就是一些不追求专业化,但力争做到简洁易用的在线设计平台:比如国内的稿定设计、美图设计室等,我下边讲的就是这类在线编辑器,比较容易嵌入到大家各自的业务中快速落地。
各家采取的技术方案上,虽然都有各自业务的考量,但是也无外乎这三种技术方案:
纯 canvas 方案
canvas 方案其实是顺理成章的选择。毕竟 canvas 本身就是在 web 领域绘制图像用的,最终导出为图片文件也很便捷。但是由于原生的 api 实在过于基础,难以应对图片编辑器这种复杂的开发场景。所以大家一般会选择一些上层框架来做。有点类似于js原生开发网页与更上层的现代前端框架的区别。整体是对象化、链式化的封装思路,最终都可以序列化为 json,方便保存在数据库里。比如老牌的fabric.js、以及后出的 konva.js 方案。两种我都深入调研过,都可使用,基本都不会有大坑,fabric因为出现较早,碰到问题时,网上的解决方案会多一些,整体会更加成熟,而 konva.js 的使用上会更加的简洁一些,TS友好,文档也更加友好。如果让我二选一,我会选 konva.js,因为 fabric.js 的文档非常不稳定,且文档的讲解顺序非常不利于新人上手。
纯 DOM 方案
这种方案很好解释,就像正常开发页面一样,利用react、vue等框架渲染出 DOM,浏览器根据 DOM 绘制图片内容。下图的美图设计室就是这样做的:
但采用这种方案有个关键的问题要解决,如何将 DOM 转为图片呢?这里提供两种思路:
将 DOM 样式转换为 canvas 样式。最具代表的是 html2canvas 这个库,但这种方案基本只适用样式比较常规且简单的业务场景,比如 H5 海报生成等。针对样式复杂的编辑器场景,基本不可能做到完美的转换,会有很多样式转换不完全或者转换错误的 bug,所以这个库到现在还是 beta 版本。调用第三方无头浏览器进行截图。 主要是浏览器本身并没有提供截图的api,但第三方的 Puppeteer 是有这个能力的。所以可以启动一个 Node 服务器,提供一个http接口,调用 Puppeteer 渲染出图片内容,进行页面截图。
具体代码可以参考下方:
export class ImageEdtorService {
@Inject()
ctx: Context;
@Inject()
puppeteerService: PuppeteerService;
@Inject()
httpService: HttpService;
@Config('egg')
eggConfig: MidwayConfig['egg'];
async schema2Image(schema: ImageSchema) {
const page = await this.puppeteerService.newPage();
// const deviceScaleFactor = 2;
page.setViewport({
width: schema.imageSize?.width,
height: schema.imageSize?.height,
// deviceScaleFactor,
});
await page.evaluateOnNewDocument(schema => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.schema = schema;
}, schema);
await page.goto(
`http://127.0.0.1:9001/editor/previewPage`,
{
waitUntil: 'networkidle0',
}
);
const screenshotBuffer = await page.screenshot({
// path: path.join(__dirname, `./screenshot_${Date.now()}.jpg`),
type: 'jpeg',
encoding: 'binary',
quality: 100,
omitBackground: true,
// optimizeForSpeed: true,
fullPage: false,
});
await this.puppeteerService.closePage(page);
return screenshotBuffer;
}
}
两种方案的组合优点:纯 canvas 方案:优点:编辑态使用DOM、图片渲染使用Canvas
稿定设计就是采用的这种方案,好处可以集两种方案的优点为一身。巧妙的地方在于,因为编辑态交互是DOM 来承担的,所以 canvas 可以直接导出没有编辑态的图片。但需要注意一点,需要同步好 DOM 编辑态与 canvas 渲染内容的位置、缩放等状态,毕竟是两套渲染体系。如下图所示那样:
最后
我实现的编辑器长这个样子:
生图采用了服务端DOM转图片的生图方案。整个开发过程,我觉得最难的不是代码实现,而是在内部 linux 机器上部署 puputter 无头浏览器的过程,耗费了很多精力才搞定。如果你有碰到类似问题,可以私信或者评论,看我的踩坑经验能不能帮到大家。
整体的开发过程,最有趣的,其实是研究的 CSS 知识越来越深了。因为要实现各种图片内容的效果嘛,设计师会提很多合理但有挑战的需求,比如 CSS 的蒙版属性,我就是第一次接触。
好了,今天的文章就到这里,小伙伴们可以按需选择适合自己业务场景的方案。如果你有更好的方案,欢迎评论区积极贡献分享哦~