I have an editable flowchart constructed from data. When I convert one of the sub-elements into a magnet so that users can form new links, all existing links emitted from that element are gone, the target pointer arrow resets to the leftmost part of the window and I am getting the following error messages until I release the mouse button:
Uncaught TypeError: Cannot read property 'el' of undefined
joint.js:16929 Error: Invalid value for <path> attribute d="M NaN NaN C NaN NaN NaN NaN NaN NaN"
joint.js:16929 Error: Invalid value for <path> attribute d="M NaN NaN C NaN NaN NaN NaN NaN NaN"
joint.js:16929 Error: Invalid value for <path> attribute transform="translate(NaN,0) scale(1,1)"
joint.js:16929 Error: Invalid value for <path> attribute transform="translate(NaN,0) scale(1,1) rotate(NaN)"
joint.js:16929 Error: Invalid value for <path> attribute transform="translate(NaN,0) scale(1,1)"
joint.js:16929 Error: Invalid value for <path> attribute transform="translate(NaN,0) scale(1,1) rotate(NaN)"
joint.js:16929 Error: Invalid value for <g> attribute transform="translate(NaN,0) scale(0.5,0.5)"
joint.js:16929 Error: Invalid value for <g> attribute transform="translate(NaN,0) scale(0.5,0.5) rotate(NaN)"
joint.js:16929 Error: Invalid value for <g> attribute transform="translate(NaN,0) scale(0.5,0.5)"
joint.js:16929 Error: Invalid value for <g> attribute transform="translate(NaN,0) scale(0.5,0.5) rotate(NaN)"
joint.js:22209 Uncaught TypeError: Cannot read property 'x' of undefined
The code I am using is the following:
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
el: $('#paper'),
width: window.innerWidth,
height: window.innerHeight,
gridSize: 1,
model: graph,
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
// Prevent loop linking
return (magnetS !== magnetT);
}
});
function invertColor(hex) { //This is just for text
var color = hex;
color = color.substring(1);
color = parseInt(color, 16);
color = 0xFFFFFF ^ color;
color = color.toString(16);
color = ("000000" + color).slice(-6);
color = "#" + color;
return color;
}
var shapenodes = []; //Here I store all the nodes with data
var connections = {}; //Here I store the SourceID: [targetIDs] relationships
var links = []; //Here I store links generated from above
for (var i=0;i<nodes.length;i++){ //Loop through data from the DB
var wraptext = joint.util.breakText(nodes[i].text, {width: nodes[i].width - 20}); //Process text so it doesn't overflow
var newNode = new joint.shapes.basic.Rect({
position: {x: nodes[i].x, y: nodes[i].y},
size: {width: nodes[i].width, height: nodes[i].height},
attrs: {
rect: {
fill: nodes[i].color,
rx: 8,
ry: 8,
stroke: '',
magnet: true //Convert the underlying the rectangle to magnet
},
text: { //This is used to drag the shape
text: wraptext,
fill: invertColor(nodes[i].color),
'font-family': 'Arial',
'font-size': 18,
'font-weight': 'bold',
'font-variant': 'small-caps'
}
},
id: nodes[i].ID //Assign the same ID as in database (unique)
});
var sourceID = newNode.id;
connections[sourceID] = []; //Store the targets
Array.prototype.push.apply(connections[sourceID], nodes[i].targets);
shapenodes.push(newNode);
}
for (var SID in connections){ //for all Source IDs in connections
if(connections[SID].length>0) { //if there are targets
_.each(connections[SID], function (target) {
var link = new joint.dia.Link({ //make link for each target
source: {id: SID},
target: {id: ''+target+''},
'smooth': true,
attrs: {
'.connection': {'stroke-width': 3},
'.marker-source':{},
'.marker-target':{d: 'M 10 0 L 0 5 L 10 10 z', stroke: 'black', fill: 'black'},
'z-index': -1 //I am not sure if this has any effect anyway...
}
});
links.push(link);
});
}
}
graph.addCells(shapenodes); //Add to graph
graph.addCell(links);
The sample data I am using is as follows:
var nodes = [ //Sample data
{
ID: 1,
Chart: 1,
x: 50,
y: 50,
width: 100,
height: 80,
color: "#003253",
text: "Try to code",
label: "Start",
targets: [2]
},
{
ID: 2,
Chart: 1,
x: 500,
y: 170,
width: 100,
height: 80,
color: "#365C5A",
text: "Fail",
label: "End",
targets: [3]
},
{
ID: 3,
Chart: 1,
x: 270,
y: 350,
width: 150,
height: 80,
color: "#81271E",
text: "Cry a lot",
label: "Mid",
targets: [1]
}
];
I looked at a lot of examples and it seems like mine should work. Do you have any idea why this is happening?
JSfiddle
Cell id has to be a string. You've provided a number.
When you use custom ids for you cells, just make sure it's always a string.
var rect = new joint.shapes.basic.Rect({ id: String(customId) });
Related
I'm creating a chart using c3.js.
I want to put the date on the X-axis label, but I get the following error:
Error: <g> attribute transform: Expected number, "translate(NaN, 0)"
Specifically, the following implementation is performed.
(If implemented as time-series, an error will occur that it can't be converted to a Date Object.)
var chart = c3.generate({
bindto: '#chart',
data: {
x: 'x',
columns: [
['x',"2021/01/01", "2021/01/02","2021/01/03","2021/01/04","2021/01/05"],
['sample', 30, 200, 100, 400, 150],
['sample2', 50, null, 20, null, 180]
]
},
axis: {
x: {tick: {values: ["2021/01/01", "2021/01/02","2021/01/03","2021/01/04","2021/01/05"]}},
y: {label: {position: 'inner-middle'}},
y2: {show: true, label: {position: 'inner-middle'}}
},
line: {
connectNull: true
},
zoom: {
enabled: true
}
});
Where is the problem?
I am creating gauge using this example, but I have to change pointer style.
Is it possible to change pointer style like this?
Chart.Type.extend({
//Passing in a name registers this chart in the Chart namespace
name: "Gauge",
//Providing a defaults will also register the deafults in the chart namespace
defaults : defaultConfig,
initialize: function(data){
//Declare segments as a static property to prevent inheriting across the Chart type prototype
this.segments = [];
var pointerDotRadius = helpers.min([this.chart.width,this.chart.height])/50;
this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
this.SegmentArc = Chart.Arc.extend({
ctx : this.chart.ctx,
x : this.chart.width/2,
y : this.chart.height - pointerDotRadius
});
All code:
jsbin.com
There's a similar looking meter chart using the RGraph ( https://www.rgraph.net ) software in the download archive:
demos/meter-needle.html
And here's the code:
var meter = new RGraph.Meter({
id: 'cvs',
min: 0,
max: 100,
value: 75,
options: {
linewidthSegments: 20,
strokestyle: 'white',
segmentRadiusStart: 105,
border: 0,
tickmarksSmallNum: 0,
tickmarksBigNum: 0,
needleRadius: 205,
labels: false,
centerpinFill: 'black',
colorsRanges: [
[0, 30, '#FFC900'],
[30, 60, '#8FCE21'],
[60, 100, '#0094DA']
]
}
}).draw();
I'm currently working on a project that involves building a dynamic diagram and am looking at using SVGs for this.
Based on this, I wish to see whether what I am after is actually possible with SVGs.
1) Using JavaScript and json, is it possible to build an SVG diagram within a HTML page dynamically, based on my json data?
Basically, I want to construct say a rectangle box within the page, where I can have say 1 to 10 svg lines as inputs into the rectangle box, on the left hand side. To determine how many actual lines appear, will come from my json object.
So as mentioned above, is this possible and if so, how might this be setup to draw this inline svg diagram?
2) Again, using JavaScript, is it possible to place hyperlink labels on these svg lines, again based on info within the json object?
I started playing with static inline SVG but unsure how to construct this using javascript instead, to cover off my two points above, i.e.:
<body>
<h1>My SVG Test</h1><hr/>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<text x="465" y="90" fill="red">Heading One</text>
<image x="100" y="110" width="50%" height="50%"
xlink:href="http://my-image.com/myimg.jpg" />
<line x1="25" y1="80" x2="350" y2="80" style="stroke: #000000; stroke-width: 2;"/>
</svg>
</body>
You can use Snap to easily create what you are looking for. In the below snippet,I added all the required attributes and type of the element and used .el() to create an svg element
The syntax for el is
Paper.el(name,attr)
Name is the element that you want to create, for example line,circle,path etc and attr is the attributes that you want to add. In the data below i have created two lines and one circle by specifying their attributes . I have also added fill and stroke
var data = [
{
type:"line",
attrs:{
x1:0,
x2:0,
y1:0,
y2:100,
stroke:"black",
"stroke-width":"5"
}
},
{
type:"line",
attrs:{
x1:100,
x2:100,
y1:0,
y2:100,
stroke:"black",
"stroke-width":"5"
}
},
{
type:"circle",
attrs:{
cx:50,
cy:50,
r:40,
fill:"orange",
}
}
]
var s = Snap("svg");
for(var x=0;x<data.length;x++){
s.el(data[x].type).attr(data[x].attrs);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>
<svg width="500" height="500" viewbox="0 0 100 100"></svg>
Adding a link is a bit hard and complex because you have to append a child text element to the a. So i have changed my code a bit.
In the following snippet i have added a new key to the json data called parent. And then set parent:true to those elements who have child elements and run another loop to append the child element to the parent
var data = [{
type: "line",
parent: false, //is false because it has no child elements
attrs: {
x1: 0,
x2: 0,
y1: 0,
y2: 100,
stroke: "black",
"stroke-width": "5"
}
}, {
type: "line",
parent: false,
attrs: {
x1: 100,
x2: 100,
y1: 0,
y2: 100,
stroke: "black",
"stroke-width": "5"
}
}, {
type: "circle",
parent: false,
attrs: {
cx: 50,
cy: 50,
r: 40,
fill: "orange",
}
}, {
type: "a",
parent: true, // Is true because this has child elements
attrs: {
x: 10,
y: 50,
"xlink:href": "http://snapsvg.io/docs/"
},
childs: [{
type: "text",
attrs: {
x: 10,
y: 50,
text: "Snap is Cool",
}
}]
}, {
type: "image",
parent: false,
attrs: {
"xlink:href": "http://i.imgur.com/5NK0H1e.jpg",
x: 0,
y: 0,
width: 50,
height: 50
}
}]
var s = Snap("svg");
for (var x = 0; x < data.length; x++) {
var p = s.el(data[x].type).attr(data[x].attrs);
if (data[x].parent) { //check if it is true
for (var y = 0; y < data[x].childs.length; y++) {
var c = s.el(data[x].childs[y].type).attr(data[x].childs[y].attrs);
p.append(c);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js?wmode=transparent"></script>
<svg width="500" height="500" viewbox="0 0 100 100"></svg>
To add more child elements the following code should be used
var data = [{
type: "g"
}, {
type: "image",
appendTo: 0,
attrs: {
"xlink:href": "http://lorempixel.com/500/500/",
x: 0,
y: 0,
width: 50,
height: 50
}
}, {
appendTo: 0,
type: "a",
parent: true,
attrs: {
x: 5,
y: 5,
target: "_blank",
"xlink:href": "http://snapsvg.io/docs/"
}
}, {
appendTo: 2,
type: "circle",
attrs: {
cx: 5,
cy: 5,
r: 2.5,
fill: "orange"
}
}]
var s = Snap("svg");
var elems = [];
for (var x = 0; x < data.length; x++) {
var e = s.el(data[x].type).attr(data[x].attrs);
if (data[x]["appendTo"] !== undefined) {
var p = elems[data[x].appendTo];
p.append(e);
}
elems.push(e);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.3.0/snap.svg-min.js"></script>
<div class="box">
<svg width="100%" height="100%" viewBox="0 0 100 100"></svg>
</div>
What I am trying to do is make a element with custom class for ports and path so that I can add an element with custom path and my own markup for ports.This way when I create an element I will pass dynamic path for its shape just like elements of path class behave and as I have also extended from PortsModelInterface I will also have my own markup for ports.
This whole effort is to make svg scalable for zomming. Previously I was using html custom element with my custom ports which was working fine but html of custom elements wasn't scaling on zooming
var graph = new joint.dia.
var paper = new joint.dia.Paper({
el: $('#paper'),
width: 800,
height: 600,
gridSize: 1,
model: graph,
snapLinks: true,
embeddingMode: true
});
joint.shapes.custom1={};
joint.shapes.custom1.Element = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><rect class = "myrect"/></g><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port<%= id %>"><circle class="port-body"/></g>',
defaults: joint.util.deepSupplement({
type: 'html.Element',
size: { width: 200, height: 110 },
inPorts: [],
outPorts: [],
attrs: {
'.': { magnet: true},
rect: {
stroke: 'none', 'fill-opacity': 0, width: 300, height: 210,
},
circle: {
r: 6, //circle radius
magnet: true,
left:0,
stroke: 'gray'
},
'.inPorts circle': { fill: 'gray', magnet: 'passive', type: 'input', y: 0},
'.outPorts circle': { fill: 'gray', type: 'output' }
}
}, joint.shapes.basic.Generic.prototype.defaults),
getPortAttrs: function (portName, index, total, selector, type) {
var attrs = {};
var portClass = 'port' + index;
var portSelector = selector + '>.' + portClass;
var portCircleSelector = portSelector + '>circle';
attrs[portCircleSelector] = { port: { id: portName || _.uniqueId(type), type: type } };
attrs[portSelector] = { ref: 'rect', 'ref-x': (index + 1) * (0.55 / total)};
if (selector === '.outPorts') {
attrs[portSelector]['ref-dy'] = 15;
}
return attrs;
}
}));
joint.shapes.custom1.Atomic = joint.shapes.custom1.Element.extend({
markup: '<g class="rotatable"><g class="scalable"><path/></g><text/></g>',
defaults: joint.util.deepSupplement({
type: 'basic.Path',
size: { width: 60, height: 60 },
attrs: {
'path': { fill: '#FFFFFF', stroke: 'black' },
'text': { 'font-size': 14, text: '', 'text-anchor': 'middle', 'ref-x': .5, 'ref-dy': 20, ref: 'path', 'y-alignment': 'middle', fill: 'black', 'font-family': 'Arial, helvetica, sans-serif' }
}
}, joint.shapes.basic.Generic.prototype.defaults)
});
var a2 = new joint.shapes.custom1.Atomic({
position: { x: 50, y: 260 },
size: { width: 100, height: 100 },
attrs: {
path: { d: 'M 30 0 L 60 30 30 60 0 30 z' },
text: {
text: 'Diamond',
'ref-y': .5 // basic.Path text is originally positioned under the element
}
},
inPorts: ['in'],
outPorts: ['out']
});
graph.addCells([a2])
The element is added in graph but some how the ports don't show up.
I don't have proper concept of adding classes so please any help will be greatly appreciated. Thanks.
Fiddle example
I suggest to define an element with custom markup for the shape and ports. Both markups should contain an SVG path, so you can set an arbitrary path data d via model.attr() on them.
joint.shapes.devs.GenericModel = joint.shapes.devs.Model.extend({
markup: '<g class="rotatable"><g class="scalable"><path class="body"/></g><text class="label"/><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port port<%= id %>"><path class="port-body"/><text class="port-label"/></g>',
defaults: joint.util.deepSupplement({
type: 'devs.GenericModel'
}, joint.shapes.devs.Model.prototype.defaults)
});
Tell the paper to use devs.ModelView for rendering.
joint.shapes.devs.GenericModelView = joint.shapes.devs.ModelView;
Now you can set or change d attribute for the shape and ports anytime you wish.
var model = new joint.shapes.devs.GenericModel({
attrs: {
'.body': { d: 'M 0 0 0 50 50 50 z'},
'.port-body': { d: 'M 0 0 10 0 10 10 0 10 z'}
}
});
model.attr('.body/d', 'M 25 0 50 50 0 50 z');
JS Fiddle: http://jsfiddle.net/kumilingus/kge023bc/
I have a sprite which has name property.
I want to find that sprite during run time and then change the animation of that.
I tried this :-
myKineticGroup.find('.myspriteName').setAnimation('set_new_animation');
I got following error : (Object [object Object] has no method 'setAnimation').
I typed this myKineticGroup.find('.myspriteName') in my console, there i am getting the sprite object.
If your "myspriteName" element is an shape or blob, give it an ID and you can access it after. Like this:
var rect = new Kinetic.Rect({
x: 300,
y: 90,
width: 100,
height: 50,
fill: 'green',
strokeWidth: 3,
offset: {
x: 50,
y: 25
},
draggable: true,
id: 'myspriteName'
});
stage.find('#myspriteName')[0].setAnimation('set_new_animation');
To access the sprite by its name we can use this:
stage.find('.myspriteName')[0].setAnimation('set_new_animation');