Cropping tool not working in Fabric 1.7+ - javascript

I'm quite new to Fabric.js and I found some fiddle that is working correctly to 1.6.7 version including. So the tool allowed me to cut the selected area of ​the photo. However, since 1.7 this function draws a path in a different location than I select on the screen. Could I ask for a clue where the cause may lie?
http://jsfiddle.net/czechue/kNEaX/852/
var canvas = new fabric.Canvas('c1', {
isDrawingMode: true
});
var src = "http://fabricjs.com/lib/pug.jpg";
canvas.backgroundColor = "#333";
canvas.freeDrawingBrush.color = "purple"
canvas.freeDrawingBrush.width = 4
var el = new fabric.Rect({
fill: 'transparent',
originX: 'left',
originY: 'top',
stroke: '#ccc',
strokeDashArray: [2, 2],
opacity: 1,
width: 1,
height: 1
});
el.visible = false;
canvas.add(el);
var object;
fabric.util.loadImage(src, function (img) {
object = new fabric.Image(img);
object.set({
selectable: false
});
object.hasRotatingPoint = true;
canvas.add(object);
canvas.renderAll();
});
canvas.on('path:created', function(options) {
var path = options.path;
canvas.isDrawingMode = false;
object.clipTo = function(ctx) {
path.set({ objectCaching: false})
path.render(ctx);
};
object.selectable = true;
disabled = true;
el.visible = false;
canvas.renderAll();
});

So i suggest since the big differences between 1.6.7 and 1.7.x, to jump directly on 2.0, but apart this i think what you need to do is to use the object as dirty after you apply a clipPath.
canvas.on('path:created', function(options) {
var path = options.path;
canvas.isDrawingMode = false;
object.clipTo = function(ctx) {
path.set({ objectCaching: false })
path.render(ctx);
};
object.selectable = true;
object.dirty = true;
disabled = true;
el.visible = false;
canvas.renderAll();
});

Related

How can i keep the Konvas layer centered after resize?

So i'm using Konvajs, to draw segments, only vertical and horizontal. My problem is the following, when i start the debug, if the window is small and then i click to maximize, the layer will stay with same size as when small. Ex:
here it s the Javascript file for the Konva creation:
function setUpDesignMap(){
var width = $('#designContainer').width();
var height = $('#designContainer').height();
var blockSnapSize = gridSize;
var isDrawing = false;
var drawShape = null;
designStage = new Konva.Stage({
container: 'designContainer',
width: width,
height: height,
draggable: true,
name:'designStage'
// dragBoundFunc: function(pos) {
// var newY = pos.y < 0 ? 0 : pos.y;
// var newX = pos.x < 0 ? 0 : pos.x;
// return {
// x: newX,
// y: newY
// };
// }
});
/*Set up grid*/
var gridLayer = new Konva.Layer();
var tipLayer = new Konva.Layer();
drawLayer = new Konva.Layer();
segGrp = new Konva.Group({name:'segments'});
/* draw grid */
for (var i = 0; i < 240; i++) {
gridLayer.add(new Konva.Line({
points: [Math.round(i * blockSnapSize), 0, Math.round(i * blockSnapSize), 120*blockSnapSize],
stroke: '#888',
strokeWidth: 0.5,
}));
}
gridLayer.add(new Konva.Line({points: [0,0,10,10]}));
for (var j = 0; j < 120; j++) {
gridLayer.add(new Konva.Line({
points: [0, Math.round(j * blockSnapSize), 240*blockSnapSize, Math.round(j * blockSnapSize)],
stroke: '#888',
strokeWidth: 0.5,
}));
}
/* Stage initial position*/
zoomVars_design = {scale: 1, factor: 1.1, origin: {x:-100*gridSize,y:-50*gridSize}};
designStage.position(zoomVars_design.origin);
designStage.scale({ x: 1, y: 1 });
/*Set up mouse tip*/
var mouseTip = new Konva.Rect({
width: 6,
height: 6,
opacity:0,
fill: '#FFCC00',
stroke: '#FFCC00'
});
tipLayer.add(mouseTip);
/* Set up length indicator */
var lenShape = new Konva.Line({
points:[],
opacity: 1,
strokeWidth: 1,
stroke: '#000',
dash:[7,5]
});
gridLayer.add(lenShape);
/* Set up length text*/
var lenText = new Konva.Text({
align: 'center',
verticalAlign: 'middle',
fontSize: 12
});
gridLayer.add(lenText);
var lenSide = new Konva.Line({
points:[],
opacity: 1,
strokeWidth: 1,
stroke: '#000',
dash:[7,5]
});
gridLayer.add(lenSide);
/*Mouse handlers*/
//remove default behaviour for the container
$("#designContainer").on('mousedown', function(e){
e.preventDefault();
});
$("#designContainer").on('contextmenu', function(e){
e.preventDefault();
});
//mouse handlers on stage
designStage.on('contentMousedown', function (e) {
if(e.evt.button == 0){
designStage.draggable(false);
}
else{
designStage.draggable(true);
return;
}
if(mouseMode == 0){ //draw mode
var startPoint = {x:(mouseTip.attrs.x+3), y:(mouseTip.attrs.y+3)};
if(!isDrawing){
if(!isIntersection(startPoint)){ //cannot be intersection to avoid crosses
//check if there's an intersection on the begining
if(isCorner(startPoint)){
intersectingSeg = false;
intersectingCorner = true;
console.log('intersectingSeg = false')
}
else{
//check if its on an existing segment
var isonseg = isOnSegment(startPoint);
if(isonseg.b){
//validate resulting segments length
var intercected = getSegmentsArray(isonseg.d)[isonseg.i];
var res = breakSegAtIntersection(startPoint,intercected);
if(isValidLength(res[0].start,res[0].end) && isValidLength(res[1].start,res[1].end)){
intersectingSeg = true;
intersectingCorner = false;
console.log('>>>>>>>Intersecting Segment at start')
}
else{
consoleAdd('Resulting segments are too small to you');
return;
}
}
}
//start drawing
drawShape = new Konva.Line({
points: [startPoint.x, startPoint.y],
strokeWidth: 15,
stroke: 'black',
opacity: 0.5,
dir:'h'
});
drawShape.on('mouseover', function (e) {
if(mouseMode == 1){
this.stroke('#031C4D');
drawLayer.draw();
}
});
drawShape.on('mouseout', function (e) {
if(mouseMode == 1){
this.stroke('black');
drawLayer.draw();
}
});
drawShape.on('mouseup', function (e) {
if(mouseMode == 1){
this.stroke('#092C70');
drawLayer.draw();
}
});
segGrp.add(drawShape);
drawLayer.add(segGrp);
drawLayer.draw();
isDrawing = true;
}
else{
consoleAdd('Cannot start on an intersection');
}
gridLayer.draw();
}
}
});
So i want the layer not to be that small, always refitting. Thank you all for your attention!
You need to manually resize your stage when the size of container element is changed.
window.addEventListener('resize', () => {
var width = $('#designContainer').width();
var height = $('#designContainer').height();
designStage.width(width);
designStage.height(height);
})
You may also need to apply the scale to the stage.
Take a look into related demo: https://konvajs.org/docs/sandbox/Responsive_Canvas.html

fabric js Polygon setCoords

I've got a question about fabric.js and the polygon-object.
I have an example of my problem in this fiddle:
Click me
First 4 fabric.Circle subobjects called linePoint are drawn.
The linePoint objects have an extra x(same as left) and y(same as top) coordinate and a reference to which polygon they belong to:
fabric.LinePoint = fabric.util.createClass(fabric.Circle,
{
initialize: function (options) {
options || (options = {});
this.callSuper('initialize', options);
options &&
this.set('type', 'line_point'),
this.set('x', this.left),
this.set('y', this.top),
this.set('polygon', options.polygon)
},
setPointCoordinates: function(new_left, new_top) {
this.set('x', new_left);
this.set('y', new_top);
this.set('left', new_left);
this.set('top', new_top);
}
With the now given x and y coordinates there is a Polygon drawn between the Points.
The problem is now, when you move the Circles, the Polygon is moved correctly, but its border (or I don't know how to exactly call it) will stay the same small rectangle as it was.
I want to update the polygon Coords too, I tried .setCoords(), but nothing happened.
Maybe you can help me. :) Thanks!
In reply also to:
https://groups.google.com/forum/#!topic/fabricjs/XN1u8E0EBiM
This is your modified fiddle:
https://jsfiddle.net/wum5zvwk/2/
fabric.LinePoint = fabric.util.createClass(fabric.Circle,
{
initialize: function (options) {
options || (options = {});
this.callSuper('initialize', options);
this.set('type', 'line_point'),
this.set('x', this.left),
this.set('y', this.top),
this.set('polygon', options.polygon)
},
setPointCoordinates: function(new_left, new_top) {
this.set('x', new_left);
this.set('y', new_top);
this.set('left', new_left);
this.set('top', new_top);
}
});
var canvas = new fabric.Canvas('canvas');
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
document.getElementById("canvas").tabIndex = 1000;
drawPolygonToCanvas();
canvas.on('object:moving', function(event) {
var object = event.target;
switch(object.type) {
case 'line_point':
//move polygon
object.setPointCoordinates(object.left, object.top);
object.polygon.setCoords();
object.polygon.initialize(object.polygon.points);
object.polygon.top += object.polygon.height / 2;
object.polygon.left += object.polygon.width / 2;
canvas.renderAll();
break;
}
});
function drawPolygonToCanvas()
{
//creting end_points and set them
old_position = canvas.getPointer(event.e);
var end_point_1 = createLinePoint(100, 100);
var end_point_2 = createLinePoint(100, 150);
var end_point_3 = createLinePoint(150, 150);
var end_point_4 = createLinePoint(150, 100);
end_points_in_use = [end_point_1, end_point_2, end_point_3, end_point_4];
canvas.add(end_point_1, end_point_2, end_point_3, end_point_4);
drawPoly(end_points_in_use);
canvas.deactivateAll();
canvas.renderAll();
}
function drawPoly(point_array)
{
var poly = new fabric.Polygon(point_array, {
left: (100 + ((150 - 100) /2)),
top: (100 + ((150 - 100) /2)),
fill: 'lightblue',
lockScalingX: true,
lockScalingY: true,
lockMovementX: true,
lockMovementY: true,
perPixelTargetFind: true,
opacity: 0.5,
type: 'polygon'
});
for (var i = 0; i < point_array.length; i++) {
point_array[i].polygon = poly;
}
canvas.add(poly);
poly.sendToBack();
}
function createLinePoint(left, top) {
return new fabric.LinePoint({
left: left,
top: top,
strokeWidth: 2,
radius: 15,
fill: '#fff',
stroke: '#666',
related_poly_point: 0,
lockScalingX: true,
lockScalingY: true
});
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<div id="canvas-wrapper" style="position:relative;width:704px;float:left;">
<canvas id="canvas" width="700" height="600" style="border:1px solid #000000;"></canvas>
</div>
Modifying the polygon points is not enough to have the bounding box adjusted. The easies thing i can think of is to re initialize the polygon with the new points coordinates.

Why I cannot draw multiple curved arrows with sceneFunc() in Shape object using KonvaJS?

I am inspired with KonvaJS tutorial Modify Curves with Anchor Points to make my own example which is to create multiple custom arrows.
on click on the selectionBox create an anchor.
on the creation of the third anchor create the curved arrow.
on the fourth click reset all variables in order to draw a new curved arrow.
var width = window.innerWidth;
var height = window.innerHeight;
// globals
var selectionBoxLayer, curveLayer, lineLayer, anchorLayer, quad, bezier;
function updateDottedLines() {
var q = quad;
var quadLine = lineLayer.get('#quadLine')[0];
quadLine.setPoints([q.start.attrs.x, q.start.attrs.y, q.control.attrs.x, q.control.attrs.y, q.end.attrs.x, q.end.attrs.y]);
lineLayer.draw();
}
function buildAnchor(x, y) {
var anchor = new Konva.Circle({
x: x,
y: y,
radius: 20,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
draggable: true
});
// add hover styling
anchor.on('mouseover', function() {
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
anchorLayer.draw();
});
anchor.on('mouseout', function() {
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
anchorLayer.draw();
});
anchor.on('dragend', function() {
drawCurves();
updateDottedLines();
});
anchorLayer.add(anchor);
anchorLayer.draw();
return anchor;
}
function drawCurves() {
var context = curveLayer.getContext();
var arrowLine = new Konva.Shape({
sceneFunc: function(context){
debugger;
// draw quad
context.beginPath();
context.moveTo(quad.start.attrs.x, quad.start.attrs.y);
context.quadraticCurveTo(quad.control.attrs.x, quad.control.attrs.y, quad.end.attrs.x, quad.end.attrs.y);
//Draw Arrow Head
var headlen = 10; // length of head in pixels
var angle = Math.atan2(quad.end.attrs.y - quad.control.attrs.y, quad.end.attrs.x - quad.control.attrs.x);
context.lineTo(quad.end.attrs.x-headlen*Math.cos(angle-Math.PI/6), quad.end.attrs.y-headlen*Math.sin(angle-Math.PI/6));
context.moveTo(quad.end.attrs.x, quad.end.attrs.y);
context.lineTo(quad.end.attrs.x- headlen*Math.cos(angle+Math.PI/6), quad.end.attrs.y-headlen*Math.sin(angle+Math.PI/6));
context.fillStrokeShape(this);
},
stroke: 'black',
strokeWidth: 4
});
curveLayer.add(arrowLine);
curveLayer.draw();
}
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
selectionBoxLayer = new Konva.Layer();
anchorLayer = new Konva.Layer();
lineLayer = new Konva.Layer();
// curveLayer just contains a canvas which is drawn
// onto with the existing canvas API
curveLayer = new Konva.Layer();
var quadLine = new Konva.Line({
dash: [10, 10, 0, 10],
strokeWidth: 3,
stroke: 'black',
lineCap: 'round',
id: 'quadLine',
opacity: 0.3,
points: [0, 0]
});
// add dotted line connectors
lineLayer.add(quadLine);
quad = {};
// keep curves insync with the lines
anchorLayer.on('beforeDraw', function() {
if(quad.start && quad.control && quad.end){
drawCurves();
updateDottedLines();
}
});
var selectionBoxBackground = new Konva.Rect({
x: 0,
y: 0,
height:stage.height(),
width: stage.width(),
fill: 'transparent',
draggable: false,
name: 'selectionBoxBackground'
});
selectionBoxLayer.add(selectionBoxBackground);
var clickCounter = 0;
selectionBoxBackground.on("click", function(){
clickCounter +=1;
var mousePos = {};
switch(clickCounter){
case 1:
mousePos = stage.getPointerPosition();
quad.start = buildAnchor(mousePos.x, mousePos.y);
break;
case 2:
mousePos = stage.getPointerPosition();
quad.control = buildAnchor(mousePos.x, mousePos.y);
break;
case 3:
mousePos = stage.getPointerPosition();
quad.end = buildAnchor(mousePos.x, mousePos.y);
drawCurves();
updateDottedLines();
break;
default:
clickCounter = 0;
quad = {};
anchorLayer.destroyChildren();
anchorLayer.draw();
}
});
stage.add(curveLayer);
stage.add(lineLayer);
stage.add(selectionBoxLayer);
stage.add(anchorLayer);
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
<script src="https://cdn.rawgit.com/konvajs/konva/0.11.1/konva.min.js"></script>
<body>
<div id="container"></div>
</body>
P.S Please note when I write in the browser console curveLayer.children, it will bring all created curved arrows.
Hint: I think on the creation of new Shape() the values of all created shapes will be changed to the new one.
I don't know what I am missing.

How to free draw circle using Fabric.js?

I am using FabricJS to draw circle in canvas:
var circle = new fabric.Circle({radius: 100,
fill: '',
stroke: 'red',
strokeWidth: 3,
originX: 'center',
originY: 'center'
});
var text = new fabric.Text('HELLO WORLD.',
{ fontSize: 30,
originX: 'center',
originY: 'center',
fill : 'red'
});
var group = new fabric.Group([ circle, text ], {
left: 150, top: 100
});
canvas.add(group);
This code draws a normal circle but I need to freely draw circle with mouse.
Any code help will be appreciated.
According to your previous code for drawing rectangle http://jsfiddle.net/Subhasish2015/8u1cqasa/2/ Here is the code for drawing circle:
$(document).ready(function(){
//Getting the canvas
var canvas1 = new fabric.Canvas("canvas2");
var freeDrawing = true;
var divPos = {};
var offset = $("#canvas2").offset();
$(document).mousemove(function(e){
divPos = {
left: e.pageX - offset.left,
top: e.pageY - offset.top
};
});
$('#2').click(function(){
//Declaring the variables
var isMouseDown=false;
var refCircle;
//Setting the mouse events
canvas1.on('mouse:down',function(event){
isMouseDown=true;
if(freeDrawing) {
var circle=new fabric.Circle({
left:divPos.left,
top:divPos.top,
radius:0,
stroke:'red',
strokeWidth:3,
fill:''
});
canvas1.add(circle);
refCircle=circle; //**Reference of rectangle object
}
});
canvas1.on('mouse:move', function(event){
if(!isMouseDown)
{
return;
}
//Getting yhe mouse Co-ordinates
if(freeDrawing) {
var posX=divPos.left;
var posY=divPos.top;
refCircle.set('radius',Math.abs((posX-refCircle.get('left'))));
canvas1.renderAll();
}
});
canvas1.on('mouse:up',function(){
canvas1.add(refCircle);
//alert("mouse up!");
isMouseDown=false;
//freeDrawing=false; // **Disables line drawing
});
});
});
var Circle = (function() {
function Circle(canvas) {
this.canvas = canvas;
this.className = 'Circle';
this.isDrawing = false;
this.bindEvents();
}
Circle.prototype.bindEvents = function() {
var inst = this;
inst.canvas.on('mouse:down', function(o) {
inst.onMouseDown(o);
});
inst.canvas.on('mouse:move', function(o) {
inst.onMouseMove(o);
});
inst.canvas.on('mouse:up', function(o) {
inst.onMouseUp(o);
});
inst.canvas.on('object:moving', function(o) {
inst.disable();
})
}
Circle.prototype.onMouseUp = function(o) {
var inst = this;
inst.disable();
};
Circle.prototype.onMouseMove = function(o) {
var inst = this;
if (!inst.isEnable()) {
return;
}
var pointer = inst.canvas.getPointer(o.e);
var activeObj = inst.canvas.getActiveObject();
activeObj.stroke = 'red',
activeObj.strokeWidth = 5;
activeObj.fill = 'red';
if (origX > pointer.x) {
activeObj.set({
left: Math.abs(pointer.x)
});
}
if (origY > pointer.y) {
activeObj.set({
top: Math.abs(pointer.y)
});
}
activeObj.set({
rx: Math.abs(origX - pointer.x) / 2
});
activeObj.set({
ry: Math.abs(origY - pointer.y) / 2
});
activeObj.setCoords();
inst.canvas.renderAll();
};
Circle.prototype.onMouseDown = function(o) {
var inst = this;
inst.enable();
var pointer = inst.canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
var ellipse = new fabric.Ellipse({
top: origY,
left: origX,
rx: 0,
ry: 0,
transparentCorners: false,
hasBorders: false,
hasControls: false
});
inst.canvas.add(ellipse).setActiveObject(ellipse);
};
Circle.prototype.isEnable = function() {
return this.isDrawing;
}
Circle.prototype.enable = function() {
this.isDrawing = true;
}
Circle.prototype.disable = function() {
this.isDrawing = false;
}
return Circle;
}());
var canvas = new fabric.Canvas('canvas');
var circle = new Circle(canvas);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
Please draw circle here
<div id="canvasContainer">
<canvas id="canvas" width="400" height="400" style="border: solid 1px"></canvas>
</div>
Here is the detail blog with jsfiddle - https://blog.thirdrocktechkno.com/sketching-circle-of-a-html5-canvas-using-the-fabricjs-f7dbfa20cf2d
Here is an example of drawing a rectangle which I carefully made and works for latest versions. Krunan example seems to work OK- just in case this is a rectangle free drawing implementation that doesn't use DOM apis like e.clientX, offsetLeft, etc to track the coords, but fabric.js APIs only which I think is safer. Also unregister event listeners - I'm still trying to refine it since I need free drawing support for my project - Since there is no official support for this I wanted to reference the example here for others.
https://cancerberosgx.github.io/demos/misc/fabricRectangleFreeDrawing.html
An easy way to add a Circle to a canvas:
canvas.add(new fabric.Circle({ radius: 30, fill: "green", top: 100, left: 100 }));

Fabric.js Sticky Note type text wrap

I am trying to make a sticky note type utility with the fabric canvas. It will help to be used as annotators.
I want the text to wrap by itself at the given rectangle's width.
Can someone update my fiddle work??
Suggestions are appreciated. Regards...
The following is the link to a part of my fiddle:
http://jsfiddle.net/U7E9q/5/
var canvas = new fabric.Canvas('fabric-canvas');
canvas.hoverCursor = 'pointer';
var text = new fabric.IText("Enter Text Here ",{
fontSize: 20,
top: 100,
left: 100,
backgroundColor: '#faa',
lockScalingX: true,
lockScalingY: true,
selectable: true
});
//alert(text.text);
var rect = new fabric.Rect({
text_field: text,
width: 200,
height: 50,
fill: '#faa',
rx: 10,
ry: 10,
top: 100,
left: 100
});
canvas.add(rect);
canvas.add(text);
canvas.on('object:moving', function (event){
canvas.renderAll();
});
createListenersKeyboard();
function createListenersKeyboard() {
document.onkeydown = onKeyDownHandler;
//document.onkeyup = onKeyUpHandler;
}
function onKeyDownHandler(event) {
//event.preventDefault();
var key;
if(window.event){
key = window.event.keyCode;
}
else{
key = event.keyCode;
}
switch(key){
//////////////
// Shortcuts
//////////////
// Copy (Ctrl+C)
case 67: // Ctrl+C
if(ableToShortcut()){
if(event.ctrlKey){
event.preventDefault();
copy();
}
}
break;
// Delete (Ctrl+D)
case 127: // Ctrl+D
if(ableToShortcut()){
if(event.deleteKey){
delet();
}
}
break;
// Paste (Ctrl+V)
case 86: // Ctrl+V
if(ableToShortcut()){
if(event.ctrlKey){
event.preventDefault();
paste();
}
}
break;
default:
// TODO
break;
}
}
function ableToShortcut(){
/*
TODO check all cases for this
if($("textarea").is(":focus")){
return false;
}
if($(":text").is(":focus")){
return false;
}
*/
return true;
}
function copy(){
if(canvas.getActiveGroup()){
for(var i in canvas.getActiveGroup().objects){
var object = fabric.util.object.clone(canvas.getActiveGroup().objects[i]);
object.set("top", object.top+5);
object.set("left", object.left+5);
copiedObjects[i] = object;
}
}
else if(canvas.getActiveObject()){
var object = fabric.util.object.clone(canvas.getActiveObject());
object.set("top", object.top+5);
object.set("left", object.left+5);
copiedObject = object;
copiedObjects = new Array();
}
}
function paste(){
if(copiedObjects.length > 0){
for(var i in copiedObjects){
canvas.add(copiedObjects[i]);
}
}
else if(copiedObject){
canvas.add(copiedObject);
}
canvas.renderAll();
}
function delet(){
var activeObject = canvas.getActiveObject();
canvas.remove(activeObject);
console.log('after remove getActiveObject(): ', canvas.getActiveObject(), activeObject === canvas.getActiveObject());
canvas.renderAll();
}
If you manage the sticky note as a grouped rect and text you can improve the same behavior. When you need to edit the text inside the group, you just ungroup and clone the elements, append the cloned elements to the canvas and set text as editable.
You need to handle an event like double click to handle this behavior and then handle the mousedown or other interactivity with canvas to regroup them.
http://jsfiddle.net/4HE3U/1/
Above is one fiddle that can satisfy you
Basically i have made one group of Text and Rectangle and i have added it to canvas. There is only one change you need to make is that you can take one textbox to get current sticky note text content as we can not edit text of i-text online once we are adding it any group. Currently there is no way for IText to handle the events as they are not handed down to it if it's contained in a group. I think this is also the prefered way to handle that as it would confuse the user - what if he starts to edit multiple texts. This might end up in a mess. Maybe you can rework your script a little to workaround this problems.
I have added Text and Rectangle
var canvas = new fabric.Canvas('fabric-canvas');
canvas.hoverCursor = 'pointer';
var text = new fabric.IText("Enter Text Here ",{
fontSize: 20,
top: 100,
left: 100,
backgroundColor: '#faa',
lockScalingX: true,
lockScalingY: true,
selectable: true
});
//alert(text.text);
var rect = new fabric.Rect({
text_field: text,
width: 200,
height: 50,
fill: '#faa',
rx: 10,
ry: 10,
top: 100,
left: 100
});
var group = new fabric.Group([ rect, text ], {
left: 100,
top: 100,
lockScalingX: true,
lockScalingY: true,
hasRotatingPoint: false,
transparentCorners: false,
cornerSize: 7
});
canvas.add(group);
//canvas.add(text);
canvas.on('object:moving', function (event){
canvas.renderAll();
});
createListenersKeyboard();
function createListenersKeyboard() {
document.onkeydown = onKeyDownHandler;
//document.onkeyup = onKeyUpHandler;
}
function onKeyDownHandler(event) {
//event.preventDefault();
var key;
if(window.event){
key = window.event.keyCode;
}
else{
key = event.keyCode;
}
switch(key){
//////////////
// Shortcuts
//////////////
// Copy (Ctrl+C)
case 67: // Ctrl+C
if(ableToShortcut()){
if(event.ctrlKey){
event.preventDefault();
copy();
}
}
break;
// Delete (Ctrl+D)
case 127: // Ctrl+D
if(ableToShortcut()){
if(event.deleteKey){
delet();
}
}
break;
// Paste (Ctrl+V)
case 86: // Ctrl+V
if(ableToShortcut()){
if(event.ctrlKey){
event.preventDefault();
paste();
}
}
break;
default:
// TODO
break;
}
}
function ableToShortcut(){
/*
TODO check all cases for this
if($("textarea").is(":focus")){
return false;
}
if($(":text").is(":focus")){
return false;
}
*/
return true;
}
function copy(){
if(canvas.getActiveGroup()){
for(var i in canvas.getActiveGroup().objects){
var object = fabric.util.object.clone(canvas.getActiveGroup().objects[i]);
object.set("top", object.top+5);
object.set("left", object.left+5);
copiedObjects[i] = object;
}
}
else if(canvas.getActiveObject()){
var object = fabric.util.object.clone(canvas.getActiveObject());
object.set("top", object.top+5);
object.set("left", object.left+5);
copiedObject = object;
copiedObjects = new Array();
}
}
function paste(){
if(copiedObjects.length > 0){
for(var i in copiedObjects){
canvas.add(copiedObjects[i]);
}
}
else if(copiedObject){
canvas.add(copiedObject);
}
canvas.renderAll();
}
function delet(){
var activeObject = canvas.getActiveObject();
canvas.remove(activeObject);
console.log('after remove getActiveObject(): ', canvas.getActiveObject(), activeObject === canvas.getActiveObject());
canvas.renderAll();
}
<canvas id="fabric-canvas" width="400" height="400"></canvas>
Here is Sticky note functionality. Text wrap working and font size changes w.r.t sticky note width and height. Editing mode activates on double click.
export const createStickyNotes = (canvas, options) => {
fabric.StickyNote = fabric.util.createClass(fabric.Group, {
type: "StickyNote",
initialize: function (options) {
this.set(options);
var height = this.height;
var width = this.width;
this.rectObj = new fabric.Rect({
width: width,
height: height,
fill: this.rectObj?.fill ?? "rgba(251,201,112,1)",
originX: "center",
originY: "center",
objectCaching: false,
stateProperties: ["fill"],
});
this.textObj = new fabric.Textbox(this.textObj?.text ?? "Notes", {
originX: "center",
originY: "center",
textAlign: "center",
width: 100,
hasControls: false,
fontSize: this.textObj?.fontSize ?? 30,
lineHeight: 1,
stateProperties: ["text", "fontSize"],
scaleX: this.textObj?.scaleX ?? 1,
scaleY: this.textObj?.scaleY ?? 1,
objectCaching: false,
breakWords: true,
fontFamily: "Open Sans",
});
this._objects = [this.rectObj, this.textObj];
// this custom _set function will set custom properties value to object when it will load from json.
// at that time loadFromJson function will call this initialize function.
// this._setCustomProperties(this.options);
canvas.renderAll();
//evenet will fire if the object is double clicked by mouse
this.on("mousedblclick", (e) => {
var pasteFlag = false;
var scaling = e.target.getScaledWidth() / 100;
var textForEditing;
canvas.bringToFront(e.target);
e.target.selectable = false;
const [rectObj, textObj] = this.getObjects();
textObj.clone(function (clonedObj) {
clonedObj.set({
left: e.target.left,
top: e.target.top,
lockMovementY: true,
lockMovementX: true,
hasBorders: false,
scaleX: scaling,
scaleY: scaling,
breakWords: true,
width: textObj.width,
stateProperties: [],
});
textForEditing = clonedObj;
});
this.remove(textObj);
canvas.add(textForEditing);
canvas.setActiveObject(textForEditing);
textForEditing.enterEditing();
textForEditing.selectAll();
textForEditing.paste = (function (paste) {
return function (e) {
disableScrolling();
pasteFlag = true;
};
})(textForEditing.paste);
textForEditing.on("changed", function (e) {
var fontSize = textForEditing.fontSize;
var charCount = Math.max(textForEditing._text.length, 1);
var charWR =
(textForEditing.textLines.length * width) / (charCount * fontSize);
if (textForEditing.height < height - 15) {
fontSize = Math.min(
Math.sqrt(
((height - 10 - fontSize) / 1.16) *
(width / (charCount * charWR))
),
30
);
}
if (textForEditing.height > height - 15) {
fontSize = Math.sqrt(
((height - 10) / 1.16) * (width / (charCount * charWR))
);
}
if (pasteFlag) {
pasteFlag = false;
while (
textForEditing.height > height - 15 &&
textForEditing.fontSize > 0
) {
fontSize = textForEditing.fontSize -= 0.2;
canvas.renderAll();
}
}
textForEditing.fontSize = fontSize;
});
textForEditing.on("editing:exited", () => {
enableScrolling();
canvas.setActiveObject(textObj);
textObj.set({
text: textForEditing.text,
fontSize: textForEditing.fontSize,
visible: true,
});
this.add(textObj);
this.selectable = true;
canvas.remove(textForEditing);
canvas.discardActiveObject();
});
});
function disableScrolling() {
var x = window.scrollX;
var y = window.scrollY;
window.onscroll = function () {
window.scrollTo(x, y);
};
}
var _wrapLine = function (_line, lineIndex, desiredWidth, reservedSpace) {
var lineWidth = 0,
splitByGrapheme = this.splitByGrapheme,
graphemeLines = [],
line = [],
// spaces in different languges?
words = splitByGrapheme
? fabric.util.string.graphemeSplit(_line)
: _line.split(this._wordJoiners),
word = "",
offset = 0,
infix = splitByGrapheme ? "" : " ",
wordWidth = 0,
infixWidth = 0,
largestWordWidth = 0,
lineJustStarted = true,
additionalSpace = splitByGrapheme ? 0 : this._getWidthOfCharSpacing();
reservedSpace = reservedSpace || 0;
desiredWidth -= reservedSpace;
for (var i = 0; i < words.length; i++) {
// i would avoid resplitting the graphemes
word = fabric.util.string.graphemeSplit(words[i]);
wordWidth = this._measureWord(word, lineIndex, offset);
offset += word.length;
// Break the line if a word is wider than the set width
if (this.breakWords && wordWidth >= desiredWidth) {
if (!lineJustStarted) {
graphemeLines.push(line);
line = [];
lineWidth = 0;
lineJustStarted = true;
}
this.fontSize *= desiredWidth / (wordWidth + 1);
// Loop through each character in word
for (var w = 0; w < word.length; w++) {
var letter = word[w];
var letterWidth =
(this.getMeasuringContext().measureText(letter).width *
this.fontSize) /
this.CACHE_FONT_SIZE;
line.push(letter);
lineWidth += letterWidth;
}
word = [];
} else {
lineWidth += infixWidth + wordWidth - additionalSpace;
}
if (lineWidth >= desiredWidth && !lineJustStarted) {
graphemeLines.push(line);
line = [];
lineWidth = wordWidth;
lineJustStarted = true;
} else {
lineWidth += additionalSpace;
}
if (!lineJustStarted) {
line.push(infix);
}
line = line.concat(word);
infixWidth = this._measureWord([infix], lineIndex, offset);
offset++;
lineJustStarted = false;
// keep track of largest word
if (wordWidth > largestWordWidth && !this.breakWords) {
largestWordWidth = wordWidth;
}
}
i && graphemeLines.push(line);
if (largestWordWidth + reservedSpace > this.dynamicMinWidth) {
this.dynamicMinWidth =
largestWordWidth - additionalSpace + reservedSpace;
}
return graphemeLines;
};
fabric.util.object.extend(fabric.Textbox.prototype, {
_wrapLine: _wrapLine,
});
function enableScrolling() {
window.onscroll = function () {};
}
},
toObject: function (propertiesToInclude) {
// This function is used for serialize this object. (used for create json)
// not inlclude this.textObj and this.rectObj into json because when object will load from json, init fucntion of this class is called and it will assign this two object textObj and rectObj again.
var obj = this.callSuper(
"toObject",
[
"objectCaching",
"textObj",
"rectObj",
// ... property list that you want to add into json when this object is convert into json using toJSON() function. (serialize)
].concat(propertiesToInclude)
);
// delete objects array from json because then object load from json, Init function will call. which will automatically re-assign object and assign _object array.
delete obj.objects;
return obj;
},
});
fabric.StickyNote.async = true;
fabric.StickyNote.fromObject = function (object, callback) {
// This function is used for deserialize json and convert object json into button object again. (called when we call loadFromJson() fucntion on canvas)
return fabric.Object._fromObject("StickyNote", object, callback);
};
return new fabric.StickyNote(options);
};
//How to use
var options = {
width: 100,
height: 100,
originX: "center",
originY: "center",
};
var notes = StickyNotes(canvas, options);
canvas.add(notes);

Categories