在 Canvas Fabric JS 中的元素上添加删除按钮
Add Delete button on element in Canvas Fabric JS
您好,我想使用 FabricJS 在元素中添加删除按钮。我有一个例子:
我尝试添加这部分代码,但是当我调整图像大小时,删除按钮不在原位。
http://jsfiddle.net/wxao1on8/13/
function addDeleteBtn(x, y, w){
$(".deleteBtn").remove();
var btnLeft = x;
var btnTop = y - 30;
var widthadjust=w/2;
btnLeft=widthadjust+btnLeft-1
var deleteBtn = '<img src="https://www.funagain.com/images/old/common/delete-icon.png" class="deleteBtn" ' +
'style="position:absolute;top:'+btnTop+'px;right:'+btnLeft+'px;cursor:pointer;"/>';
$(".canvas-container").append(deleteBtn);
}
canvas.on('object:selected',function(e){
addDeleteBtn(e.target.oCoords.mt.x, e.target.oCoords.mt.y, e.target.width);
});
canvas.on('mouse:down',function(e){
if(!canvas.getActiveObject())
{
$(".deleteBtn").remove();
}
});
canvas.on('object:modified',function(e){
addDeleteBtn(e.target.oCoords.mt.x, e.target.oCoords.mt.y, e.target.width);
});
canvas.on('object:moving',function(e){
$(".deleteBtn").remove();
});
$(document).on('click',".deleteBtn",function(){
if(canvas.getActiveObject())
{
canvas.remove(canvas.getActiveObject());
$(".deleteBtn").remove();
}
});
target.oCoords.mt
是中上角。
改为使用右上角:e.target.oCoords.tr
var canvas = new fabric.Canvas('canvas');
var HideControls = {
'tl':true,
'tr':false,
'bl':true,
'br':true,
'ml':true,
'mt':true,
'mr':true,
'mb':true,
'mtr':true
};
fabric.Image.fromURL('http://serio.piiym.net/CVBla/txtboard/thumb/1260285874089s.jpg', function (img) {
img.top = 60;
img.left = 30;
img.setControlsVisibility(HideControls);
canvas.add(img);
});
canvas.renderAll();
function addDeleteBtn(x, y){
$(".deleteBtn").remove();
var btnLeft = x-10;
var btnTop = y-10;
var deleteBtn = '<img src="https://www.funagain.com/images/old/common/delete-icon.png" class="deleteBtn" style="position:absolute;top:'+btnTop+'px;left:'+btnLeft+'px;cursor:pointer;width:20px;height:20px;"/>';
$(".canvas-container").append(deleteBtn);
}
canvas.on('object:selected',function(e){
addDeleteBtn(e.target.oCoords.tr.x, e.target.oCoords.tr.y);
});
canvas.on('mouse:down',function(e){
if(!canvas.getActiveObject())
{
$(".deleteBtn").remove();
}
});
canvas.on('object:modified',function(e){
addDeleteBtn(e.target.oCoords.tr.x, e.target.oCoords.tr.y);
});
canvas.on('object:scaling',function(e){
$(".deleteBtn").remove();
});
canvas.on('object:moving',function(e){
$(".deleteBtn").remove();
});
canvas.on('object:rotating',function(e){
$(".deleteBtn").remove();
});
$(document).on('click',".deleteBtn",function(){
if(canvas.getActiveObject())
{
canvas.remove(canvas.getActiveObject());
$(".deleteBtn").remove();
}
});
你好,我建议使用一种不同的方法来实现此功能 它更稳定,因为它不会在 DOM 上添加元素,我们可以在任意数量的对象上使用它,我们不需要隐藏和显示自定义角按钮,每次对象处于活动状态时角按钮都可见(本机 fabricjs 函数)。
- 我将覆盖对象的原型 _drawControl 函数,以添加我的自定义角图像。
- 并覆盖 canvas 原型 _setCornerCursor ,根据哪个角结束实时更改鼠标光标。
- 我在这里做了一个直播 fiddle : https://jsfiddle.net/tornado1979/j987gb6f/
A. 首先我需要预加载自定义角图片,这样无论何时我们点击一个对象都不会有任何延迟(我使用随机图片只是为了显示).
var ctrlImages = new Array()
function preload() {
for (i = 0; i < preload.arguments.length; i++) {
ctrlImages[i] = new Image();
ctrlImages[i].src = preload.arguments[i];
}
}
preload(
"https://cdn1.iconfinder.com/data/icons/ui-color/512/Untitled-12-128.png",
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/sync-16.png",
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/write-compose-16.png",
B. 我重写了 _drawcontrol(我只展示了改变角落的片段):
switch (control)
{
case 'tl':
SelectedIconImage.src = ctrlImages[1].src;//our custom img
break;
case 'tr':
if (flipiX && !flipiY) { n='2'; }
if (!flipiX && flipiY) { n='3'; }
if (flipiX && flipiY) { n='4'; }
SelectedIconImage.src = ctrlImages[0].src;//our custom img
break;
case 'mt':
break;
case 'bl':
if (flipiY) { n='2'; }
SelectedIconImage.src = ctrlImages[3].src;//our custom img
break;
case 'br':
if (flipiX || flipiY) { n='2'; }
if (flipiX && flipiY) { n=''; }
SelectedIconImage.src = ctrlImages[2].src;//our custom img
break;
case 'mb':
break;
case 'ml':
break;
case 'mr':
break;
default:
ctx[methodName](left, top, sizeX, sizeY);
break;
}
C. 覆盖 _setCornerCursor 函数,当鼠标悬停在对象的角上时改变光标。
在函数内部,我使用了 setCursor() 函数,它实际上将 string 作为参数,所以我们可以在这里查看游标:https://www.w3.org/TR/css3-ui/#cursor
fabric.Canvas.prototype._setCornerCursor = function(corner, target) {
//for top left corner
if(corner == "tl"){
this.setCursor(this.rotationCursor); return false;
//for top right corner
}else if(corner == "tr"){
this.setCursor('pointer'); return false;
//for bottom left corner
}else if(corner == "bl"){
this.setCursor('help'); return false;
//for bottom right corner
}else if(corner == "br"){
this.setCursor('copy'); return false;
}
};
D. 最后 mouse:down 我检查角落并添加功能
canvas.on('mouse:down',函数(e){..}
希望有所帮助,祝你好运。
你的推理是正确的。
不幸的是,我在你的例子中
"TypeError: t is undefined --> fabric.min.js:1:14099"
我对您的示例进行了一些修改。
我重写了四个方法:
- _drawControl --> 绘制图标
- _setCornerCursor --> 显示光标
- _getActionFromCorner --> 按下鼠标创建动作
- _performTransformAction --> 通过鼠标移动执行动作
var canvas = new fabric.Canvas('canvas');
var DIMICON = 15;
var HideControls = {
'tl':true,
'tr':true,
'bl':true,
'br':true,
'ml':false,
'mt':false,
'mr':false,
'mb':false,
'mtr':false
};
var dataImage = [
"https://cdn1.iconfinder.com/data/icons/streamline-interface/60/cell-8-10-120.png", /*scale*/
"https://cdn1.iconfinder.com/data/icons/ui-color/512/Untitled-12-128.png", /*delete*/
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/sync-16.png", /*rotate*/
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/write-compose-16.png", /*change text*/
"https://cdn3.iconfinder.com/data/icons/social-messaging-productivity-1/128/save-16.png" /*save*/
];
//********override*****//
fabric.Object.prototype._drawControl = function(control, ctx, methodName, left, top) {
if (!this.isControlVisible(control)) {
return;
}
var SelectedIconImage = new Image();
var size = this.cornerSize;
/* fabric.isVML() ||*/ this.transparentCorners || ctx.clearRect(left, top, size, size);
switch (control)
{
case 'tl':/*delete*/
SelectedIconImage.src = dataImage[1];
break;
case 'tr':/*scale*/
SelectedIconImage.src = dataImage[0];
break;
case 'bl':/*scale*/
SelectedIconImage.src = dataImage[0];
break;
case 'br':/*rotate*/
SelectedIconImage.src = dataImage[2];
break;
default:
ctx[methodName](left, top, size, size);
}
if (control == 'tl' || control == 'tr' || control == 'bl' || control == 'br')
{
try {
ctx.drawImage(SelectedIconImage, left, top, DIMICON, DIMICON);
} catch (e) {
ctx[methodName](left, top, size, size);
}
}
}
//override prorotype _setCornerCursor to change the corner cusrors
fabric.Canvas.prototype._setCornerCursor = function(corner, target) {
if (corner === 'mtr' && target.hasRotatingPoint) {
this.setCursor(this.rotationCursor);
/*ADD*/
}else if(corner == "tr" || corner == "bl" ){
this.setCursor('sw-resize');
}else if(corner == "tl" || corner == "br"){
this.setCursor('pointer');
}
/*ADD END*/
else {
this.setCursor(this.defaultCursor);
return false;
}
};
fabric.Canvas.prototype._getActionFromCorner = function(target, corner){
var action = 'drag';
if (corner){
switch(corner){
case 'ml':
case 'mr':
action = 'scaleX';
break;
case 'mt':
case 'mb':
action = 'scaleY';
break;
case 'mtr':
action = 'rotate';
break;
/**ADD **/
case 'br':
action = 'rotate';
break;
case 'tl'://delete function if mouse down
action = 'delete';
canvas.remove(canvas.getActiveObject());
break;
/**ADD END**/
default: action = 'scale';
}
return action;
}
}
fabric.Canvas.prototype._performTransformAction = function(e, transform, pointer) {
var x = pointer.x,
y = pointer.y,
target = transform.target,
action = transform.action;
if (action === 'rotate') {
this._rotateObject(x, y);
this._fire('rotating', target, e);
}
else if (action === 'scale') {
this._onScale(e, transform, x, y);
this._fire('scaling', target, e);
}
else if (action === 'scaleX') {
this._scaleObject(x, y, 'x');
this._fire('scaling', target, e);
}
else if (action === 'scaleY') {
this._scaleObject(x, y, 'y');
this._fire('scaling', target, e);
}
/**ADD**/
else if (action === 'delete') {
//do nothing, because here function executed when mouse moves
}
/**ADD END**/
else {
this._translateObject(x, y);
this._fire('moving', target, e);
this.setCursor(this.moveCursor);
}
}
//********override END*****//
//create a rect object
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
canvas.add(rect);
rect.setControlsVisibility(HideControls);
fabric.Image.fromURL('http://serio.piiym.net/CVBla/txtboard/thumb/1260285874089s.jpg', function (img) {
img.top = 60;
img.left = 250;
img.setControlsVisibility(HideControls);
canvas.add(img);
});
canvas.renderAll();
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<div id="canvas-container" class="over">
<div class="canvas-container" style="width: 800px; height: 600px; position: relative; -webkit-user-select: none;">
<canvas id="canvas" width="800" height="600"></canvas>
</div>
</div>
此答案应更新为 fabric.js 的最新版本,该版本现在具有“控件”的概念。这似乎是现在最好的解决方案。
您好,我想使用 FabricJS 在元素中添加删除按钮。我有一个例子:
我尝试添加这部分代码,但是当我调整图像大小时,删除按钮不在原位。
http://jsfiddle.net/wxao1on8/13/
function addDeleteBtn(x, y, w){
$(".deleteBtn").remove();
var btnLeft = x;
var btnTop = y - 30;
var widthadjust=w/2;
btnLeft=widthadjust+btnLeft-1
var deleteBtn = '<img src="https://www.funagain.com/images/old/common/delete-icon.png" class="deleteBtn" ' +
'style="position:absolute;top:'+btnTop+'px;right:'+btnLeft+'px;cursor:pointer;"/>';
$(".canvas-container").append(deleteBtn);
}
canvas.on('object:selected',function(e){
addDeleteBtn(e.target.oCoords.mt.x, e.target.oCoords.mt.y, e.target.width);
});
canvas.on('mouse:down',function(e){
if(!canvas.getActiveObject())
{
$(".deleteBtn").remove();
}
});
canvas.on('object:modified',function(e){
addDeleteBtn(e.target.oCoords.mt.x, e.target.oCoords.mt.y, e.target.width);
});
canvas.on('object:moving',function(e){
$(".deleteBtn").remove();
});
$(document).on('click',".deleteBtn",function(){
if(canvas.getActiveObject())
{
canvas.remove(canvas.getActiveObject());
$(".deleteBtn").remove();
}
});
target.oCoords.mt
是中上角。
改为使用右上角:e.target.oCoords.tr
var canvas = new fabric.Canvas('canvas');
var HideControls = {
'tl':true,
'tr':false,
'bl':true,
'br':true,
'ml':true,
'mt':true,
'mr':true,
'mb':true,
'mtr':true
};
fabric.Image.fromURL('http://serio.piiym.net/CVBla/txtboard/thumb/1260285874089s.jpg', function (img) {
img.top = 60;
img.left = 30;
img.setControlsVisibility(HideControls);
canvas.add(img);
});
canvas.renderAll();
function addDeleteBtn(x, y){
$(".deleteBtn").remove();
var btnLeft = x-10;
var btnTop = y-10;
var deleteBtn = '<img src="https://www.funagain.com/images/old/common/delete-icon.png" class="deleteBtn" style="position:absolute;top:'+btnTop+'px;left:'+btnLeft+'px;cursor:pointer;width:20px;height:20px;"/>';
$(".canvas-container").append(deleteBtn);
}
canvas.on('object:selected',function(e){
addDeleteBtn(e.target.oCoords.tr.x, e.target.oCoords.tr.y);
});
canvas.on('mouse:down',function(e){
if(!canvas.getActiveObject())
{
$(".deleteBtn").remove();
}
});
canvas.on('object:modified',function(e){
addDeleteBtn(e.target.oCoords.tr.x, e.target.oCoords.tr.y);
});
canvas.on('object:scaling',function(e){
$(".deleteBtn").remove();
});
canvas.on('object:moving',function(e){
$(".deleteBtn").remove();
});
canvas.on('object:rotating',function(e){
$(".deleteBtn").remove();
});
$(document).on('click',".deleteBtn",function(){
if(canvas.getActiveObject())
{
canvas.remove(canvas.getActiveObject());
$(".deleteBtn").remove();
}
});
你好,我建议使用一种不同的方法来实现此功能 它更稳定,因为它不会在 DOM 上添加元素,我们可以在任意数量的对象上使用它,我们不需要隐藏和显示自定义角按钮,每次对象处于活动状态时角按钮都可见(本机 fabricjs 函数)。
- 我将覆盖对象的原型 _drawControl 函数,以添加我的自定义角图像。
- 并覆盖 canvas 原型 _setCornerCursor ,根据哪个角结束实时更改鼠标光标。
- 我在这里做了一个直播 fiddle : https://jsfiddle.net/tornado1979/j987gb6f/
A. 首先我需要预加载自定义角图片,这样无论何时我们点击一个对象都不会有任何延迟(我使用随机图片只是为了显示).
var ctrlImages = new Array()
function preload() {
for (i = 0; i < preload.arguments.length; i++) {
ctrlImages[i] = new Image();
ctrlImages[i].src = preload.arguments[i];
}
}
preload(
"https://cdn1.iconfinder.com/data/icons/ui-color/512/Untitled-12-128.png",
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/sync-16.png",
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/write-compose-16.png",
B. 我重写了 _drawcontrol(我只展示了改变角落的片段):
switch (control)
{
case 'tl':
SelectedIconImage.src = ctrlImages[1].src;//our custom img
break;
case 'tr':
if (flipiX && !flipiY) { n='2'; }
if (!flipiX && flipiY) { n='3'; }
if (flipiX && flipiY) { n='4'; }
SelectedIconImage.src = ctrlImages[0].src;//our custom img
break;
case 'mt':
break;
case 'bl':
if (flipiY) { n='2'; }
SelectedIconImage.src = ctrlImages[3].src;//our custom img
break;
case 'br':
if (flipiX || flipiY) { n='2'; }
if (flipiX && flipiY) { n=''; }
SelectedIconImage.src = ctrlImages[2].src;//our custom img
break;
case 'mb':
break;
case 'ml':
break;
case 'mr':
break;
default:
ctx[methodName](left, top, sizeX, sizeY);
break;
}
C. 覆盖 _setCornerCursor 函数,当鼠标悬停在对象的角上时改变光标。 在函数内部,我使用了 setCursor() 函数,它实际上将 string 作为参数,所以我们可以在这里查看游标:https://www.w3.org/TR/css3-ui/#cursor
fabric.Canvas.prototype._setCornerCursor = function(corner, target) {
//for top left corner
if(corner == "tl"){
this.setCursor(this.rotationCursor); return false;
//for top right corner
}else if(corner == "tr"){
this.setCursor('pointer'); return false;
//for bottom left corner
}else if(corner == "bl"){
this.setCursor('help'); return false;
//for bottom right corner
}else if(corner == "br"){
this.setCursor('copy'); return false;
}
};
D. 最后 mouse:down 我检查角落并添加功能 canvas.on('mouse:down',函数(e){..}
希望有所帮助,祝你好运。
你的推理是正确的。 不幸的是,我在你的例子中 "TypeError: t is undefined --> fabric.min.js:1:14099"
我对您的示例进行了一些修改。 我重写了四个方法:
- _drawControl --> 绘制图标
- _setCornerCursor --> 显示光标
- _getActionFromCorner --> 按下鼠标创建动作
- _performTransformAction --> 通过鼠标移动执行动作
var canvas = new fabric.Canvas('canvas');
var DIMICON = 15;
var HideControls = {
'tl':true,
'tr':true,
'bl':true,
'br':true,
'ml':false,
'mt':false,
'mr':false,
'mb':false,
'mtr':false
};
var dataImage = [
"https://cdn1.iconfinder.com/data/icons/streamline-interface/60/cell-8-10-120.png", /*scale*/
"https://cdn1.iconfinder.com/data/icons/ui-color/512/Untitled-12-128.png", /*delete*/
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/sync-16.png", /*rotate*/
"https://cdn2.iconfinder.com/data/icons/social-messaging-productivity-1/128/write-compose-16.png", /*change text*/
"https://cdn3.iconfinder.com/data/icons/social-messaging-productivity-1/128/save-16.png" /*save*/
];
//********override*****//
fabric.Object.prototype._drawControl = function(control, ctx, methodName, left, top) {
if (!this.isControlVisible(control)) {
return;
}
var SelectedIconImage = new Image();
var size = this.cornerSize;
/* fabric.isVML() ||*/ this.transparentCorners || ctx.clearRect(left, top, size, size);
switch (control)
{
case 'tl':/*delete*/
SelectedIconImage.src = dataImage[1];
break;
case 'tr':/*scale*/
SelectedIconImage.src = dataImage[0];
break;
case 'bl':/*scale*/
SelectedIconImage.src = dataImage[0];
break;
case 'br':/*rotate*/
SelectedIconImage.src = dataImage[2];
break;
default:
ctx[methodName](left, top, size, size);
}
if (control == 'tl' || control == 'tr' || control == 'bl' || control == 'br')
{
try {
ctx.drawImage(SelectedIconImage, left, top, DIMICON, DIMICON);
} catch (e) {
ctx[methodName](left, top, size, size);
}
}
}
//override prorotype _setCornerCursor to change the corner cusrors
fabric.Canvas.prototype._setCornerCursor = function(corner, target) {
if (corner === 'mtr' && target.hasRotatingPoint) {
this.setCursor(this.rotationCursor);
/*ADD*/
}else if(corner == "tr" || corner == "bl" ){
this.setCursor('sw-resize');
}else if(corner == "tl" || corner == "br"){
this.setCursor('pointer');
}
/*ADD END*/
else {
this.setCursor(this.defaultCursor);
return false;
}
};
fabric.Canvas.prototype._getActionFromCorner = function(target, corner){
var action = 'drag';
if (corner){
switch(corner){
case 'ml':
case 'mr':
action = 'scaleX';
break;
case 'mt':
case 'mb':
action = 'scaleY';
break;
case 'mtr':
action = 'rotate';
break;
/**ADD **/
case 'br':
action = 'rotate';
break;
case 'tl'://delete function if mouse down
action = 'delete';
canvas.remove(canvas.getActiveObject());
break;
/**ADD END**/
default: action = 'scale';
}
return action;
}
}
fabric.Canvas.prototype._performTransformAction = function(e, transform, pointer) {
var x = pointer.x,
y = pointer.y,
target = transform.target,
action = transform.action;
if (action === 'rotate') {
this._rotateObject(x, y);
this._fire('rotating', target, e);
}
else if (action === 'scale') {
this._onScale(e, transform, x, y);
this._fire('scaling', target, e);
}
else if (action === 'scaleX') {
this._scaleObject(x, y, 'x');
this._fire('scaling', target, e);
}
else if (action === 'scaleY') {
this._scaleObject(x, y, 'y');
this._fire('scaling', target, e);
}
/**ADD**/
else if (action === 'delete') {
//do nothing, because here function executed when mouse moves
}
/**ADD END**/
else {
this._translateObject(x, y);
this._fire('moving', target, e);
this.setCursor(this.moveCursor);
}
}
//********override END*****//
//create a rect object
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
canvas.add(rect);
rect.setControlsVisibility(HideControls);
fabric.Image.fromURL('http://serio.piiym.net/CVBla/txtboard/thumb/1260285874089s.jpg', function (img) {
img.top = 60;
img.left = 250;
img.setControlsVisibility(HideControls);
canvas.add(img);
});
canvas.renderAll();
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<div id="canvas-container" class="over">
<div class="canvas-container" style="width: 800px; height: 600px; position: relative; -webkit-user-select: none;">
<canvas id="canvas" width="800" height="600"></canvas>
</div>
</div>
此答案应更新为 fabric.js 的最新版本,该版本现在具有“控件”的概念。这似乎是现在最好的解决方案。