I am trying to do an interactive drawing using Fabric.js. Now I can draw a rectangle by using mouse. But after I finish drawing the rectangle, I can not select it unless I resize it once by using the left top controller after drawing. I wonder what break the event system.
Here is my code: http://jsfiddle.net/rmFgX/1/
var canvas = new fabric.Canvas('canvas');
var rect = new fabric.Rect({
top : 100,
left : 100,
width : 60,
height : 70,
fill : 'red'
});
canvas.add(rect);
canvas.on('mouse:down', function (option) {
console.log(option);
if (typeof option.target != "undefined") {
return;
} else {
var startY = option.e.offsetY,
startX = option.e.offsetX;
console.log(startX, startY);
var rect2 = new fabric.Rect({
top : startY,
left : startX,
width : 0,
height : 0,
fill : 'transparent',
stroke: 'red',
strokewidth: 4
});
canvas.add(rect2);
console.log("added");
canvas.on('mouse:move', function (option) {
var e = option.e;
rect2.set('width', e.offsetX - startX);
rect2.set('height', e.offsetY - startY);
rect2.saveState();
});
}
});
canvas.on('mouse:up', function () {
canvas.off('mouse:move');
});
updated the fiddle http://jsfiddle.net/rmFgX/5/
For an object when changing position/dimension -related properties (left, top, scale, angle, etc.) set does not update position of object's borders/controls.
If you need to update those, call:
setCoords().
Related
I loaded multiple objects from svg by following code:
var canvas = new fabric.Canvas('drawing');
fabric.loadSVGFromURL('images/circle.svg', function(objects) {
canvas.add.apply(canvas, objects);
canvas.forEachObject(function(o){ o.hasBorders = o.hasControls = false;
});
canvas.renderAll();
});
Now I want to free draw only inside one object(like the image below).How can I achieve that using fabric.js?
You can mask the canvas (using canvas.clipTo) and make it match your SVG form. If it is a circle as in your example image it would be simple.
See the following example:
// define a drawing canvas
const canvas = new fabric.Canvas(
'drawing',
{ isDrawingMode: true }
);
canvas.freeDrawingBrush.color = 'red'
// create a circle (here you could load your svg circle instead)
const circle = new fabric.Circle({
top: 25,
left: 25,
radius: 50,
fill: 'transparent',
stroke: 'black'
});
// create the clipping mask using the circle coordinates
canvas.clipTo = function (ctx) {
ctx.arc(
circle.top + circle.radius,
circle.left + circle.radius,
circle.radius + 1, // +1px for the circle stroke
circle.radius + 1,
Math.PI*2,
true
)
}
// add the circle to the canvas
canvas.add(circle);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.5/fabric.min.js"></script>
<canvas id="drawing" width="600" height="600"></canvas>
Check this fiddle setting width and height to circle object:
circle.set({ radius: Math.abs(origX - pointer.x), width : 50,height :50 });
so bounding box will be 50*50
its drawing fine for fabricjs 1.5.0, but not in newer version.
I want to draw outside bounding box, how can I acheive it?
var canvas = new fabric.Canvas('c', { selection: false });
var circle, isDown, origX, origY;
canvas.on('mouse:down', function(o){
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
circle = new fabric.Circle({
left: pointer.x,
top: pointer.y,
radius: 1,
strokeWidth: 5,
stroke: 'red',
objectCaching : false,
originX: 'center', originY: 'center'
});
canvas.add(circle);
});
canvas.on('mouse:move', function(o){
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
circle.set({ radius: Math.abs(origX - pointer.x), width : 50,height :50 });
canvas.renderAll();
});
canvas.on('mouse:up', function(o){
isDown = false;
});
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
use objectCaching:false to get the desired effect.
Just change this...
circle.set({ radius: Math.abs(origX - pointer.x), width : 50,height :50 });
To this...
var newAbsValue = Math.abs(origX - pointer.x);
circle.set({ radius: newAbsValue, width : newAbsValue * 2, height : newAbsValue * 2 });
Here's your JSFiddle updated, http://jsfiddle.net/rekrah/8rb8Lahb/.
I was also facing the same problem. In my case i was trying to draw line outside the bounding box of rectangle. I have validated from other examples that it is not possible to do it in new versions of fabric js. This makes sense as it might cause some other problems. So, you have to figure out something else. In my case i am using path to fulfill my requirement.
i use zoomtopoint and zoom to object. but not working with background.
I want to zoom in on the object and the background. How can i ?
Example :
object size : 100x100 not zoom.
after zoom object size :100x100
after zoom and The grid remained the same.
zoom event
document.addEventListener('wheel', function(event) {
var evt = window.event || event;
var delta = evt.detail? evt.detail*(-120) : evt.wheelDelta;
var curZoom = canvas.getZoom();
var newZoom = curZoom + delta / 4000;
var x = event.offsetX;
var y = event.offsetY;
canvas.zoomToPoint({ x: x, y: y }, newZoom);
if(event != null)event.preventDefault();
return false;
canvas.calcOffset();
}, false);
grid code:
canvas.setBackgroundColor({source: src, repeat: 'repeat'}, canvas.renderAll.bind(canvas), function () {
canvas.renderAll();
proceed();
});
EDİT
#canvas-background { display: inline-block; position: absolute; top: 0; right: 0; }
but i dont use css.
Background image
Thank you
Sorry, i cant speak english well.
Relevant code is below (and the rest is in the JSFiddle), note the canvas size and CSS container trick used to allow zooming out.
Example : object size : 100x100 not zoom.
after zoom object size :100x100
after zoom and The grid is no longer the same.
fabric.Image.fromURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAASdEVYdFNvZnR3YXJlAEdyZWVuc2hvdF5VCAUAAADLSURBVFhH7ZnBCoMwEET9/68URBHSNj0UolFoI+aQickKlT05jz0MGQIPkb2kadu3ta42ff/MTtLRazct55bajOMjO0lHr920vnWMMTGV0GuphVALoRaiqNV1dq4TLsdUIrTe+z0fw+ndmEo0w/D61AmXYyqh1179WjGVuNLyl0eohVALuZ8Wtzwgt9zyiNxSC6EWQi1EUYtbHpBbbnlEbqmFUAuhFqKoxS0PyC23PCK31EKohVAL0dXK3vLSOX0TnKZ1z8fw/3uiW37L27QIZwrV4gAAAABJRU5ErkJggg==', function(img) {
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(img);
var pattern = new fabric.Pattern({
source: function() {
patternSourceCanvas.setDimensions({
width: img.getWidth(),
height: img.getHeight()
});
return patternSourceCanvas.getElement();
},
repeat: 'repeat'
});
var background = new fabric.Rect({
width: canvas.getWidth(),
height: canvas.getHeight(),
fill: pattern,
selectable: false
});
canvas.add(background, rectangle);
canvas.renderAll();
});
Here's a working JSFiddle, https://jsfiddle.net/rekrah/86t2b8bs/.
Hope this helps.
UPDATE:
#forguta also wanted to ensure objects didn't go behind the background when sendToBack() was run. Made "repeated" background image into one image so setBackgroundImage() could be used (image size only increased to 64K and I'm sure could be compressed further).
Simpler code:
canvas.setBackgroundImage('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACcQAAAnECAIAAAChEWJqAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QMDDwYflNda9wAAIABJREFUeNrs2TGO67qWhlHSotSFm7z5z+4NoQsoXJbFs8HObYoGbJxmslYk4JfgD053/u9///............', canvas.renderAll.bind(canvas), {
// Needed to position backgroundImage at 0/0
originX: 'left',
originY: 'top'
});
Here's an updated working JSFiddle, https://jsfiddle.net/rekrah/u0srdsdr/.
I want to replicate the basic functionality of a free transform tool (no rotation), by dragging on the border of a easeljs Shape and adjusting the container to match it. I'm currently using the scaleX and scaleY properties and it sort of works but is not quite right.
If you do one scaling transformation it works pretty well. However if you release, then do another scaling transformation, it jumps very glitchily, and can occasionally break sending the x/y coordinates all the way to stage 0. Any help on this issue would be great!
http://jsfiddle.net/frozensoviet/dsczvrpw/13/
//circle
var circle = new createjs.Shape(new createjs.Graphics()
.beginFill("#b2ffb2")
.drawCircle(0, 0, 50));
circle.setBounds(0, 0, 50, 50);
//create the border as a seperate object
var cBorder = new createjs.Shape(new createjs.Graphics().setStrokeStyle(10)
.beginStroke("#000").drawCircle(0, 0, 50));
cBorder.setBounds(0, 0, 50, 50);
//add both to the container
circleContainer.addChild(circle);
circleContainer.addChild(cBorder);
var cWidth = circleContainer.getBounds().width;
var cHeight = circleContainer.getBounds().height;
//find initial mouse position relative to circle center
cBorder.on("mousedown", function (evt) {
//initial mouse pos
this.initial = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
});
//set the relevant circle axis scale to ratio of mouse pos/initial mouse pos
cBorder.on("pressmove", function (evt) {
//current moouse pos
this.offset = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
if (this.initial.x > this.initial.y) {
//sides
circleContainer.scaleX = this.offset.x / this.initial.x;
} else if (this.initial.x < this.initial.y) {
//top/bottom
circleContainer.scaleY = this.offset.y / this.initial.y;
} else {
//diagonals
circleContainer.scaleX = this.offset.x / this.initial.x;
circleContainer.scaleY = this.offset.y / this.initial.y;
}
stage.update();
});
The issue is your initial calculations don't account for the change in the scale of the circle. You would have to transform the coordinates using localToGlobal. Fortunately, there is an even easier way:
this.initial = {
x: Math.abs(evt.localX),
y: Math.abs(evt.localY)
};
You can also turn on ignoreScale on the border, which makes it not stretch:
createjs.Graphics().setStrokeStyle(10,null,null,null,true) // The 5th argument
Lastly, your bounds setting might work for your demo, but it is not correct. Your circle draws from the center, so it should be:
cBorder.setBounds(-25, -25, 50, 50);
Here is an updated fiddle: http://jsfiddle.net/tfy1sjnj/3/
With Snap SVG I am trying to create an rect dynamically.
When I click, it should create a rect at the position of the mouse click.
it's create the rect, BUT the postion is always wrong.
Here is my script : http://jsfiddle.net/noteStylet/L2kpd6yt/
So My question is Who to create a rect at the mouse click postion and not below.
HTML :
<h1>Click to create </h1>
<div>
<svg id="plan" width="900" height="500"></svg>
</div>
Javascript :
var snap = Snap("#plan");
//create an image
var imagePlan = snap.image("http://upload.wikimedia.org/wikipedia/commons/4/42/Cathedral_schematic_plan_fr_vectorial.svg", 10, 10, 900, 500);
imagePlan.click(function (evt) {
//When click, create a rect
var rect1 = snap.rect(evt.clientX, evt.clientY, 40, 40);
});
Thanks !
We should use this method :
var transformed = pt.matrixTransform(mySvg.getScreenCTM().inverse());
var rect1 = s.rect(transformed.x, transformed.y, 40, 40);
instead of
var rect1 = snap.rect(evt.clientX, evt.clientY, 40, 40);
We have to use :
var mySvg = $("#svg1")[0];
var pt = mySvg.createSVGPoint(); // create the point;
imagePlan.click(function(evt)
{
pt.x = evt.x;
pt.y = evt.y;
// convert the mouse X and Y so that it's relative to the svg element
var transformed = pt.matrixTransform(mySvg.getScreenCTM().inverse());
var rect1 = s.rect(transformed.x, transformed.y, 40, 40);
});
Here is the Example : http://jsfiddle.net/noteStylet/L2kpd6yt/6/
Your problem is the grid system of the svg you need to re-abjust it so that your mouse is not the (0,0) but rather the (-20,-80).
change the rect creation code to :
var rect1 = snap.rect(evt.clientX-20, evt.clientY-80, 40, 40);
and it should work
update:
Ok so what you need is 1.get mouse position and 2.the position of the image ( left & top values). Then subtract them from current mouse position so you can adjust the svg grid to feet your needs
var snap = Snap("#plan");
//create an image
var imagePlan = snap.image("http://upload.wikimedia.org/wikipedia/commons/4/42/Cathedral_schematic_plan_fr_vectorial.svg", 10, 10, 900, 500);
var currentMousePos = { x: -1, y: -1 };
$(document).mousemove(function(event) {
currentMousePos.x = event.pageX;
currentMousePos.y = event.pageY;
var image = $( "#plan" );
var position = image.position();
imagePlan.click(function()
{
//When click, create a rect
var rect1 = snap.rect(currentMousePos.x-position.left ,currentMousePos.y-position.top, 40, 40);
});
});