前言
最近在使用 Fabric.js 开发图形操作项目,元素的布局处理在图形操作的场景中非常常见:居中,放大,移动,旋转,等等等。客户通常在使用的时候会频繁的调整图像的大小或者位置,那么我们就可以提供一些预设好的定位方法,来帮助客户快速的实现定位排版。
本文将详细介绍如何使用Fabric.js实现三种常见的图像缩放模式:填充、适应和拉伸。
填充、适应、拉伸的缩放方案
在深入代码实现之前,让我们先明确这三种缩放模式的具体定义:
1. 填充
填充模式的目标是确保图像完全覆盖目标区域,同时保持原始宽高比。这意味着:
- 图像将按原比例放大或缩小
- 图像可能会超出画布边界(部分内容可能不可见)
- 不会有任何留白区域
- 适合需要背景图像完全覆盖的场景
填充模式的关键是选择较大的缩放比例,确保图像至少在一个维度上完全覆盖目标区域。
我们可以类比 css 背景中的 background-size: cover属性,它会保持图像的宽高比,同时确保图像覆盖整个容器,可能会导致图像的部分区域不可见。
2. 适应
适应模式的目标是确保整个图像都在目标区域内可见,同时保持原始宽高比。这意味着:
- 图像将按原比例放大或缩小
- 图像完全在画布内可见(没有内容会被裁剪)
- 可能会有留白区域
- 适合需要展示完整图像的场景
适应模式的关键是选择较小的缩放比例,确保图像在两个维度上都不超出目标区域。
我们可以类比 css 中的 background-size: contain或object-fit: contain属性,它会保持图像的宽高比,同时确保整个图像都可见,可能会在容器中留下空白区域。
3. 拉伸
拉伸模式的目标是使图像完全填满目标区域,不保持原始宽高比。这意味着:
- 图像在水平和垂直方向上独立缩放
- 图像完全填满画布(没有留白)
- 不保持原始宽高比(可能导致图像变形)
- 适合需要填满特定区域且不关心比例的场景
拉伸模式的关键是分别计算水平和垂直方向的缩放比例,使图像在两个维度上都刚好填满目标区域。
我们可以类比 css 中的 background-size: 100% 100%或object-fit: fill属性,它会拉伸或压缩图像以完全填满容器,不保持原始宽高比。
Fabric.js 缩放元素的方式
Fabric.js中,对象的缩放不是通过直接修改宽度和高度实现的,而是通过缩放属性来实现的,这点要额外注意。
让我们通过一个简单的测试案例来理解Fabric.js中的缩放机制:
// 创建一个100x100的矩形
const rect = new fabric.Rect({
width: 100,
height: 100,
fill: 'red'
});
// 添加到画布
canvas.add(rect);
// 查看原始尺寸
console.log("原始尺寸:", rect.width, rect.height); // 输出: 100 100
console.log("原始缩放:", rect.scaleX, rect.scaleY); // 输出: 1 1
console.log("实际显示尺寸:", rect.width * rect.scaleX, rect.height * rect.scaleY); // 输出: 100 100
// 设置缩放因子为2
rect.set({
scaleX: 2,
scaleY: 2
});
canvas.renderAll();
// 查看缩放后的尺寸
console.log("缩放后原始尺寸:", rect.width, rect.height); // 输出: 100 100 (原始尺寸不变)
console.log("缩放因子:", rect.scaleX, rect.scaleY); // 输出: 2 2
console.log("实际显示尺寸:", rect.width * rect.scaleX, rect.height * rect.scaleY); // 输出: 200 200
从上面的例子可以看出,当我们设置scaleX和scaleY为2时,对象在画布上的实际显示尺寸变为原来的两倍,但原始的width和height属性保持不变。这种机制使得我们可以轻松地实现各种缩放效果,同时保留原始尺寸信息。
对于图像对象,这一机制同样适用:
fabric.Image.fromURL('image.jpg', img => {
// 假设图像原始尺寸为200x150
console.log("图像原始尺寸:", img.width, img.height); // 输出: 200 150
// 设置缩放因子,使图像显示为原来的一半大小
img.set({
scaleX: 0.5,
scaleY: 0.5
});
canvas.add(img);
console.log("实际显示尺寸:", img.width * img.scaleX, img.height * img.scaleY); // 输出: 100 75
});
实现方案
核心思路
实现这三种缩放模式的核心在于如何计算合适的scaleX和scaleY值。我们可以设计一个通用函数,根据不同的模式计算并应用缩放因子:
- 获取画布和对象的尺寸
- 计算宽高比
- 根据不同模式计算缩放因子
- 应用缩放并居中对象
代码实现
首先,我们需要一些辅助函数来获取画布尺寸和居中对象:
/**
* 获取画布尺寸
*/
function getCanvasDimensions() {
if (!canvas.value) return { width: 800, height: 600 }
return {
width: canvas.value.width || 800,
height: canvas.value.height || 600,
}
}
/**
* 将对象居中到画布中央
*/
function centerObject(
obj: fabric.Object,
canvasWidth: number,
canvasHeight: number,
) {
obj.setPositionByOrigin(
new fabric.Point(canvasWidth / 2, canvasHeight / 2),
'center',
'center',
)
if (canvas.value) {
canvas.value.renderAll()
}
}
然后,我们实现一个通用的缩放函数,根据不同的模式计算并应用缩放因子:
/**
* 调整对象大小并居中的通用函数
* @param mode 调整模式:'fill' 填充模式,'fit' 适应模式,'stretch' 拉伸模式
*/
function resizeAndCenterObject(mode: 'fill' | 'fit' | 'stretch') {
const obj = canvas.value?.getActiveObject()
if (!obj || !canvas.value) return
// 获取画布尺寸
const { width: canvasWidth, height: canvasHeight } = getCanvasDimensions()
// 计算对象和画布的宽高比
const objRatio = obj.width! / obj.height!
const canvasRatio = canvasWidth / canvasHeight
let scaleX, scaleY
// 计算各种模式下的缩放比例
if (mode === 'fill') {
// 填充模式:对象需要完全覆盖画布,可能会超出画布边界
// 选择较大的缩放比例,确保覆盖整个画布
if (objRatio < canvasRatio) {
// 对象较窄,以宽度为基准进行缩放
scaleX = canvasWidth / obj.width!
scaleY = scaleX // 保持原比例
} else {
// 对象较宽,以高度为基准进行缩放
scaleY = canvasHeight / obj.height!
scaleX = scaleY // 保持原比例
}
} else if (mode === 'fit') {
// 适应模式:对象完全在画布内,可能有留白
// 选择较小的缩放比例,确保对象完全可见
const fitScaleX = canvasWidth / obj.width!
const fitScaleY = canvasHeight / obj.height!
const scale = Math.min(fitScaleX, fitScaleY)
scaleX = scale
scaleY = scale
} else if (mode === 'stretch') {
// 拉伸模式:宽度和高度都刚好撑满画布,不保持原始比例
scaleX = canvasWidth / obj.width!
scaleY = canvasHeight / obj.height!
}
// 应用缩放
obj.set({
scaleX,
scaleY,
})
// 居中对象
centerObject(obj, canvasWidth, canvasHeight)
// 取消激活后重新激活对象,确保控制点正确显示
canvas.value?.discardActiveObject()
canvas.value?.setActiveObject(obj)
canvas.value?.renderAll()
}
最后,我们为每种模式创建简单的包装函数,以提高代码的可读性:
// 填充模式 - 将选中对象填充至画布大小(可能超出画布边界)
function applyFill() {
resizeAndCenterObject('fill')
}
// 适应模式 - 将选中对象适应画布大小(完全在画布内)
function applyFit() {
resizeAndCenterObject('fit')
}
// 拉伸模式 - 拉伸对象以填满画布(不保持比例)
function applyStretch() {
resizeAndCenterObject('stretch')
}
测试
添加一张图片
填充
适应
拉伸
总结
本文介绍了如何使用Fabric.js实现三种常见的图像缩放模式:填充、适应和拉伸。通过理解Fabric.js的缩放机制,实现了这三种方法。
这种实现方式不仅适用于图像,还可以应用于任何Fabric.js对象,如矩形、文本等。