参考资料
简介
Fabric.js 是一个功能强大的 Canvas 库,它在原生 Canvas 之上提供了交互式对象模型、多种易用的 API 和 SVG 解析器等,通过简洁的 API 就可以在画布上进行丰富的操作,并且支持多种的事件方法。
注:本文中使用的 Fabric.js 版本为 v5。
常用的对象、属性和方法
常用对象
名称 | 描述 |
---|---|
ActiveSelection | 选区 |
Group | 分组 |
Image | 图像 |
Line | 线段 |
Rect | 矩形 |
Text | 文本(不可编辑,不换行) |
IText | 可编辑文本(可编辑,不换行) |
Textbox | 文本框(可编辑,自动换行) |
常用属性
属性 | 描述 |
---|---|
type | 对象类型 |
data | 用于添加自定义的数据 |
originX | 对象转换的水平原点(left / center / right) |
originY | 对象转换的垂直原点(top / center / bottom) |
left | 水平坐标 |
top | 垂直坐标 |
width | 宽度 |
height | 高度 |
angle | 旋转角度 |
scaleX | 水平方向缩放倍数 |
scaleY | 垂直方向缩放倍数 |
stroke | 线段颜色 |
strokeWidth | 线段宽度 |
fill | 填充颜色 |
fontFamily | 文本字体名称 |
fontSize | 文本字体大小 |
opacity | 对象的不透明度 |
borderColor | 选区边框颜色 |
borderDashArray | 选区边框虚线样式 |
borderOpacityWhenMoving | 选区拖拽时的边框透明度 |
cornerColor | 选区“操作点”填充颜色 |
cornerSize | 选区“操作点”大小 |
cornerStrokeColor | 选区“操作点”描边颜色 |
cornerStyle | 选区“操作点”样式(circle / rect) |
transparentCorners | 选区“操作点”中心是否透明(只有描边) |
hasControls | 是否有“操作点” |
lockRotation | 是否禁止旋转对象 |
lockMovementX | 是否禁止水平移动对象 |
lockMovementY | 是否禁止垂直移动对象 |
lockScaleX | 是否禁止水平缩放对象 |
lockScaleY | 是否禁止垂直缩放对象 |
selectable | 是否可以被选中 |
hoverCursor | 光标在对象上方时的样式(子对象优先) |
moveCursor | 光标在可移动对象上方时的样式(子对象优先) |
常用方法
画布(Canvas)
方法 | 描述 |
---|---|
add(...object) | 添加对象到画布 |
insertAt(object, index, nonSplicing) | 添加对象到画布指定层级 |
moveTo(object, index) | 更改对象所在的层级 |
remove(...object) | 移除画布上的对象 |
discardActiveObject() | 取消选中对象 |
getActiveObject() | 获取选中的对象 |
setActiveObject() | 更改选中的对象 |
getPointer() | 传递事件对象,获取相对于画布的坐标 |
fire() | 触发画布事件 |
forEachObject() | 遍历画布上的对象 |
getZoom() | 获取画布缩放比例 |
setZoom() | 设置画布缩放比例 |
on() | 监听画布事件 |
requestRenderAll() | 刷新画布 |
set() | 设置画布属性 |
对象(Object)
方法 | 描述 |
---|---|
bringToFront() | 改变对象层级,置顶 |
bringForward() | 改变对象层级,上移 |
sendBackwards() | 改变对象层级,下移 |
sendToBack() | 改变对象层级,置底 |
intersectsWithObject() | 检测两个对象是否相交(重叠) |
isContainedWithinObject() | 检测对象是在另外一个对象内 |
on() | 监听对象事件 |
set() | 设置对象属性 |
toDataURL() | 转换为 Base64 |
创建对象
画布
let canvas = new fabric.Canvas({
// 画布宽度
width: 800,
// 画布高度
height: 480,
// 背景颜色
backgroundColor: '#FFFFFF',
// 激活对象时不将对象置顶
preserveObjectStacking: true,
// 选区框样式
selectionBorderColor: '#4CAF50',
selectionColor: '#4CAF5040',
selectionFullyContained: true,
selectionLineWidth: 1,
// 关闭按比例缩放
uniformScaling: false,
// 其他配置选项
...
});
矩形
// 创建对象
let el = new fabric.Rect({
left: 100, // 相对于画布左侧的距离
top: 100, // 相对于画布顶部的距离
width: 200, // 对象宽度 100px
height: 100, // 对象高度 100px
fill: '#FFFFFF', // 填充颜色
stroke: '#1E88E5', // 边框颜色
strokeWidth: 2, // 边框粗细
});
// 添加对象
canvas.add(el);
线段
// 创建对象
let el = new fabric.Line([
100, 100, // 起始点坐标
200, 100, // 结束点坐标
], {
stroke: '#1E88E5', // 线段颜色
strokeWidth: 2, // 线段粗细
});
// 添加对象
canvas.add(el);
// 创建对象
let el = new fabric.Line([], {
stroke: '#1E88E5',
strokeWidth: 2,
});
// 设置位置和长度
el.set({
left: 100,
top: 100,
width: 100,
// 注:因为是线段,高度为 0
height: 0,
});
// 添加对象
canvas.add(el);
图片
let img = new Image();
// 监听图片加载,加载完成后再添加对象
img.onload = function() {
// 创建对象
let el = new fabric.Image(img, {
top: 100,
left: 100,
width: 200,
height: 150,
});
// 添加对象
canvas.add(el);
};
// 设置 crossorigin 属性,开启 CORS 校验
// 若不设置,无法将画布或画布上的对象导出为图片。
// 值设置为 anonymous,表示对此元素的 CORS 请求将不设置凭据标志。
img.setAttribute('crossorigin', 'anonymous');
// 设置图片地址
img.setAttribute('src', 'http://fabricjs.com/assets/69.svg');
文本框
// 创建对象
let el = new fabric.Textbox('文本内容', {
top: 100,
left: 100,
// 设置宽度后,文本内容超出宽度后会自动换行。
// 注意:文本只会在空格处自动换行。
// 高度为自适应,不需要设置。
width: 200,
});
// 添加对象
canvas.add(el);
场景和实现方法
画布内容撤销、恢复
主要通过 Fabric.js 画布对象的 toObject()
和 loadFromJSON()
方法实现。
为了提高性能,默认情况下 toObject()
只会导出对象上常用的属性,其他属性(例如 data
、selectable
)不会被导出,需要手动在其 propertiesToInclude
参数中指定(例如 canvas.toObject(['data', 'selectable'])
)。
画布内容导出为图片
将画布(显示区域)或对象导出为图片:
toDataURL(options)
参数:options(可选)
属性名 | 数据类型 | 默认值 | 简介 |
---|---|---|---|
format | String | png | 导出图片的格式,可选值:jpeg、png |
quality | Number | 1 | 图片质量(0 ~ 1),仅 jpeg 格式可用 |
multiplier | Number | 1 | 缩放比例 |
left | Number | 裁剪左偏移量(1.2.14+) | |
top | Number | 裁剪上偏移量(1.2.14+) | |
width | Number | 裁剪宽度(1.2.14+) | |
height | Number | 裁剪高度(1.2.14+) | |
enableRetinaScaling | Boolean | 为克隆图像启用 Retina 缩放(2.0.0+) |
用法示例:
let dataURL = canvas.toDataURL({
format: 'jpeg',
quality: 0.8,
});
let dataURL = rect.toDataURL({
format: 'png',
});
注意:
若导出的对象中包含使用 URL(非 DataURL)加载的图片对象,需要使用 crossOrigin
属性解决资源跨域问题,否则会出现以下错误提示:
The canvas has been tainted by cross-origin data.
其中一种解决方式是先使用 new Image()
设置 crossorigin
属性,加载图片,再创建 fabric.Image
对象(参考“创建对象 - 图片”)。
画布自适应父 DOM 元素宽高
方案一:监听浏览器窗口大小变化事件
// 父元素(DOM)
let el = vm.$el;
// 画布对象
let canvas = vm.canvasInstance;
// 防抖定时器
let debounce = null;
// 事件处理函数
const handler = function () {
clearTimeout(debounce);
debounce = setTimeout(() => {
if (canvas) {
// 设置宽高
canvas.setWidth(el.clientWidth);
canvas.setHeight(el.clientHeight);
// 刷新画布(正常情况下设置宽高后会自动刷新)
// canvas.requestRenderAll();
} else {
console.error('处理失败,画布对象不存在!');
}
}, 500);
};
// 添加事件监听
window.addEventListener('resize', handler);
// 移除事件监听
window.removeEventListener('resize', handler);
clearTimeout(debounce);
方案二:使用 ResizeObserver 监听 DOM 元素大小更改
参考资料:ResizeObserver - Web API 接口参考 | MDN
方案优点:在窗口大小不变化的情况下也能实现自适应
// 父元素(DOM)
let el = vm.$el;
// 画布对象
let canvas = vm.canvasInstance;
// 防抖定时器
let debounce = null;
/**
* @desc 观察器回调函数
* @type {ResizeObserverCallback}
*/
const handler = function (entries) {
clearTimeout(debounce);
debounce = setTimeout(() => {
const entry = entries[0];
if (canvas && entry) {
const { width, height } = entry.contentRect;
// 设置宽高
canvas.setWidth(width);
canvas.setHeight(height);
// 刷新画布(正常情况下设置宽高后会自动刷新)
// canvas.requestRenderAll();
} else {
console.error('处理失败,Canvas 或 ResizeObserverEntry 不存在!');
}
}, 500);
};
/**
* @desc 观察器配置选项
* @type {ResizeObserverOptions}
*/
const options = {
// 设置观察器以哪种盒子模型来观察变化
// 可选值:content-box(默认)、border-box、device-pixel-content-box
box: 'border-box',
};
// 创建观察器,并传递回调函数
const observer = new ResizeObserver(handler);
// 传递配置选项
observer.observe(el, options);
// 停止观察器
observer.disconnect();
clearTimeout(debounce);
解决画布缩放后,对象显示模糊
把对象的 objectCaching
属性设置为 false
,关闭缓存即可。
例如:
const rect = new fabric.Rect({
left: 100,
top: 100,
width: 200,
height: 200,
fill: '#FFFFFF',
stroke: '#66CCFF',
strokeWidth: 2,
objectCaching: false,
});
canvas.add(rect);
注意:不建议大量使用,以免影响性能。
实现对象溢出隐藏(裁剪)效果
主要用到了 clipPath
功能,设置对象的裁剪路径。
// 创建用于作为“容器”的对象
const container = new fabric.Rect({
left: 0,
top: 0,
width: 800,
height: 480,
fill: '#FFFFFF',
stroke: '#000000',
strokeWidth: 2,
// 将该属性设置为 true 后,裁剪的区域会动态更新
absolutePositioned: true,
});
// 创建子对象
const item = new fabric.Rect({
left: 100,
top: 100,
width: 200,
height: 100,
fill: '#FFFFFF',
stroke: '#66CCFF',
strokeWidth: 2,
// 将 container 对象设为该对象的 clipPath
clipPath: container,
});
关于 absolutePositioned
属性:
仅当对象作为 clipPath
使用时才有意义。如果为 true
,clipPath
的位置将会相对于画布,且不受对象变换影响。(JSDoc: Class: Object#absolutePositioned)
注意:
后续通过 .set()
更新对象的 clipPath
属性时,建议同时将其 dirty
属性设置为 true
,以便清除缓存,防止裁剪区域没有更新。(JSDoc: Class: Object#dirty)
item.set({ clipPath: null, dirty: true });
鼠标滚轮缩放画布
主要用到了画布的鼠标事件(mouse:wheel
)以及调用画布的 zoomToPoint()
方法改变视图缩放。
// 监听事件:鼠标滚轮
canvas.on('mouse:wheel', function (ev) {
const evt = ev.e;
const cursorX = evt.offsetX;
const cursorY = evt.offsetY;
// 若按下 Ctrl 键,则为缩放
if (evt.ctrlKey) {
evt.preventDefault();
} else {
return;
}
// 若按下 Alt 键,则为重置缩放
if (evt.altKey) {
canvas.zoomToPoint({
x: cursorX,
y: cursorY,
}, 1);
return;
}
// 正数:向下滚动
// 负数:向上滚动
const delta = evt.deltaY;
// 画布当前缩放值
let zoom = canvas.getZoom();
let min = 0.1;
let max = 10;
zoom += zoom * (delta > 0 ? -0.2 : 0.2);
// 限制缩放范围
(zoom < min) && (zoom = min);
(zoom > max) && (zoom = max);
// 设置画布缩放比例
// 参数1:将画布的缩放点设置为鼠标当前位置
// 参数2:传入缩放值
canvas.zoomToPoint({
x: cursorX,
y: cursorY,
}, zoom);
});
鼠标拖拽移动画布
主要用到了画布的鼠标事件(mouse:down
、mouse:move
和 mouse:up
)以及调用画布的 setViewportTransform()
方法改变视图偏移量。
注意:在拖拽前,若鼠标选中了对象,需要将对象的 lockMovementX
和 lockMovementY
属性设置为 true
,锁定对象的移动,否则拖拽后对象的位置会变化。
// 监听事件:鼠标按下
canvas.on('mouse:down', function ({ e: evt, target }) {
// 仅在按下了 Ctrl 键时为拖拽画布
if (!evt.ctrlKey) {
return;
}
if (target) {
if (target.lockMovementX || target.lockMovementY) {
// 记录原始锁定状态
target.isLock = true;
} else {
// 记录原始锁定状态
target.isLock = false;
// 锁定对象的移动
target.set({
lockMovementX: true,
lockMovementY: true,
});
}
}
canvas.set({
// 关闭选区框
selection: false,
// 自定义属性,标记拖拽状态
isDragCanvas: true,
// 自定义属性,记录起始位置
startPosX: evt.clientX,
startPosY: evt.clientY,
startVpt: [...canvas.viewportTransform],
});
});
// 监听事件:鼠标移动
canvas.on('mouse:move', function (ev) {
// 检测是否为拖拽画布模式
if (canvas.isDragCanvas) {
const evt = ev.e;
const vptOld = canvas.startVpt;
const vptNew = [...vptOld];
// 计算新的偏移量
vptNew[4] += (evt.clientX - canvas.startPosX);
vptNew[5] += (evt.clientY - canvas.startPosY);
// 更新视图偏移量
canvas.setViewportTransform(vptNew);
}
});
// 监听事件:鼠标松开
canvas.on('mouse:up', function ({ target }) {
// 解锁对象的移动(如果可以)
if (target && !target.isLock) {
target.set({
lockMovementX: false,
lockMovementY: false,
});
}
// 更新画布属性
canvas.set({
// 开启选区框
selection: true,
// 自定义属性,标记拖拽状态
isDragCanvas: false,
});
});
鼠标事件中获取分组(Group)内的目标对象
默认情况下,对于分组,ev.target
只能获取到整个分组,并不能具体到其中的对象。
在创建分组时,将 subTargetCheck
属性设置为 true
,即可通过 ev.subTargets
获取触发事件时的子对象。
对于支持该功能的事件(mouse:down
、mouse:up
、mouse:move
等),该属性为 fabric.Object
数组;
对于不支持该功能的事件,该属性为 undefined
。
例如:
const group = new fabric.Group([obj1, obj2], {
subTargetCheck: true,
...
});
canvas.add(group);
canvas.on('mouse:down', function (ev) {
console.log(ev.subTargets);
});
文本框强制自动换行
Fabeic.js 的文本框对象(Textbox)默认支持文本自动换行,但只能在空格处换行,对于类似中文句子这种不带空格的字符串并不会换行,且文本框的宽度会自动被撑开。
在 Fabric.js 2.6.0 版本中,新增了一个 splitByGrapheme
属性,将其设置为 true
即可启用在任意字符之间自动换行(参考:JSDoc: Global),例如:
const textbox = new fabric.Textbox('文本框', {
left: 100,
top: 100,
width: 200,
splitByGrapheme: true,
});
canvas.add(textbox);
选中并聚焦对象(将对象置于视图的中心)
// 获取对象的中心点坐标
// 坐标值相对于画布左上角起始点
// 坐标值不受缩放比例影响
const {
x: elCenterX,
y: elCenterY,
} = el.getCenterPoint();
// 获取画布当前的缩放比例
const zoom = canvas.getZoom();
// 计算新的视图坐标
// 中心点坐标值需要与画布缩放比例相乘,与视图统一
// 画布坐标的起始点在左上角,坐标值需要减去画布宽高的一半
const point = {
x: elCenterX * zoom - canvas.width / 2,
y: elCenterY * zoom - canvas.height / 2,
};
// 移动视图
canvas.absolutePan(point);
// 选中对象
canvas.setActiveObject(el);
选中多个对象,创建选区
主要用到了 Fabric.js 的 ActiveSelection
对象,手动创建选区:
// 画布对象
const canvas = this.canvasInstance;
// 选区中包含的对象
const items = [a, b, c, ...];
// 新建一个选区对象
const sel = new fabric.ActiveSelection(items, {
// 选区所属的画布
// 必须包含该参数,否则会出现奇怪的问题(不会报错)
canvas,
// 其他配置选项,例如:
// borderColor,
// borderDashArray,
// opacity,
// hasControls,
...
});
// 使选区生效
canvas.setActiveObject(sel);
隐私政策和规则
点击此处查看详情
转载说明
欢迎任何网络媒体和网站转载本站的内容,转载时请注明作者和博客文章链接。
其他说明
本站部分内容可能已过时,仅供参考,请留意内容发布日期。
\#(深思)
一直想学是下前端(nodejs)
大佬带带!@(太开心)