水印新功能上线以及 bug 修复
由 Fabric 实现的水印编辑器上线有一段时间了(详见「使用 Fabric.js 实现实时水印预览」)。上一版的代码以及调研工作都是 Benny 同学完成滴,作为 WebP Cloud Services 的前端开发,现在才开始学习 Fabric,真是惭愧惭愧 🤪。
在此期间也发现了一些 bug,同时又了一些新的想法。因此,我们把水印功能进行了一些改进,让水印功能看上去更加优雅且可用性更好。
新功能
这次更新总共有三个新功能,分别是:
- 增加可编辑和宽度调整功能
- 预设水印位置
- 不同尺寸图片的水印效果图预览
页面效果大致如下图,是不是看上去大致上没啥变化呢?(雾
画布中文字增加编辑和宽度调整功能
之前的版本中,只能通过画布左侧的 config 来设置文字的内容,不能直接在画布中编辑。这次的改动增加了可编辑的功能,双击画布中文字后,可以直接进行编辑。 需要注意的是,由于 Fabric 的 TextBox 支持换行,所以当文字编辑完成后,需要再次点击画布空白处,才能保存编辑的内容。
增加了可编辑功能之后,这个时候发现了 Fabric 的第一个坑:文本框的宽度它居然变长了之后不能变短了~,且这个时候设置 width 属性也不生效。所以我去问了 ChatGPT:
好吧,那就改为文字的宽度也应该支持自定义吧!所以我们增加了左右两个控制点,让用户可以自己调节宽度。
代码层面上,也做了一些重构和优化:
canvas 实例绑定的事件从
object:scaling
和object:moving
改为mouse:up
,由此来减少事件触发的频率,事件的触发需要直到用户操作完成(松开鼠标)。并判断宽高和偏移量是否有变化,如果有变化再进行后续步骤。this.canvas.on("mouse:up", this.handleObject.bind(this));
将 canvas 实例从
Fabric.Text
改为Fabric.Textbox
, 这样可以支持文字的换行;并设置editable: true
,这样就可以在画布中编辑文字。const textObject = new Fabric.Textbox(text, { editable: true, // 初始化文字框 }); textObject.setControlsVisibility({ // 隐藏控制点 });
textbox 实例绑定了
editing:exited
事件,当内容编辑完成后触发。textObject.on("editing:exited", this.afterEditText.bind(this)); this.canvas.add(textObject);
减少 textbox 实例赋值的次数,将多个属性的赋值合并为一次。
const updateProperties: Record<string, string | number> = {}; keys.forEach((key) => { updateProperties[key] = value; }); textObject.set(updateProperties); this.canvas.renderAll();
添加预设水印位置
根据大多数人使用水印的习惯(包括我们在内),我们增加了一些预设的水印位置,方便用户快速选择。这些预设位置包括左上、右上、左下、右下、中间等,可以根据自己的需求选择。点击按钮能直接将水印移动到对应的位置,且会请求预览图片,方便查看效果。
添加不同尺寸图片的水印效果图预览
给出水印画布的比例是 1:1,但是实际使用中,可能会有不同尺寸的图片,所以我们增加了不同尺寸图片的水印效果图预览。在预览图片中,可以看到不同尺寸的图片的水印效果。
bug 修复
水印位置超出画布边界没有恢复到画布中
水印超出边界的情况目前考虑到两种场景
拖动文本框至边界外:这种场景下,需要将文本框恢复到画布中,根据实际拖动的位置和角度,调整文本框至边界为 0 的偏移量的位置。
const boundingRect = textObject.getBoundingRect(); const canvasWidth = this.canvas.width; const canvasHeight = this.canvas.height; const { left = 0, top = 0, width = 0, height = 0 } = boundingRect; // 检查并调整左边界 if (left < 0) { textObject.left = 0; } // 检查并调整上边界 if (top < 0) { textObject.top = 0; } // 检查并调整右边界 if (left + width > canvasWidth) { textObject.left = canvasWidth - width; } // 检查并调整下边界 if (top + height > canvasHeight) { textObject.top = canvasHeight - height; }
缩放文本框宽/高度超出画布:这种场景下,需要根据画布的宽高,调整文本框的宽高,使其最大宽高在画布范围内。
本以为一切顺利进行时,却发现了 Fabric 的第二个坑:Fabric 的 Textbox 有个特性,缩放文本框之后,文本框实例(也就是上面代码中的 textObject)的宽高不会更新,只有 scaleX/scaleY 的值会发生变化。
那么要怎么取得当前的宽高的信息呢?还好 Fabric 提供了 getBoundingRect 给我们使用,这个方法会返回当前文本框的宽高和偏移量信息。
由此可见,当控制文本框的宽高范围时,不能只简单的设置 widht/height 的值,而是需要更改 scaleX/scaleY(并且这俩值还得一样,不然字体就要变瘦/变胖咯)。同时还需要控制 left/top 的值,让文本框不要飘出画布外了。
否则就会出现奇怪的现象:当缩放超出画布边界后,文本框的宽度甚至比缩放结束时更大了!原因自然是因为其宽度的计算是 width * scaleX,而不是 width 本身。
const maxWidth = this.canvas.width, maxHeight = this.canvas.height; const { width, height } = boundingRect; // 检查并设置最大宽度 if (width >= maxWidth) { if (!textObject.width) return; const max_scale = maxWidth / textObject.width; textObject.scaleX = max_scale; textObject.scaleY = max_scale; textObject.left = 0; } // 检查并设置最大高度 if (height >= maxHeight) { if (!textObject.height) return; const max_scale = maxHeight / textObject.height; textObject.scaleX = max_scale; textObject.scaleY = max_scale; textObject.top = 0; }
优化
输入框增加 debounce,减少请求次数
之前线上的版本没有做输入框的防抖处理,导致每次输入都会请求一次新的预览图片,这样会导致请求次数过多,影响性能。所以我们在输入框中增加了防抖处理(现在是 500ms 发一次请求),减少请求次数。
import { Subject, debounceTime } from 'rxjs';
private searchSubject: Subject<string> = new Subject();
ngOnInit(): void {
this.searchSubject.pipe(debounceTime(500)).subscribe(() => {
// 请求预览图片
});
}
requestPreview() {
this.searchSubject.next();
}
画布和预览图片背景图片修改
之前的纯色图片由于带有颜色,文字颜色和背景色可能会与图片融合,导致水印效果不明显。所以我们将背景图片修改为通用的透明背景素材。
列表页面布局优化
现有的水印列表页面能看到所有的相关内容,展示的排列显得杂乱无章。所以我们重新设计和排版了列表页面,使其更加清晰。 因为列表页会展示每一个水印的预览图片,所以选择将图片放大,能一目了然的看到不同水印的效果;同时,为了更好的展示效果,我们将细节信息隐藏,在鼠标悬停时再展示。
水印字体展示优化
之前的版本中,我们没有选择提前下载所有的字体文件,而是在每次选择不同字体后,下载对应的字体文件,其实这样做的目的也是为了减少带宽消耗,毕竟所有的字体文件加起来体积很大。但是这样做的问题是,每次首次选择不同字体后,都会有一段时间的等待(因为没有缓存,并且我们设置了最长字体加载时间 10s),除了体验不好以外,还可能超时,最后字体可能并不会更新。 所以我们选择了不在画布中展示字体,而是在请求服务端生成好的预览图片中展示。
WebP Cloud Services 团队是一个来自上海和赫尔辛堡的三人小团队,由于我们不融资,且没有盈利压力 ,所以我们会坚持做我们认为正确的事情,力求在我们的资源和能力允许范围内尽量把事情做到最好, 同时也会在不影响对外提供的服务的情况下整更多的活,并在我们产品上实践各种新奇的东西。
如果你觉得我们的这个服务有意思或者对我们服务感兴趣,欢迎登录 WebP Cloud Dashboard 来体验,如果你好奇它还有哪些神奇的功能,可以来看看我们的文档 WebP Cloud Services Docs,希望大家玩的开心~
Discuss on Hacker News