Is there a way to save the resultant svg jvectormap as a png? I would like users to be able to click on a save or download button, and be able to download the map in some sort of image format to their desktop.
There are several ways to do this, here is a most efficient way (by using canvg),
here is a working example on JSfiddle..
$(function(){
$('#world-map').vectorMap({
map: 'world_mill_en',
backgroundColor: 'white',
normalizeFunction: 'polynomial',
regionsSelectable: true,
regionsSelectableOne: true,
zoomOnScroll: true,
zoomButtons: true,
regionStyle: {
initial: {
fill: "red",
"fill-opacity": 1,
stroke: "none",
"stroke-width": 0,
"stroke-opacity": 1
},
hover: {
fill: "blue",
"fill-opacity": 1
},
selected: {
fill: "#EC6602",
"fill-opacity": 1
},
selectedHover: {
fill: "#EC6602",
"fill-opacity": 1
}
},
onRegionClick: function(e, country){
var map = $("#world-map").vectorMap("get", "mapObject");
$("#world-map").vectorMap("set", "focus", country);
}
});
});
function saveImage() {
var oSerializer = new XMLSerializer();
var sXML = oSerializer.serializeToString(document.querySelector("#world-map svg"));
canvg(document.getElementById('canvas'), sXML,{ ignoreMouse: true, ignoreAnimation: true })
var imgData = canvas.toDataURL("image/png");
window.location = imgData.replace("image/png", "image/octet-stream");
// You can use http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js
// if you want to force filename.ext
}
Related
After adding tool view to links on single click over link I am unable to get the link's double click event. I am using react to develop a interactive graph application using RappidJS library. Please help.
i am using link:pointerup to add Tool view to link like this::
this.paper.on('link:pointerup', (linkView) => {
var tools;
var ns = joint.linkTools;
var toolsView = new joint.dia.ToolsView({
name: 'link:pointerup',
tools: [
new ns.Vertices(),
new ns.Remove({ focusOpacity: 0.5,
distance: 5,
offset: 10}),
new ns.TargetArrowhead(),
new ns.Segments(),
new ns.Boundary({ padding: 10 }),
]
});
joint.ui.Halo.clear(this.paper);
joint.ui.FreeTransform.clear(this.paper);
this.paper.removeTools();
linkView.addTools(toolsView);
});
For Link Creation:::
export class Link2 extends joint.shapes.standard.Link {
defaults() {
return joint.util.defaultsDeep({
router: {
name: 'normal',
},
connector: {
name: 'normal',
},
labels: [],
attrs: {
line: {
stroke: '#284f96',
strokeDasharray: '0',
strokeWidth: 1,
fill: 'none',
sourceMarker: {
d: 'M 0 0 0 0',
fill: {
type: 'color-palette',
group: 'marker-source',
when: { ne: { 'attrs/line/sourceMarker/d': 'M 0 0 0 0' } },
index: 2
},
},
targetMarker: {
d: ' M 0 -3 -6 0 0 3 z',
},
}
}
}, joint.shapes.standard.Link.prototype.defaults);
}
defaultLabel = {
attrs: {
rect: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 2,
refWidth: 10,
refHeight: 10,
refX: 0,
refY: 0
}
}
};
getMarkerWidth(type: any) {
const d = (type === 'source') ? this.attr('line/sourceMarker/d') : this.attr('line/targetMarker/d');
console.log("marker width", this.getDataWidth(d));
return this.getDataWidth(d);
}
getDataWidth = memoize(function (d: any) {
return (new joint.g.Rect(d)).bbox().width;
});
static connectionPoint(line: any, view: any, magnet: any, opt: any, type: any, linkView: any) {
// const markerWidth = linkView.model.getMarkerWidth(type);
opt = { offset: 5, stroke: true };
// connection point for UML shapes lies on the root group containg all the shapes components
const modelType = view.model.get('type');
if (modelType.indexOf('uml') === 0) opt.selector = 'root';
// taking the border stroke-width into account
if (modelType === 'standard.InscribedImage') opt.selector = 'border';
return joint.connectionPoints.boundary.call(this, line, view, magnet, opt, type, linkView);
}
}
and i am using following code to get double click event on Link:::
this.rappid.paper.on(
"link:pointerdblclick", (linkView) => {
// linkView.model.attr("priority",0);
console.log("linkview", linkView);
// this.currentLink = linkView
this.setState({ modalView: "lableModal", visible: true, currentLink: linkView });
});
I need x-axis labels in different colors, I am using "chart.js". I tried below code but it is not working, just showing single color-
scales: {
xAxes: [{
ticks: {
fontColor: [
'rgba(245,88,97,1)',
'rgba(245,88,97,1)',
'rgba(245,88,97,1)',
'rgba(145,151,163,1)',
'rgba(70,180,220,1)',
'rgba(70,180,220,1)',
'rgba(70,180,220,1)'
]
}
}]
}
Output:
Need:
You can make use of the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code snippet, I use the afterDraw hook to draw text of the same color as the corresponding bar.
chart.data.labels.forEach((l, i) => {
var value = chart.data.datasets[0].data[i];
var x = xAxis.getPixelForValue(l);
ctx.fillStyle = chart.data.datasets[0].backgroundColor[i];
ctx.fillText(l, x, yAxis.bottom + 17);
});
When drawing your own tick labels, you need to instruct Chart.js not to display the default labels. This can be done through the following definition inside the chart options.
scales: {
xAxes: [{
ticks: {
display: false
}
}],
You also need to define some padding for the bottom of the chart, otherwise you won't see your custom tick labels.
layout: {
padding: {
bottom: 20
}
},
Please take a look at the following sample code that illustrates how to change the labels on the x-axis depending on the values.
new Chart('myChart', {
type: 'bar',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
ctx.save();
ctx.textAlign = 'center';
ctx.font = '12px Arial';
chart.data.labels.forEach((l, i) => {
var value = chart.data.datasets[0].data[i];
var x = xAxis.getPixelForValue(l);
ctx.fillStyle = chart.data.datasets[0].backgroundColor[i];
ctx.fillText(l, x, yAxis.bottom + 17);
});
ctx.restore();
}
}],
data: {
labels: ["-3", "-2", "-1", "0", "+1", "+2", "+3"],
datasets: [{
label: "My First Dataset",
data: [60, 59, 80, 81, 60, 55, 40],
fill: false,
backgroundColor: ['rgba(245,88,97,1)', 'rgba(245,88,97,1)', 'rgba(245,88,97,1)', 'rgba(145,151,163,1)', 'rgba(70,180,220,1)', 'rgba(70,180,220,1)', 'rgba(70,180,220,1)'],
borderWidth: 1
}]
},
options: {
layout: {
padding: {
bottom: 20
}
},
scales: {
xAxes: [{
ticks: {
display: false
}
}],
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
canvas {
max-width: 300px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
I'm a newbie in jointjs. Today I have a small example as below:
I have a start Activity
var startEndActivity = function (x, y, name, fillColor, textColor, size) {
fillColor = fillColor || '#007FBE';
textColor = textColor || "#000";
size = size || { width: 100, height: 40 };
var rect = new joint.shapes.basic.Rect({
position: { x: x, y: y },
size: size,
attrs: {
rect: { fill: fillColor, rx: 5, ry: 5, 'stroke-width': 1, stroke: '#002F5D' },
text: {
text: name, fill: textColor,
'font-size': 14, 'font-family': 'sans-serif'
}
}
});
graph.addCell(rect);
return rect;}
I have a condition Activity
var activityDecision = function (x, y, name, fillColor, textColor, size{
fillColor = fillColor || '#BF664C';
textColor = textColor || "#080808";
size = size || { width: 200, height: 60 };
var node = new joint.shapes.basic.Rhombus({
position: { x: x, y: y },
size: size,
});
node.attr({
rect: { fill: fillColor, 'stroke-width': 1, stroke: 'white' },
text: {
text: name, fill: textColor,
}
});
graph.addCell(node);
return node;}
I want to click on start activity and I can draw a arrow to connect between 2 elements. Thank you so much
The most common approach that I know of is to use ports on your elements. This link should get you started on that route:
WORKING WITH PORTS
If you prefer to have the entire element behave as a port you need to look into the "magnetic" attribute. This link should help you get started researching what you need (especially the first answer):
How to interactively create links in JointJS
I found solution for this. Thank you so much. Just add more attribute like this
el.attr('rect/magnet', true).attr('text/pointer-events', 'none');
function create(type) {
var link = new joint.dia.Link({
source: { x: 10, y: 20 },
target: { x: 350, y: 20 },
attrs: {}
});
link.prop({...});
link.addTo(graph)
}
//init connection:
new joint.dia.Link({
source: { id: 'source-id' },
target: { id: 'target-id', port: 'port_id'}
});
In order to connect two element you have to work with Ports (documentation):
My best advice for you is to learn how to implement ports by looking in the JointJS source code, as a reference look for object: joint.shapes.devs.Model (live demo + source code inside)
something like this:
var myRect = joint.shapes.devs.Model.extend({
portMarkup: '<circle class="port-body myCustomClass"/>',
defaults: _.defaultsDeep({
type: 'myRect',
}, joint.shapes.basic.Generic.prototype.defaults),
});
and inside of startEndActivity function change the var rect to:
var startRect = new myRect({
position: { x: x, y: y },
size: size,
attrs: {
rect: { fill: fillColor, rx: 5, ry: 5, 'stroke-width': 1, stroke: '#002F5D' },
text: {
text: name, fill: textColor,
'font-size': 14, 'font-family': 'sans-serif'
}
},
ports: {
groups: {
'out': {
position: {
name: 'right'
},
attrs: {
'.port-label': {
fill: '#000'
},
'.port-body': {
fill: '#fff',
stroke: '#000',
r: 10,
magnet: true
}
},
label: {
position: {
name: 'right',
args: {
y: 10
}
}
}
}
}
});
do the same for the second element.
I'm creating elements with ports using this code (including the portion to move the ports to top and bottom):
joint.shapes.devs.flowchartProcess = joint.shapes.devs.Model.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g><text class="body-label"/><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port port<%= id %>"><circle class="port-body"/><text class="port-label"/></g>',
defaults: joint.util.deepSupplement({
// type: 'devs.flowchartProcess',
attrs: {
'.body': { stroke: 'black' },
'.body-label': { 'ref-x': .5, 'ref-y': .5, ref: '.body', 'y-alignment': 'middle', 'x-alignment': 'middle' },
'.port-body': { r: portRadius, magnet: 'active' },
'.inPorts .port-body': { stroke: portBodyStroke, fill: inPortFill },
'.outPorts .port-body': { stroke: portBodyStroke, fill: outPortFill},
'.inPorts .port-label': { 'font-size': 0},
'.outPorts .port-label': {'font-size': 0 }
},
parentID: none
}, joint.shapes.devs.Model.prototype.defaults),
getPortAttrs: function(portName, index, total, selector, type) {
var attrs = {};
var portClass = 'port' + index;
var portSelector = selector + '>.' + portClass;
var portLabelSelector = portSelector + '>.port-label';
var portBodySelector = portSelector + '>.port-body';
attrs[portLabelSelector] = { text: portName };
attrs[portBodySelector] = { port: { id: portName || _.uniqueId(type) , type: type } };
// CHANGED: swap x and y ports coordinates ('ref-y' => 'ref-x')
attrs[portSelector] = { ref: '.body', 'ref-x': (index + 0.5) * (1 / total) };
// ('ref-dx' => 'ref-dy')
if (selector === '.outPorts') { attrs[portSelector]['ref-dy'] = 0; }
//
return attrs;
}
}));
joint.shapes.devs.flowchartProcessView = joint.shapes.devs.ModelView;
Then I instantiate the above element like this:
newElement = new joint.shapes.devs.flowchartProcess ({
id: id,
size: { width: width, height: height },
inPorts: ['in1'],
outPorts: ['out1'],
attrs: {
text: { text: elementLabel }
}
});
The issue I'm having is in trying to validate connections dragged between element ports. I tried this sample code based on the API and tutorials:
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
// Prevent linking from input ports.
if (magnetS && magnetS.getAttribute('type') === 'input') return false;
// Prevent linking from output ports to input ports within one element.
if (cellViewS === cellViewT) return false;
// Prevent linking to input ports.
return magnetT && magnetT.getAttribute('type') === 'input';
}
No links were validating so I did some digging. I then replaced the above code with this:
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
console.log(cellViewS + " | " + magnetS.getAttribute('type') + " | " + cellViewT + " | " + magnetT.getAttribute('class') + " | " + end + " | " + linkView);
return true;
}
From there I saw that there is no "type" attribute being set, though the "class" for the target port was fine. Setting a watch on those variables in Chrome verified that attribute isn't there.
I was under the impression that using the devs.Model library automatically set up the ports with all the needed stuff. Is whether the type is an input or output port something I still need to set manually? Have I managed to mess up the definition or instantiation somehow, preventing the proper attributes from being defined?
Many thanks in advance!
Okay, so solved it myself. This line:
attrs[portBodySelector] = { port: { id: portName || _.uniqueId(type) , type: type } };
isn't setting type in the main attributes in the magnetic port template so this code:
if (magnetS && magnetS.getAttribute('type') === 'input') return false;
is trying to get an attribute that doesn't exist. The 'in' and 'out' attributes exist in various places but not where the magnetS can find it. There is probably a deeper fix that would be more elegant but this workaround got me rolling again:
attrs: {
'.body': { stroke: 'black' },
'.body-label': { 'ref-x': .5, 'ref-y': .5, ref: '.body', 'y-alignment': 'middle', 'x-alignment': 'middle' },
'.port-body': { r: portRadius, magnet: 'active' },
'.inPorts .port-body': { stroke: portBodyStroke, fill: inPortFill, type: 'input' }, // add type here
'.outPorts .port-body': { stroke: portBodyStroke, fill: outPortFill, type: 'output' }, // and here
'.inPorts .port-label': { 'font-size': 0},
'.outPorts .port-label': {'font-size': 0 }
},
That defines the type attribute in the proper place for the validate code to find it and all is then well.
Can i add color dynamically by calling through a function passed by the user.
I already have the options variable defined by default, the color which the user passes should sit in the option variable. Is this possible? please help
var options = {
series: {
lines: {
show: true
},
points: {
show: true
},
color: '#00c7ce'--> user should pass dynamically
},
xaxis: {
mode: "time",
tickSize: [1, "month"],
tickLength: 0,
},
yaxis: {
show: false
}
}
};
You should be able to pass a color to the options. Setup your input then use that variable as your color.
<input id="userInput"></input>
var usrColor = $("#userInput").val();
var options = {
series: {
lines: { show: true},
points: {show: true, radius: 4},
color: usrColor
}
};
fiddle - http://jsfiddle.net/Rnusy/4/