1.fabric
[ˈfæbrɪk']
是一个功能强大的运行在HTML5 canvas
的JavaScript
,fabric
为canvas
提供了一个交互式对象模型,通过简洁的api
就可以在画布上进行丰富的操作。
2.可以使用包管理工具直接安装:npm install fabric
3.fabric
兼容pc
端和移动端,不支持运行在小程序环境下
1.首先创建一个canvas
元素,作为容器
<canvas id="mapCanvas" width="350" height="200">canvas>
2.实例化一个canvas
上下文对象,接管原生容器,根据使用api
的不同,初始化后的画布可以分为 可交互 和 不可交互 两种
import { fabric } from "fabric";
//可交互画布,用于需要编辑图片等场景
const canvasCtx = new fabric.Canvas("mapCanvas", {
enableRetinaScaling: true,
perPixelTargetFind: true, // 对象基于像素检测
skipTargetFind: true,
//框选
selection: true, //是否支持鼠标框选
selectable: true, //是否支持鼠标框选
selectionColor: "rgba(255,255,255,0.3)", // 鼠标框选背景色
selectionLineWidth: 1, // 画布中鼠标框选边框
preserveObjectStacking: true, //取消被选中的元素会处于顶层的默认行为
backgroundColor: '#fff', //画布背景颜色
});
//不可交互画布, 用于画海报等场景,鼠标事件都无效
const canvasCtx = new fabric.StaticCanvas("mapCanvas",{})
3.创建canvas
元素时可直接设置width
和height
,当一开始不确定宽高时可不设置,之后使用setWidth
,setHeight
动态设置
canvasCtx.setWidth(400)
canvasCtx.setHeight(400)
4.相应的可以使用getWidth
和getHeight
获取canvas
的宽高
canvasCtx.getWidth()
canvasCtx.getHeight()
1.fabric
已经内置了许多基础图像,调用相应api
,可直接应用,免去了原生繁琐的过程。
(1)矩形 [ 圆角矩形 ]
(2)圆
(3)椭圆
(4)直线
(5)曲线
(6)折线
(7)虚线
(8)多边形
(9)三角形
2.矩形绘制 fabric.Rect
(1)使用fabric.Rect
绘制矩形,主要配置项为左上角的(left,top)
和width
,height
let forbiddenArea = new fabric.Rect({
//主要配置属性
left: 0, //距离画布左侧的距离,单位是像素
top: 0, //距离画布上边的距离
width: 100,
height: 100,
//样式配置属性
fill: "#fef0f08a", //填充的颜色
stroke: "#fab6b6", //边界的颜色
//通过配置rx,ry,可以让矩形具有圆角
rx:20,
ry:20,
});
2.绘制圆 fabric.Circle
(1)使用fabric.Circle
绘制圆,主要配置项为中心点(left,top)
和半径radius
let circle = new fabric.Circle({
//主要配置属性
left: 10, //距离左边的距离
top: 10, //距离上边的距离
radius: 2, //圆的半径
//样式配置属性
fill: "#eee", //填充的颜色
stroke: "#fab6b6", //边界的颜色
});
3.绘制椭圆 fabric.Ellipse
(1)使用fabric.Ellipse
绘制圆,主要配置项为中心点(left,top)
和长短半轴rx
,ry
let circle = new fabric.Ellipse({
//主要配置属性
left: 10, //距离左边的距离
top: 10, //距离上边的距离
rx: 10,
ry: 10,
//样式配置属性
fill: "#eee", //填充的颜色
});
4.绘制直线 fabric.Line
(1)使用fabric.Line
绘制直线,主要配置项为两个点x1,y1,x2,y2
let line = new fabric.Line([x1,y1,x2,y2],{
stroke: "#c45656",
strokeWidth: 3,
});
5.绘制线段 fabric.Polyline
(1)使用fabric.Polyline
绘制线段,主要配置项为多个点x1,y1,x2,y2
const polyline = new fabric.Polyline([
{x: 30, y: 30},
{x: 150, y: 140},
{x: 240, y: 150},
{x: 100, y: 30}
], {
fill: 'transparent', // 如果画折线,需要填充透明
stroke: '#6639a6', // 线段颜色
strokeWidth: 5 // 线段粗细
})
6.绘制不规则图形 fabric.Path
(1)使用fabric.Path
绘制不规则图形,主要配置项为一个字符串,其中M
代表移动到某个点,L
是中途的点,z
表示闭合
const path = new fabric.Path('M 0 0 L 200 100 L 170 200 z');
path.set({ left: 120, top: 120,fill:'red' });
7.绘制曲线
(1)fabric
提供了自由画笔的功能让我们可以随意绘制线条
(2)使用isDrawingMode
来开启自由绘制模式
//1.在初始化canvas实例时,直接开启自由画笔
const canvasCtx = new fabric.Canvas("mapCanvas", {
isDrawingMode:true,
});
//2.设置`isDrawingMode`动态开启
canvasCtx.isDrawingMode =true;
//设置画笔颜色
canvasCtx.freeDrawingBrush.color = "#f56c6c";
//设置画笔粗细
canvasCtx.freeDrawingBrush.width = 3;
(3)关闭自由绘制
//设置isDrawingMode为false
canvasCtx.isDrawingMode =false;
//关闭的时机可以根据需求设置,这里介绍鼠标弹起时关闭
canvasCtx.on("mouse:up", function (opt) {
this.isDrawingMode = false;
});
(4)自由绘制的曲线,也会生成实例对象
8.绘制虚线
(1)绘制虚线要使用strokeDashArray
属性,strokeDashArray[a,b]
含义为 每隔a
个像素空b
个像素,直线和线段都可以使用这个属性来绘制成虚线。
const line = new fabric.Line([x1,y1,x2,y2],{
stroke: "#c45656",
strokeWidth: 3,
strokeDashArray: [20, 10],
});
const polyline = new fabric.Polyline(
[
{ x: 30, y: 30 },
{ x: 150, y: 140 },
{ x: 240, y: 150 },
{ x: 100, y: 30 },
],
{
fill: "transparent", // 如果画折线,需要填充透明
hasBorder: true,
strokeDashArray: [20, 10],
}
);
1.将图片绘制进canvas
有两种方式
(1)方式一:根据图片元素绘制图片,使用fabric.Image
let image = new Image();
image.src = 'xxxx';
image.onload = ()=>{
//1.创建图片实例对象
let imgInstance = new fabric.Image(image, {
left: 0, //位置
top: 0,
scaleX:1,
scaleY:1,
erasable:false, //是否可擦除
hasControls: false, // 是否开启图层的控件
evented:true,//是否可以支持事件
...
});
//实例对象的width和height可以获取图片的大小
imgInstance.width
imgInstance.height
//2.添加到画布
canvasCtx.add(imgInstance);
};
(2)方式二:根据图片地址绘制图片fabric.Image.fromURL
new fabric.Image.fromURL("/resources/images/robot.png", function (oImg) {
//oImg是图片对象实例
oImg.set({
top:10,
left:10,
scaleX:1,
scaleY:1,
evented: false,
...
});
//实例对象的width和height可以获取图片的大小
oImg.width
oImg.height
canvasCtx.add(oImg);
});
2.当我们已经声明了一个fabric
图片对象,可以在维持原对象的情况下使用setSrc
动态修改图片。
//注意,第一个参数只能是图片地址远程地址本地地址,base64等,不能是图像元素
oimg.setSrc("./img.png", () => {
canvasCtx.renderAll();
});
1.绘制普通文本
let text = new fabric.Text(`hello \n world!`, {
left: 0,
top: 0,
fill: 'red', // 字体颜色
fontSize: 12, //字体大小
fontWeight: 100, //字体粗细
charSpacing: 10 //字体间距,
angle: 30, // 旋转
backgroundColor: '#ffd460', // 背景色
stroke: '#3f72af', // 文字描边颜色(蓝色)
strokeWidth: 2, // 文字描边粗细
textAlign: 'lfet', // 对齐方式:left 左对齐; right 右对齐; center 居中
opacity: 0.8, // 不透明度
selectable: true, // 能否被选中,默认true
shadow: 'rgba(0, 0, 0, 0.5) 5px 5px 5px', // 投影
...
});
2.绘制可编辑文本
let text = new fabric.IText(`hello \n world!`, {
left: 0,
top: 0,
cornerColor: 'pink', // 角的颜色(被选中时)
borderColor: 'yellowGreen', // 边框颜色(被选中时)
...
});
3.绘制文本框
let text = new fabric.Textbox(`hello \n world!`, {
left: 0,
top: 0,
cornerColor: 'pink', // 角的颜色(被选中时)
borderColor: 'yellowGreen', // 边框颜色(被选中时)
...
});
4.可以通过set
实例方法动态修改文本,和其他属性
text.set({
text:'change content'
})
1.在fabric
中,我们使用对象的top
和left
来定位对象在画布中的位置,且top
和left
默认是对象的左上角位置
2.我们可以通过配置对象的originX
和originY
来修改默认原点的位置
3.originX
和originY
分别代表对象平移的水平起始点和垂直起始点,可以设置的值如下
originX : "left"|"right"|"center"
originY : "top"|"bottom"|"center"
4.如下图所示,相同的left
和top
值,原点不一样时,对象的位置是不一样的,红色的为默认的原点,紫色的originX
和originY
为center
5.除了left
和top
, 我们可以通过对象的aCoords
获取对象在没缩放没移动时的原始位置,
kclass
对象1.往深层面看,绘制的每一个图形都是一个kclass
对象,如下
2.对于所有的对象,需要使用add
方法添加进画布
3.我们可以使用getObjects
获取所有绘制的对象,返回一个元素为kclass
对象的数组,注意没有add
进画布的对象,无法通过getObjects
获取到
canvasCtx.getObjects()
3.在kclass
可以获取对象的一些有用的信息其中
(1)lineCoords
: 线性对象边界的四个点坐标,是在画布缩放移动时,动态变化的当前时刻快照的对象坐标
(2)aCoords
: 原始对象边界的四个点坐标原始值,该属性反映了画布没缩放没移动时的对象位置,
每一个lineCoords
都对应着一个aCoords
,lineCoords
是当前肉眼看到的,aCoords
是隐藏在背后的原始位置。
(3)top
,left
:对象定位时的top
和left
(4)angle
:对象旋转的角度
(5)自定义的一些属性,也会在kclass
对象中展示
(6)配置的属性等等
1.对于所有对象,除了在创建时可以声明属性,我们还可以使用对象的set
实例方法动态设置属性
directionArrow.set({
left: 10,
top: 22,
});
1.正常情况下一个可交互的画布上的元素支持缩放,平移和旋转的操作
2.我们可以通过设置 hasControls
为false
禁用所以功能,此时不会渲染操作框
3.同时我们也可以通过属性配置,限制一部分行为。
(1)限制元素的旋转:object.lockRotation = true
(2)限制元素x
轴的缩放:object.lockScalingX= true
(3)限制元素y
轴的缩放:object.lockScalingY= true
(4)限制元素上下移动:object.lockMovementY =true
(5)限制元素左右移动:object.lockMovementX =true
4.另一种方式使用对象的实例方法setControlsVisibility
,该方法用于隐藏操作框的部分corner
,从而达到禁用功能
workNode.setControlsVisibility({
bl: false,
br: false,
mb: false,
ml: false,
mr: false,
mt: false,
tl: false,
tr: false,
mtr:false,
});
5.对于删除按钮,需要自定义
var deleteImg = document.createElement("img");
deleteImg.src = deleteIcon;
fabric.Object.prototype.controls.deleteControl = new fabric.Control({
x: 0.5,
y: -0.5,
offsetY: -16,
offsetX: 16,
cursorStyle: "pointer",
mouseUpHandler: function (eventData, transform) {
var target = transform.target;
//target 为当前对象,这里可执行业务判断
canvasCtx.remove(target);
canvasCtx.requestRenderAll();
},
render: function (ctx, left, top, styleOverride, fabricObject) {
//fabricObject为当前对象,可根据当前对象自定义是否渲染删除按钮
var size = this.cornerSize + 2;
ctx.save();
ctx.translate(left, top);
ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));
//deleteImg 是图片元素
ctx.drawImage(deleteImg, -size / 2, -size / 2, size, size);
ctx.restore();
},
cornerSize: 18,
});
1.画布对象和元素对象都有on
实例方法用于绑定事件,当设置对象的属性 evented
为false
时,绑定的事件不生效
canvasCtx.on(eventname,handler)
canvasCtx.on("mouse:down",(e)=>{})
text.on("mouse:down",(e)=>{})
2.支持的常用事件
(1)鼠标上常用的操作事件
mouse:down 或 mousedown 鼠标按下时触发
mouse:move
mouse:up 或 mouseup 鼠标抬起时触发
mouse:down:before
mouse:move:before
mouse:up:before
mouse:over 或 mouseover 鼠标悬浮时触发
mouse:out 或 mouseout 鼠标进入后离开时触发
mouse:dbclick 或 mousedblclick 鼠标双击时触发
//for example
canvasCtx.on("mouse:down",(e)=>{})
text.on("mouse:down",(e)=>{})
(2)对象操作的一些监控
modified
rotating
scaling
moving
skewing
//for example
text.on("rotating",(e)=>{})
3.fabric
的鼠标事件能够兼容在移动端的手势操作
mouse:down ==> 在移动端手指点击一下也会触发
mouse:move ==> 在移动端手指移动也会触发
mouse:up ==> 在移动端手指抬起也会触发
mouse:down:before
mouse:move:before
mouse:up:before
mouse:over ==> 不支持移动端
mouse:out ==> 移动端手指离开元素触发
mouse:dbclick ==> 在移动端双击也会触发
4.注意,虽然部分鼠标事件兼容移动端,但是同一个事件在PC
端和移动端返回的事件对象是不一样的,pc
端返回的mouseEvent
,移动端返回的touchEvent
,两者返回的字段含义不一样,要自己做兼容。
5.注意,当对画布进行拖动和缩放后,事件对象的offsetX
,offsetY
会产生偏移,不能准确的表示位置,需要使用如下的方法转化坐标
//根据缩放等级转化鼠标的x,y
function getTransformedPosX(canvasCtx, x) {
let zoom = Number(canvasCtx.getZoom());
return (x - canvasCtx.viewportTransform[4]) / zoom;
}
function getTransformedPosY(canvasCtx, y) {
let zoom = Number(canvasCtx.getZoom());
return (y - canvasCtx.viewportTransform[5]) / zoom;
}
1.fabric.js
的基础包并没有包含橡皮擦模块,要使用橡皮擦功能可以使用npm
安装fabric-with-erasing
。
2.fabric-with-erasing
是在fabric.js
的基础上加上橡皮擦,因此在使用fabric-with-erasing
后,可以不需要fabric
包。
3.开启橡皮擦
canvasCtx.isDrawingMode = true; // 进入绘画模式
canvasCtx.freeDrawingBrush = new fabric.EraserBrush(canvasCtx); // 使用橡皮擦画笔
canvasCtx.freeDrawingBrush.width = 8; // 设置画笔粗细为 10
4.恢复橡皮擦擦除部分
canvasCtx.isDrawingMode = true;
canvasCtx.freeDrawingBrush = new fabric.EraserBrush(canvasCtx);
canvasCtx.freeDrawingBrush.width = 10;
canvasCtx.freeDrawingBrush.inverted = true;
5.关闭橡皮擦功能
canvasCtx.isDrawingMode = false;
6.橡皮擦经过的部分会变成透明的
zoom
和获取zoom
1.canvas
默认层级是1
2.设置层级方法: canvasCtx.setZoom(zoom);
3.获取层级方法: canvasCtx.getZoom();
4.根据鼠标滚轮来设置层级:
canvasCtx.on("mouse:wheel", function (opt) {
var delta = opt.e.deltaY;
var zoom = canvasCtx.getZoom();
zoom *= 0.999 ** delta;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvasCtx.setZoom(zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
1.fabric
支持修改不同状态下默认的鼠标样式
(1)默认的鼠标样式
canvasCtx.defaultCursor = 'default'
(2)鼠标移动到对象上的样式
canvasCtx.hoverCursor = 'move'
(3)鼠标拖动时的样式
canvasCtx.moveCursor = 'move'
(4)自由绘制时的样式
canvasCtx.freeDrawingCursor = 'crosshair'
(5)旋转时的样式
canvasCtx.rotationCursor = 'crosshair'
2.上面的样式都是默认样式,我们可以赋值为其它css
支持的cursor
值
1.使用toDataURL
可以将canvas
转为图片,该函数返回的是图片的base64
格式字符串。
let imgUrl = canvasCtx.toDataURL({
format: "png",
})
2.可以通过top
,left
, width
和height
参数将指定部分canvas
绘制成图片。
let imgUrl = canvasCtx.toDataURL({
format: "png",
width: mapImgInstance.lineCoords.br.x - mapImgInstance.lineCoords.bl.x,
height: mapImgInstance.lineCoords.bl.y - mapImgInstance.lineCoords.tr.y,
left: mapImgInstance.lineCoords.bl.x,
top: mapImgInstance.lineCoords.tl.y,
});
canvas
1.使用dispose
可以释放已经创建的canvas
canvasCtx.dispose()
1.使用fabric_with_gestures
版本
??做个假想,使用这个版本纯天然支持双指缩放
/* 要想触发 必须是fabric-with-gestures
touch:gesture
touch:drag
touch:shake
touch:longpress
2.双指缩放
handleDoubleFinger(options) {
const {
clientX: finger1X,
clientY: finger1Y
} = options.e.targetTouches[0];
const {
clientX: finger2X,
clientY: finger2Y
} = options.e.targetTouches[1];
const powX = (finger2X - finger1X) * (finger2X - finger1X);
const powY = (finger2Y - finger1Y) * (finger2Y - finger1Y);
// 计算两个手指之间的距离
const distance = Math.sqrt(powX + powY);
// 每次缩放的比例
let ratio = -0.05;
if (distance > preDistance) {
ratio = 0.05;
}
preDistance = distance;
const x = scaleCenter.x || (Math.abs(finger1X + finger2X)) / 2;
const y = scaleCenter.y || (Math.abs(finger1Y + finger2Y)) / 2;
scaleCenter = {
x, y
};
// 计算当前缩放的大小
let zoom = ratio + canvasCtx.getZoom(); // 获取当前缩放比
zoom = Math.max(0.5, zoom);
zoom = Math.min(3, zoom);
const zoomPoint = new fabric.Point(x, y);
canvasCtx.zoomToPoint(zoomPoint, zoom);
}
1.fabric
好像没有直接操作像素的方法,想要操作像素可以使用原生的canvas
2.关键的两个api
是getImageData
和putImageData
//初始化canvas上下文
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
//初始化图像元素
let img = new Image();
img.crossOrigin = "anonymous";
img.src = url; // url是要改变图像的地址
//图像加载完成后
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
//往canvas绘制图像元素
ctx.drawImage(img, 0, 0);
//获取图像像素对象
let imgData = ctx.getImageData(0, 0, img.width, img.height);
//imgData.data 类似于 [r1,g1,b1,a1,r2,g2,b2,a2,r3,g3,b3,a3,...] 每四个元素组成一个rgba
//...根据索引可以直接修改像素值
//使用putImageData可以把改变后的图片,重新绘制到canvas上
ctx.putImageData(imgData, 0, 0);
//之后可以将图片重新下载下来
let url = canvas.toDataURL("image/png", { quality: 1 });
}
1.中文api文档