Related
I will attempt to explain my issue as clearly as possible while also avoid making this topic too long. I recently found the Chart.js library, which is excellent for what I need. Now, since I am using Node.js and need a png of the graph, I am utilizing the chartjs-node-canvas library. Having this information in mind, I will try to split my topic into multiple sections for a clearer understanding.
Ultimate Goal
Before getting into the problem itself, I would like to discuss my ultimate goal. This is to give a general idea on what I'm trying to do so the responses are fitted accordingly. To keep this short, I have data in the form of {awardedDate: "2022-06-22T12:21:17.22Z", badgeId: 1234567}, with awardedDate being a timestamp of when the badge was awarded, and the badgeId being the ID of the badge that was awarded (which is irrelevant to the graph, but it exists because it's part of the data). Now, I have a sample with around 2,787 of these objects, with all having different award dates and IDs, and with dates ranging from 2016 to 2022. My objective is to group these badges by month-year, and that month-year will have the amount of badges earned for that month during that year. With that data, I then want to make a waterfall graph which is based on the amount of badges earned that month of that year. As of right now, there isn't a specific structure on how this will look like, but it could range from an object that looks like {"02-2022": 10, "03-2022": 5} to anything else. I can of course restructure this format based on what is required for a waterfall graph.
Actual Questions
Now that you have a general idea of what my ultimate goal is, my actual question is how I'd be able to make a floating (we can leave the waterfall structure stuff for another topic) bar graph with that data. Since the data can have blank periods (it is possible for a dataset to have gaps that are months long), I cannot really utilize labels (unless I am saying something wrong), so an x-y relation works the best. I tried using the structure of {x: "2022-06-22T12:21:17.226Z", y: [10, 15]}, but that didn't really yield any results. As of right now, I am using a sample code to test how the graph reacts with the data, and of course I'll replace the test values with actual values once I have a finished product. Here is my code so far:
const config = {
type: "bar",
data: {
datasets: [{
label: "Badges",
data: [
{
x: "2022-06-22T12:41:17.226Z",
y: [10, 15]
}
],
borderColor: "rgb(75, 192, 192)",
borderSkipped: false
}]
},
options: {
plugins: {
legend: {
display: false
},
title: {
display: true,
text: "Test",
color: "#FFFFFF"
}
},
scales: {
x: {
type: 'time',
title: {
display: true,
text: 'Time',
color: "#FFFFFF"
},
min: "2022-06-22T12:21:17.226Z",
max: "2022-06-22T14:21:17.226Z",
grid: {
borderColor: "#FFFFFF",
color: "#FFFFFF"
},
ticks: {
color: "#FFFFFF"
}
},
y: {
title: {
display: true,
text: 'Number of Badges',
borderColor: "#FFFFFF",
color: "#FFFFFF"
},
min: 0,
max: 50,
grid: {
borderColor: "#FFFFFF",
color: "#FFFFFF"
},
ticks: {
color: "#FFFFFF"
}
}
}
},
plugins: [
{
id: 'custom_canvas_background_color',
beforeDraw: (chart) => {
const ctx = chart.ctx;
ctx.save();
ctx.fillStyle = '#303030';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
}
]
};
const imageBuffer = await canvasRenderService.renderToBuffer(config)
fs.writeFileSync("./chart2.png", imageBuffer)
And this is the graph that the code produces:
What is supposed to happen, of course, is that a float bar should be generated near the start that ranges from 5 to 10, but as seen above, nothing happens. If someone could assist me in my problem, that would be amazing. Thank you very much for your time and help, I greatly appreciate it.
Inspired by this answer, I came up with the following solution.
const baseData = [
{ awardedDate: "2022-06-22T12:21:17.22Z" },
{ awardedDate: "2022-06-18T12:21:17.22Z" },
{ awardedDate: "2022-06-15T12:21:17.22Z" },
{ awardedDate: "2022-05-20T12:21:17.22Z" },
{ awardedDate: "2022-05-10T12:21:17.22Z" },
{ awardedDate: "2022-04-16T12:21:17.22Z" },
{ awardedDate: "2022-04-09T12:21:17.22Z" },
{ awardedDate: "2022-04-03T12:21:17.22Z" },
{ awardedDate: "2022-04-01T12:21:17.22Z" },
{ awardedDate: "2022-02-18T12:21:17.22Z" },
{ awardedDate: "2022-02-12T12:21:17.22Z" },
{ awardedDate: "2022-01-17T12:21:17.22Z" }
];
const badgesPerMonth = baseData
.map(o => o.awardedDate)
.sort()
.map(v => moment(v))
.map(m => m.format('MMM YYYY'))
.reduce((acc, month) => {
const badges = acc[month] || 0;
acc[month] = badges + 1;
return acc;
}, {});
const months = Object.keys(badgesPerMonth);
const labels = months.concat('Total');
const data = [];
let total = 0;
for (let i = 0; i < months.length; i++) {
const vStart = total;
total += badgesPerMonth[months[i]];
data.push([vStart, total]);
}
data.push(total);
const backgroundColors = data
.map((o, i) => 'rgba(255, 99, 132, ' + (i + (11 - data.length)) * 0.1 + ')');
new Chart('badges', {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Badges',
data: data,
backgroundColor: backgroundColors,
barPercentage: 1,
categoryPercentage: 0.95
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: ctx => {
const v = data[ctx.dataIndex];
return Array.isArray(v) ? v[1] - v[0] : v;
}
}
}
},
scales: {
y: {
ticks: {
beginAtZero: true,
stepSize: 2
}
}
}
}
});
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.min.js"></script>
<canvas id="badges" height="95"></canvas>
If you also want to see the gaps, you would first have to initialize badgesPerMonth with following months between the earliest and latest date, each with value zero. Please take a look at this answer to get an idea about how this could be done.
After reading #uminder's reply, I was able to create the following code which solved my problem:
dateGroups = Object.fromEntries(
Object.entries(dateGroups).sort(([d1,],[d2,]) => {return (d1 < d2) ? -1 : ((d1 > d2) ? 1 : 0)})
)
const dateTimesConst = Object.keys(dateGroups)
const dateValuesConst = Object.values(dateGroups)
let dateTimes = []
let dateValues = []
let prevLength = 0
let mostBadgesPerMonth = 0
for (let i = 0; i < dateValuesConst.length; i++) {
const currentMonth = new Date(Date.parse(dateTimesConst[i]))
const previousMonth = new Date(Date.UTC(currentMonth.getUTCFullYear(), currentMonth.getUTCMonth() - 1, 1, 0, 0, 0, 0)).toISOString()
const nextMonth = new Date(Date.UTC(currentMonth.getUTCFullYear(), currentMonth.getUTCMonth() + 1, 1, 0, 0, 0, 0)).toISOString()
// if (!dateTimesConst.includes(previousMonth)) prevLength = 0
const length = dateValuesConst[i].length
dateValues.push([prevLength, length])
dateTimes.push(dateTimesConst[i])
prevLength = length
if (length > mostBadgesPerMonth) mostBadgesPerMonth = length
// if (!dateTimesConst.includes(nextMonth) && i !== dateValuesConst.length - 1) {
// dateTimes.push(nextMonth)
// dateValues.push([length, 0])
// prevLength = 0
// }
}
function barColorCode() {
return (ctx) => {
const start = ctx.parsed._custom.start
const end = ctx.parsed._custom.end
return start <= end ? "rgba(50, 168, 82, 1)" : (start > end) ? "rgba(191, 27, 27, 1)" : "black"
}
}
const config = {
type: "bar",
data: {
labels: dateTimes,
datasets: [{
label: "Badges",
data: dateValues,
elements: {
bar: {
backgroundColor: barColorCode()
}
},
barPercentage: 1,
categoryPercentage: 0.95,
borderSkipped: false
}]
},
options: {
plugins: {
legend: {
display: false
},
title: {
display: true,
text: "Test",
color: "#FFFFFF"
}
},
scales: {
x: {
type: 'time',
title: {
display: true,
text: 'Date',
color: "#FFFFFF"
},
time: {
unit: "month",
round: "month"
},
min: dateTimesConst[0],
max: dateTimesConst[dateTimesConst.length - 1],
grid: {
borderColor: "#FFFFFF",
color: "#FFFFFF"
},
ticks: {
color: "#FFFFFF"
}
},
y: {
title: {
display: true,
text: 'Number of Badges',
borderColor: "#FFFFFF",
color: "#FFFFFF"
},
min: 0,
max: mostBadgesPerMonth + 1,
grid: {
borderColor: "#FFFFFF",
color: "#FFFFFF"
},
ticks: {
color: "#FFFFFF"
}
}
}
},
plugins: [
{
id: 'custom_canvas_background_color',
beforeDraw: (chart) => {
const ctx = chart.ctx;
ctx.save();
ctx.fillStyle = '#303030';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
}
]
};
const imageBuffer = await canvasRenderService.renderToBuffer(config)
fs.writeFileSync("./chart2.png", imageBuffer)
Again, big thanks to #uminder for the inspiration.
I've been using Chart.JS a little more than a week, and I've faced some problems, but right now I'm really stuck.
I need to create something similar to this, there are 1 product(blue) value and the orange one is an estimate of its value on the next month. So what I need to do is basically (((a — b) * 100) / a (or b I can't really remember)) and this as a label not actual data, and the result would be some percentage that would be used as a line on top of one product.
So far I got only this code, but it's not working like I need to.
{
type: "line",
label: monthsLabels?.mesBaseLabel,
data: [
productsValues?.receitaLiquidaBase[0],
productsValues?.receitaLiquidaOrcado[0],
],
fill: false,
},
{
type: "line",
label: monthsLabels?.mesOrcadoLabel,
data: [
productsValues?.receitaLiquidaBase[0],
productsValues?.receitaLiquidaOrcado[0],
],
// data: productsValues?.receitaLiquidaOrcado,
fill: false,
},
{
type: "bar",
label: monthsLabels?.mesBaseLabel,
data: productsValues?.receitaLiquidaBase,
// [
// productsValues?.receitaLiquidaBase[0],
// productsValues?.receitaLiquidaBase[1],
// ],
backgroundColor: ["rgba(42,62,176, 1)"],
borderWidth: 4,
datalabels: {
// anchor: "end",
// align: "top",
font: {
size: 10,
},
rotation: -90,
color: "white",
formatter: (value, context) => {
// console.log(value);
if (value !== 0) {
return value?.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, "$&,");
} else {
return 0;
}
},
},
},
{
type: "bar",
label: monthsLabels?.mesOrcadoLabel,
data: productsValues?.receitaLiquidaOrcado,
backgroundColor: "orange",
borderWidth: 4,
datalabels: {
// anchor: "end",
// align: "top",
font: {
size: 10,
},
rotation: -90,
color: "black",
formatter: (value, context) => {
// console.log(value);
if (value !== 0) {
return value?.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, "$&,");
} else {
return 0;
}
},
},
},
Objects on, data and label are an array of number coming from some fetching.
You are best off using a custom inline plugin for this:
var options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'red'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
backgroundColor: 'blue'
}
]
},
options: {
plugins: {
customValue: {
offset: 5,
dash: [5, 15]
}
}
},
plugins: [{
id: 'customValue',
afterDraw: (chart, args, opts) => {
const {
ctx,
data: {
datasets
},
_metasets
} = chart;
datasets[1].data.forEach((dp, i) => {
let increasePercent = dp * 100 / datasets[0].data[i] >= 100 ? Math.round((dp * 100 / datasets[0].data[i] - 100) * 100) / 100 : Math.round((100 - dp * 100 / datasets[0].data[i]) * 100) / 100 * -1;
let barValue = `${increasePercent}%`;
const lineHeight = ctx.measureText('M').width;
const offset = opts.offset || 0;
const dash = opts.dash || [];
ctx.textAlign = 'center';
ctx.fillText(barValue, _metasets[1].data[i].x, (_metasets[1].data[i].y - lineHeight * 1.5), _metasets[1].data[i].width);
if (_metasets[0].data[i].y >= _metasets[1].data[i].y) {
ctx.beginPath();
ctx.setLineDash(dash);
ctx.moveTo(_metasets[0].data[i].x, _metasets[0].data[i].y);
ctx.lineTo(_metasets[0].data[i].x, _metasets[1].data[i].y - offset);
ctx.lineTo(_metasets[1].data[i].x, _metasets[1].data[i].y - offset);
ctx.stroke();
} else {
ctx.beginPath();
ctx.setLineDash(dash);
ctx.moveTo(_metasets[0].data[i].x, _metasets[0].data[i].y - offset);
ctx.lineTo(_metasets[1].data[i].x, _metasets[0].data[i].y - offset);
ctx.lineTo(_metasets[1].data[i].x, _metasets[1].data[i].y - offset - lineHeight * 2);
ctx.stroke();
}
});
}
}]
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.4.1/chart.js"></script>
</body>
I've made this bar chart http://imageshack.com/a/img901/7186/cnOfhh.png, and the code for it is:
//compute & mark average color
for (var i = 0; i < zdata.length; i++) {
if (zdata[i].TargetTime == null) zdata[i].TargetTime = 0;
if (zdata[i].TimePlayed == null) zdata[i].TimePlayed = 0;
if (zdata[i].TargetTime >= zdata[i].TimePlayed) {
zdata[i]['Color'] = 'green';
} else {
zdata[i]['Color'] = 'red';
}
}
//localsitelist
var element = {
rt: 'D',
Id: rid,
courselist: clist,
selcourseId: selCid,
selcourse: selCname,
cartlist: wData,
selSiteId: lsid,
selsite: sitename,
dataList: zdata
}; //, carts: _mVM.availableCarts()}; //
//if rid exists, is update, else its new
var found = -1;
for (var k = 0; k < document.pvm.rapArray().length; k++) {
if (document.pvm.rapArray()[k].Id() == rid) {
document.pvm.rapArray()[k].update(element);
//build chart data
var values = []; //, series = Math.floor(Math.random() * 6) + 6;
for (var i = 0; i < zdata.length; i++) {
values[i] = {
data: [
[zdata[i].HoleSequence, zdata[i].TimePlayed]
],
color: zdata[i].Color
};
}
//var data = [{ data: [[0, 1]], color: "red" }, { data: [[1, 2]], color: "yellow" },{ data: [[2, 3]], color: "green" }];
BarChart('#ChartD-Overview' + rid, values);
found = 1;
break;
}
}
if (found == -1) {
var rvm = new panelViewModel(element);
document.pvm.rapArray.push(rvm);
//build chart data
var values = []; //, series = Math.floor(Math.random() * 6) + 6;
for (var i = 0; i < zdata.length; i++) {
values[i] = {
data: [
[zdata[i].HoleSequence, zdata[i].TimePlayed]
],
color: zdata[i].Color
};
}
BarChart('#ChartD-Overview' + rvm.Id(), values);
}
and the BarChart function:
function BarChart(id, data) {
$.plot(id, data, {
series: {
bars: {
show: true,
barWidth: 0.6,
align: "center"
}
},
stack: true,
xaxis: {
mode: "categories",
tickLength: 0
}
});
}
The problem is that I can't manage to get something like this https://imageshack.us/i/expGGpOkp, the little line should be zdata[i].TargetTime. I've tried something using stacked bar chart idea but the result was way different... What am I doing wrong? Can anyone help me with a suggestion to start with to get the same bar chart like in the last image?
Here is something like your second picture using another bar dataseries where the start and end of the bars are the same thereby reducing them to lines, you don't need to stack any of the bars just give them the right y-values (fiddle):
$(function () {
var dataBarsRed = {
data: [
[2, 3], ],
label: 'Bars in Red',
color: 'red'
};
var dataBarsGreen = {
data: [
[1, 2],
[3, 1],
[4, 3]
],
label: 'Bars in Green',
color: 'green'
};
var dataLines = {
data: [
[1, 3, 3],
[2, 3.5, 3.5],
[3, 1.5, 1.5],
[4, 2.5, 2.5]
],
label: 'Lines',
color: 'navy',
bars: {
barWidth: 0.5
}
};
var plot = $.plot("#placeholder", [dataBarsRed, dataBarsGreen, dataLines], {
points: {
show: false
},
lines: {
show: false
},
bars: {
show: true,
align: 'center',
barWidth: 0.6
},
grid: {
hoverable: true,
autoHighlight: true
},
xaxis: {
min: 0,
max: 5
},
yaxis: {
min: 0,
max: 5
}
});
});
I've made this bar chart http://imageshack.com/a/img901/7186/cnOfhh.png, and the code for it is:
//compute & mark average color
for (var i = 0; i < zdata.length; i++) {
if (zdata[i].TargetTime == null) zdata[i].TargetTime = 0;
if (zdata[i].TimePlayed == null) zdata[i].TimePlayed = 0;
if (zdata[i].TargetTime >= zdata[i].TimePlayed) {
zdata[i]['Color'] = 'green';
} else {
zdata[i]['Color'] = 'red';
}
}
//localsitelist
var element = {
rt: 'D',
Id: rid,
courselist: clist,
selcourseId: selCid,
selcourse: selCname,
cartlist: wData,
selSiteId: lsid,
selsite: sitename,
dataList: zdata
}; //, carts: _mVM.availableCarts()}; //
//if rid exists, is update, else its new
var found = -1;
for (var k = 0; k < document.pvm.rapArray().length; k++) {
if (document.pvm.rapArray()[k].Id() == rid) {
document.pvm.rapArray()[k].update(element);
//build chart data
var values = []; //, series = Math.floor(Math.random() * 6) + 6;
for (var i = 0; i < zdata.length; i++) {
values[i] = {
data: [
[zdata[i].HoleSequence, zdata[i].TimePlayed]
],
color: zdata[i].Color
};
}
//var data = [{ data: [[0, 1]], color: "red" }, { data: [[1, 2]], color: "yellow" },{ data: [[2, 3]], color: "green" }];
BarChart('#ChartD-Overview' + rid, values);
found = 1;
break;
}
}
if (found == -1) {
var rvm = new panelViewModel(element);
document.pvm.rapArray.push(rvm);
//build chart data
var values = []; //, series = Math.floor(Math.random() * 6) + 6;
for (var i = 0; i < zdata.length; i++) {
values[i] = {
data: [
[zdata[i].HoleSequence, zdata[i].TimePlayed]
],
color: zdata[i].Color
};
}
BarChart('#ChartD-Overview' + rvm.Id(), values);
}
and the BarChart function:
function BarChart(id, data) {
$.plot(id, data, {
series: {
bars: {
show: true,
barWidth: 0.6,
align: "center"
}
},
stack: true,
xaxis: {
mode: "categories",
tickLength: 0
}
});
}
The problem is that I can't manage to get something like this https://imageshack.us/i/expGGpOkp, the little line should be zdata[i].TargetTime. I've tried something using stacked bar chart idea but the result was way different... What am I doing wrong? Can anyone help me with a suggestion to start with to get the same bar chart like in the last image?
Here is something like your second picture using another bar dataseries where the start and end of the bars are the same thereby reducing them to lines, you don't need to stack any of the bars just give them the right y-values (fiddle):
$(function () {
var dataBarsRed = {
data: [
[2, 3], ],
label: 'Bars in Red',
color: 'red'
};
var dataBarsGreen = {
data: [
[1, 2],
[3, 1],
[4, 3]
],
label: 'Bars in Green',
color: 'green'
};
var dataLines = {
data: [
[1, 3, 3],
[2, 3.5, 3.5],
[3, 1.5, 1.5],
[4, 2.5, 2.5]
],
label: 'Lines',
color: 'navy',
bars: {
barWidth: 0.5
}
};
var plot = $.plot("#placeholder", [dataBarsRed, dataBarsGreen, dataLines], {
points: {
show: false
},
lines: {
show: false
},
bars: {
show: true,
align: 'center',
barWidth: 0.6
},
grid: {
hoverable: true,
autoHighlight: true
},
xaxis: {
min: 0,
max: 5
},
yaxis: {
min: 0,
max: 5
}
});
});
I'm trying to create a flowchart with jsPlumb. I have two elements and I want to connect them with a line. I want a line, but not a huge dot as I currently have. I've tried setting the radius of the dot everywhere I can, but it seems unaffected and remains at the default 20px. What am I doing wrong?
<div id="start">Start</div>
<div id="msgtype">Lorem ipsum dolor sit amet</div>
jsPlumb.importDefaults({
Endpoints : [ [ 'Dot', {radius:1} ], [ 'Dot', { radius:1 } ]]
});
var connectorPaintStyle = {
lineWidth:4,
fillStyle:'#333333',
joinstyle:'round',
outlineWidth:0,
radius: 1,
}
var eleStart = jsPlumb.addEndpoint('start', {
isSource:true,
isTarget:false
});
var eleMsgtypeTop = jsPlumb.addEndpoint('msgtype', {
isTarget:true,
parameters:{
endpoint:'dot',
radius: 1
},
anchor: 'TopCenter',
radius: 1,
connectorStyle:connectorPaintStyle
});
jsPlumb.connect({ source:eleStart,
target:eleMsgtypeTop,
connectorStyle:connectorPaintStyle,
endpoint:[ 'Dot', { radius:1, hoverClass:'myEndpointHover' }],
connectorStyle:connectorPaintStyle
});
Producing this:
JSFiddle Demo
Try this: http://jsfiddle.net/GT884/1/
;(function () {
window.jsPlumbDemo = {
init: function () {
jsPlumb.importDefaults({
DragOptions: {
cursor: "pointer",
zIndex: 2000
},
HoverClass: "connector-hover"
});
var connectorStrokeColor = "rgba(50, 50, 200, 1)",
connectorHighlightStrokeColor = "rgba(180, 180, 200, 1)",
hoverPaintStyle = {
strokeStyle: "#7ec3d9"
};
var connection1 = jsPlumb.connect({
source: "start",
target: "msgtype",
connector: "Bezier",
cssClass: "c1",
endpoint: "Blank",
endpointClass: "c1Endpoint",
anchors: ["BottomCenter", [0.75, 0, 0, -1]],
paintStyle: {
lineWidth: 6,
strokeStyle: "#a7b04b",
outlineWidth: 1,
outlineColor: "#666"
},
endpointStyle: {
fillStyle: "#a7b04b"
},
hoverPaintStyle: hoverPaintStyle
});
// make all .window divs draggable
jsPlumb.draggable(jsPlumb.getSelector(".window"));
}
};
})();
jsPlumb.bind("ready", function () {
jsPlumb.init();
// chrome fix.
document.onselectstart = function () {
return false;
};
// render mode
var resetRenderMode = function (desiredMode) {
var newMode = jsPlumb.setRenderMode(desiredMode);
$(".rmode").removeClass("selected");
$(".rmode[mode='" + newMode + "']").addClass("selected");
$(".rmode[mode='canvas']").attr("disabled", !jsPlumb.isCanvasAvailable());
$(".rmode[mode='svg']").attr("disabled", !jsPlumb.isSVGAvailable());
$(".rmode[mode='vml']").attr("disabled", !jsPlumb.isVMLAvailable());
//var disableList = (newMode === jsPlumb.VML) ? ",.rmode[mode='svg']" : ".rmode[mode='vml']";
// $(disableList).attr("disabled", true);
jsPlumbDemo.init();
};
$(".rmode").bind("click", function () {
var desiredMode = $(this).attr("mode");
if (jsPlumbDemo.reset) jsPlumbDemo.reset();
jsPlumb.reset();
resetRenderMode(desiredMode);
});
// explanation div is draggable
$("#explanation,.renderMode").draggable();
resetRenderMode(jsPlumb.SVG);
});
The full, final solution was:
;(function () {
window.plumbify = {
init: function () {
jsPlumb.importDefaults({
DragOptions: {
cursor: 'pointer',
zIndex: 2000
},
HoverClass: 'connector-hover'
});
var connectorStrokeColor = 'rgba(50, 50, 200, 1)',
connectorHighlightStrokeColor = 'rgba(180, 180, 200, 1)',
paintStyle = {
lineWidth: 3,
strokeStyle: '#a7b04b',
outlineWidth: 1,
outlineColor: '#666'
},
hoverPaintStyle = {
strokeStyle: '#7ec3d9'
};
var connection1 = jsPlumb.connect({
source: 'start',
target: 'msgtype',
connector: 'Bezier',
cssClass: 'c1',
endpoint: 'Blank',
anchors: [[0.25, 1, 0, 0.5], [0.75, 0, 0, -0.5]],
paintStyle: paintStyle,
endpointStyle: {
fillStyle: '#a7b04b'
},
overlays:[
[ 'Arrow', { width:20, length:30, location:0.5, paintStyle: paintStyle } ],
],
hoverPaintStyle: hoverPaintStyle
});
// make all .window divs draggable
jsPlumb.draggable(jsPlumb.getSelector('.window'));
}
};
})();
jsPlumb.bind('ready', function () {
jsPlumb.init();
// chrome fix.
document.onselectstart = function () {
return false;
};
// render mode
var resetRenderMode = function (desiredMode) {
var newMode = jsPlumb.setRenderMode(desiredMode);
plumbify.init();
};
resetRenderMode(jsPlumb.SVG);
});
Resulting in:
jsPlumb.addEndpoint(element.id,{
...
paintStyle:{radius:1};
});