fabric.js使用
- fabric.js 是 常用的 canvas 插件
- 1, 在项目中使用
- 2, 特殊用法
- ①, 基本设置
- ②, 画板数据的导入导出
- ③, 遮罩 Pattern ( 引用官网案例 )
- ④, 多个对象合并, 并设置为 fabric 背景 ( 适用于变色和更多场景 )
- ⑤, 把 canvas对象 或者 fabric对象 导出为图片
- ⑥, 位置的获取
- 思路一: (计算法, 没算出来)
- 思路二 (记录位置) 补充: 鼠标位移留痕不规则线条在如下代码中
- 注意事项
fabric.js 是 常用的 canvas 插件
官方地址: 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>
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.add( rect );
canvas.renderAll();
2, 特殊用法
下面是在使用时用到和遇到的场景
①, 基本设置
canvas.skipTargetFind = false;
canvas.selection = false;
canvas.freeDrawingBrush.color = "red";
canvas.freeDrawingBrush.width = 1;
canvas.on({
'mouse:down': this.mouseDownDrawRect,
'mouse:move': this.mouseMoving,
'mouse:up': this.mouseUpDrawRect,
'mouse:wheel': this.zoomsHandle,
});
②, 画板数据的导入导出
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)}
JSON.parse(inkTrace).forEach((v,i) => {
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',
left: 0,
top: 0,
width: image.width,
height: image.height
});
}
⑥, 位置的获取
应用场景: 要求背景为一块矩形, 所有的遮罩, 痕迹, 只准出现在背景矩形上, 不准超出
思路一: (计算法, 没算出来)
在有旋转角度的情况下,
位移: 算出位移之后出现在矩形上的位置(如,右侧超出出现在右侧)
缩放: 在有旋转角度的情况下, 拉伸已旋转的矩形, 如果超出背景矩形, 则缩放至最大高度(没算出来)
旋转: 旋转矩形如果超出背景矩形, 计算矩形能在背景矩形旋转的最大角度(没算出来)
mycanvas.on({
'object:moved': this.objectMov,
'object:scaled': this.mouseScaleDrawRect,
'object:rotated': 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检测是否超出, 是则回退至原来的位置
mycanvas.on({
'mouse:down': this.mouseDownDrawRect,
'mouse:move': this.mouseMoving,
'mouse:up': this.mouseUpDrawRect,
});
mouseDownDrawRect = () => {
_this_obj : JSON.parse(JSON.stringify(mycanvas.getActiveObject()))
};
mouseMoving = (options) => {
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;
let scy = mycanvas.backgroundImage.height / obj.height;
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;
zoom = (wheelPost > 0 ? -zoomSpeed : zoomSpeed) + canvas.getZoom();
zoom = Math.max(0.1, zoom);
zoom = Math.min(3, zoom);
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 };
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)