Charts
JointJS+ provides you with a set of flexible, good looking and interactive charts that you can use in your applications. Note that from the JointJS perspective, a chart is just another JointJS shape. This means that you can add it to your diagram, resize it, rotate it, clone it, connect it with other elements, serialize/deserialize it to/from JSON format or export it to SVG, PNG or JPEG. No other library can give you this flexibility!
All chart shapes are included in shapes.chart
namespace.
All charts are built with a deprecated css selectors approach. To use charts in the current version, you need to set the useCSSSelectors
config option to true
.
Knob
Knob chart is a great component for displaying numbers in a fancy way. They can also be animated to better visualize changes in values.
Create a basic knob
The code snippet below creates a basic Knob. The Knob contains only one value.
import { dia, shapes } from '@joint/plus';
const graph = new dia.Graph({}, { cellNamespace: shapes });
const paper = new dia.Paper({
el: document.getElementById('paper'),
width: 200,
height: 200,
model: graph,
cellViewNamespace: shapes
});
const knob = new shapes.chart.Knob({
position: { x: 20, y: 20 },
size: { width: 100, height: 100 },
min: 0, max: 100, value: 80,
fill: '#2c97de'
});
graph.addCell(knob);
Styling the knob
To set color of the knob, simply pass fill
attribute with color of your choice to the knob constructor. Moreover, as it is common in JointJS/JointJS+, you can style any graphical element of the knob shape. For example, you might want to change the font family or other font properties of the legend. For this, you can customize the knob using the usual JointJS approach, via the attrs
object or the attr() method. This means that by just knowing the structure of the SVG elements the knob is built from, you have full flexibility in styling your knob. Only SVG and CSS standards are your limits.
Let's have a look at some examples demonstrating different knob styles. For the full SVG structure of the knob, please see the api section. Once you know the structure, it is easy to make CSS selectors referencing the SVG elements that you want to style.
In the example above, we showed how to pass styling in the attrs
object when creating a new knob but you can also change attributes of an existing knob:
knob.attr('.slice .slice-fill/stroke', 'black')
knob.attr('.slice .slice-fill/stroke-width', 2)
knob.attr('.legend-slice text/font-size', 16)
Note that you can use not only fill
but a whole range of other SVG attributes to style your knobs. Moreover, JointJS provides a facility to use gradients and filters. You can take advantage of them to make your charts even prettier:
const knob = new shapes.chart.Knob({
...
attrs: {
'.data': {
filter: {
name: 'dropShadow',
args: { dx: 0, dy: 0, blur: 3, color: 'black' },
attrs: {
filterUnits: 'objectBoundingBox',
x: -1,
y: -1,
width: 3,
height: 3
}
}
}
}
...
});
Multiple values
The Knob chart supports multiple series. The value
, fill
, min
and max
properties of the Knob constructor accept arrays. Simply pass more values into the arrays and the Knob chart will automatically render multiple series as in the following example:
const chart = new shapes.chart.Knob({
...
value: [30, 60, 90],
fill: ['#F2C500', '#4CC3D9', '#E94B35'],
min: 0, max: 100
});
Tooltips
Tooltips are a great way to show contextual information in your charts. The following demo shows how to use the JointJS+ Tooltip component in combination with Knob chart:
When setting up tooltips, we no longer work with the chart model. Instead, we request the paper for the view that represents the chart. This view triggers mousemove
, and mouseout
events that we use to hide and show our tooltip. The mousemove
event handler gets passed two important arguments: slice
and event
. These two arguments mean the following:
slice
- an object with useful information about the slice the event was triggered on. This object contains the following properties:sliceIndex
,serieIndex
,value
,percentage
,fill
,offset
,angle
,startAngle
,endAngle
,outerRadius
,innerRadius
, andlabel
. Most of the time, for tooltips, the important ones arevalue
andlabel
.event
- This is the DOM event object. For tooltips, the two important properties areclientX
andclientY
. We use these to render the tooltip at a particular position.
All these three arguments help us render our tooltip.
Animation
In general, you can animate any property of the Knob chart (change its value over a certain period of time). In the following demo, we will show how to animate some properties of the Knobs. Using the same technique, you can animate an arbitrary property.
knob.transition('value', 100, { duration: 1000, timingFunction: util.timing.exponential });
knob.transition('pieHole', .4, { duration: 1000, timingFunction: util.timing.exponential });
You can find more about JointJS transitions in the API reference.
Matrix
Matrix chart is great for displaying co-occurrences in a matrix.
Create a matrix chart
The code snippet below creates a basic Matrix chart with three rows and three columns.
const graph = new dia.Graph({}, { cellNamespace: shapes });
const paper = new dia.Paper({
el: document.getElementById('paper'),
width: 500,
height: 500,
model: graph,
cellViewNamespace: shapes
});
const matrix = new shapes.chart.Matrix({
position: { x: 50, y: 50 },
size: { width: 170, height: 170 },
cells: [
[{ fill: 'red' }, { fill: 'black' }, { fill: 'black' }],
[{ fill: 'black' }, { fill: 'red', rx: 50, ry: 50 }, { fill: 'black' }],
[{ fill: 'black' }, { fill: 'black' }, { fill: 'red' }]
],
labels: {
rows: [{ text: '1' }, { text: '2' }, { text: '3' }],
columns: [{ text: 'A' }, { text: 'B' }, { text: 'C' }],
}
});
graph.addCell(matrix);
Styling the Matrix
All the cells in the matrix are SVG rectangles. The objects in the cells
array can contain any SVG attributes that will be used to style those rectangles. The most common one is fill
which allows you to set the fill color of the rectangle. Another useful ones are radii rx
and ry
which can effectively turn the rectangles into circles if the radii specified are big enough (as we saw in the above example).
However, there is more we can do to make our matrices much prettier. For example, we can use opacity
to make our cells semi-transparent, set stroke
, style labels, grid lines or the background of the matrix.
Real-world example
In our previous examples, we were trying to show the basics of working with Matrix chart. However, the real power of Matrix charts is to show co-occurrences. The following chart is somewhat bigger and shows the relationships from the Les Miserables book by Victor Hugo. The demo also integrates the Tooltip component to show how we can display tooltips when cells are hovered.
Conway's Game of Life example
The following example shows the great Conway's Game of Life cellular automaton.
Pie
Pie shape is a great chart type for displaying numerical proportions. Donut charts, multiple series, animation, changing look & feel of any part of the chart, drop shadows and other filters and gradients are all supported.
Create a basic Pie chart
The code snippet below creates a basic Pie chart. The pie chart contains only one serie and its legend is displayed by default.
const graph = new dia.Graph({}, { cellNamespace: shapes });
const paper = new dia.Paper({
el: document.getElementById('paper'),
width: 500,
height: 500,
model: graph,
cellViewNamespace: shapes
});
const chart = new shapes.chart.Pie({
position: { x: 50, y: 10 },
size: { width: 100, height: 100 },
series: [ { data: [
{ value: 40, label: 'Organic', fill: '#8bce5d' },
{ value: 20, label: 'Email', fill: '#53abdd' },
{ value: 20, label: 'Social', fill: '#c377b1' },
{ value: 20, label: 'Referral', fill: '#ffe891' }
]}]
});
graph.addCell(chart);
Styling Pie Slices
Normally, you can just pass a color of each slice in the fill
property of the slice object as you can see in the example above. However, as it is common in JointJS/JointJS+, you can style any graphical element of the chart shape. For example, you might want to change the font family or other font properties of the slice labels, or the legend. For this, you can customize the chart using the usual JointJS approach, via the attrs
object or the attr() method. This means that by just knowing the structure of the SVG elements the chart is built from, you have full flexibility in styling your chart. Only SVG and CSS standards are your limits. Following the JointJS philosophy, we don't try to invent special parameters. Instead, we try to follow SVG and CSS standards as much as we can.
Let's have a look at one snippet of the SVG structure of the charts that is used to render a slice. For the full SVG structure of charts, please see the section Chart SVG Structure in our api. Once you know the structure, it is easy to make CSS selectors referencing the SVG elements that you want to style. We'll show an example of that below. the SVG structure of one serie is the following:
<g class="slice serie-[index of the serie] slice-[index of the slice]">
<path class="slice-fill">
<path class="slice-border">
<text class="slice-inner-label">
</g>
Knowing that, we can, for example, change color and size of the text label of the first slice:
const chart = new shapes.chart.Pie({
... /* the same as before */ ...
attrs: {
'.slice-0 .slice-inner-label': { fill: 'black', 'font-size': 20 }
}
});
In the example above, we showed how to pass styling in the attrs
object when creating a new chart but you can also change attributes of an existing chart:
chart.attr('.slice-0 .slice-inner-label/fill', 'blue')
Note that you can use not only fill
but a whole range of other SVG attributes to style your charts. Moreover, JointJS provides a facility to use gradients and filters. You can take advantage of them to make your charts even prettier:
const chart = new shapes.chart.Pie({
...
series: [{
data: [
{
value: 40,
label: 'Organic',
fill: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#b4f200' },
{ offset: '80%', color: '#759d00' },
],
attrs: {
x1: '0%',
y1: '0%',
x2: '0%',
y2: '100%',
},
},
},
// ...
],
}],
...
});
Multiple series
The Pie chart supports multiple series. Simply add more series to the series
array. This is useful for creating pie charts that, for example, compare data (from different sources, periods, ...).
const chart = new shapes.chart.Pie({
...
series: [
{ label: '2014', data: [
{ value: 20.3, label: 'IE', fill: '#4CC3D9' },
{ value: 18.3, label: 'Firefox', fill: '#F16745' },
{ value: 34.2, label: 'Chrome', fill: '#7BC8A4' }
]},
{ label: '2013', data: [
{ value: 27.5, label: 'IE', fill: '#4CC3D9' },
{ value: 20, label: 'Firefox', fill: '#F16745' },
{ value: 30, label: 'Chrome', fill: '#7BC8A4' }
]},
...
],
...
});
All series have automatically generated legends. This legend is interactive. Based on the effect you defined, the user can either hover or click on the labels in the legend and offset (or enlarge) the associated slices in the chart.
Exploding a Slice
Slices in the Pie chart can be separated from the rest (exploded) by setting the offset
property of the slice object:
Donut Charts
The difference between Donut and Pie charts is very small, the only thing that differs is the hole inside the pie. While there is no hole for Pie chart, Donut charts have a hole that can be set arbitrarily. The pieHole
property can either be a floating point number between 0
and 1
, in which case the size of the hole is proportional to the radius of the pie chart. If the pieHole
property is bigger than 1
, it is considered to be an absolute size of the hole in pixels:
Tooltips
Tooltips are a great way to show contextual information in your charts. The following demo shows how to use the Tooltip component in combination with Pie chart:
When setting up tooltips, we no longer work with the chart model. Instead, we request the paper for the view that represents the chart. This view triggers mousemove
, and mouseout
events that we use to hide and show our tooltip. The mousemove
event handler gets passed two important arguments: slice
and event
. These two arguments mean the following:
slice
- an object with useful information about the slice the event was triggered on. This object contains the following properties:sliceIndex
,serieIndex
,value
,percentage
,fill
,offset
,angle
,startAngle
,endAngle
,outerRadius
,innerRadius
, andlabel
. Most of the time, for tooltips, the important ones arevalue
andlabel
.event
- This is the DOM event object. For tooltips, the two important properties areclientX
andclientY
. We use these to render the tooltip at a particular position.
Legend
The chart legend is auto-generated and contains labels and colors of all the data series. The legend is interactive by default and allows the user to highlight the slices displayed in the chart while hovering the legend items. This sections shows you how you can customize the legend and its position.
The legend is rendered as an SVG group element with the class legend
. Inside this group, we have legend-item
s each containing an SVG circle and text elements. Please refer to the Chart SVG structure section for the full SVG structure of the chart. This is all you need to know to be able to style the legend. By default, the legend is positioned on the top right corner of the pie chart.
The styling of the legend item text (name of the associated slice) and the legend item circle can be done the usual
JointJS way, through standard SVG attributes as you can see below. Another useful properties for styling the legend
items are legendLabelLineHeight
, legendLabelMargin
and labelLineHeight
.
The first two can be defined in the slice objects (or in
the sliceDefaults
for all slices) while the third one is specific
to the legend item for the serie and therefore is defined in the serie object
(or in serieDefaults
).
// Positioning to the top (80px above the chart and centered).
chart3.attr('.legend/ref-y', -80);
chart3.attr('.legend/ref-x', .5);
chart3.attr('.legend/x-alignment', -.5);
// Styling of the legend text and circle.
chart3.prop('sliceDefaults/legendLabel', '{label} is {value}%');
chart3.prop('sliceDefaults/legendLabelLineHeight', 8);
chart3.prop('sliceDefaults/legendLabelMargin', 25);
chart3.attr('.legend-slice text/fill', '#336699');
chart3.attr('.legend-slice text/font-weight', 'bold');
chart3.attr('.legend-slice text/text-decoration', 'underline');
chart3.attr('.legend-slice text/font-size', 13);
chart3.attr('.legend-slice circle/r', 7);
chart3.attr('.legend-slice circle/stroke', 'black');
chart3.attr('.legend-slice circle/stroke-width', 1);
Animation
In general, you can animate any property of the Pie chart (change its value over a certain period of time). In the following demo, we will show how to animate some properties of the Pie chart. Using the same technique, you can animate an arbitrary property.
chart.transition('pieHole', .6, { duration: 700 });
chart.transition('series/0/data/0/offset', 50, { duration: 700 });
chart.transition('attrs/.slice-inner-label/font-size', 30, { duration: 700 });
chart.transition('series/0/data/0/fill', '#ff0000', {
duration: 700,
valueFunction: util.interpolate.hexColor
});
You can find more about JointJS transitions in the API reference.
Effects
The effect applied when the user either clicks or hover a slice in the pie can be customized. There are two actions that trigger an effect (onClickEffect
and onHoverEffect
) and two types of effects: enlarge
and offset
. Try to interact (hover/click slices) with the pie chart in the demo below to see the different effects applied.
const chart = new shapes.chart.Pie({
...
sliceDefaults: {
onClickEffect: { type: 'offset', offset: 20 },
onHoverEffect: { type: 'enlarge', scale: 1.5 }
}
});
Plot
The Plot chart allows you to create Line, Bar and Area charts and their combinations.
const graph = new dia.Graph({}, { cellNamespace: shapes });
const paper = new dia.Paper({
el: document.getElementById('paper'),
width: 500,
height: 500,
model: graph,
cellViewNamespace: shapes
});
const chart = new shapes.chart.Plot({
position: { x: 50, y: 50 },
size: { width: 300, height: 100 },
series: [
{
name: 'myserie',
data: [{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 2 }, { x: 4, y: 3 }]
}
]
});
graph.addCell(chart);
Series styling
All the elements of the chart can be customized using the usual JointJS approach, via the attrs
object or the attr() method. This means that by just knowing the structure of the SVG elements the chart is built from, you have full flexibility in styling your chart. Following the JointJS philosophy, we don't try to invent special parameters. Instead, we try to follow SVG and CSS standards as much as we can in the way things are styled.
Let's have a look at one snippet of the SVG structure of the charts that is used to render a serie. For the full SVG structure of charts, please see the section Chart SVG Structure in our API reference. Once you know the structure, it is easy to make CSS selectors referencing the SVG elements that you want to style. We'll show an example of that below. the SVG structure of one serie is the following:
<g class="serie [name of the serie]">
<path>
<g class="points">
<g class="point">
<circle>
<text>
</g>
<!-- ... possibly more points -->
</g>
</g>
Knowing that, we can, for example, set the stroke color of the serie SVG path like this:
const chart = new shapes.chart.Plot({
position: { x: 50, y: 50 },
size: { width: 300, height: 100 },
series: [
{
name: 'myserie',
data: [{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 2 }, { x: 4, y: 3 }]
}
],
attrs: {
'.myserie path': {
stroke: 'green', 'stroke-width': 3
}
}
});
In the example above, we showed how to pass styling when creating a new chart but you can also change attributes of an existing chart:
chart.attr('.myserie path/stroke', 'red');
Note that not only you can change strokes but you can also use other SVG presentational attributes. An interesting one is the fill
attribute. By setting the fill
attribute on our serie path, we effectively render our serie as an Area chart. In the following demos, we even use the JointJS gradients for the fill
attribute making our charts even prettier:
Multiple series
The Chart plugin supports multiple series. Simply add more series to the series
array. As you might have noticed, series objects have the name
property. This name
is then set as a class name on the serie SVG grouping element. This allows you to style series based on their names as we've already shown in the previous demos. In the following example, we plot multiple series and style them differently based on their names.
All series have automatically generated legends. This legend is interactive. You can click on the labels in the legend and turn off and on the rendering of the associated serie in the chart.
Markings
Markings (a.k.a trend lines) are vertical or horizontal lines or rectangles highlighting a certain area in the chart. You can have an unlimited number of markings in your charts. Markings are defined in the markings
array with coordinates of the start and end of the marking.
The following is a snippet from the options passed to the chart constructor that defines four markings and styles them. For the full source code, please follow the link below the demo.
markings: [
{
name: 'target',
start: { y: 3.2 },
label: 'My Target'
},
{
name: 'current',
start: { x: 4 }
},
{
name: 'previous',
start: { x: 2.8 },
end: { x: 3.2 }
},
{
name: 'next',
start: { x: 4.8, y: 3.5 },
end: { x: 5.2, y: 2.5 }
}
],
attrs: {
'.marking.target rect': {
fill: 'red'
},
'.marking.next rect': {
fill: '#3498DB', rx: 1000, ry: 1000, 'fill-opacity': .6, stroke: 'black'
},
'.marking.previous rect': {
fill: '#27AE60', 'fill-opacity': .8
}
}
Guidelines
Guidelines are lines that appear when you hover over the chart with your mouse pointer. They help the user orient himself in the chart. Guidelines are disabled by default but can be very easily turned on.
Here is the snippet from the attrs
object that enables guidelines:
attrs: {
'.guideline': {
'stroke-dasharray': '3,1', 'stroke-width': .8, display: 'block'
},
'.x-guideline': {
stroke: 'red'
},
'.y-guideline': {
stroke: 'green'
}
}
Hover over the chart to see the guidelines following your mouse cursor.
Tooltips
Tooltips are a great way to show contextual information in your charts. The JointJS Chart plugin also provides a facility to get to the closest points to the mouse cursor from all the series data points. Moreover, the chart view offers the renderPoint()
method that makes it easy to display a point marker at the place of any data point from any serie. Let's start with a demo and then see how this can be implemented.
When setting up tooltips, we no longer work with the chart model. Instead, we request the paper for the view that represents the chart. This view triggers mouseover
and mouseout
events that we use to hide and show our tooltip. The mouseover
event handler gets passed three important arguments: dataPoint
, clientPoint
and closestPoints
. These three arguments mean the following:
dataPoint
- the point in the chart area. This point is interpolated based on the minimum and maximum x and y values and the position of the mouse cursor in the chart.clientPoint
- basically the mouse eventclientX
andclientY
coordinates but transformed to the coordinate system of the chart view.closestPoints
- an array of from each data serie that are the closest points to the mouse cursor. Each object in this array is of the form:{ x: [number], y: [number], serie: [serie object] }
.
Legend
The chart legend is auto-generated and contains labels and colors of all the data series. The legend is also interactive by default and allows the user to turn on and off data series displayed in the chart. This sections shows you how you can customize the legend and its position and also how to turn series on and off programmatically.
The legend is rendered as an SVG group element with the class legend
. Inside this group, we have legend-item
s each containing an SVG circle and text elements. Please refer to the Chart SVG structure API section for the full SVG structure of the chart. This is all you need to know to be able to style the legend. Moreover, we have prepared a syntactic sugar method (legendPosition()
) for positioning the legend so that you don't have to deal with the low-level positioning using the JointJS special attributes:
chart.legendPosition('nw');
Also, the styling of the legend item text (name of the associated serie), the legend item circle and the their look in the disabled state (when the serie is turned off) can be done the usual JointJS way:
chart.attr('.legend-item text/fill', 'red');
chart.attr('.legend-item circle/r', 6);
chart.attr('.legend-item circle/stroke', 'black');
chart.attr('.legend-item.disabled text/fill', 'blue');
Real-time data
Every change to the series
array triggers an update of the chart view and new series are rendered. However, the Chart plugin provides two methods that are handy when dealing with real-time data: addPoint(p, serieName, op)
and lastPoint(serieName)
. Use addPoint()
to add a new data point to a serie identified by serieName
. This method automatically triggers an update and the associated view is re-rendered:
setInterval(function() {
chart.addPoint({ x: chart.lastPoint('myserie').x + 1, y: Math.random() * 2 + 2 }, 'myserie', { maxLen: 6 });
}, 600);
Fixed axis
By default, the chart plugin automatically choses the minimum and maximum values on each of the axis based on the minimum/maximum data points. By specifying min
and max
values in the axis object, you can define the precise minimum and maximum values for the axis:
const chart = new shapes.chart.Plot({
// ...
axis: {
'y-axis': {
min: -3,
max: 10
},
'x-axis': {
min: -5,
max: 10
}
}
// ...
})
Time series
By going through the previous sections, you already know all you need to be able to plot time series. There is nothing special about plotting time series except of being able to format the data/times on the x axis. For that, the Chart plugin allows you to define your own formatting functions for the tick labels. This means that for representing time series data, your data point x coordinates should be in the UNIX time format. Then you just provide a function that formats this timestamp into a more readable representation:
const chart = new shapes.chart.Plot({
// ...
series: [
{ name: 'temp', data: [{ x: 1397560472344, y: 21.5 }, ... ] }
],
axis: {
'x-axis': {
tickFormat: function(t) {
const d = new Date(t);
return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getHours() + '/' + d.getMinutes()
}
},
'y-axis': {
tickFormat: function(v) { return util.format.number('.1f', v) + ' dgC'}
}
}
// ...
});
Bar series
The chart plugin supports bar series. To turn a serie to a bar serie, just set the bars
option to true
:
const chart = new shapes.chart.Plot({
// ...
series: [
{ name: 'myserie', bars: true, data: [{ x: 2, y: 20 }, ... ] }
]
// ...
});
There is couple of recommended settings when working with bar charts, namely padding
, align
and barWidth
. padding
is not specific to bar charts but is especially useful when using this kind of chart. This is because when bars are rendered, they must be aligned to a certain value on the x axis. By default, the top-left corner of each bar is at the position of the associated x
data point value. That means that without any padding, the last bar won't be visible (only a very tiny fraction of its left border). Therefore, we have to set at least right
padding
to make the bar visible. The align
property of bars
object controls the alignment of the bars with respect to the associated x
data point value. Possible values are 'left'
(the default), 'right'
and 'middle'
. barWidth
controls the width of the bars. If the value is a floating point number between 0
and 1
, it is used to set the final width of each bar proportionally to the calculated width. If the value is an integer higher than 1
, it is considered to be an absolute width in pixels. For a full description of the bar options, please see the Series options object, especially the bars
object.
Bar & Line series intermixed
Bar and Line series can be rendered together as part of one chart.
const chart = new shapes.chart.Plot({
// ...
series: [
{ name: 'mybars', bars: { barWidth: .7 }, data: [{ x: 1, y: 15 }/* ... */ ] },
{ name: 'myotherbars', bars: { barWidth: .7 }, data: [{ x: 1, y: 12 }/* ... */ ] },
{ name: 'myline', interpolate: 'bezier', data: [{ x: 1, y: 20 }/* ... */ ] }
]
// ...
});
As you can see in the code above, we have two bar series and one line serie as part of one chart. It is important to note that if there are more than one bar serie in the chart, by default, the bars of the different series will be rendered one over another. For cases where this is not desirable, you can offset the bars of one of the series as shown in the demo below:
As you might have noticed in the demo above, negative values for the series are fully supported.