FabricJS getCircle by Id or Name - javascript

I am creating multiple circle objects, each with a unique name (or id).
When I want to select an object, I have to search through all circle objects and return the right object. With many objects this is not good for the performance I guess. Is there a way to select an object by a unique attribute?
I created a simple JSFiddle to show my current workaround to get a circle Object:
https://jsfiddle.net/t47vvtec/5/
I compare every object with the attribute and if one matches it, the object will be returned. Maybe you can help me to find an easier solution.
Here is my code to get an object so far:
function getPoint(name)
{
var line_point_array = canvas.getObjects('circle');
for (var i = 0; i < line_point_array.length; i++) {
var point = line_point_array[i];
if (point.name == name) {
return point;
}
}
}
Thanks :)

You could try something like (https://jsfiddle.net/cssimsek/akbe97c5/1/):
var FabricCanvasObject = function(canvasId, attrSet) {
this.theCanvas = new fabric.Canvas(canvasId, attrSet);
this.addShape = function(shapeAttrSet) {
var newShape = this.theCanvas.add(new fabric.Circle(shapeAttrSet));
this.canvasElements.length += 1;
this.canvasElements[shapeAttrSet.name + this.canvasElements.length] = newShape;
};
this.canvasElements = { length: 0 };
};
var myFabric = new FabricCanvasObject('c', {
targetFindTolerance: 15
});
console.log(myFabric);
myFabric.addShape({
radius: 20,
fill: 'green',
left: 100,
top: 100,
name: 'circle',
id: 1
});
myFabric.addShape({
radius: 20,
fill: 'red',
left: 150,
top: 150,
name: 'circle',
id: 2
});
myFabric.addShape({
radius: 20,
fill: 'blue',
left: 200,
top: 200,
name: 'circle',
id: 3
});
console.log(myFabric.canvasElements);
console.log(myFabric.canvasElements.circle1);

Related

Select all text objects on a Fabric.js canvas

var canvas = new fabric.Canvas();
// select all objects
function selectAllCanvasObjects(){
var objs = canvas.getObjects().map(function(o) {
return o.set('active', true);
});
var group = new fabric.Group(objs, {
originX: 'center',
originY: 'center'
});
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();
}
I have a canvas and I need to select all text objects and skip others. This is the code to select all objects, how can I make it only select all text objects and skip others?
The following example only selects items with the type of 'text'.
In summary:
The Fabric JS get method allows us to inspect the type of the current item that we're iterating over
If the type is equal to 'text' then we return the item
N.B. We now use filter instead of map, as we now only want to return items that match the type of 'text', instead of every item
var canvas = new fabric.Canvas('c');
// Add some example shapes
var circle = new fabric.Circle({
radius: 20, fill: 'green', left: 100, top: 100
});
var triangle = new fabric.Triangle({
width: 20, height: 30, fill: 'blue', left: 50, top: 50
});
canvas.add(circle, triangle);
// Add some example text
var text1 = new fabric.Text('hello world', { left: 100, top: 100 });
var text2 = new fabric.Text('test', { left: 0, top: 0 });
canvas.add(text1, text2);
// Select all objects
function selectAllCanvasObjects(){
var objs = canvas.getObjects().filter(function(o) {
if (o.get('type') === 'text') {
return o.set('active', true);
}
});
var group = new fabric.Group(objs, {
originX: 'center',
originY: 'center'
});
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();
}
selectAllCanvasObjects();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.8/fabric.min.js"></script>
<canvas id="c"></canvas>
You can use below code to select all text object.
var object_length = parseInt(canvas.getObjects().length) - 1;
for(var i = 0; i <= object_length; i++)
{
canvas.setActiveObject(canvas.item(i));
var obj = canvas.getActiveObject();
var object_type = obj.type;
if(object_type == "text")
{
//Write your code here
canvas.renderAll();
}
}
canvas.deactivateAllWithDispatch();
canvas.renderAll();
After completion of execution deselect all objects so it will not show you last selected object as selected.

SVG and mouseover

I'm a JS newbie and have converted an SVG to JS using Raphael. I'm trying to make an interactive map of the USA with mouseover effects over multiple paths and circles.
I have the states as variables with a parent variable and the cities I've visted as variables with a parent variable. Here's a snippet of my JS:
var states = rsr.set();
var connecticut = rsr.path("M877.198,184.1l-0.6-4.2l-0.8-4.4l-1.602-6L870,170.4l-21.802,4.8l0.602,3.3l1.5,7.3v8.102 l-1.102,2.3l1.802,2.101l5-3.399l3.6-3.2l1.9-2.1l0.8,0.6l2.7-1.5l5.198-1.1L877.198,184.1z").attr({fill: '#D3D3D3','stroke-width': '0','stroke-opacity': '1'});
connecticut.node.id = 'Connecticut';
states.push(connecticut);
var cities = rsr.set();
var losAngeles = rsr.circle(87, 349, 5).attr({fill: '#3F3F3F','stroke-width': '0','stroke-opacity': '1'});
cities.push(losAngeles);
I'm having trouble creating mouseover effects on both the states AND cities. I'm thinking it could have something to do with the z-index?
I've written these for loops so far but only one ever works at a time.
for (var i = 0; i <= states.length; i++) {
states[i].mouseover(function() {
this.animate({
fill: '#fff',
transform: 's1.05'
}, 200);
});
states[i].mouseout(function() {
this.animate({
fill: '#D3D3D3',
transform: 's1'
}, 200);
});
}
for (var i = 0; i <= cities.length; i++) {
cities[i].mouseover(function() {
this.animate({
r : 10,
}, 200);
});
cities[i].mouseout(function() {
this.animate({
r : 5,
}, 200);
});
}
I've tried using toFront(); and toBack(); and still can't get it to work. Any suggestions?

Adding elements in bulk on JointJS leads to performance issues

We are trying to build a Visualization Framework using JointJS (we are not using Rappid). SVG is used to render graphical data on the browser.
We have a scenario where we have a model with approximately 4500 ports.
We have used ports as elements and NOT as attributes. Meaning to say that we have embedded ports as cells inside a model. There is a certain reason why need such a setup.
When such a model is added onto the graph, it takes roughly 8 to 9 seconds to render. Once the model is rendered, moving and repositioning the model works okay. However, when we try to add further models the browser tends to crash.
This is how we have created the Paper object
function createPaper(options) {
var paper = new joint.dia.Paper({
el: options.el,
width: options.width,
height: options.height,
model: options.graph,
gridSize: 1,
label:options.name,
async : { batchSize: 250}
});
return paper;
}
This is how we have created the model
function createModel(options) {
var model = new joint.shapes.devs.Model({
position:options.position,
size:options.size,
attrs: options.attrs
});
return model;
}
This is how we have created the port as a custom model
joint.shapes.custom = {};
joint.shapes.custom.Port = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><path class="port-body" d = "M 0 0 L 0 0 L 15 5 L 0 10 L 0 10 z"></path></g><text class="port-label"/></g>',
defaults: joint.util.deepSupplement({
type: 'devs.Port',
size: { width: 10, height: 10 },
position : {x : 0, y : 0},
attrs: {
'.': { magnet: false },
'.body': {
width: 150, height: 250,
stroke: '#000000'
},
'.port-body': {
r: 5,
magnet: true,
stroke: '#000000',
fill : 'yellow'
},
text: {
'pointer-events': 'none'
},
}
}, joint.shapes.basic.Generic.prototype.defaults),
}));
joint.shapes.custom.PortView = joint.dia.ElementView.extend(joint.shapes.basic.PortsViewInterface);
function createPort(options){
var port=new joint.shapes.custom.Port({
position:options.position,
size:options.size,
attrs: options.attrs
});
return port;
}
Below is the way we are adding models and ports to the graph
var cells = [];
var model = createModel(options);
//Add the cell to the graph
cells.push(model);
var totalInports=options.inPorts.length;
var totalOutports=options.outPorts.length;
//Add the inports
for (var portIndex = 0; portIndex < totalInports; portIndex++) {
port = createPort(options.inPorts[portIndex]);
model.embed(port);
cells.push(port);
}
//Add the outports
for (var portIndex = 0; portIndex < totalOutports; portIndex++) {
port = createPort(options.outPorts[portIndex]);
model.embed(port);
cells.push(port);
}
graph.addCells(cells);
Could anybody kindly give some answers to the below questions -
Are we using the paper and models in the right way ?
Are there better ways of adding cells to the graph which could improve the
performance ?
Thanks in Advance
I know that this is an old question that has been left unanswered for a long time. My take is to use:
graph.fromJSON or graph.addCells(/*Array of json cells*/)
Then, try to translate all the operations as a compound json that will be passed to graph.fromJSON or graph.addCells.

How I can have different values and different labels at graphael?

I am using a linechart at graphael. My datapoints are dates,which are not recognisable by the graphael. So I have represented every date, using 1,2,3 ....
The fact is that I need to display dates at my chart, as the x axis labels. How I can do that? I tried the label property, but it does not working.
My code is shown below:
var lines = r.linechart(30, 30, 600, 440,[[1,2,3,4,5]],[[100,150,130,85,100]], {axisxstep : 20,nostroke: false, axis: "0 0 1 1", symbol: "circle", smooth: true }).hoverColumn(function () {
this.tags = r.set();
for (var i = 0, ii = this.y.length; i < ii; i++) {
this.tags.push(r.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{ fill: "#fff" }, { fill: this.symbols[i].attr("fill") }]));
}
}, function () {
this.tags && this.tags.remove();
});
Take a look at this fiddle. You have to change the attribute of each label (date for us) inside chartobject:
var lineChart = r.linechart(0, 0, 200, 250, xValues, yValues1, {
smooth: true,
colors: ['#F00', '#0F0', '#FF0'],
symbol: 'circle',
axis: '0 0 1 1'
});
for (var i = 0; i < lineChart.axis[0].text.items.length; i++) {
var label = lineChart.axis[0].text.items[i];
var originalDate = new Date(parseInt(label.attr('text'), 10));
var newText = originalDate.getDate() + "/" + (originalDate.getMonth() + 1) + "/" + (originalDate.getFullYear());
label.rotate(60);
label.attr({
'text': newText
});
}
Also, the data for graphael can only be numerical type to plot the graph, we have to create a Date object for each label in x like this:
var xValues = [
new Date("01/05/2014"),
new Date("01/06/2014"),
new Date("01/07/2014"),
new Date("01/08/2014"),
new Date("01/09/2014"),
new Date("01/10/2014"),
new Date("01/11/2014"),
new Date("01/12/2014"),
new Date("01/13/2014")
];

Connecting objects with a line in Raphael

I have a list of json objects.
var Planets = [
{name:"maths", percComp: "2", preReq: "english"},
{name:"english", percComp: "20", preReq: "geog"},
{name:"geog", percComp: "57", preReq: "maths"}
];
These objects are planets that i will add to a universe. They are school subjects
I also have a planet class
function Planet(planet, index)
{
this.name = planet.name;
this.percComp = planet.percComp;
this.preReq = planet.preReq;
CreatePlanet(this, index);
this.line = paper.path('');
function RedrawLine(planetFrom, planetTo, strokeWidth)
{
line.attr({
path:"M" + planetFrom.attrs.cx + "," + planetFrom.attrs.cy + "L"+ planetTo.attrs.cx + "," + planetTo.attrs.cy,
"stroke-width": strokeWidth
});
}
function CreatePlanet(planet)
{
var planetName = planet.name;
planetName = paper.circle(200 * index, 100, 80/index);
planetName.attr({
fill:'red',
stroke: '#3b4449',
'stroke-width': 6
});
SetGlow(30, true, 0, 0, 'grey', planetName)
SetHover(planetName);
}
function SetHover(planet)
{
var radius = planet.attrs.r;
planet.mouseover(function(){
this.animate({ r: radius * 1.3}, 150)
}).mouseout(function(){
this.animate({ r: radius}, 150)
})
}
function SetGlow(width, fill, offSetx, offsety, color, planet)
{
planet.glow({
width: 30,
fill: true,
offsetx: 0,
offsety: 0,
color: 'grey'
});
}
}
The code to initiate the program is
var element = document.getElementById('Main-Content')
var paper = new Raphael(element, 1000, 1000);
window.onload = function()
{
var planetsSet = paper.set();
var index = 1;
$(jQuery.parseJSON(JSON.stringify(Planets))).each(function(){
var planet = new Planet(this, index);
planetsSet.push(planet);
index++;
});
for (i=0; i<planetsSet.length; i++)
{
var planetTo = planetsSet[i];
// This is a test to see if RedrawLine works
var planeFrom = planetsFrom[i+1];
planetTo.RedrawLine(planetTo, planeFrom, 30)
var preReq = this.preReq;
}
}
The code populates the screen with 3 planets. I am trying to connect these with a line. The line will connect a planet with its pre requisite that is declared in the json object. So maths has a pre requisite english, and english has a pre requisite geog. I have a function in the Planet class that will draw the line, but I cant access the objects after they have been drawn.
Is this a problem with Raphael or can it be done?
several errors in your approach:
Paper.set is not Array, use Array for such purpse
you messed scopes, pass paper to your object constructor
public methods should be declared at least as this.method not just function method()
keep a public properties of your planet in this.property object or at least not call this.planet as PlanetName.
you forget to use this.line in your redraw method
little code to fill the void:
var planetsSet = [];
working sample
homework:
Remove planetFrom from RedrawLine method parameters, to be possible call this method as
PlanetFrom.RedrawLine(planetTo);

Categories

Resources