HTMLcopy
1
<div id="container"></div>
CSScopy
x
1
html,
2
body,
3
#container {
4
width: 100%;
5
height: 100%;
6
margin: 0;
7
padding: 0;
8
}
9
10
.anychart-tooltip {
11
background: #fff;
12
padding: 15px;
13
color: #333;
14
box-shadow: 1px 1px 3px #333;
15
}
16
17
.anychart-tooltip h4 {
18
margin-bottom: 0;
19
}
20
21
.anychart-tooltip ul {
22
margin: 0;
23
}
JavaScriptcopy
354
1
var scale;
2
var timeline;
3
4
anychart.onDocumentReady(function () {
5
// The data used in this sample can be obtained from the CDN
6
// https://cdn.anychart.com/samples-data/gantt-general-features/custom-drawing/gantt-custom-drawing.js
7
var treeData = anychart.data.tree(getData(), 'as-table'); // eslint-disable-line no-undef
8
9
// map loaded data for the gantt project chart
10
var mapping = treeData.mapAs({
11
actualStart: 'mostLikelyStart',
12
actualEnd: 'mostLikelyEnd'
13
});
14
15
// create gantt project chart
16
var chart = anychart.ganttProject();
17
18
// set data for the chart
19
chart.data(mapping);
20
21
// set chart row's height
22
chart.defaultRowHeight(60);
23
24
// get data grid
25
var dataGrid = chart.dataGrid();
26
27
// set data grid tooltip's formatter
28
dataGrid.tooltip().useHtml(true);
29
dataGrid.tooltip().format(tooltipFormatter);
30
31
// get timeline
32
timeline = chart.getTimeline();
33
34
// set timeline tooltip's formatter
35
timeline.tooltip().useHtml(true);
36
timeline.tooltip().format(tooltipFormatter);
37
38
// set default task bar and progress fill settings
39
timeline.tasks().selected().fill('#45738B');
40
timeline.tasks().progress().selected().fill('#47B7F2');
41
42
timeline
43
.groupingTasks()
44
.labels()
45
.padding(0, 0, 5, 0)
46
.position('center')
47
.anchor('center');
48
49
timeline
50
.groupingTasks()
51
.labels()
52
.format(function () {
53
return (
54
'Duration: ' +
55
Math.round(
56
(this.autoEnd - this.autoStart) / (24 * 60 * 60 * 1000)
57
) +
58
' days'
59
);
60
});
61
62
// set shapes for timeline tasks rendering
63
timeline
64
.tasks()
65
.rendering()
66
.shapes([
67
{
68
name: 'pessimisticTask',
69
shapeType: 'path',
70
disablePointerEvents: false
71
},
72
{
73
name: 'mostLikelyTask',
74
shapeType: 'path',
75
disablePointerEvents: false
76
},
77
{
78
name: 'optimisticTask',
79
shapeType: 'path',
80
disablePointerEvents: false
81
}
82
]);
83
84
timeline
85
.groupingTasks()
86
.rendering()
87
.shapes([
88
{
89
name: 'mostLikelyTask',
90
shapeType: 'path',
91
disablePointerEvents: false
92
}
93
]);
94
95
// get timeline's scale
96
scale = timeline.scale();
97
98
// setup custom drawer for timeline tasks
99
timeline.tasks().rendering().drawer(taskDrawer);
100
101
// setup custom drawer for timeline grouping tasks
102
timeline.groupingTasks().rendering().drawer(groupingTasksDrawer);
103
104
// set task progress' settings
105
timeline.tasks().progress().height('15%').selected().stroke('#666 .6');
106
107
// set grouping task progress' settings
108
timeline
109
.groupingTasks()
110
.progress()
111
.height('50%')
112
.fill('#0078CD .7')
113
.offset('50%')
114
.selected()
115
.fill('#47B7F1 .7');
116
117
// set container id for the chart
118
chart.container('container');
119
120
// initiate chart drawing
121
chart.draw();
122
123
// Set scale maximum and minimum.
124
scale.minimumGap(0.08);
125
scale.maximumGap(0.15);
126
127
// zoom chart all dates range
128
chart.fitAll();
129
});
130
131
// custom drawer for the tasks
132
function taskDrawer() {
133
var path;
134
var shift;
135
var left;
136
var top;
137
var width;
138
var height;
139
var itemBounds;
140
var startRatio;
141
var endRatio;
142
143
// get timeline width and left border coordinates
144
var tlBounds = timeline.getPixelBounds();
145
var tlWidth = tlBounds.width;
146
var tlLeft = tlBounds.left;
147
148
// get recommended bounds for drawing
149
var bounds = this.predictedBounds;
150
151
// get bar height
152
var barHeight = Math.round(bounds.height / 3);
153
154
/* OPTIMISTIC BAR */
155
// get path from shapes
156
path = this.shapes.optimisticTask;
157
158
// set path's fill and stroke settings
159
path.fill('#90D6C1 .6');
160
path.stroke('#80A291 .8');
161
162
// get shift value
163
shift = halfShift(path.strokeThickness());
164
165
// calculate start and end ratio for the optimistic bar using it's data
166
startRatio = scale.transform(this.item.get('optimisticStart'));
167
endRatio = scale.transform(this.item.get('optimisticEnd'));
168
169
// calculate X coordinate for the optimistic bar
170
left = Math.round(tlWidth * startRatio + tlLeft) + shift;
171
172
// calculate Y coordinate for the optimistic bar
173
top = Math.round(bounds.top) - 4 + shift;
174
175
// calculate optimistic bar's width
176
width = Math.round(tlWidth * (endRatio - startRatio));
177
178
// set optimistic bar's height
179
height = barHeight;
180
181
// set optimistic bar's bounds
182
itemBounds = anychart.math.rect(left, top, width, height);
183
184
// draw rounded rectangle on the path
185
anychart.graphics.vector.primitives.roundedRect(path, itemBounds, 3);
186
187
/* MOST LIKELY BAR */
188
// get path prom shapes
189
path = this.shapes.mostLikelyTask;
190
191
// set stroke color and opacity
192
path.stroke('#666 .6');
193
194
// get shift value
195
shift = halfShift(path.strokeThickness());
196
197
// calculate start and end ratio for the most-likely bar using it's data
198
startRatio = scale.transform(this.item.get('mostLikelyStart'));
199
endRatio = scale.transform(this.item.get('mostLikelyEnd'));
200
201
// calculate X coordinate for the most-likely bar
202
left = Math.round(tlWidth * startRatio + tlLeft) + shift;
203
204
// calculate Y coordinate for the most-likely bar
205
top =
206
Math.round(bounds.top + (bounds.height - barHeight) / 2) - 1 + shift;
207
208
// calculate most-likely bar's width
209
width = Math.round(tlWidth * (endRatio - startRatio));
210
211
// set most-likely bar's height
212
height = barHeight;
213
214
// set most-likely bar's bounds
215
itemBounds = anychart.math.rect(left, top, width, height);
216
217
// draw rounded rectangle on the path
218
anychart.graphics.vector.primitives.roundedRect(path, itemBounds, 3);
219
220
/* PESSIMISTIC BAR */
221
// get path from shapes
222
path = this.shapes.pessimisticTask;
223
224
// set path's fill and stroke settings
225
path.fill('#FF4B12 .4');
226
path.stroke('#6F5264 .6');
227
228
// get shift value
229
shift = halfShift(path.strokeThickness());
230
231
// calculate start and end ratio for the pessimistic bar using it's data
232
startRatio = scale.transform(this.item.get('pessimisticStart'));
233
endRatio = scale.transform(this.item.get('pessimisticEnd'));
234
235
// calculate X coordinate for the pessimistic bar
236
left = Math.round(tlWidth * startRatio + tlLeft) + shift;
237
238
// calculate Y coordinate for the pessimistic bar
239
top = Math.round(bounds.top + bounds.height - barHeight + 2) + shift;
240
241
// calculate pessimistic bar's width
242
width = Math.round(tlWidth * (endRatio - startRatio)) + shift;
243
244
// set pessimistic bar's height
245
height = barHeight;
246
247
// set pessimistic bar's bounds
248
itemBounds = anychart.math.rect(left, top, width, height);
249
250
// draw rounded rectangle on the path
251
anychart.graphics.vector.primitives.roundedRect(path, itemBounds, 3);
252
}
253
254
// custom drawer for the grouping tasks
255
function groupingTasksDrawer() {
256
// get path prom shapes
257
var path = this.shapes.mostLikelyTask;
258
259
// set path stroke settings
260
path.stroke({
261
dash: '3 7 2',
262
thickness: 5,
263
color: '#385960'
264
});
265
266
// get shift value
267
var shift = halfShift(path.strokeThickness());
268
269
// get recommended bounds for drawing
270
var bounds = this.predictedBounds;
271
272
// get parameters for the element drawer
273
var left = Math.round(bounds.left) + shift;
274
var height = Math.round(bounds.height);
275
var center = Math.round(height / 2);
276
var top = Math.round(bounds.top + center - 2) + shift;
277
var width = Math.round(bounds.width);
278
var right = left + width;
279
280
// draw grouping task
281
path
282
.moveTo(left, top + center)
283
.lineTo(right, top + center)
284
.close();
285
}
286
287
// formatter function for the tooltips
288
function tooltipFormatter() {
289
if (!this.item.numChildren()) {
290
// formatter for the timeline task
291
return (
292
'<h4 style="color: #80A291">Optimistic (' +
293
Math.round(
294
(this.item.get('optimisticEnd') -
295
this.item.get('optimisticStart')) /
296
(24 * 60 * 60 * 1000)
297
) +
298
' days):</h4><ul><li>Start - ' +
299
anychart.format.dateTime(
300
this.item.get('optimisticStart'),
301
'dd.MM.yyyy'
302
) +
303
'</li><li>End - ' +
304
anychart.format.dateTime(
305
this.item.get('optimisticEnd'),
306
'dd.MM.yyyy'
307
) +
308
'</li></ul><hr>' +
309
'<h4 style="color: #00A6DA">Most Likely (' +
310
Math.round(
311
(this.item.get('mostLikelyEnd') -
312
this.item.get('mostLikelyStart')) /
313
(24 * 60 * 60 * 1000)
314
) +
315
' days):</h4><ul><li>Start - ' +
316
anychart.format.dateTime(
317
this.item.get('mostLikelyStart'),
318
'dd.MM.yyyy'
319
) +
320
'</li><li>End - ' +
321
anychart.format.dateTime(
322
this.item.get('mostLikelyEnd'),
323
'dd.MM.yyyy'
324
) +
325
'</li></ul><hr>' +
326
'<h4 style="color: #E24B26">Pessimistic (' +
327
Math.round(
328
(this.item.get('pessimisticEnd') -
329
this.item.get('pessimisticStart')) /
330
(24 * 60 * 60 * 1000)
331
) +
332
' days):</h4><ul><li>Start - ' +
333
anychart.format.dateTime(
334
this.item.get('pessimisticStart'),
335
'dd.MM.yyyy'
336
) +
337
'</li><li>End - ' +
338
anychart.format.dateTime(
339
this.item.get('pessimisticEnd'),
340
'dd.MM.yyyy'
341
) +
342
'</li></ul>'
343
);
344
} // formatter for the grouping task
345
return (
346
'Duration: ' +
347
Math.round((this.autoEnd - this.autoStart) / (24 * 60 * 60 * 1000)) +
348
' days'
349
);
350
}
351
352
function halfShift(strokeThickness) {
353
return strokeThickness % 2 ? 0.5 : 0;
354
}