官方地址: http://fabricjs.com/
小编用的版本为 4.5.1
1, 在项目中使用
#myCanvas{
width:10000px;
height:10000px;
}
<canvas id="myCanvas" width="10000" height="10000">canvas>
<script src="./jquery-2.0.0.min.js">script>
<script src="./fabric.5.1.0.min.js">script>
// 初始化 canvas 为 fabric 对象
var canvas = this.__canvas = new fabric.Canvas('myCanvas');
// 画一个矩形
var rect = new fabric.Rect({
width: 1000,
height: 1000,
left: 0,
top: 0,
fill: 'rgba(255, 0, 0, 0.4)',
});
// canvas 添加矩形并渲染
canvas.add( rect );
canvas.renderAll();
2, 特殊用法
①, 基本设置下面是在使用时用到和遇到的场景
//画板元素不能被选中
canvas.skipTargetFind = false;
//画板不显示选中
canvas.selection = false;
canvas.freeDrawingBrush.color = "red";
canvas.freeDrawingBrush.width = 1;
//画板不限制调整时的宽高比
// mycanvas.uniScaleTransform = true;
// //绑定画板事件
canvas.on({
// 'mouse:dblclick': this.dblShowRectFrom,//双击区域d出
'mouse:down': this.mouseDownDrawRect, //按下,
'mouse:move': this.mouseMoving, //移动中,
'mouse:up': this.mouseUpDrawRect, //抬起,
'mouse:wheel': this.zoomsHandle, //缩放
// 'object:moved': this.objectMoved, //移动完成
// // 'object:moving': objectMov,//移动中
// 'object:scaled': this.mouseScaleDrawRect,//缩放完成
// 'object:scaling': this.mouseScalingDrawRect,//缩放中
});
②, 画板数据的导入导出
// 1, 导出 检测 画板上所有对象 并保存为 字符串
let inkTrace = [];
canvas.getObjects().forEach((v,i) => {
let klass = v.toJSON();
if(klass.type == 'image'){
// 对其中一些特殊对象进行处理
}
inkTrace.push(klass)
})
// 对数据最后加工
if(inkTrace.length==0){inkTrace=""}else{inkTrace = JSON.stringify(inkTrace)}
// 2, 导入 根据字符串处理为 fabric 可识别的对象
JSON.parse(inkTrace).forEach((v,i) => {
// 不同类型的数据要分别处理 数据中的 type 属性
if(v.type=="image"){
new fabric.Image.fromObject(v, (e) => {
mycanvas.add(e)
})
}
if(v.type=="path"){
new fabric.Path.fromObject(v, (e) => {
canvas.add(e)
})
}
})
canvas.renderAll();
③, 遮罩 Pattern ( 引用官网案例 )
var text = new fabric.Text('Honey,\nI\'m subtle', {
fontSize: 250,
left: 0,
top: 0,
lineHeight: 1,
originX: 'left',
fontFamily: 'Helvetica',
fontWeight: 'bold',
statefullCache: true,
scaleX: 0.4,
scaleY: 0.4
});
var shape = new fabric.Rect({
width: 200,
height: 100,
left: 10,
top: 300,
});
canvas.add(text, shape);
loadPattern('./xxx.png');
function loadPattern(url) {
fabric.util.loadImage(url, function(img) {
console.log(img)
text.set('fill', new fabric.Pattern({
source: img,
repeat: "no-repeat"
}));
rect.set('fill', new fabric.Pattern({
source: img,
repeat: "no-repeat"
}));
canvas.renderAll();
});
}
④, 多个对象合并, 并设置为 fabric 背景 ( 适用于变色和更多场景 )
// 创建对象
let image = new fabric.Image(image, {
left: 10,
top: 10,
scaleX: 1,
scaleY: 1,
crossOrigin: 'anonymous',// 跨域
})
let shape = new fabric.Rect({
width: image.width,
height: image.height,
opacity:0.4,
fill: "#FF0000",
left: 10,
top: 10,
scaleX: 1,
scaleY: 1,
});
// 合并
let group = new fabric.Group([image, shape], {})
// 设置为背景
canvas.setBackgroundImage(group, canvas.renderAll.bind(canvas), {});
⑤, 把 canvas对象 或者 fabric对象 导出为图片
let image = new Image();
// 先引入一张图片作为底图, 不引入也没关系
image.src = './xxx.png';
image.setAttribute('crossOrigin', 'anonymous');
image.onload = () => {
let fabricImage = new fabric.Image(image, {
left: 0,
top: 0,
width: image.width,
height: image.height
})
let shapeLine = new fabric.Rect({
width: 1700,
height: 800,
fill: "#333",
left: 0,
top: 0,
});
let shapeVert = new fabric.Rect({
width: 235,
height: image.height,
fill: "#333",
left: 0,
top: 0,
});
// 把这几个对象合并一下
let group = new fabric.Group([fabricImage, shapeLine, shapeVert], {})
let imgs = group.toDataURL({
format: 'png', // jpeg或png
left: 0,
top: 0,
width: image.width,
height: image.height
});
// imgs 就是导出后的图片, 放在 img 的src中即可使用
}
⑥, 位置的获取
思路一: (计算法, 没算出来)应用场景: 要求背景为一块矩形, 所有的遮罩, 痕迹, 只准出现在背景矩形上, 不准超出
在有旋转角度的情况下,
位移: 算出位移之后出现在矩形上的位置(如,右侧超出出现在右侧)
缩放: 在有旋转角度的情况下, 拉伸已旋转的矩形, 如果超出背景矩形, 则缩放至最大高度(没算出来)
旋转: 旋转矩形如果超出背景矩形, 计算矩形能在背景矩形旋转的最大角度(没算出来)
mycanvas.on({
//'mouse:down': this.mouseDownDrawRect, //按下,
//'mouse:move': this.mouseMoving, //移动中,
//'mouse:up': this.mouseUpDrawRect, //抬起,
//'mouse:wheel': this.zoomsHandle, //缩放
'object:moved': this.objectMov, //移动完成
//'object:moving': this.objectMov,//移动中
'object:scaled': this.mouseScaleDrawRect,//缩放完成
//'object:scaling': this.mouseScalingDrawRect,//缩放中
'object:rotated': this.objectRotated,// 旋转完成
//'object:rotating': this.objectRotated,// 旋转中
});
// 移动完成计算
objectMov = (t) => {
const { mainCanvas } = this.state;
let mycanvas = mainCanvas,oImgList = mycanvas.getObjects(),oImg = t?oImgList[oImgList.length-1]:mycanvas.getActiveObject();
if(!oImg) { return false;}
let ep = oImg.aCoords,// 目标对象的四个坐标点
angle= Math.floor(( oImg.angle ) / 90),// 目标对象的旋转角度
ptl = mycanvas.backgroundImage.aCoords.tl,
pbr = mycanvas.backgroundImage.aCoords.br,et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0;
switch (angle) {
case 0:
et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.bl.x }); // 左侧超出
if(er > pbr.x) { // 右侧超出
if((ep.tr.x - ep.bl.x) <= (pbr.x - ptl.x)) { oImg.set({ left:pbr.x - (ep.tr.x - ep.tl.x) })}
if((ep.tr.x - ep.bl.x) > (pbr.x - ptl.x)) { oImg.set({ left:ptl.x }) }
}
et < ptl.y && oImg.set({ top:ptl.y })// 上侧超出
if(eb > pbr.y) { // 下侧超出
if((ep.br.y - ep.tl.y) <= (pbr.y - ptl.y)) { oImg.set({ top:pbr.y - (ep.br.y - ep.tl.y) })}
if((ep.br.y - ep.tl.y) > (pbr.y - ptl.y)) { oImg.set({ top:ptl.y }) }
}
break;
case 1:
et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
er > pbr.x && oImg.set({ left:pbr.x })// 右侧超出
el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.br.x });// 左侧超出
et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.bl.y) })// 上侧超出
eb > pbr.y && oImg.set({ top:pbr.y - (ep.tr.y - ep.tl.y) })// 下侧超出
break;
case 2:
et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.tr.x });// 左侧超出
er > pbr.x && oImg.set({ left:pbr.x - (ep.bl.x - ep.tl.x)})// 右侧超出
et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.br.y) })// 上侧超出
eb > pbr.y && oImg.set({ top:pbr.y })// 下侧超出
break;
case 3:
et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
el < ptl.x && oImg.set({ left:ptl.x }); // 左侧超出
er > pbr.x && oImg.set({ left:pbr.x - (ep.br.x - ep.tl.x)})// 右侧超出
et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.tr.y) })// 上侧超出
eb > pbr.y && oImg.set({ top:pbr.y - (ep.bl.y - ep.tl.y) })// 下侧超出
}
mycanvas.renderAll()
}
获取所有的对象: mycanvas.getObjects()
获取最后一个对象: var objList = mycanvas.getObjects(),obj = objList[objList.length-1]
获取被选中的对象: mycanvas.getActiveObject()
缩放和旋转没算出来, 代码删了留着日后解决,
思路二 (记录位置) 补充: 鼠标位移留痕不规则线条在如下代码中方法非常简单, mousedown记录选中矩形状态信息, mouseup检测是否超出, 是则回退至原来的位置
// objectMov 位移
mycanvas.on({
'mouse:down': this.mouseDownDrawRect, //按下,
'mouse:move': this.mouseMoving, //移动中,
'mouse:up': this.mouseUpDrawRect, //抬起,
//'mouse:wheel': this.zoomsHandle, //缩放
//'object:moved': this.objectMov, //移动完成
//'object:moving': this.objectMov,//移动中
//'object:scaled': this.mouseScaleDrawRect,//缩放完成
//'object:scaling': this.mouseScalingDrawRect,//缩放中
//'object:rotated': this.objectRotated,// 旋转完成
//'object:rotating': this.objectRotated,// 旋转中
});
mouseDownDrawRect = () => {
_this_obj : JSON.parse(JSON.stringify(mycanvas.getActiveObject()))
};
mouseMoving = (options) => {
// canvasPointSave 在 mouseUpDrawRect 中定义
// 判断是否可以留痕
if( chousedType == 0 && mycanvas.backgroundImage && canvasPointSave.tl){
let ptl = canvasPointSave.tl,
pbr = canvasPointSave.br,
eposition = options.pointer;
if ( eposition.x < ptl.x || eposition.x > pbr.x || eposition.y < ptl.y || eposition.y > pbr.y) {
mycanvas.isDrawingMode = false;
}else{
mycanvas.isDrawingMode = true;
}
}
};
mouseUpDrawRect = (options) => {
// 如果是鼠标画不规则线条, 则重新判断位置
mycanvas.isDrawingMode = false;
let canvasObjArr = mycanvas.getObjects();
if( chousedType == 0 && canvasObjArr.length > 0){
this.referDrawRect();
}
// 如果是位移,缩放,旋转则计算位置
if( options.transform ){
if( options.transform.action== "drag"){
this.objectMov();
}
if( options.transform.action== "scaleX" || options.transform.action== "scaleY" || options.transform.action== "scale"){
this.mouseScalingDrawRect();this.objectMov();
}
if( options.transform.action== "rotate"){
this.objectRotated();this.objectMov();
}
}else if(options.target){
this.objectMov();
}
// 记录位置
if(mycanvas.backgroundImage) { canvasPointSave = mycanvas.backgroundImage.lineCoords }
};
objectMov = (t) => {
const { mainCanvas } = this.state;
let mycanvas = mainCanvas,oImgList = mycanvas.getObjects(),oImg = t?oImgList[oImgList.length-1]:mycanvas.getActiveObject();
if(!oImg) { return false;}
let ep = oImg.aCoords,// 目标对象的四个坐标点
angle= Math.floor(( oImg.angle ) / 90),// 目标对象的旋转角度
ptl = mycanvas.backgroundImage.aCoords.tl,
pbr = mycanvas.backgroundImage.aCoords.br,et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0;
switch (angle) {
case 0:
et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.bl.x }); // 左侧超出
if(er > pbr.x) { // 右侧超出
if((ep.tr.x - ep.bl.x) <= (pbr.x - ptl.x)) { oImg.set({ left:pbr.x - (ep.tr.x - ep.tl.x) })}
if((ep.tr.x - ep.bl.x) > (pbr.x - ptl.x)) { oImg.set({ left:ptl.x }) }
}
et < ptl.y && oImg.set({ top:ptl.y })// 上侧超出
if(eb > pbr.y) { // 下侧超出
if((ep.br.y - ep.tl.y) <= (pbr.y - ptl.y)) { oImg.set({ top:pbr.y - (ep.br.y - ep.tl.y) })}
if((ep.br.y - ep.tl.y) > (pbr.y - ptl.y)) { oImg.set({ top:ptl.y }) }
}
break;
case 1:
et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
er > pbr.x && oImg.set({ left:pbr.x })// 右侧超出
el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.br.x });// 左侧超出
et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.bl.y) })// 上侧超出
eb > pbr.y && oImg.set({ top:pbr.y - (ep.tr.y - ep.tl.y) })// 下侧超出
break;
case 2:
et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
el < ptl.x && oImg.set({ left:ptl.x + ep.tl.x - ep.tr.x });// 左侧超出
er > pbr.x && oImg.set({ left:pbr.x - (ep.bl.x - ep.tl.x)})// 右侧超出
et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.br.y) })// 上侧超出
eb > pbr.y && oImg.set({ top:pbr.y })// 下侧超出
break;
case 3:
et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
el < ptl.x && oImg.set({ left:ptl.x }); // 左侧超出
er > pbr.x && oImg.set({ left:pbr.x - (ep.br.x - ep.tl.x)})// 右侧超出
et < ptl.y && oImg.set({ top:ptl.y + (ep.tl.y - ep.tr.y) })// 上侧超出
eb > pbr.y && oImg.set({ top:pbr.y - (ep.bl.y - ep.tl.y) })// 下侧超出
}
mycanvas.renderAll()
}
mouseScalingDrawRect = () => {
const { mainCanvas,_this_obj } = this.state;
let mycanvas = mainCanvas,obj = mycanvas.getActiveObject();
if(!obj) { return false;}
let ep = obj.aCoords,// 目标对象的四个坐标点
angle= Math.floor(( obj.angle ) / 90),et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0;// 目标对象的旋转角度
switch (angle) {
case 0:
et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
w = ep.tr.x - ep.bl.x ;h = ep.br.y - ep.tl.y ;
break;
case 1:
et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
w = ep.tl.x - ep.br.x ;h = ep.tr.y - ep.bl.y ;
break;
case 2:
et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
w = ep.bl.x - ep.tr.x ;h = ep.tl.y - ep.br.y ;
break;
case 3:
et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
w = ep.br.x - ep.tl.x ;h = ep.bl.y - ep.tr.y ;
}
if(w > mycanvas.backgroundImage.width || h > mycanvas.backgroundImage.height){
obj.set({
scaleY: _this_obj.scaleY,
scaleX: _this_obj.scaleX,
left: _this_obj.left,
top: _this_obj.top,
})
}
mycanvas.renderAll()
}
referDrawRect = () => {
const { mainCanvas } = this.state;
let mycanvas = mainCanvas,objList = mycanvas.getObjects(),obj = objList[objList.length-1];
if(!obj) { return false;}
let scx = mycanvas.backgroundImage.width / obj.width;// -0.05
let scy = mycanvas.backgroundImage.height / obj.height;// -0.05
if (obj.scaleX > scx) {
obj.set({scaleX:scx})
};
if (obj.scaleY > scy) {
obj.set({scaleY:scy})
};
mycanvas.renderAll()
this.objectMov(true)
}
objectRotated = () => {
const { mainCanvas, _this_obj } = this.state;
let mycanvas = mainCanvas,oImg = mycanvas.getActiveObject();// 目标对象
if(!oImg) { return false;}
let ep = oImg.aCoords,// 目标对象的四个坐标点
angle= Math.floor(( oImg.angle ) / 90),// 目标对象的旋转角度
ptl = mycanvas.backgroundImage.aCoords.tl,
pbr = mycanvas.backgroundImage.aCoords.br,et = 0,el = 0,eb = 0,er = 0,w = 0,h = 0,lock = false;
switch (angle) {
case 0:
et = ep.tl.y;el = ep.bl.x;eb = ep.br.y;er = ep.tr.x;
w = ep.tr.x - ep.bl.x ;h = ep.br.y - ep.tl.y ;
break;
case 1:
et = ep.bl.y;el = ep.br.x;eb = ep.tr.y;er = ep.tl.x;
w = ep.tl.x - ep.br.x ;h = ep.tr.y - ep.bl.y ;
break;
case 2:
et = ep.br.y;el = ep.tr.x;eb = ep.tl.y;er = ep.bl.x;
w = ep.bl.x - ep.tr.x ;h = ep.tl.y - ep.br.y ;
break;
case 3:
et = ep.tr.y;el = ep.tl.x;eb = ep.bl.y;er = ep.br.x;
w = ep.br.x - ep.tl.x ;h = ep.bl.y - ep.tr.y ;
}
if(w > mycanvas.backgroundImage.width || h > mycanvas.backgroundImage.height ) {
oImg.set({
angle: _this_obj.angle,
left: _this_obj.left,
top: _this_obj.top,
})
}
mycanvas.renderAll()
}
注意事项
1, canvas 使用和 img 相关 *** 作时 如果 跨域 需要加上 crossOrigin: 'anonymous' 属性; 2, fabric.js 是根据 html 中 canvas标签的id 来初始化的, 如需 初始化多个fabric , 则需 注意 canvas 标签的 id, 以及 window.全局配置的参数(使用vue和react时)
3, fabric 初始化过后的画布, 如果发生 位移 和 缩放 , 再进行放置对象是要 进行计算的
计算规则如下
// 发生位移计算
// 右上角
let tr = canvas.backgroundImage.lineCoords.tr;
// 右下角
let br = canvas.backgroundImage.lineCoords.br;
// 左上角
let tl = canvas.backgroundImage.lineCoords.tl;
// 左下角
let bl = canvas.backgroundImage.lineCoords.bl;
//判断试卷最右侧 是否离开了可视区域
if (tr.x <= 50) {
var delta = new fabric.Point(50, 0);
relativeMouseX += 50;
canvas.relativePan(delta);
return;
}
//判断试卷最上侧 是否离开了可视区域
if (tr.y > screenHeight - 50) {
var delta = new fabric.Point(0, -50);
relativeMouseY -= 50;
canvas.relativePan(delta);
return;
}
//判断试卷最下侧 是否离开了可视区域
if (br.y <= 50) {
var delta = new fabric.Point(0, 50);
relativeMouseY += 50;
canvas.relativePan(delta);
return;
}
//判断试卷最左侧 是否离开了可视区域
if (tl.x > screenWidth - 50) {
var delta = new fabric.Point(-50, 0);
relativeMouseX -= 50;
canvas.relativePan(delta);
return;
}
var delta = new fabric.Point(options.e.movementX, options.e.movementY);
canvas.relativePan(delta);
relativeMouseX += options.e.movementX; //累计每一次移动时候的偏差
relativeMouseY += options.e.movementY;
// 发生缩放计算
// 缩放代码
let wheelPost = e?e.e.deltaY:wheel,
pointerX = e.pointer.x,
pointerY = e.pointer.y,
zoomSpeed = 0.03,
zoom, zoomPoint, lastzoom, lastzoomPoint={x:0,y:0}, lastmousePoint={x:0,y:0}, relativeMouseX, relativeMouseY;// 通过zoomSpeed 控制缩放速度
zoom = (wheelPost > 0 ? -zoomSpeed : zoomSpeed) + canvas.getZoom();
zoom = Math.max(0.1, zoom); //最小为原来的1/10
zoom = Math.min(3, zoom); //最大是原来的3倍
zoomPoint = new fabric.Point(pointerX, pointerY);
canvas.zoomToPoint(zoomPoint, zoom);
lastzoomPoint.x =
lastzoomPoint.x + (zoomPoint.x - lastmousePoint.x - relativeMouseX) / lastzoom;
lastzoomPoint.y =
lastzoomPoint.y + (zoomPoint.y - lastmousePoint.y - relativeMouseY) / lastzoom;
lastmousePoint.x = zoomPoint.x;
lastmousePoint.y = zoomPoint.y;
lastzoom = zoom;
relativeMouseX = 0;
relativeMouseY = 0;
// 需要获取值时
transformMouse = (mouseX, mouseY) => {
let { lastzoomPoint,zoomPoint,relativeMouseX,relativeMouseY } = this.state;
let {
mainCanvas
} = this.state;
let mycanvas = mainCanvas;
let x = lastzoomPoint.x + (mouseX - zoomPoint.x - relativeMouseX) / mycanvas.getZoom();
let y = lastzoomPoint.y + (mouseY - zoomPoint.y - relativeMouseY) / mycanvas.getZoom();
return { x, y };
};
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)