Browse Source

Creating examples, lots of bugfixes!

css_transitions
Alex de Mulder 10 years ago
parent
commit
db46e27732
20 changed files with 2219 additions and 628 deletions
  1. +59
    -41
      dist/vis.css
  2. +1174
    -376
      dist/vis.js
  3. +1
    -1
      dist/vis.min.css
  4. +14
    -14
      dist/vis.min.js
  5. +23
    -78
      examples/graph2d/01_basic.html
  6. +53
    -0
      examples/graph2d/02_bars.html
  7. +111
    -0
      examples/graph2d/03_groups.html
  8. +125
    -0
      examples/graph2d/04_rightAxis.html
  9. +139
    -0
      examples/graph2d/05_bothAxis.html
  10. +100
    -0
      examples/graph2d/06_interpolation.html
  11. +102
    -0
      examples/graph2d/09_performance.html
  12. +237
    -57
      src/timeline/Graph2d.js
  13. +0
    -5
      src/timeline/Range.js
  14. +3
    -3
      src/timeline/Timeline.js
  15. +14
    -10
      src/timeline/component/DataAxis.js
  16. +17
    -0
      src/timeline/component/GraphGroup.js
  17. +5
    -7
      src/timeline/component/Legend.js
  18. +40
    -34
      src/timeline/component/Linegraph.js
  19. +1
    -1
      src/timeline/component/css/dataaxis.css
  20. +1
    -1
      src/util.js

+ 59
- 41
dist/vis.css View File

@ -53,6 +53,25 @@
position: relative; position: relative;
} }
.vis.timeline .vispanel .shadow {
position: absolute;
width: 100%;
height: 1px;
box-shadow: 0 0 10px rgba(0,0,0,0.8);
/* TODO: find a nice way to ensure shadows are drawn on top of items
z-index: 1;
*/
}
.vis.timeline .vispanel .shadow.top {
top: -1px;
left: 0;
}
.vis.timeline .vispanel .shadow.bottom {
bottom: -1px;
left: 0;
}
.vis.timeline .labelset { .vis.timeline .labelset {
position: relative; position: relative;
@ -97,11 +116,6 @@
margin: 0; margin: 0;
box-sizing: border-box; box-sizing: border-box;
/* FIXME: get transition working for rootpanel and itemset
-webkit-transition: height 4s ease-in-out;
transition: height 4s ease-in-out;
/**/
} }
.vis.timeline .itemset .background, .vis.timeline .itemset .background,
@ -111,10 +125,6 @@
height: 100%; height: 100%;
} }
.vis.timeline .itemset.foreground {
overflow: hidden;
}
.vis.timeline .axis { .vis.timeline .axis {
position: absolute; position: absolute;
width: 100%; width: 100%;
@ -123,27 +133,16 @@
z-index: 1; z-index: 1;
} }
.vis.timeline .group {
.vis.timeline .foreground .group {
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
border-bottom: 1px solid #bfbfbf; border-bottom: 1px solid #bfbfbf;
} }
.vis.timeline .group:last-child {
.vis.timeline .foreground .group:last-child {
border-bottom: none; border-bottom: none;
} }
/*
.vis.timeline.top .group {
border-top: 1px solid #bfbfbf;
border-bottom: none;
}
.vis.timeline.bottom .group {
border-top: none;
border-bottom: 1px solid #bfbfbf;
}
*/
.vis.timeline .item { .vis.timeline .item {
position: absolute; position: absolute;
@ -153,11 +152,6 @@
background-color: #D5DDF6; background-color: #D5DDF6;
display: inline-block; display: inline-block;
padding: 5px; padding: 5px;
/* TODO: enable css transitions
-webkit-transition: top .4s ease-in-out, bottom .4s ease-in-out;
transition: top .4s ease-in-out, bottom .4s ease-in-out;
/**/
} }
.vis.timeline .item.selected { .vis.timeline .item.selected {
@ -192,15 +186,13 @@
border-radius: 4px; border-radius: 4px;
} }
.vis.timeline .item.range,
.vis.timeline .item.rangeoverflow{
.vis.timeline .item.range {
border-style: solid; border-style: solid;
border-radius: 2px; border-radius: 2px;
box-sizing: border-box; box-sizing: border-box;
} }
.vis.timeline .item.range .content,
.vis.timeline .item.rangeoverflow .content {
.vis.timeline .item.range .content {
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
@ -216,11 +208,6 @@
width: 0; width: 0;
border-left-width: 1px; border-left-width: 1px;
border-left-style: solid; border-left-style: solid;
/* TODO: enable css transitions
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
transition: height .4s ease-in-out, top .4s ease-in-out;
/**/
} }
.vis.timeline .item .content { .vis.timeline .item .content {
@ -238,8 +225,7 @@
cursor: pointer; cursor: pointer;
} }
.vis.timeline .item.range .drag-left,
.vis.timeline .item.rangeoverflow .drag-left {
.vis.timeline .item.range .drag-left {
position: absolute; position: absolute;
width: 24px; width: 24px;
height: 100%; height: 100%;
@ -250,8 +236,7 @@
z-index: 10000; z-index: 10000;
} }
.vis.timeline .item.range .drag-right,
.vis.timeline .item.rangeoverflow .drag-right {
.vis.timeline .item.range .drag-right {
position: absolute; position: absolute;
width: 24px; width: 24px;
height: 100%; height: 100%;
@ -322,6 +307,39 @@
cursor: move; cursor: move;
z-index: 1; z-index: 1;
} }
.vis.timeline.root {
/*
-webkit-transition: height .4s ease-in-out;
transition: height .4s ease-in-out;
*/
}
.vis.timeline .vispanel {
/*
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
transition: height .4s ease-in-out, top .4s ease-in-out;
*/
}
.vis.timeline .axis {
/*
-webkit-transition: top .4s ease-in-out;
transition: top .4s ease-in-out;
*/
}
/* TODO: get animation working nicely
.vis.timeline .item {
-webkit-transition: top .4s ease-in-out;
transition: top .4s ease-in-out;
}
.vis.timeline .item.line {
-webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
transition: height .4s ease-in-out, top .4s ease-in-out;
}
/**/
.vis.timeline .vispanel.background.horizontal .grid.horizontal { .vis.timeline .vispanel.background.horizontal .grid.horizontal {
position: absolute; position: absolute;
@ -359,7 +377,7 @@
.vis.timeline .dataaxis .yAxis.minor{ .vis.timeline .dataaxis .yAxis.minor{
position: absolute; position: absolute;
width: 100%; width: 100%;
color: #979797;
color: #bebebe;
white-space: nowrap; white-space: nowrap;
} }

+ 1174
- 376
dist/vis.js
File diff suppressed because it is too large
View File


+ 1
- 1
dist/vis.min.css
File diff suppressed because it is too large
View File


+ 14
- 14
dist/vis.min.js
File diff suppressed because it is too large
View File


+ 23
- 78
examples/graph2d/01_basic.html View File

@ -1,7 +1,7 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html> <html>
<head> <head>
<title>Graph2d | Basic demo</title>
<title>Graph2d | Basic Example</title>
<style type="text/css"> <style type="text/css">
body, html { body, html {
@ -13,92 +13,37 @@
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" /> <link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head> </head>
<body> <body>
<h2>Graph2D | Basic Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the most basic functionality of the vis.js Graph2D module. An array or a vis.Dataset can be used as input.
In the following examples we'll explore the options Graph2D offest for customization. This example uses all default settings.
There are 10 predefined styles that will be cycled through automatically when you add different groups. Alternatively you can
create your own styling.
<br /><br />
Graph2D is built upon the framework of the newly refactored timeline. A lot of the timeline options will also apply to Graph2D.
In these examples however, we will focus on what's new in Graph2D!
</div>
<br />
<div id="visualization"></div> <div id="visualization"></div>
<script type="text/javascript"> <script type="text/javascript">
var options = {
yAxisOrientation: 'left',
shaded: {
enabled: true,
orientation: 'bottom' // top, bottom
},
drawPoints: {
enabled: false,
size: 6,
style: 'circle' // square, circle
}
};
// create a data set with groups
var names = ['John', 'Alston', 'Lee', 'Grant'];
var groups = new vis.DataSet();
groups.add({
id: 0,
content: names[0],
// className: "graphGroup9",
options: {
drawPoints: {
style: 'square' // square, circle
},
shaded: {
orientation: 'bottom' // top, bottom
}
}});
groups.add({
id: 1,
content: names[1],
options: {
style:'bar',
// yAxisOrientation: 'right',
drawPoints: false, // square, circle
shaded: {
orientation: 'top' // top, bottom
}
}});
groups.add({
id: 2,
content: names[2],
options: {
yAxisOrientation: 'right',
shaded: {
enabled: false,
orientation: 'top' // top, bottom
}
}});
var container = document.getElementById('visualization'); var container = document.getElementById('visualization');
var items = [ var items = [
{x: '2014-06-11', y: 10 },
{x: '2014-06-12', y: 25 },
{x: '2014-06-13', y: 30, group: 0},
{x: '2014-06-14', y: 10, group: 0},
{x: '2014-06-15', y: 15, group: 1},
{x: '2014-06-16', y: 30, group: 1},
{x: '2014-06-17', y: 100, group: 1},
{x: '2014-06-18', y: 150, group: 1},
{x: '2014-06-19', y: 520, group: 1},
{x: '2014-06-20', y: 100, group: 1},
{x: '2014-06-21', y: 20, group: 2},
{x: '2014-06-22', y: 60, group: 2},
{x: '2014-06-23', y: 10, group: 2},
{x: '2014-06-24', y: 25, group: 2},
{x: '2014-06-25', y: 30, group: 2}
// {x: '2014-06-11', y: 10},
// {x: '2014-06-12', y: 25},
// {x: '2014-06-13', y: 30},
// {x: '2014-06-14', y: 10},
// {x: '2014-06-15', y: 15},
// {x: '2014-06-16', y: 30}
{x: '2014-06-11', y: 10},
{x: '2014-06-12', y: 25},
{x: '2014-06-13', y: 30},
{x: '2014-06-14', y: 10},
{x: '2014-06-15', y: 15},
{x: '2014-06-16', y: 30}
]; ];
var dataset = new vis.DataSet(items); var dataset = new vis.DataSet(items);
var graph2d = new vis.Graph2d(container);
graph2d.setOptions(options);
graph2d.setGroups(groups);
graph2d.setItems(dataset);
var options = {
start: '2014-06-10',
end: '2014-06-18'
};
var graph2d = new vis.Graph2d(container, dataset, options);
</script> </script>
</body> </body>

+ 53
- 0
examples/graph2d/02_bars.html View File

@ -0,0 +1,53 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Graph2d | Bar Graph Example</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Graph2D | Bar Graph Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the most the same data as the first example, except we plot the data as bars! The
dataAxis (y-axis) icons have been enabled as well. These icons are generated automatically from the CSS
styling of the graphs. Finally, we've used the option from Timeline where we draw the x-axis (time-axis) on top.
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
var container = document.getElementById('visualization');
var items = [
{x: '2014-06-11', y: 10},
{x: '2014-06-12', y: 25},
{x: '2014-06-13', y: 30},
{x: '2014-06-14', y: 10},
{x: '2014-06-15', y: 15},
{x: '2014-06-16', y: 30}
];
var dataset = new vis.DataSet(items);
var options = {
style:'bar',
drawPoints: false,
dataAxis: {
icons:true
},
orientation:'top',
start: '2014-06-10',
end: '2014-06-18'
};
var graph2d = new vis.Graph2d(container, items, options);
</script>
</body>
</html>

+ 111
- 0
examples/graph2d/03_groups.html View File

@ -0,0 +1,111 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Graph2d | Groups Example</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Graph2D | Groups Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the groups functionality within Graph2D. This works in the same way as it does in Timeline,
We have however simplified the constructor to accept groups as well to shorten the code. These groups are the
method used in Graph2D to define individual graphs. These groups can be given an individual class as well as all the
styling options you can supply to Graph2D! This example, as well as the ones that follow will showcase a few different usages
of these options. <br /> <br />
This example also introduces the automatically generated legend. The icons are automatically generated and the label is the
content as you define it in the groups. If you have datapoints that are not part of a group, a default group is created with the label: 'default'.
In this example, the setting <code>defaultGroup</code> is used to rename the default group to 'ungrouped'.
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
// create a dataSet with groups
var names = ['SquareShaded', 'Bar', 'Blank', 'CircleShaded'];
var groups = new vis.DataSet();
groups.add({
id: 0,
content: names[0],
options: {
drawPoints: {
style: 'square' // square, circle
},
shaded: {
orientation: 'bottom' // top, bottom
}
}});
groups.add({
id: 1,
content: names[1],
options: {
style:'bar'
}});
groups.add({
id: 2,
content: names[2],
options: {drawPoints: false}
});
groups.add({
id: 3,
content: names[3],
options: {
drawPoints: {
style: 'circle' // square, circle
},
shaded: {
orientation: 'top' // top, bottom
}
}});
var container = document.getElementById('visualization');
var items = [
{x: '2014-06-13', y: 60},
{x: '2014-06-14', y: 40},
{x: '2014-06-15', y: 55},
{x: '2014-06-16', y: 40},
{x: '2014-06-17', y: 50},
{x: '2014-06-13', y: 30, group: 0},
{x: '2014-06-14', y: 10, group: 0},
{x: '2014-06-15', y: 15, group: 1},
{x: '2014-06-16', y: 30, group: 1},
{x: '2014-06-17', y: 10, group: 1},
{x: '2014-06-18', y: 15, group: 1},
{x: '2014-06-19', y: 52, group: 1},
{x: '2014-06-20', y: 10, group: 1},
{x: '2014-06-21', y: 20, group: 2},
{x: '2014-06-22', y: 60, group: 2},
{x: '2014-06-23', y: 10, group: 2},
{x: '2014-06-24', y: 25, group: 2},
{x: '2014-06-25', y: 30, group: 2},
{x: '2014-06-26', y: 20, group: 3},
{x: '2014-06-27', y: 60, group: 3},
{x: '2014-06-28', y: 10, group: 3},
{x: '2014-06-29', y: 25, group: 3},
{x: '2014-06-30', y: 30, group: 3}
];
var dataset = new vis.DataSet(items);
var options = {
defaultGroup: 'ungrouped',
legend: true,
start: '2014-06-10',
end: '2014-07-04'
};
var graph2d = new vis.Graph2d(container, dataset, options, groups);
</script>
</body>
</html>

+ 125
- 0
examples/graph2d/04_rightAxis.html View File

@ -0,0 +1,125 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Graph2d | Right Axis Example</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
.customStyle1 {
fill: #0df200;
fill-opacity:0;
stroke-width:2px;
stroke: #0df200;
}
.customStyle2 {
fill: #f23303;
fill-opacity:0;
stroke-width:2px;
stroke: #ff0004;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Graph2D | Right Axis Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the all of the graphs outlined on the right side using the <code>yAxisOrientation</code> option.
We also show a few custom styles for the graph and show icons on the axis, which are adhering to the custom styling.
Finally, the legend is manually positioned. Both the left and right axis
have their own legend. If one of the axis is unused, the legend is not shown. The options for the legend have been split
in a <code>left</code> and a <code>right</code> segment. Since this example shows the right axis, the right legend is configured.
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
// create a dataSet with groups
var names = ['Custom1', 'Custom2', 'Blank', 'CircleShaded'];
var groups = new vis.DataSet();
groups.add({
id: 0,
content: names[0],
className: 'customStyle1',
options: {
drawPoints: {
style: 'square' // square, circle
},
shaded: {
orientation: 'bottom' // top, bottom
}
}});
groups.add({
id: 1,
content: names[1],
className: 'customStyle2',
options: {
style:'bar',
drawPoints: {style: 'circle',
size: 10
}
}});
groups.add({
id: 2,
content: names[2],
options: {
drawPoints: false
}
});
groups.add({
id: 3,
content: names[3],
options: {
drawPoints: {
style: 'circle' // square, circle
},
shaded: {
orientation: 'top' // top, bottom
}
}});
var container = document.getElementById('visualization');
var items = [
{x: '2014-06-13', y: 30, group: 0},
{x: '2014-06-14', y: 10, group: 0},
{x: '2014-06-15', y: 15, group: 1},
{x: '2014-06-16', y: 30, group: 1},
{x: '2014-06-17', y: 10, group: 1},
{x: '2014-06-18', y: 15, group: 1},
{x: '2014-06-19', y: 52, group: 1},
{x: '2014-06-20', y: 10, group: 1},
{x: '2014-06-21', y: 20, group: 2},
{x: '2014-06-22', y: 60, group: 2},
{x: '2014-06-23', y: 10, group: 2},
{x: '2014-06-24', y: 50, group: 2},
{x: '2014-06-25', y: 30, group: 2},
{x: '2014-06-26', y: 20, group: 3},
{x: '2014-06-27', y: 60, group: 3},
{x: '2014-06-28', y: 10, group: 3},
{x: '2014-06-29', y: 85, group: 3},
{x: '2014-06-30', y: 30, group: 3}
];
var dataset = new vis.DataSet(items);
var options = {
legend: {right: {position: 'top-left'}},
yAxisOrientation: 'right', // right, left
dataAxis: {icons: true},
start: '2014-06-10',
end: '2014-07-04'
};
var graph2d = new vis.Graph2d(container, dataset, options, groups);
</script>
</body>
</html>

+ 139
- 0
examples/graph2d/05_bothAxis.html View File

@ -0,0 +1,139 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Graph2d | Both Axis Example</title>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<style type="text/css">
body, html {
font-family: sans-serif;
}
.customStyle1 {
fill: #f2ea00;
fill-opacity:0;
stroke-width:2px;
stroke: #b3ab00;
}
.customStyle2 {
fill: #00a0f2;
fill-opacity:0;
stroke-width:2px;
stroke: #050092;
}
.customStyle3 {
fill: #00f201;
fill-opacity:0;
stroke-width:2px;
stroke: #029200;
}
path.customStyle3.fill {
fill-opacity:0.5 !important;
stroke: none;
}
</style>
<script src="../../dist/vis.js"></script>
</head>
<body>
<h2>Graph2D | Both Axis Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the some of the graphs outlined on the right side using the <code>yAxisOrientation</code> option within the groups.
We also show a few more custom styles for the graphs.
Finally, the legend is manually positioned. Both the left and right axis
have their own legend. If one of the axis is unused, the legend is not shown. The options for the legend have been split
in a <code>left</code> and a <code>right</code> segment. Since this example shows the right axis, the right legend is configured.
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
// create a dataSet with groups
var names = ['SquareShaded', 'Bar', 'Blank', 'CircleShaded'];
var groups = new vis.DataSet();
groups.add({
id: 0,
content: names[0],
className: 'customStyle1',
options: {
drawPoints: {
style: 'square' // square, circle
},
shaded: {
orientation: 'bottom' // top, bottom
}
}});
groups.add({
id: 1,
content: names[1],
className: 'customStyle2',
options: {
style:'bar',
drawPoints: {style: 'circle',
size: 10
}
}});
groups.add({
id: 2,
content: names[2],
options: {
yAxisOrientation: 'right', // right, left
drawPoints: false
}
});
groups.add({
id: 3,
content: names[3],
className: 'customStyle3',
options: {
yAxisOrientation: 'right', // right, left
drawPoints: {
style: 'circle' // square, circle
},
shaded: {
orientation: 'top' // top, bottom
}
}});
var container = document.getElementById('visualization');
var items = [
{x: '2014-06-12', y: 0 , group: 0},
{x: '2014-06-13', y: 30, group: 0},
{x: '2014-06-14', y: 10, group: 0},
{x: '2014-06-15', y: 15, group: 1},
{x: '2014-06-16', y: 30, group: 1},
{x: '2014-06-17', y: 10, group: 1},
{x: '2014-06-18', y: 15, group: 1},
{x: '2014-06-19', y: 52, group: 1},
{x: '2014-06-20', y: 10, group: 1},
{x: '2014-06-21', y: 20, group: 2},
{x: '2014-06-22', y: 600, group: 2},
{x: '2014-06-23', y: 100, group: 2},
{x: '2014-06-24', y: 250, group: 2},
{x: '2014-06-25', y: 300, group: 2},
{x: '2014-06-26', y: 200, group: 3},
{x: '2014-06-27', y: 600, group: 3},
{x: '2014-06-28', y: 1000, group: 3},
{x: '2014-06-29', y: 250, group: 3},
{x: '2014-06-30', y: 300, group: 3}
];
var dataset = new vis.DataSet(items);
var options = {
dataAxis: {showMinorLabels: false},
legend: {left:{position:"bottom-left"},right:{position:'top-right'}},
start: '2014-06-09',
end: '2014-07-03'
};
var graph2d = new vis.Graph2d(container, items, options, groups);
</script>
</body>
</html>

+ 100
- 0
examples/graph2d/06_interpolation.html View File

@ -0,0 +1,100 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Graph2d | Interpolation</title>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<style type="text/css">
body, html {
font-family: sans-serif;
}
</style>
<script src="../../dist/vis.js"></script>
</head>
<body>
<h2>Graph2D | Interpolation</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the most basic functionality of the vis.js Graph2D module. An array or a vis.Dataset can be used as input.
In the following examples we'll explore the options Graph2D offest for customization. This example uses all default settings.
There are 10 predefined styles that will be cycled through automatically when you add different groups. Alternatively you can
create your own styling.
<br /><br />
Graph2D is built upon the framework of the newly refactored timeline. A lot of the timeline options will also apply to Graph2D.
In these examples however, we will focus on what's new in Graph2D!
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
// create a dataSet with groups
var names = ['centripetal', 'chordal', 'uniform', 'disabled'];
var groups = new vis.DataSet();
groups.add({
id: 0,
content: names[0],
options: {
drawPoints: false,
catmullRom: {
parametrization: 'centripetal'
}
}});
groups.add({
id: 1,
content: names[1],
options: {
drawPoints: false,
catmullRom: {
parametrization: 'chordal'
}
}});
groups.add({
id: 2,
content: names[2],
options: {
drawPoints: false,
catmullRom: {
parametrization: 'uniform'
}
}
});
groups.add({
id: 3,
content: names[3],
options: {
drawPoints: { style: 'circle' },
catmullRom: false
}});
var container = document.getElementById('visualization');
var dataset = new vis.DataSet();
for (var i = 0; i < names.length; i++) {
dataset.add( [
{x: '2014-06-12', y: 0 , group: i},
{x: '2014-06-13', y: 30, group: i},
{x: '2014-06-14', y: 10, group: i},
{x: '2014-06-15', y: 15, group: i},
{x: '2014-06-15', y: 30, group: i},
{x: '2014-06-17', y: 10, group: i},
{x: '2014-06-18', y: 15, group: i},
{x: '2014-06-19', y: 52, group: i},
{x: '2014-06-20', y: 10, group: i},
{x: '2014-06-21', y: 20, group: i}
]);
}
var options = {
dataPoints: false,
dataAxis: {visible: false},
legend: true,
start: '2014-06-10',
end: '2014-06-22'
};
var graph2d = new vis.Graph2d(container, dataset, options, groups);
</script>
</body>
</html>

+ 102
- 0
examples/graph2d/09_performance.html View File

@ -0,0 +1,102 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Graph2d | Basic demo</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="visualization"></div>
<script type="text/javascript">
var options = {
yAxisOrientation: 'left',
shaded: {
enabled: true,
orientation: 'bottom' // top, bottom
},
drawPoints: {
enabled: false,
size: 6,
style: 'circle' // square, circle
}
};
// create a data set with groups
var names = ['John', 'Alston', 'Lee', 'Grant'];
var groups = new vis.DataSet();
groups.add({
id: 0,
content: names[0],
// className: "graphGroup9",
options: {
drawPoints: {
style: 'square' // square, circle
},
shaded: {
orientation: 'bottom' // top, bottom
}
}});
groups.add({
id: 1,
content: names[1],
options: {
style:'bar',
// yAxisOrientation: 'right',
drawPoints: false, // square, circle
shaded: {
orientation: 'top' // top, bottom
}
}});
groups.add({
id: 2,
content: names[2],
options: {
yAxisOrientation: 'right',
shaded: {
enabled: false,
orientation: 'top' // top, bottom
}
}});
var container = document.getElementById('visualization');
var items = [
{x: '2014-06-11', y: 10 },
{x: '2014-06-12', y: 25 },
{x: '2014-06-13', y: 30, group: 0},
{x: '2014-06-14', y: 10, group: 0},
{x: '2014-06-15', y: 15, group: 1},
{x: '2014-06-16', y: 30, group: 1},
{x: '2014-06-17', y: 100, group: 1},
{x: '2014-06-18', y: 150, group: 1},
{x: '2014-06-19', y: 520, group: 1},
{x: '2014-06-20', y: 100, group: 1},
{x: '2014-06-21', y: 20, group: 2},
{x: '2014-06-22', y: 60, group: 2},
{x: '2014-06-23', y: 10, group: 2},
{x: '2014-06-24', y: 25, group: 2},
{x: '2014-06-25', y: 30, group: 2}
// {x: '2014-06-11', y: 10},
// {x: '2014-06-12', y: 25},
// {x: '2014-06-13', y: 30},
// {x: '2014-06-14', y: 10},
// {x: '2014-06-15', y: 15},
// {x: '2014-06-16', y: 30}
];
var dataset = new vis.DataSet(items);
var graph2d = new vis.Graph2d(container, items, options, groups);
</script>
</body>
</html>

+ 237
- 57
src/timeline/Graph2d.js View File

@ -5,7 +5,7 @@
* @param {Object} [options] See Graph2d.setOptions for the available options. * @param {Object} [options] See Graph2d.setOptions for the available options.
* @constructor * @constructor
*/ */
function Graph2d (container, items, options) {
function Graph2d (container, items, options, groups) {
var me = this; var me = this;
this.defaultOptions = { this.defaultOptions = {
start: null, start: null,
@ -13,12 +13,11 @@ function Graph2d (container, items, options) {
autoResize: true, autoResize: true,
orientation: 'bottom',
width: null, width: null,
height: null, height: null,
maxHeight: null, maxHeight: null,
minHeight: null minHeight: null
// TODO: implement options moveable and zoomable
}; };
this.options = util.deepExtend({}, this.defaultOptions); this.options = util.deepExtend({}, this.defaultOptions);
@ -74,6 +73,10 @@ function Graph2d (container, items, options) {
this.setOptions(options); this.setOptions(options);
} }
if (groups) {
this.setGroups(groups);
}
// create itemset // create itemset
if (items) { if (items) {
this.setItems(items); this.setItems(items);
@ -83,13 +86,13 @@ function Graph2d (container, items, options) {
} }
} }
// turn Timeline into an event emitter
// turn Graph2d into an event emitter
Emitter(Graph2d.prototype); Emitter(Graph2d.prototype);
/** /**
* Create the main DOM for the Timeline: a root panel containing left, right,
* Create the main DOM for the Graph2d: a root panel containing left, right,
* top, bottom, content, and background panel. * top, bottom, content, and background panel.
* @param {Element} container The container element where the Timeline will
* @param {Element} container The container element where the Graph2d will
* be attached. * be attached.
* @private * @private
*/ */
@ -99,18 +102,26 @@ Graph2d.prototype._create = function (container) {
this.dom.root = document.createElement('div'); this.dom.root = document.createElement('div');
this.dom.background = document.createElement('div'); this.dom.background = document.createElement('div');
this.dom.backgroundVertical = document.createElement('div'); this.dom.backgroundVertical = document.createElement('div');
this.dom.backgroundHorizontal = document.createElement('div');
this.dom.backgroundHorizontalContainer = document.createElement('div');
this.dom.centerContainer = document.createElement('div'); this.dom.centerContainer = document.createElement('div');
this.dom.leftContainer = document.createElement('div'); this.dom.leftContainer = document.createElement('div');
this.dom.rightContainer = document.createElement('div'); this.dom.rightContainer = document.createElement('div');
this.dom.backgroundHorizontal = document.createElement('div');
this.dom.center = document.createElement('div'); this.dom.center = document.createElement('div');
this.dom.left = document.createElement('div'); this.dom.left = document.createElement('div');
this.dom.right = document.createElement('div'); this.dom.right = document.createElement('div');
this.dom.top = document.createElement('div'); this.dom.top = document.createElement('div');
this.dom.bottom = document.createElement('div'); this.dom.bottom = document.createElement('div');
this.dom.shadowTop = document.createElement('div');
this.dom.shadowBottom = document.createElement('div');
this.dom.shadowTopLeft = document.createElement('div');
this.dom.shadowBottomLeft = document.createElement('div');
this.dom.shadowTopRight = document.createElement('div');
this.dom.shadowBottomRight = document.createElement('div');
this.dom.background.className = 'vispanel background'; this.dom.background.className = 'vispanel background';
this.dom.backgroundVertical.className = 'vispanel background vertical'; this.dom.backgroundVertical.className = 'vispanel background vertical';
this.dom.backgroundHorizontalContainer.className = 'vispanel background horizontal';
this.dom.backgroundHorizontal.className = 'vispanel background horizontal'; this.dom.backgroundHorizontal.className = 'vispanel background horizontal';
this.dom.centerContainer.className = 'vispanel center'; this.dom.centerContainer.className = 'vispanel center';
this.dom.leftContainer.className = 'vispanel left'; this.dom.leftContainer.className = 'vispanel left';
@ -120,22 +131,40 @@ Graph2d.prototype._create = function (container) {
this.dom.left.className = 'content'; this.dom.left.className = 'content';
this.dom.center.className = 'content'; this.dom.center.className = 'content';
this.dom.right.className = 'content'; this.dom.right.className = 'content';
this.dom.shadowTop.className = 'shadow top';
this.dom.shadowBottom.className = 'shadow bottom';
this.dom.shadowTopLeft.className = 'shadow top';
this.dom.shadowBottomLeft.className = 'shadow bottom';
this.dom.shadowTopRight.className = 'shadow top';
this.dom.shadowBottomRight.className = 'shadow bottom';
this.dom.root.appendChild(this.dom.background); this.dom.root.appendChild(this.dom.background);
this.dom.root.appendChild(this.dom.backgroundVertical); this.dom.root.appendChild(this.dom.backgroundVertical);
this.dom.root.appendChild(this.dom.backgroundHorizontal);
this.dom.root.appendChild(this.dom.backgroundHorizontalContainer);
this.dom.root.appendChild(this.dom.centerContainer); this.dom.root.appendChild(this.dom.centerContainer);
this.dom.root.appendChild(this.dom.leftContainer); this.dom.root.appendChild(this.dom.leftContainer);
this.dom.root.appendChild(this.dom.rightContainer); this.dom.root.appendChild(this.dom.rightContainer);
this.dom.root.appendChild(this.dom.top); this.dom.root.appendChild(this.dom.top);
this.dom.root.appendChild(this.dom.bottom); this.dom.root.appendChild(this.dom.bottom);
this.dom.backgroundHorizontalContainer.appendChild(this.dom.backgroundHorizontal);
this.dom.centerContainer.appendChild(this.dom.center); this.dom.centerContainer.appendChild(this.dom.center);
this.dom.leftContainer.appendChild(this.dom.left); this.dom.leftContainer.appendChild(this.dom.left);
this.dom.rightContainer.appendChild(this.dom.right); this.dom.rightContainer.appendChild(this.dom.right);
this.dom.centerContainer.appendChild(this.dom.shadowTop);
this.dom.centerContainer.appendChild(this.dom.shadowBottom);
this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft);
this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
this.on('rangechange', this.redraw.bind(this)); this.on('rangechange', this.redraw.bind(this));
this.on('change', this.redraw.bind(this)); this.on('change', this.redraw.bind(this));
this.on('touch', this._onTouch.bind(this));
this.on('pinch', this._onPinch.bind(this));
this.on('dragstart', this._onDragStart.bind(this));
this.on('drag', this._onDrag.bind(this));
// create event listeners for all interesting events, these events will be // create event listeners for all interesting events, these events will be
// emitted via emitter // emitted via emitter
@ -146,8 +175,8 @@ Graph2d.prototype._create = function (container) {
var me = this; var me = this;
var events = [ var events = [
'pinch',
//'tap', 'doubletap', 'hold', // TODO: catching the events here disables selecting an item
'touch', 'pinch',
'tap', 'doubletap', 'hold',
'dragstart', 'drag', 'dragend', 'dragstart', 'drag', 'dragend',
'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
]; ];
@ -172,30 +201,72 @@ Graph2d.prototype._create = function (container) {
right: {}, right: {},
top: {}, top: {},
bottom: {}, bottom: {},
border: {}
border: {},
scrollTop: 0,
scrollTopMin: 0
}; };
this.touch = {}; // store state information needed for touch events
// attach the root panel to the provided container // attach the root panel to the provided container
if (!container) throw new Error('No container provided'); if (!container) throw new Error('No container provided');
container.appendChild(this.dom.root); container.appendChild(this.dom.root);
}; };
/**
* Destroy the Graph2d, clean up all DOM elements and event listeners.
*/
Graph2d.prototype.destroy = function () {
// unbind datasets
this.clear();
// remove all event listeners
this.off();
// stop checking for changed size
this._stopAutoResize();
// remove from DOM
if (this.dom.root.parentNode) {
this.dom.root.parentNode.removeChild(this.dom.root);
}
this.dom = null;
// cleanup hammer touch events
for (var event in this.listeners) {
if (this.listeners.hasOwnProperty(event)) {
delete this.listeners[event];
}
}
this.listeners = null;
this.hammer = null;
// give all components the opportunity to cleanup
this.components.forEach(function (component) {
component.destroy();
});
this.body = null;
};
/** /**
* Set options. Options will be passed to all components loaded in the Graph2d. * Set options. Options will be passed to all components loaded in the Graph2d.
* @param {Object} [options] * @param {Object} [options]
* {String} orientation
* Vertical orientation for the Graph2d,
* can be 'bottom' (default) or 'top'.
* {String | Number} width * {String | Number} width
* Width for the timeline, a number in pixels or * Width for the timeline, a number in pixels or
* a css string like '1000px' or '75%'. '100%' by default. * a css string like '1000px' or '75%'. '100%' by default.
* {String | Number} height * {String | Number} height
* Fixed height for the Timeline, a number in pixels or
* Fixed height for the Graph2d, a number in pixels or
* a css string like '400px' or '75%'. If undefined, * a css string like '400px' or '75%'. If undefined,
* The Timeline will automatically size such that
* The Graph2d will automatically size such that
* its contents fit. * its contents fit.
* {String | Number} minHeight * {String | Number} minHeight
* Minimum height for the Timeline, a number in pixels or
* Minimum height for the Graph2d, a number in pixels or
* a css string like '400px' or '75%'. * a css string like '400px' or '75%'.
* {String | Number} maxHeight * {String | Number} maxHeight
* Maximum height for the Timeline, a number in pixels or
* Maximum height for the Graph2d, a number in pixels or
* a css string like '400px' or '75%'. * a css string like '400px' or '75%'.
* {Number | Date | String} start * {Number | Date | String} start
* Start date for the visible window * Start date for the visible window
@ -205,7 +276,7 @@ Graph2d.prototype._create = function (container) {
Graph2d.prototype.setOptions = function (options) { Graph2d.prototype.setOptions = function (options) {
if (options) { if (options) {
// copy the known options // copy the known options
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end'];
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
// enable/disable autoResize // enable/disable autoResize
@ -289,7 +360,6 @@ Graph2d.prototype.setItems = function(items) {
} }
}; };
/** /**
* Set groups * Set groups
* @param {vis.DataSet | Array | google.visualization.DataTable} groups * @param {vis.DataSet | Array | google.visualization.DataTable} groups
@ -312,7 +382,6 @@ Graph2d.prototype.setGroups = function(groups) {
this.linegraph.setGroups(newDataSet); this.linegraph.setGroups(newDataSet);
}; };
/** /**
* Clear the Graph2d. By Default, items, groups and options are cleared. * Clear the Graph2d. By Default, items, groups and options are cleared.
* Example usage: * Example usage:
@ -345,7 +414,7 @@ Graph2d.prototype.clear = function(what) {
}; };
/** /**
* Set Timeline window such that it fits all items
* Set Graph2d window such that it fits all items
*/ */
Graph2d.prototype.fit = function() { Graph2d.prototype.fit = function() {
// apply the data range as range // apply the data range as range
@ -381,26 +450,28 @@ Graph2d.prototype.fit = function() {
Graph2d.prototype.getItemRange = function() { Graph2d.prototype.getItemRange = function() {
// calculate min from start filed // calculate min from start filed
var itemsData = this.itemsData, var itemsData = this.itemsData,
min = null,
max = null;
min = null,
max = null;
if (itemsData) { if (itemsData) {
// calculate the minimum value of the field 'start' // calculate the minimum value of the field 'start'
var minItem = itemsData.min('start'); var minItem = itemsData.min('start');
min = minItem ? minItem.start.valueOf() : null;
min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null;
// Note: we convert first to Date and then to number because else
// a conversion from ISODate to Number will fail
// calculate maximum value of fields 'start' and 'end' // calculate maximum value of fields 'start' and 'end'
var maxStartItem = itemsData.max('start'); var maxStartItem = itemsData.max('start');
if (maxStartItem) { if (maxStartItem) {
max = maxStartItem.start.valueOf();
max = util.convert(maxStartItem.start, 'Date').valueOf();
} }
var maxEndItem = itemsData.max('end'); var maxEndItem = itemsData.max('end');
if (maxEndItem) { if (maxEndItem) {
if (max == null) { if (max == null) {
max = maxEndItem.end.valueOf();
max = util.convert(maxEndItem.end, 'Date').valueOf();
} }
else { else {
max = Math.max(max, maxEndItem.end.valueOf());
max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf());
} }
} }
} }
@ -419,7 +490,7 @@ Graph2d.prototype.getItemRange = function() {
* unselected. * unselected.
*/ */
Graph2d.prototype.setSelection = function(ids) { Graph2d.prototype.setSelection = function(ids) {
this.itemSet && this.itemSet.setSelection(ids);
this.linegraph && this.linegraph.setSelection(ids);
}; };
/** /**
@ -427,7 +498,7 @@ Graph2d.prototype.setSelection = function(ids) {
* @return {Array} ids The ids of the selected items * @return {Array} ids The ids of the selected items
*/ */
Graph2d.prototype.getSelection = function() { Graph2d.prototype.getSelection = function() {
return this.itemSet && this.itemSet.getSelection() || [];
return this.linegraph && this.linegraph.getSelection() || [];
}; };
/** /**
@ -471,9 +542,11 @@ Graph2d.prototype.getWindow = function() {
*/ */
Graph2d.prototype.redraw = function() { Graph2d.prototype.redraw = function() {
var resized = false, var resized = false,
options = this.options,
props = this.props,
dom = this.dom;
options = this.options,
props = this.props,
dom = this.dom;
if (!dom) return; // when destroyed
// update class names // update class names
dom.root.className = 'vis timeline root ' + options.orientation; dom.root.className = 'vis timeline root ' + options.orientation;
@ -505,14 +578,14 @@ Graph2d.prototype.redraw = function() {
// TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM)
var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); var contentHeight = Math.max(props.left.height, props.center.height, props.right.height);
var autoHeight = props.top.height + contentHeight + props.bottom.height + var autoHeight = props.top.height + contentHeight + props.bottom.height +
borderRootHeight + props.border.top + props.border.bottom;
borderRootHeight + props.border.top + props.border.bottom;
dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px');
// calculate heights of the content panels // calculate heights of the content panels
props.root.height = dom.root.offsetHeight; props.root.height = dom.root.offsetHeight;
props.background.height = props.root.height - borderRootHeight; props.background.height = props.root.height - borderRootHeight;
var containerHeight = props.root.height - props.top.height - props.bottom.height - var containerHeight = props.root.height - props.top.height - props.bottom.height -
borderRootHeight;
borderRootHeight;
props.centerContainer.height = containerHeight; props.centerContainer.height = containerHeight;
props.leftContainer.height = containerHeight; props.leftContainer.height = containerHeight;
props.rightContainer.height = props.leftContainer.height; props.rightContainer.height = props.leftContainer.height;
@ -533,13 +606,14 @@ Graph2d.prototype.redraw = function() {
// resize the panels // resize the panels
dom.background.style.height = props.background.height + 'px'; dom.background.style.height = props.background.height + 'px';
dom.backgroundVertical.style.height = props.background.height + 'px'; dom.backgroundVertical.style.height = props.background.height + 'px';
dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px';
dom.backgroundHorizontalContainer.style.height = props.centerContainer.height + 'px';
dom.centerContainer.style.height = props.centerContainer.height + 'px'; dom.centerContainer.style.height = props.centerContainer.height + 'px';
dom.leftContainer.style.height = props.leftContainer.height + 'px'; dom.leftContainer.style.height = props.leftContainer.height + 'px';
dom.rightContainer.style.height = props.rightContainer.height + 'px'; dom.rightContainer.style.height = props.rightContainer.height + 'px';
dom.background.style.width = props.background.width + 'px'; dom.background.style.width = props.background.width + 'px';
dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; dom.backgroundVertical.style.width = props.centerContainer.width + 'px';
dom.backgroundHorizontalContainer.style.width = props.background.width + 'px';
dom.backgroundHorizontal.style.width = props.background.width + 'px'; dom.backgroundHorizontal.style.width = props.background.width + 'px';
dom.centerContainer.style.width = props.center.width + 'px'; dom.centerContainer.style.width = props.center.width + 'px';
dom.top.style.width = props.top.width + 'px'; dom.top.style.width = props.top.width + 'px';
@ -550,8 +624,8 @@ Graph2d.prototype.redraw = function() {
dom.background.style.top = '0'; dom.background.style.top = '0';
dom.backgroundVertical.style.left = props.left.width + 'px'; dom.backgroundVertical.style.left = props.left.width + 'px';
dom.backgroundVertical.style.top = '0'; dom.backgroundVertical.style.top = '0';
dom.backgroundHorizontal.style.left = '0';
dom.backgroundHorizontal.style.top = props.top.height + 'px';
dom.backgroundHorizontalContainer.style.left = '0';
dom.backgroundHorizontalContainer.style.top = props.top.height + 'px';
dom.centerContainer.style.left = props.left.width + 'px'; dom.centerContainer.style.left = props.left.width + 'px';
dom.centerContainer.style.top = props.top.height + 'px'; dom.centerContainer.style.top = props.top.height + 'px';
dom.leftContainer.style.left = '0'; dom.leftContainer.style.left = '0';
@ -563,21 +637,33 @@ Graph2d.prototype.redraw = function() {
dom.bottom.style.left = props.left.width + 'px'; dom.bottom.style.left = props.left.width + 'px';
dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px';
// update the scrollTop, feasible range for the offset can be changed
// when the height of the Graph2d or of the contents of the center changed
this._updateScrollTop();
// reposition the scrollable contents // reposition the scrollable contents
var offset;
if (options.orientation == 'top') {
offset = 0;
}
else { // orientation == 'bottom'
// keep the items aligned to the axis at the bottom
offset = 0; //props.centerContainer.height - props.center.height;
}
dom.center.style.left = '0';
dom.center.style.top = offset+ 'px';
dom.left.style.left = '0';
dom.left.style.top = offset+ 'px';
dom.right.style.left = '0';
dom.right.style.top = offset+ 'px';
var offset = this.props.scrollTop;
// if (options.orientation == 'bottom') {
// offset += Math.max(this.props.centerContainer.height - this.props.center.height, 0);
// }
dom.center.style.left = '0';
dom.center.style.top = offset + 'px';
dom.backgroundHorizontal.style.left = '0';
dom.backgroundHorizontal.style.top = offset + 'px';
dom.left.style.left = '0';
dom.left.style.top = offset + 'px';
dom.right.style.left = '0';
dom.right.style.top = offset + 'px';
// show shadows when vertical scrolling is available
var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : '';
var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : '';
dom.shadowTop.style.visibility = visibilityTop;
dom.shadowBottom.style.visibility = visibilityBottom;
dom.shadowTopLeft.style.visibility = visibilityTop;
dom.shadowBottomLeft.style.visibility = visibilityBottom;
dom.shadowTopRight.style.visibility = visibilityTop;
dom.shadowBottomRight.style.visibility = visibilityBottom;
// redraw all components // redraw all components
this.components.forEach(function (component) { this.components.forEach(function (component) {
@ -591,7 +677,7 @@ Graph2d.prototype.redraw = function() {
// TODO: deprecated since version 1.1.0, remove some day // TODO: deprecated since version 1.1.0, remove some day
Graph2d.prototype.repaint = function () { Graph2d.prototype.repaint = function () {
throw new Error('Function repaint is deprecated. Use redraw instead.');
throw new Error('Function repaint is deprecated. Use redraw instead.');
}; };
/** /**
@ -642,7 +728,7 @@ Graph2d.prototype._startAutoResize = function () {
this._stopAutoResize(); this._stopAutoResize();
function checkSize() {
this._onResize = function() {
if (me.options.autoResize != true) { if (me.options.autoResize != true) {
// stop watching when the option autoResize is changed to false // stop watching when the option autoResize is changed to false
me._stopAutoResize(); me._stopAutoResize();
@ -652,19 +738,19 @@ Graph2d.prototype._startAutoResize = function () {
if (me.dom.root) { if (me.dom.root) {
// check whether the frame is resized // check whether the frame is resized
if ((me.dom.root.clientWidth != me.props.lastWidth) || if ((me.dom.root.clientWidth != me.props.lastWidth) ||
(me.dom.root.clientHeight != me.props.lastHeight)) {
(me.dom.root.clientHeight != me.props.lastHeight)) {
me.props.lastWidth = me.dom.root.clientWidth; me.props.lastWidth = me.dom.root.clientWidth;
me.props.lastHeight = me.dom.root.clientHeight; me.props.lastHeight = me.dom.root.clientHeight;
me.emit('change'); me.emit('change');
} }
} }
}
};
// TODO: automatically cleanup the event listener when the frame is deleted
util.addEventListener(window, 'resize', checkSize);
// add event listener to window resize
util.addEventListener(window, 'resize', this._onResize);
this.watchTimer = setInterval(checkSize, 1000);
this.watchTimer = setInterval(this._onResize, 1000);
}; };
/** /**
@ -677,5 +763,99 @@ Graph2d.prototype._stopAutoResize = function () {
this.watchTimer = undefined; this.watchTimer = undefined;
} }
// TODO: remove event listener on window.resize
// remove event listener on window.resize
util.removeEventListener(window, 'resize', this._onResize);
this._onResize = null;
};
/**
* Start moving the timeline vertically
* @param {Event} event
* @private
*/
Graph2d.prototype._onTouch = function (event) {
this.touch.allowDragging = true;
};
/**
* Start moving the timeline vertically
* @param {Event} event
* @private
*/
Graph2d.prototype._onPinch = function (event) {
this.touch.allowDragging = false;
};
/**
* Start moving the timeline vertically
* @param {Event} event
* @private
*/
Graph2d.prototype._onDragStart = function (event) {
this.touch.initialScrollTop = this.props.scrollTop;
};
/**
* Move the timeline vertically
* @param {Event} event
* @private
*/
Graph2d.prototype._onDrag = function (event) {
// refuse to drag when we where pinching to prevent the timeline make a jump
// when releasing the fingers in opposite order from the touch screen
if (!this.touch.allowDragging) return;
var delta = event.gesture.deltaY;
var oldScrollTop = this._getScrollTop();
var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta);
if (newScrollTop != oldScrollTop) {
this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already
}
};
/**
* Apply a scrollTop
* @param {Number} scrollTop
* @returns {Number} scrollTop Returns the applied scrollTop
* @private
*/
Graph2d.prototype._setScrollTop = function (scrollTop) {
this.props.scrollTop = scrollTop;
this._updateScrollTop();
return this.props.scrollTop;
};
/**
* Update the current scrollTop when the height of the containers has been changed
* @returns {Number} scrollTop Returns the applied scrollTop
* @private
*/
Graph2d.prototype._updateScrollTop = function () {
// recalculate the scrollTopMin
var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero
if (scrollTopMin != this.props.scrollTopMin) {
// in case of bottom orientation, change the scrollTop such that the contents
// do not move relative to the time axis at the bottom
if (this.options.orientation == 'bottom') {
this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin);
}
this.props.scrollTopMin = scrollTopMin;
}
// limit the scrollTop to the feasible scroll range
if (this.props.scrollTop > 0) this.props.scrollTop = 0;
if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin;
return this.props.scrollTop;
};
/**
* Get the current scrollTop
* @returns {number} scrollTop
* @private
*/
Graph2d.prototype._getScrollTop = function () {
return this.props.scrollTop;
}; };

+ 0
- 5
src/timeline/Range.js View File

@ -293,21 +293,16 @@ Range.prototype._onDragStart = function(event) {
Range.prototype._onDrag = function (event) { Range.prototype._onDrag = function (event) {
// only allow dragging when configured as movable // only allow dragging when configured as movable
if (!this.options.moveable) return; if (!this.options.moveable) return;
var direction = this.options.direction; var direction = this.options.direction;
validateDirection(direction); validateDirection(direction);
// refuse to drag when we where pinching to prevent the timeline make a jump // refuse to drag when we where pinching to prevent the timeline make a jump
// when releasing the fingers in opposite order from the touch screen // when releasing the fingers in opposite order from the touch screen
if (!this.props.touch.allowDragging) return; if (!this.props.touch.allowDragging) return;
var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY, var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY,
interval = (this.props.touch.end - this.props.touch.start), interval = (this.props.touch.end - this.props.touch.start),
width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height, width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height,
diffRange = -delta / width * interval; diffRange = -delta / width * interval;
this._applyRange(this.props.touch.start + diffRange, this.props.touch.end + diffRange); this._applyRange(this.props.touch.start + diffRange, this.props.touch.end + diffRange);
this.body.emitter.emit('rangechange', { this.body.emitter.emit('rangechange', {
start: new Date(this.start), start: new Date(this.start),
end: new Date(this.end) end: new Date(this.end)

+ 3
- 3
src/timeline/Timeline.js View File

@ -639,9 +639,9 @@ Timeline.prototype.redraw = function() {
// reposition the scrollable contents // reposition the scrollable contents
var offset = this.props.scrollTop; var offset = this.props.scrollTop;
if (options.orientation == 'bottom') {
offset += Math.max(this.props.centerContainer.height - this.props.center.height, 0);
}
// if (options.orientation == 'bottom') {
// offset += Math.max(this.props.centerContainer.height - this.props.center.height, 0);
// }
dom.center.style.left = '0'; dom.center.style.left = '0';
dom.center.style.top = offset + 'px'; dom.center.style.top = offset + 'px';
dom.left.style.left = '0'; dom.left.style.left = '0';

+ 14
- 10
src/timeline/component/DataAxis.js View File

@ -6,7 +6,7 @@
* @extends Component * @extends Component
* @param body * @param body
*/ */
function DataAxis (body, options) {
function DataAxis (body, options, svg) {
this.id = util.randomUUID(); this.id = util.randomUUID();
this.body = body; this.body = body;
@ -21,10 +21,10 @@ function DataAxis (body, options) {
labelOffsetY: 2, labelOffsetY: 2,
iconWidth: 20, iconWidth: 20,
width: '40px', width: '40px',
height: '300px',
visible: true visible: true
}; };
this.linegraphSVG = svg;
this.props = {}; this.props = {};
this.DOMelements = { // dynamic elements this.DOMelements = { // dynamic elements
lines: {}, lines: {},
@ -40,7 +40,7 @@ function DataAxis (body, options) {
this.setOptions(options); this.setOptions(options);
this.width = Number(this.options.width.replace("px","")); this.width = Number(this.options.width.replace("px",""));
this.height = Number(this.options.height.replace("px",""));
this.height = this.linegraphSVG.offsetHeight;
this.stepPixels = 25; this.stepPixels = 25;
this.stepPixelsForced = 25; this.stepPixelsForced = 25;
@ -96,10 +96,8 @@ DataAxis.prototype.setOptions = function (options) {
'labelOffsetY', 'labelOffsetY',
'iconWidth', 'iconWidth',
'width', 'width',
'height',
'visible']; 'visible'];
util.selectiveExtend(fields, this.options, options); util.selectiveExtend(fields, this.options, options);
if (redraw == true && this.dom.frame) { if (redraw == true && this.dom.frame) {
this.hide(); this.hide();
this.show(); this.show();
@ -114,11 +112,11 @@ DataAxis.prototype.setOptions = function (options) {
DataAxis.prototype._create = function() { DataAxis.prototype._create = function() {
this.dom.frame = document.createElement('div'); this.dom.frame = document.createElement('div');
this.dom.frame.style.width = this.options.width; this.dom.frame.style.width = this.options.width;
this.dom.frame.style.height = this.options.height;
this.dom.frame.style.height = this.height;
this.dom.lineContainer = document.createElement('div'); this.dom.lineContainer = document.createElement('div');
this.dom.lineContainer.style.width = '100%'; this.dom.lineContainer.style.width = '100%';
this.dom.lineContainer.style.height = this.options.height;
this.dom.lineContainer.style.height = this.height;
// create svg element for graph drawing. // create svg element for graph drawing.
this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg");
@ -183,7 +181,7 @@ DataAxis.prototype.hide = function() {
} }
if (this.dom.lineContainer.parentNode) { if (this.dom.lineContainer.parentNode) {
this.body.dom.backgroundHorizontal.removeChild(this.dom.lineContainer);
this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer);
} }
}; };
@ -207,6 +205,10 @@ DataAxis.prototype.redraw = function () {
this.hide(); this.hide();
} }
else { else {
this.height = this.linegraphSVG.offsetHeight;
this.dom.lineContainer.style.height = this.height + 'px';
this.width = this.options.visible ? Number(this.options.width.replace("px","")) : 0; this.width = this.options.visible ? Number(this.options.width.replace("px","")) : 0;
var props = this.props; var props = this.props;
@ -301,11 +303,13 @@ DataAxis.prototype._redrawLabels = function () {
marginStartPos = max * stepPixels; marginStartPos = max * stepPixels;
var isMajor = step.isMajor(); var isMajor = step.isMajor();
if (this.options['showMinorLabels'] && isMajor == false || this.master == false) {
if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) {
this._redrawLabel(y - 2, step.current, orientation, 'yAxis minor', this.props.minorCharHeight); this._redrawLabel(y - 2, step.current, orientation, 'yAxis minor', this.props.minorCharHeight);
} }
if (isMajor && this.options['showMajorLabels'] && this.master == true) {
if (isMajor && this.options['showMajorLabels'] && this.master == true ||
this.options['showMinorLabels'] == false && this.master == false && isMajor == true) {
if (y >= 0) { if (y >= 0) {
this._redrawLabel(y - 2, step.current, orientation, 'yAxis major', this.props.majorCharHeight); this._redrawLabel(y - 2, step.current, orientation, 'yAxis major', this.props.majorCharHeight);
} }

+ 17
- 0
src/timeline/component/GraphGroup.js View File

@ -24,6 +24,23 @@ GraphGroup.prototype.setOptions = function(options) {
util._mergeOptions(this.options, options,'catmullRom'); util._mergeOptions(this.options, options,'catmullRom');
util._mergeOptions(this.options, options,'drawPoints'); util._mergeOptions(this.options, options,'drawPoints');
util._mergeOptions(this.options, options,'shaded'); util._mergeOptions(this.options, options,'shaded');
if (options.catmullRom) {
if (typeof options.catmullRom == 'object') {
if (options.catmullRom.parametrization) {
if (options.catmullRom.parametrization == 'uniform') {
this.options.catmullRom.alpha = 0;
}
else if (options.catmullRom.parametrization == 'chordal') {
this.options.catmullRom.alpha = 1.0;
}
else {
this.options.catmullRom.parametrization = 'centripetal';
this.options.catmullRom.alpha = 0.5;
}
}
}
}
} }
}; };

+ 5
- 7
src/timeline/component/Legend.js View File

@ -5,18 +5,16 @@ function Legend(body, options, side) {
this.body = body; this.body = body;
this.defaultOptions = { this.defaultOptions = {
enabled: true, enabled: true,
axisIcons: true,
icons: true,
iconSize: 20, iconSize: 20,
iconSpacing: 6, iconSpacing: 6,
left: { left: {
visible: true, visible: true,
position: 'top-left', // top/bottom - left,center,right
textAlign: 'left'
position: 'top-left' // top/bottom - left,center,right
}, },
right: { right: {
visible: true, visible: true,
position: 'top-left', // top/bottom - left,center,right
textAlign: 'right'
position: 'top-left' // top/bottom - left,center,right
} }
} }
this.side = side; this.side = side;
@ -95,12 +93,12 @@ Legend.prototype.show = function() {
}; };
Legend.prototype.setOptions = function(options) { Legend.prototype.setOptions = function(options) {
var fields = ['orientation','icons','left','right'];
var fields = ['enabled','orientation','icons','left','right'];
util.selectiveDeepExtend(fields, this.options, options); util.selectiveDeepExtend(fields, this.options, options);
} }
Legend.prototype.redraw = function() { Legend.prototype.redraw = function() {
if (this.options[this.side].visible == false || this.amountOfGroups == 0) {
if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false) {
this.hide(); this.hide();
} }
else { else {

+ 40
- 34
src/timeline/component/Linegraph.js View File

@ -6,10 +6,11 @@ function Linegraph(body, options) {
this.defaultOptions = { this.defaultOptions = {
yAxisOrientation: 'left', yAxisOrientation: 'left',
label: 'default',
defaultGroup: 'default',
graphHeight: '400px',
shaded: { shaded: {
enabled: true,
orientation: 'top' // top, bottom
enabled: false,
orientation: 'bottom' // top, bottom
}, },
style: 'line', // line, bar style: 'line', // line, bar
barChart: { barChart: {
@ -28,11 +29,12 @@ function Linegraph(body, options) {
dataAxis: { dataAxis: {
showMinorLabels: true, showMinorLabels: true,
showMajorLabels: true, showMajorLabels: true,
icons: true,
icons: false,
width: '40px', width: '40px',
visible: true visible: true
}, },
legend: { legend: {
enabled: false,
icons: true, icons: true,
left: { left: {
visible: true, visible: true,
@ -126,17 +128,17 @@ Linegraph.prototype._create = function(){
// create svg element for graph drawing. // create svg element for graph drawing.
this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg");
this.svg.style.position = "relative"; this.svg.style.position = "relative";
this.svg.style.height = "300px";
this.svg.style.height = ('' + this.options.graphHeight).replace("px",'') + 'px';
this.svg.style.display = "block"; this.svg.style.display = "block";
frame.appendChild(this.svg); frame.appendChild(this.svg);
// data axis // data axis
this.options.dataAxis.orientation = 'left'; this.options.dataAxis.orientation = 'left';
this.options.dataAxis.height = this.svg.style.height;
this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis);
this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg);
this.options.dataAxis.orientation = 'right'; this.options.dataAxis.orientation = 'right';
this.yAxisRight = new DataAxis(this.body, this.options.dataAxis);
this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg);
delete this.options.dataAxis.orientation;
// legends // legends
this.legendLeft = new Legend(this.body, this.options.legend, 'left'); this.legendLeft = new Legend(this.body, this.options.legend, 'left');
@ -151,8 +153,12 @@ Linegraph.prototype._create = function(){
*/ */
Linegraph.prototype.setOptions = function(options) { Linegraph.prototype.setOptions = function(options) {
if (options) { if (options) {
var fields = ['label','yAxisOrientation','style','barChart','dataAxis','legend'];
var fields = ['defaultGroup','graphHeight','yAxisOrientation','style','barChart','dataAxis'];
util.selectiveDeepExtend(fields, this.options, options); util.selectiveDeepExtend(fields, this.options, options);
util._mergeOptions(this.options, options,'catmullRom');
util._mergeOptions(this.options, options,'drawPoints');
util._mergeOptions(this.options, options,'shaded');
util._mergeOptions(this.options, options,'legend');
if (options.catmullRom) { if (options.catmullRom) {
if (typeof options.catmullRom == 'object') { if (typeof options.catmullRom == 'object') {
@ -171,21 +177,17 @@ Linegraph.prototype.setOptions = function(options) {
} }
} }
util._mergeOptions(this.options, options,'catmullRom');
util._mergeOptions(this.options, options,'drawPoints');
util._mergeOptions(this.options, options,'shaded');
if (this.yAxisLeft) { if (this.yAxisLeft) {
if (options.dataAxis) {
this.yAxisLeft.setOptions(options.legend);
this.yAxisRight.setOptions(options.legend);
if (options.dataAxis !== undefined) {
this.yAxisLeft.setOptions(this.options.dataAxis);
this.yAxisRight.setOptions(this.options.dataAxis);
} }
} }
if (this.legend) {
if (options.legend) {
this.legendLeft.setOptions(options.legend);
this.legendRight.setOptions(options.legend);
if (this.legendLeft) {
if (options.legend !== undefined) {
this.legendLeft.setOptions(this.options.legend);
this.legendRight.setOptions(this.options.legend);
} }
} }
} }
@ -389,10 +391,10 @@ Linegraph.prototype._updateGroup = function (group, groupId) {
* @protected * @protected
*/ */
Linegraph.prototype._updateUngrouped = function() { Linegraph.prototype._updateUngrouped = function() {
var group = {id: UNGROUPED, content: this.options.label};
this._updateGroup(group, UNGROUPED);
if (this.itemsData != null) { if (this.itemsData != null) {
var group = {id: UNGROUPED, content: this.options.defaultGroup};
this._updateGroup(group, UNGROUPED);
var datapoints = this.itemsData.get({ var datapoints = this.itemsData.get({
filter: function (item) {return item.group === undefined;}, filter: function (item) {return item.group === undefined;},
showInternalIds:true showInternalIds:true
@ -407,12 +409,16 @@ Linegraph.prototype._updateUngrouped = function() {
var pointInUNGROUPED = this.itemsData.get({filter: function (item) {return item.group == UNGROUPED;}}); var pointInUNGROUPED = this.itemsData.get({filter: function (item) {return item.group == UNGROUPED;}});
if (pointInUNGROUPED.length == 0) { if (pointInUNGROUPED.length == 0) {
this.legend.deleteGroup(UNGROUPED);
delete this.groups[UNGROUPED]; delete this.groups[UNGROUPED];
this.yAxisLeft.yAxisRight(UNGROUPED);
this.yAxisLeft.deleteGroup(UNGROUPED);
this.legendLeft.removeGroup(UNGROUPED);
this.legendRight.removeGroup(UNGROUPED);
this.yAxisLeft.removeGroup(UNGROUPED);
this.yAxisLeft.removeGroup(UNGROUPED);
} }
} }
this.legendLeft.redraw();
this.legendRight.redraw();
}; };
@ -423,6 +429,7 @@ Linegraph.prototype._updateUngrouped = function() {
Linegraph.prototype.redraw = function() { Linegraph.prototype.redraw = function() {
var resized = false; var resized = false;
this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px';
if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) { if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) {
resized = true; resized = true;
} }
@ -649,10 +656,9 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) {
path = DOMutil.getSVGElement('path', this.svgElements, this.svg); path = DOMutil.getSVGElement('path', this.svgElements, this.svg);
path.setAttributeNS(null, "class", group.className); path.setAttributeNS(null, "class", group.className);
// construct path from dataset // construct path from dataset
if (group.options.catmullRom.enabled == true) { if (group.options.catmullRom.enabled == true) {
d = this._catmullRom(dataset);
d = this._catmullRom(dataset, group);
} }
else { else {
d = this._linear(dataset); d = this._linear(dataset);
@ -740,7 +746,7 @@ Linegraph.prototype._prepareData = function (datapoints, options) {
Linegraph.prototype._catmullRomUniform = function(data) { Linegraph.prototype._catmullRomUniform = function(data) {
// catmull rom // catmull rom
var p0, p1, p2, p3, bp1, bp2; var p0, p1, p2, p3, bp1, bp2;
var d = "M" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var d = Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var normalization = 1/6; var normalization = 1/6;
var length = data.length; var length = data.length;
for (var i = 0; i < length - 1; i++) { for (var i = 0; i < length - 1; i++) {
@ -784,15 +790,15 @@ Linegraph.prototype._catmullRomUniform = function(data) {
* @returns {string} * @returns {string}
* @private * @private
*/ */
Linegraph.prototype._catmullRom = function(data) {
var alpha = this.options.catmullRom.alpha;
Linegraph.prototype._catmullRom = function(data, group) {
var alpha = group.options.catmullRom.alpha;
if (alpha == 0 || alpha === undefined) { if (alpha == 0 || alpha === undefined) {
return this._catmullRomUniform(data); return this._catmullRomUniform(data);
} }
else { else {
var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M;
var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA;
var d = "" + Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var d = Math.round(data[0].x) + "," + Math.round(data[0].y) + " ";
var length = data.length; var length = data.length;
for (var i = 0; i < length - 1; i++) { for (var i = 0; i < length - 1; i++) {
@ -866,10 +872,10 @@ Linegraph.prototype._linear = function(data) {
var d = ""; var d = "";
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
if (i == 0) { if (i == 0) {
d += "M" + data[i].x + "," + data[i].y;
d += Math.round(data[i].x) + "," + Math.round(data[i].y);
} }
else { else {
d += " " + data[i].x + "," + data[i].y;
d += " " + Math.round(data[i].x) + "," + Math.round(data[i].y);
} }
} }
return d; return d;

+ 1
- 1
src/timeline/component/css/dataaxis.css View File

@ -35,7 +35,7 @@
.vis.timeline .dataaxis .yAxis.minor{ .vis.timeline .dataaxis .yAxis.minor{
position: absolute; position: absolute;
width: 100%; width: 100%;
color: #979797;
color: #bebebe;
white-space: nowrap; white-space: nowrap;
} }

+ 1
- 1
src/util.js View File

@ -1041,7 +1041,7 @@ util.copyObject = function(objectFrom, objectTo) {
* @private * @private
*/ */
util._mergeOptions = function (mergeTarget, options, option) { util._mergeOptions = function (mergeTarget, options, option) {
if (options[option]) {
if (options[option] !== undefined) {
if (typeof options[option] == 'boolean') { if (typeof options[option] == 'boolean') {
mergeTarget[option].enabled = options[option]; mergeTarget[option].enabled = options[option];
} }

Loading…
Cancel
Save