偶尔间看到一款国外的应用:Screely,是一款截图美化工具。通常用截图工具截的图标注一下就发出去了,截图有大有小,发到文章或者社交网络并不整齐也不美观(可以参见我之前发我的文章😂,妥妥反面案例)。Screely 就是解决这个问题的,他可以给截图加上背景、圆角、Mac窗口,也可以加上标注和贴纸。这样分享截屏既美观也不会突兀。
如果你经常上推的话就会发现,大多数博主发的图片都带有背景、圆角。整体美观度会提升不少。

然后我就在想,我能不能也做一个类似的工具😎。这时候我想到了 fabritor,是时候让她继续发挥余热了。
fabritor 作为一个图片编辑器,已经比较完善了,做一个截图美化工具应该问题不大,随便折腾一下,有那味了。

是的,我做了 Photor
fork 一下 fabritor 的代码,停停写写,也差不多有一个月的时间,完成了名为 Photor 的截图美化工具,等不及的可以先去官网试用一下:www.photor.fun/。


基本包含了截图美化工具的所有功能,包括上传、背景、边距、圆角、窗口、水印、定制截图尺寸、各类标注、裁剪等等。
简单推广过一波,目前 UV 稳定在 100 左右,说明还是有用户的。😂
简单技术分析
总体还是基于 fabritor 实现,像画布尺寸、背景渐变、圆角、裁剪、各类标注图形的基础方法,代码都是直接来自于 fabritor,下面会挑几个不一样的点说一下。
定制尺寸
很多网站上传图片会有建议尺寸甚至强制尺寸,这时候临时去截取和缩放都会比较麻烦,索性加一个定制尺寸的功能。定制尺寸其实直接修改的就是画布的尺寸,画布固定后,缩放图片即可。但图片上有时候还会附带 Mac 窗口,有时候缩放会比较奇怪,还需要优化。

标注绘制
fabritor 内,矩形等形状都是直接添加到画布然后编辑的,而对于截图标注的话,还是鼠标拖动绘制的好。之前掘金文章下面也有同学问过,fabricjs 提供了丰富的鼠标事件,事件内附带鼠标位置,要实现也不是很难。
这里以矩形为例:
typescript
// mouse down 事件,在鼠标按下位置创建矩形。
drawShapeRef.current = {
isDraw: true,
lastPoint: opt.absolutePointer,
tmp: createRect({
left: opt.absolutePointer.x,
top: opt.absolutePointer.y,
width: 1, height: 1, rx: 10, ry: 10,
fill: 'rgba(0,0,0,0)',
stroke: '#D31638',
strokeWidth: 6
}),
type: 'rect'
}
// mouse move 事件,修改矩形属性
tmp.set({
left: Math.min(opt.absolutePointer.x, lastPoint.x),
top: Math.min(opt.absolutePointer.y, lastPoint.y),
width: Math.abs(opt.absolutePointer.x - lastPoint.x),
height: Math.abs(opt.absolutePointer.y - lastPoint.y)
});
// mouse up 完成绘制
canvas.setActiveObject(tmp);
Mac 窗口
如果分享的是代码图片的话,带一个 Mac 窗口可以加分很多。我现在采用的方案是使用 圆形和矩形的组合(Group)。fabricjs 里使用组合的话可以比较方便的做一些复杂的图形。之前 issue 里有同学问过如何实现一个复杂的图形,我也是建议这样做。
typescript
fabric.FMacWindow = fabric.util.createClass(fabric.Group, {
type: 'f-mac-window',
initialize (options) {
this.callSuper('initialize', [
this.background,
this.closeCircle,
this.minCircle,
this.maxCircle
], rest, false);
}
})
不过组合起来简单,但有个问题是 Mac 窗口覆盖到图片上,只能有两个圆角,而 fabricjs 默认是四个圆角的,也没有给出配置实现,只能自定义实现了。
所以需要自定义一下 rect 的 _render 。

同样覆盖在图片上时,图片上方的两个圆角也要去掉。
定制线条 control
fabricjs 内,默认的线条 control 也像矩形一样,并不像常见的线条 control,通常线条只需要两个端点修改长度和位置就好。

fabricjs 使用 new fabric.Control 定制。
typescript
const linePositionHandler = (x, y) => {
return (dim, finalMatrix, fabricObject) => {
const points = fabricObject.calcLinePoints();
const localPoint = new fabric.Point(points[x], points[y]);
// move 不会改变 x1 y1 x2 y2
// fabricObject.toLocalPoint(new fabric.Point(points[x], points[y]), 'center', 'center');
const point = fabric.util.transformPoint(localPoint, fabric.util.multiplyTransformMatrices(
fabricObject.canvas.viewportTransform,
fabricObject.calcTransformMatrix()
));
return point;
}
}
const changeLineEnd = (eventData, transform, x, y) => {
const { target } = transform;
target.set({ x2: x, y2: y });
return true;
}
const changeLineStart = (eventData, transform, x, y) => {
const { target } = transform;
target.set({ x1: x, y1: y });
return true;
}
const objectControls = fabric.Object.prototype.controls;
if (fabric.Line) {
const lineControls: any = fabric.Line.prototype.controls = {};
lineControls.copy = objectControls.copy;
lineControls.del = objectControls.del;
lineControls.l1 = new fabric.Control({
positionHandler: linePositionHandler('x1', 'y1'),
actionHandler: changeLineStart,
cursorStyleHandler: () => 'crosshair',
actionName: 'line-change',
render: objectControls.br.render
});
lineControls.l2 = new fabric.Control({
positionHandler: linePositionHandler('x2', 'y2'),
actionHandler: changeLineEnd,
cursorStyleHandler: () => 'crosshair',
actionName: 'line-change',
render: objectControls.br.render
});
}
图片文字模糊
使用 fabritor 的同学不知道有没有发现,如果图片上有文字,就能感觉出添加到画布的图片是糊的。
我这里找到一个 issue:github.com/fabricjs/fa... 里面讨论了图片和 clipPath 一起使用时,为了性能和缓存,是要牺牲图片清晰度的。
禁用缓存是可以解决这个问题,但会牺牲一定性能。
typescript
fabric.Object.prototype.needsItsOwnCache = () => false
浏览器插件
此外,为了更方便的使用截图,我还开了浏览器插件,已经上架到 Chrome 和 Edge 插件商店。即可以截取这个页面,也可以选定部分区域,截图后直接进入 Photor 内编辑。

可以通过官方底部的链接下载使用。

写在后面
开发 Photor 这个应用,一方面是为了拿 fabritor 做点事情,另一方面也是尝试一下独立开发一个完整的产品。如果你也喜欢这种截图方式,不妨日常使用一下。(本文截图都是使用 Photor 制作