Add Delete button on an item in Canvas Fabric JS

Hi, I want to add a delete button to an element using FabricJS. I have an example:

[1]: http://i.imgur.com/kEcWKYY.png

I am trying to add this part of the code, but when the image is resized, the delete button does not stay in place.

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(); } }); 
+6
source share
3 answers

target.oCoords.mt is the average angle.

Use the upper right corner e.target.oCoords.tr : 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(); } }); 
+9
source

Hello, I would suggest a different approach to this functionality , so that it is more stable, since it does not add elements to the DOM, we can use it on as many objects as we like, we do not need to hide and show custom corner buttons and corner buttons are visible every time an object is active (fabricjs native functions).

  • I am going to override the prototype of the _drawControl object to add my own angular images.
  • And redefine the canvas prototype _setCornerCursor to change the real-time mouse pointer where the corner ended.
  • I made a live fiddle here: https://jsfiddle.net/tornado1979/j987gb6f/

A. First of all, I need to preload custom angle images, so whenever we click on an object, there will be no delay (I used random images only for shows).

 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. I redefine _drawcontrol (I only show a fragment that changes angles):

 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. Override _setCornerCursor to change the cursor when the mouse is over the corner of an object. Inside the function I use the setCursor () function, which actually takes a string as a parameter, so we can look here for cursors: 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. Finally, on the mouse: down I check the angle and add the functionality of canvas.on ('mouse: down', function (e) {..}

custom angle images and cursors

Hope helps, good luck.

+7
source

your reasoning is correct. Unfortunately, in your example, "TypeError: t - undefined → fabric.min.js: 1: 14099"

I have your example slightly modified. I exceeded four methods:

  • _drawControl → drawing icon
  • _setCornerCursor → show cursor
  • _getActionFromCorner -> create action with the mouse down
  • _performTransformAction → perform an action by moving the mouse.

 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> 
+2
source

All Articles