我们以前文中的一个例子作为示例。如下图,当我们点击一个画布中的对象,对象周围就出现了一个控制框,控制框上一共9个控制点。
如上图所示,控制框的范围由控制线限定。 自定义时,根据需要,我们一般会修改控制线的以下属性
是否显示
颜色
与框选对象间的内边距
如上图所示,点击并拖动不同的控制点,会产生不同的交互效果。 下面区分不同的控制点类型分别进行介绍:
1、水平缩放控制点: 即第一列中间和第三列中间两个控制点。点击并进行拖动,会改变对象的水平缩放值(scaleX,初始值为1)
2、竖直缩放控制点: 即第二列第二行和第三行两个控制点。点击并进行拖动,会改变对象的竖直缩放值(scaleY,初始值为1)
3、等比例缩放控制点: 即左上、右上、左下、右下四个控制点。点击并进行拖动,会同时并等比例改变对象的scaleX和scaleY两个值
4、中心旋转控制点: 即第二列最上方的控制点。点击并进行拖动,会改变对象的角度值(angle,初始值为0)
左上的等比例缩放控制点还会改变对象的top和left值。
左中的水平缩放控制点还会改变对象的left值。
中心旋转控制点还会改变对象的top和left值。
上文中提到,我们一般对控制线是否显示、颜色、与框选对象间的内边距这几个属性进行自定义修改。这里用另一个例子,对上述属性逐一进行介绍。
如图所示,其默认的控制线样式如下:
fabric.Object.prototype.padding = 10;
代码说明
padding即内边距,目前只支持4个方向的统一设置,不支持单独设置
我们之前见过的编辑器,其旋转控制点和主体之间一般没有那条控制线,这里我们对它进行隐藏。
代码
fabric.Object.prototype.controls.mtr.withConnection = false;
代码说明
fabric.Object是所有对象的父类,修改其属性即可对它的子类(Rect、Circle…)都生效。
controls包含了Object类的所有控制点的信息。
mtr是middle top rotation的缩写,即中心旋转控制点。
withConnection即mtr是否和主体有连线,此处设为false。
结果
可见,旋转控制点和主体之间的那条连接线没有了
我们将控制线默认的浅蓝色改为 #dc143c,一种红色
代码
fabric.Object.prototype.borderColor = 'dodgerblue';
对于控制点,我们可以自定义修改其形状、大小、边框颜色、填充颜色等属性。这里对于各个属性不再一一介绍,我们用一个综合的例子对它们进行演示。
代码
// 修改控制点的形状,默认为`rect`矩形,可选的值还有`circle`圆形
fabric.Object.prototype.cornerStyle = "circle";
// 修改控制点的填充色为白色
fabric.Object.prototype.cornerColor = "white";
// 修改控制点的大小为10px
fabric.Object.prototype.cornerSize = 10;
// 设置控制点不透明,即可以盖住其下的控制线
fabric.Object.prototype.transparentCorners = false;
// 修改控制点的边框颜色为`gray`灰色
fabric.Object.prototype.cornerStrokeColor = "gray";
// 单独修改旋转控制点距离主体的纵向距离为-20px
fabric.Object.prototype.controls.mtr.offsetY = -20;
// 单独修改旋转控制点,光标移动到该点上时的样式为`pointer`,一个手的形状
fabric.Object.prototype.controls.mtr.cursorStyle = "pointer";
/*控制选中边框 旋转图标 start*/
// 渲染图标的方法
function renderIcon(image, initialAngle) {
return function (ctx, left, top, styleOverride, fabricObject) {
let size = this.cornerSize;
ctx.save();
ctx.translate(left, top);
ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle + initialAngle));
ctx.drawImage(image, -size / 2, -size / 2, size, size);
ctx.restore();
};
}
// 图标的下载链接省略
const iconURL = "../img/xuanzhuan.png";
const callback = (image, isError) => {
if (!isError) {
fabric.Object.prototype.controls.ml = new fabric.Control({
x: 0,
y: -0.5,
offsetY: -20,
cursorStyle: 'pointer',
actionHandler: fabric.controlsUtils.rotationWithSnapping,
cursorStyleHandler: fabric.controlsUtils.rotationStyleHandler,
// 渲染图标
render: renderIcon(image._element, 0),
// 设置控制点大小
cornerSize: 30
});
}
};
fabric.Image.fromURL(iconURL, callback);
/*控制选中边框 旋转图标 stop*/
有的时候,我们可能不满足于默认提供的9个控制点,想要自己添加一些控制点上去,比如用于删除对象的控制点。这个小节,我们使用一个垃圾桶图标来实现它。
首先看一下实现效果:
/*控制选中边框 增加自对应图标 start*/
// 从画布中删除当前选中的对象
function deleteObject() {
// 获取画布当前选中的对象
let activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.remove(activeObject);
canvas.renderAll();
}
}
// 垃圾桶图标的下载链接
const deleteIconURL = "../img/delete.png";
const deletecallback = (img, isError) => {
console.log(img);
if (!isError) {
fabric.Object.prototype.controls.delete = new fabric.Control({
// x和y设置该控制点和第二列中间的控制点重合
x: 0,
y: -0.5,
// offsetX和offsetY设置该控制点在水平和竖直两个方向上
// 偏移的距离(单位px)
offsetX: 28,
offsetY: -20,
// 光标移动到该控制点时变为一个手的图标
cursorStyle: 'pointer',
// 自定义的值,可忽略
actionName: "delete",
// 设置当点击了该控制点,鼠标弹起是执行的动作处理方法
mouseUpHandler: () => deleteObject(),
//渲染图标
render: renderIcon(img._element, 0),
cornerSize: 39
});
}
};
fabric.Image.fromURL(deleteIconURL, deletecallback);
/*控制选中边框 增加自对应图标 stop*/
注意
renderIcon 方法在上边定义过了,可以直接调用
DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Fabric.js 上传图片保存title>
<script src="../fabric5.2.1.js">script>
<style>
div#container {
padding: 30px;
font-family: 'verdana', lucida;
}
input {
background-color: #ccc;
padding: 0;
width: 300px;
color: #777;
}
#lnkDownload {
display: block;
padding: 0;
margin-top: 10px;
text-decoration: none;
}
style>
head>
<body>
<div id="container">
<canvas id="imageCanvas" width="300" height="300">canvas>
<a id="lnkDownload" href="#">
<button> 保存图片button>
a>
div>
<script src="script.js">script>
body>
html>
var canvas = new fabric.Canvas('imageCanvas', {
backgroundColor: 'rgb(240,240,240)',
includeDefaultValues: false,// 指示toObject/toDatalessObject是否应该包含默认值,如果设置为false,则优先于对象值
perPixelTargetFind: true, //这一句说明选中的时候以图形的实际大小来选择而不是以边框来选择
hasBorders: false,
});
canvas.setWidth(500);
canvas.setHeight(500);
/*控制选中边框 start*/
//将内边距设置为10px
fabric.Object.prototype.padding = 10;
// 修改控制点的形状,默认为`rect`矩形,可选的值还有`circle`圆形
fabric.Object.prototype.cornerStyle = "circle";
// 修改控制点的填充色为白色
fabric.Object.prototype.cornerColor = "white";
//将控制线默认的浅蓝色改为 #dc143c,
fabric.Object.prototype.borderColor = '#dc143c';
// 修改控制点的大小为10px
fabric.Object.prototype.cornerSize = 10;
// 设置控制点不透明,即可以盖住其下的控制线
fabric.Object.prototype.transparentCorners = false;
// 修改控制点的边框颜色为`gray`灰色
fabric.Object.prototype.cornerStrokeColor = "blue";
// 单独修改旋转控制点距离主体的纵向距离为-20px
fabric.Object.prototype.controls.mtr.offsetY = -20;
// 单独修改旋转控制点,光标移动到该点上时的样式为`pointer`,一个手的形状
fabric.Object.prototype.controls.mtr.cursorStyle = "pointer";
//旋转控制点和主体之间一般没有那条控制线,这里我们对它进行隐藏。
fabric.Object.prototype.controls.mtr.withConnection = false;
/*控制选中边框 stop*/
/*控制选中边框 旋转图标 start*/
// 渲染图标的方法
function renderIcon(image, initialAngle) {
return function (ctx, left, top, styleOverride, fabricObject) {
let size = this.cornerSize;
ctx.save();
ctx.translate(left, top);
ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle + initialAngle));
ctx.drawImage(image, -size / 2, -size / 2, size, size);
ctx.restore();
};
}
// 图标的下载链接省略
const iconURL = "../img/xuanzhuan.png";
const callback = (image, isError) => {
if (!isError) {
fabric.Object.prototype.controls.ml = new fabric.Control({
x: 0,
y: -0.5,
offsetY: -20,
cursorStyle: 'pointer',
actionHandler: fabric.controlsUtils.rotationWithSnapping,
cursorStyleHandler: fabric.controlsUtils.rotationStyleHandler,
// 渲染图标
render: renderIcon(image._element, 0),
// 设置控制点大小
cornerSize: 30
});
}
};
fabric.Image.fromURL(iconURL, callback);
/*控制选中边框 旋转图标 stop*/
/*控制选中边框 增加自对应图标 start*/
// 从画布中删除当前选中的对象
function deleteObject() {
// 获取画布当前选中的对象
let activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.remove(activeObject);
canvas.renderAll();
}
}
// 垃圾桶图标的下载链接
const deleteIconURL = "../img/delete.png";
const deletecallback = (img, isError) => {
console.log(img);
if (!isError) {
fabric.Object.prototype.controls.delete = new fabric.Control({
// x和y设置该控制点和第二列中间的控制点重合
x: 0,
y: -0.5,
// offsetX和offsetY设置该控制点在水平和竖直两个方向上
// 偏移的距离(单位px)
offsetX: 28,
offsetY: -20,
// 光标移动到该控制点时变为一个手的图标
cursorStyle: 'pointer',
// 自定义的值,可忽略
actionName: "delete",
// 设置当点击了该控制点,鼠标弹起是执行的动作处理方法
mouseUpHandler: () => deleteObject(),
//渲染图标
render: renderIcon(img._element, 0),
cornerSize: 39
});
}
};
fabric.Image.fromURL(deleteIconURL, deletecallback);
/*控制选中边框 增加自对应图标 stop*/
// 矩形
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'orange',
width: 100,
height: 100,
})
canvas.add(rect);
// 圆角矩形
var rect2 = new fabric.Rect({
left: 300,
top: 100,
fill: 'yellowgreen',
width: 100,
height: 100,
rx: 20,
ry: 20
})
canvas.add(rect2);
// 圆形
var circle = new fabric.Circle({
radius: 50,
fill: 'green',
left: 200,
top: 200,
controls: false, // 不可编辑
hasControls: false, // 控件将不显示,并且不能用于操作对象
});
canvas.add(circle);
// 使用 IText,可编辑文本
var text = new fabric.IText(
'奇葩呀,www.qipa250.com',
{
top:300,
fontSize:14,
fontFamily: 'Comic Sans'
}
)
canvas.add(text);
var imageSaver = document.getElementById('lnkDownload');
imageSaver.addEventListener('click', saveImage, false);
function saveImage() {
console.log('toJSON==', canvas.toJSON());
console.log('toObject==', canvas.toObject()); // 输出序列化的内容
this.href = canvas.toDataURL({
format: 'png',
quality: 0.8
});
this.download = 'canvas.png';
}