fabricjs label on hover - javascript

I try to add label when user hover element:
var rect = new fabric.Rect({
originX: 'top',
originY: 'top',
width: 150,
height: 120,
fill: 'rgba(255,0,0,0.5)',
transparentCorners: true
});
var text = new fabric.Text('hello world', {
fontSize: 30,
originX: 'top',
originY: 'top'
});
canvas.on('mouse:over', function(e) {
var group = new fabric.Group([ rect, text ], {
left: e.target.left,
top: e.target.top
});
canvas.add(group);
canvas.renderAll();
});
canvas.on('mouse:out', function(e) {
//e.target.set('fill', 'green');
canvas.remove(group);
canvas.renderAll();
});
But when mouse:out fires i get:
Uncaught ReferenceError: group is not defined
at i.<anonymous> (can.js:38)
at i.r (fabric.min.js:1)
at i._fireOverOutEvents (fabric.min.js:3)
at i.findTarget (fabric.min.js:3)
at i.__onMouseMove (fabric.min.js:4)
at i._onMouseMove (fabric.min.js:4)
How can i make a group global with left and top of hovered element, or there is a better way to do that?

group is scoped as a private variable local to the mouse:over event. Try
removing var from:
var group = new fabric.Group([ rect, text ], {
to scope group globally:
group = new fabric.Group([ rect, text ], {

DEMO
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
originX: 'top',
originY: 'top',
width: 150,
height: 120,
fill: 'rgba(255,0,0,0.5)',
});
var text = new fabric.Text('hello world', {
fontSize: 30,
originX: 'top',
originY: 'top'
});
var group = new fabric.Group([ rect, text ], {
left: 0,
top: 0,
selectable : false,
visible: false,
});
canvas.add(group);
canvas.renderAll();
canvas.on('mouse:move', function(e) {
var p = canvas.getPointer(e.e);
group.set({
left: p.x,
top: p.y,
visible: true
});
canvas.renderAll();
});
canvas.on('mouse:out', function(e) {
group.set({
visible: false
})
canvas.renderAll();
});
canvas {
border:2px dotted blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
<canvas id='c' width=400 height=400></canvas>
You no need to create group on every mouse over, you just set visible: true/false, so according to this it will visible. Check DEMO.

Related

FabricJS modify custom properties

I am using the FabricJS graphics library and have added an additional property (name) to a fabric.Rect object. All well and good and it serializes out to JSON correctly.
I am struggling though with the code needed to allow me to subsequently change the customer property once set i.e. to change 'some name' to something else. It is driving me a bit crazy.
Any additional help really appreciated.
Thanks,
Shaun
const o = new fabric.Rect({
width: width,
height: height,
fill: tableFill,
stroke: tableStroke,
strokeWidth: 2,
shadow: tableShadow,
originX: "center",
originY: "center",
centeredRotation: true,
snapAngle: 45,
selectable: true,
strokeUniform: true
});
o.toObject = (function(toObject) {
return function(propertiesToInclude) {
return fabric.util.object.extend(toObject.call(this, propertiesToInclude), {
name: 'some name'
});
};
})(o.toObject);
console.log(o.toObject().name)
So, basically run this code to allow any additional properties to be serialised to and from JSON. In this example a property called name is added.
const originalToObject = fabric.Object.prototype.toObject;
const myAdditional = ['name'];
fabric.Object.prototype.toObject = function (additionalProperties) {
return originalToObject.call(this, myAdditional.concat(additionalProperties));
}
Then create a new Fabric object and set or get the additional properties as needed...
const o = new fabric.Rect({
width: width,
height: height,
fill: tableFill,
stroke: tableStroke,
strokeWidth: 2,
shadow: tableShadow,
originX: "center",
originY: "center",
centeredRotation: true,
snapAngle: 45,
selectable: true,
strokeUniform: true
});
o.name = 'Fred'
console.log(o.toJSON())

Show full text outside group object fabricjs

I am trying to merge textbox and group in fabricjs
when I set text, It doesn't show full text.
how to set full text?
var iText4 = new fabric.Textbox('Text noasasasasasasasasasabcdefghxyz', {
left: 50,
top: 100,
fontFamily: 'Helvetica',
width: 30,
styles: {
0: {
0: { textBackgroundColor: 'blue', fill: 'green' },
1: { textBackgroundColor: '#faa' },
2: { textBackgroundColor: 'lightblue' },
}
}
});
var group = new fabric.Group([ iText4 ], {
left: 150,
top: 100,
width: 60,
});
var canvas = new fabric.Canvas('c');
canvas.add(group);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id='c' width='500' height='400'></canvas>
The group's width is smaller than the text's width, causing it to be cut off. Removing it should solve your problem.
var group = new fabric.Group([ iText4 ], {
left: 150,
top: 100
});
See here: https://jsfiddle.net/p6c2trg8/1/

Start next line from somewhere else

Now i have choice of different colors, but when selecting other color, line is still drawn from last point of latest line. How could i draw in new fresh place after choosing other color?
function drawLine() {
canvas.on("mouse:down", function(event) {
var pointer = canvas.getPointer(event.e);
var positionX = pointer.x;
var positionY = pointer.y;
var circlePoint = new fabric.Circle({
radius: 1,
fill: pipeColor,
left: positionX,
top: positionY,
selectable: false,
originX: "center",
originY: "center",
hoverCursor: "auto"
});
canvas.add(circlePoint);
// Store the points to draw the lines
pipePoints.push(circlePoint);
// console.log(pipePoints);
if (pipePoints.length > 1) {
var startPoint = pipePoints[pipePoints.length - 2];
var endPoint = pipePoints[pipePoints.length - 1];
var pipeLine = new fabric.Line(
[
startPoint.get("left"),
startPoint.get("top"),
endPoint.get("left"),
endPoint.get("top")
],
{
stroke: pipeColor,
strokeWidth: 2,
hasControls: false,
hasBorders: false,
selectable: false,
lockMovementX: true,
lockMovementY: true,
hoverCursor: "default",
originX: "center",
originY: "center"
}
);
pipeLines.push(pipeLine);
canvas.add(pipeLine);
}
});
$('#colorpicker').change(function () {
canvas.item(0).selectable = false;
pipeColor = $(this).val();
drawLine();
});
}
For now I can draw any lines i want starting at point X and finishing at point Y, but when i change color of line i wanna start at point Z not point Y
It is not much FabricJS related question. It is more about the implementation and the needs of the app. It can vary cause of requirements and the programmer's experience. Anyway, here is an example width a demo how you can potentially draw multiple lines and go back to the previous lines and continue editing them. You can check the inline StackOverflow snipper or check it on CodeSandbox:
https://codesandbox.io/s/stackoverflow-60753858-fabric-js-1720-6ke80
var canvas = new fabric.Canvas("canvas");
var currentLine = "water";
document
.getElementsByClassName("options")[0]
.addEventListener("click", function(e) {
if (e.target.classList.contains("line-type")) {
// Remove active class from previous element
document
.querySelector(".line-type.is-active")
.classList.remove("is-active");
e.target.classList.add("is-active");
currentLine = e.target.dataset.lineType;
console.warn("Current line: " + currentLine);
}
});
var linesData = {
water: {
linePoints: [],
lineLines: [],
color: "blue"
},
electricity: {
linePoints: [],
lineLines: [],
color: "yellow"
},
internet: {
linePoints: [],
lineLines: [],
color: "gray"
}
};
canvas.on("mouse:down", function(event) {
var pointer = canvas.getPointer(event.e);
var positionX = pointer.x;
var positionY = pointer.y;
// Add small circle as an indicative point
var circlePoint = new fabric.Circle({
radius: 5,
fill: linesData[currentLine].color,
left: positionX,
top: positionY,
selectable: false,
originX: "center",
originY: "center",
hoverCursor: "auto"
});
canvas.add(circlePoint);
// Store the points to draw the lines
linesData[currentLine].linePoints.push(circlePoint);
if (linesData[currentLine].linePoints.length > 1) {
// Just draw a line using the last two points, so we don't need to clear
// and re-render all the lines
var startPoint =
linesData[currentLine].linePoints[
linesData[currentLine].linePoints.length - 2
];
var endPoint =
linesData[currentLine].linePoints[
linesData[currentLine].linePoints.length - 1
];
var waterLine = new fabric.Line(
[
startPoint.get("left"),
startPoint.get("top"),
endPoint.get("left"),
endPoint.get("top")
],
{
stroke: linesData[currentLine].color,
strokeWidth: 4,
hasControls: false,
hasBorders: false,
selectable: false,
lockMovementX: true,
lockMovementY: true,
hoverCursor: "default",
originX: "center",
originY: "center"
}
);
linesData[currentLine].lineLines.push(waterLine);
canvas.add(waterLine);
}
});
canvas.renderAll();
document
.getElementById("clear-water-pipe")
.addEventListener("click", function(e) {
linesData[currentLine].linePoints.forEach(function(point) {
canvas.remove(point);
});
linesData[currentLine].linePoints = [];
linesData[currentLine].lineLines.forEach(function(line) {
canvas.remove(line);
});
linesData[currentLine].lineLines = [];
});
body {
font-family: sans-serif;
}
canvas {
border: 2px solid #333;
}
button {
margin-top: 10px;
padding: 10px;
border: 1px solid #555;
cursor: pointer;
}
.options {
padding-top: 10px;
}
.lines {
display: flex;
align-items: center;
}
.lines > div {
border: 2px solid #333;
color: white;
margin-right: 10px;
padding: 10px;
}
.lines > div.is-active {
border: 5px solid red;
}
.water-pipe {
background-color: blue;
}
.lines .electricity-line {
background-color: yellow;
color: #555;
}
.internet-line {
background-color: gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js"></script>
<div id="app">
<canvas id="canvas" width="500" height="350"></canvas>
<div class="options">
<div class="lines" id="lines">
<div class="line-type water-pipe is-active" data-line-type="water">
Water pipe
</div>
<div class="line-type electricity-line" data-line-type="electricity">
Electricity line
</div>
<div class="line-type internet-line" data-line-type="internet">
Internet line
</div>
</div>
<button id="clear-water-pipe">Clear active line</button>
</div>
</div>

How can I hover on a rectangle in Javascript?

I created a rectangle on the screen and I want some buttons to appear on the rectangle when mouseover it. But I couldn't handle this work. My code is below. I couldn't understand that why
this.$('.control').removeClass('hide');
this row is not working. It does not give any error also.
$(this.rect).html(................
........css({ position: 'absolute', padding: '10px' });
I couldn't understand also that this part of my code. (Stack didn't allow the divs. I don't know why).
var KineticModel = Backbone.Model.extend({
myRect: null,
createRect : function() {
alert("rectangle created.");
var rect=new Kinetic.Rect({
x: 50,
y: 50,
width: 150,
height: 150,
fill: 'green',
stroke: 'black',
strokeWidth: 1,
offset: [0, 0],
draggable: true,
});
rect.on("mouseover",function(){
alert("Hoover : ");
$('.control').removeClass('hide');
});
rect.on("mouseout",function(){
alert("Out : ");
});
rect.on("mousedown",function(){
alert("Down : ");
});
rect.on("mouseup",function(){
alert("Up : ");
});
return rect;
}
});
var KineticView = Backbone.View.extend({
tagName: 'span',
stage: null,
layer: null,
initialize: function (options) {
model: options.model;
el: options.el;
this.layer = new Kinetic.Layer();
this.stage = new Kinetic.Stage({ container: this.el, width: 1000, height: 500 });
this.stage.add(this.layer);
},
events: {
'click': 'spanClicked'
},
render: function () {
var rect = this.model.createRect();
$(this.rect).html('<div class="shape"/>'
+ '<div class="control delete hide"/>'
+ '<div class="control change-color hide"/>'
+ '<div class="control resize hide"/>'
+ '<div class="control rotate hide"/>').css({ position: 'absolute', padding: '10px' });
this.layer.add(rect);
this.stage.add(this.layer);
alert("render");
return this;
},
spanClicked: function () {
}
});
var kModel = new KineticModel({});
var kView = new KineticView({ el: '#container', model: kModel });
$('#shapetest').click(function() {
kView.render();
});
change this.$('.control').removeClass('hide'); to :
$('.control').removeClass('hide');
And so on ...

Is it possible to combine kinetic.js and backbone.js?

I want to code an app that simply puts a rectangle on the screen. But I need to combine kinetic.js and backbone.js for this and i am not sure it can be done.
Kinetic code is:
document.getElementById('rect').addEventListener('click', function() {
rect = new Kinetic.Rect({
x: 239,
y: 75,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
offset: [50,25],
draggable: true,
});
And backbone code
$(function() {
var Shape = Backbone.Model.extend({
defaults: { x:50, y:50, width:150, height:150, color:'gray' },
setTopLeft: function(x,y) { this.set({ x:x, y:y }); },
setDim: function(w,h) { this.set({ width:w, height:h }); },
isCircle: function() { return !!this.get('circle'); }
});
*I added .html file these paths
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript" src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.3.min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.2.2/underscore-min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js"></script>
All i want to place kinetic part instead of default values in backbone. Is it possible?
With your help, we wrote this example of work which puts a rectangle on the screen using both kinetic.js and backbone.js. I wish it would be useful for who is looking for this kind of integrated code.
Thanks a lot for your help!
var KineticModel = Backbone.Model.extend({
myRect: null,
createRect : function() {
alert("rectangle created.");
var rect=new Kinetic.Rect({
x: 10,
y: 10,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 1,
offset: [0, 0],
draggable: true,
});
return rect;
}
});
var KineticView = Backbone.View.extend({
tagName: 'span',
stage: null,
layer: null,
initialize: function (options) {
model: options.model;
el: options.el;
this.layer = new Kinetic.Layer();
this.stage = new Kinetic.Stage({ container: this.el, width: 400, height: 400 });
this.stage.add(this.layer);
},
events: {
'click': 'spanClicked'
},
render: function () {
var rect = this.model.createRect();
this.layer.add(rect);
this.stage.add(this.layer);
alert("render");
},
spanClicked: function () {
}
});
var kModel = new KineticModel({});
var kView = new KineticView({ el: '#container', model: kModel });
$('#shapetest').click(function() {
kView.render();
});
Yes this is definitely possible. I would just create a model that stores the data that you will be using in your shape, use a view to render a span tag with click me, attach an event listener to the span and then output the rectangle when the user clicks.
var ShapeModel = Backbone.Model.extend({});
var rectangle = new ShapeModel({
x: 10,
y: 10,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
offset: [0, 0],
draggable: true,
});
var RectangleView = Backbone.View.extend({
tagName: 'span',
initialize: function (options) {
model: options.model;
el: options.el;
},
events: {
'click': 'spanClicked'
},
render: function () {
this.$el.text('click me');
},
spanClicked: function () {
var stage = new Kinetic.Stage({
container: this.el,
width: 200,
height: 200
});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect(this.model.toJSON());
layer.add(rect);
stage.add(layer);
}
});
var rectangleView = new RectangleView({ el: '#shapetest', model: rectangle });
rectangleView.render();
I would upgrade to the latest version of Backbone and Underscore too.
Also, thanks for pointing out Kinetic. Hopefully it has support for drawing on the canvas on a mobile device.

Categories

Resources