-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
419 lines (373 loc) · 17.3 KB
/
Copy pathscript.js
File metadata and controls
419 lines (373 loc) · 17.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
const glowGradient =`
-webkit-box-shadow: 0px 0px 15px 10px #FFF789, 0px 0px 10px 23px #FFFFC1; box-shadow: 0px 0px 15px 10px #FFF789, 0px 0px 10px 23px #FFFFC1;`
// Store worm-face SVGs as a string variables
const smileSVG = `
<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="41" height="41" fill="none"/>
<path d="M34 22C34 28.0751 28.1797 33 21 33C13.8203 33 8 28.0751 8 22" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
<path d="M12 14C12 12.3431 9.98528 11 7.5 11C5.01472 11 3 12.3431 3 14" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
<path d="M37 14C37 12.3431 35.2091 11 33 11C30.7909 11 29 12.3431 29 14" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
</svg>
`;
const mehSVG = `
<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="41" height="41" fill="none"/>
<path d="M30 28C30 28 25.9706 28 21 28C16.0294 28 12 28 12 28" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
<path d="M12 15C12 16.6569 10.2091 18 8 18C5.79086 18 4 16.6569 4 15" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
<path d="M37 15C37 16.6569 34.9853 18 32.5 18C30.0147 18 28 16.6569 28 15" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
</svg>
`;
const sadSVG = `
<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="41" height="41" fill="none"/>
<path d="M30 33C25.3333 29.7975 25.3333 31.3987 20.6667 30.5981C16 28.9168 14.6667 31.3988 10 30.5981" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
<path d="M30.5 21C29 20 29.7091 18.5 27.5 18.5C25.2909 18.5 27.5 19 24 18" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
<path d="M13.5 17C12 18 12.2091 18.5 10 18.5C7.79086 18.5 10 18 6.5 19" stroke="#D772B3" stroke-width="3" stroke-linecap="round"/>
</svg>
`;
const book = `
<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="41" height="41" fill="none"/>
<path d="M4 15.8492L18.8492 1L37.234 19.3848L22.3848 34.234M4 15.8492L22.3848 34.234M4 15.8492C4 15.8492 3.48845 17.4466 3.5 18.5C3.51091 19.4956 4 21 4 21C4 21 15.2051 32.2051 22.3848 39.3848M22.3848 34.234C22.3848 34.234 22.0319 35.603 22 36.5C21.9595 37.6358 22.048 38.2993 22.3848 39.3848M22.3848 39.3848C27.7019 34.0677 36 25.7696 36 25.7696M36 20.6188V23.3015M36 25.7696L37.234 24.5355L36 23.3015M36 25.7696V23.3015" stroke="#446455"/>
</svg>
`;
const compute = `
<svg width="41" height="41" viewBox="0 0 41 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="41" height="41" fill="none"/>
<rect x="5.5" y="2.5" width="30" height="20" rx="0.5" stroke="#446455"/>
<path d="M5.79199 22.5H35.208C35.44 22.5 35.6412 22.6599 35.6943 22.8857L39.2236 37.8857C39.2974 38.1995 39.0597 38.5 38.7373 38.5H2.2627C1.94032 38.5 1.70263 38.1995 1.77637 37.8857L5.30566 22.8857C5.35881 22.6599 5.55998 22.5 5.79199 22.5Z" stroke="#446455"/>
<rect x="7.5" y="4.5" width="26" height="16" rx="0.5" stroke="#446455"/>
<path d="M8 25H33" stroke="#446455" stroke-linecap="round"/>
<path d="M7 28H34" stroke="#446455" stroke-linecap="round"/>
<path d="M6 31L35 31" stroke="#446455" stroke-linecap="round"/>
<path d="M14.3828 33.5H25.6172C25.8692 33.5 26.0818 33.6875 26.1133 33.9375L26.3633 35.9375C26.4006 36.2359 26.1679 36.5 25.8672 36.5H14.1328C13.8321 36.5 13.5994 36.2359 13.6367 35.9375L13.8867 33.9375C13.9143 33.7189 14.0803 33.5482 14.29 33.5088L14.3828 33.5Z" stroke="#446455"/>
</svg>
`;
// *********************************
// ** MAKING CHARTY THINGS APPEAR **
// *********************************
// This is where I write the function containing all the stuff to draw using the data, which is loaded in at the bottom.
function drawGraphic(dataset) {
// check data is available
console.log("Hi! Here is your data:", dataset);
// Set height and width with chart in mind
let w = 700;
let h = 1100;
let marginLeft = 250
let marginRight = 50
let chartWidth = w - marginRight - marginLeft
// edge is the ratio to which the completed bar is shrunk by
let edge = 0.1
let minBarSize = 5
let colours = [ "#F5CDB4", "#C7B19C", "#FFDB94", "#D772B3", "white"]
// calculate percentage complete
let percentage = function(d) {
let total = d.completed + d.incomplete;
let percent = total === 0 ? 0 : d.completed / total;
return percent;
}
// dataset.forEach(d => {
// console.log(`percent complete for ${d.name}:`, percentage(d));
// });
// logging my types of data
dataset.forEach(d => {
console.log("Data types in dataset:", `name: ${typeof d.name}, completed: ${typeof d.completed}, incomplete: ${typeof d.incomplete}`);
});
// Adding percentage to the chart's array, and then sorting it :)
dataset.forEach(d => {
d.percentage = percentage(d);
});
const sorted = dataset.sort((a, b) => d3.descending(percentage(a), percentage(b)));
console.log("Hi! Here is your SORTED data:", sorted);
// setting up an x scale. This isn't currently used anywhere.
var xScale = d3.scaleLinear()
.domain([0, 1
// d3.max(dataset, function (d) {return d.completed + d.incomplete})
])
.range([0, w - marginLeft])
// set up y scale
var yScale = d3.scaleBand()
.domain(dataset.map(d => d.name))
.range([0, h])
.paddingInner(0.2);
// setting up a y axis
var yAxis = d3.axisLeft(yScale)
// Create SVG for chart
var svg = d3.selectAll("body")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("class", "chart");
svg.append("g") // g is a group of elements
.call(yAxis)
.attr("class", "axis")
.attr("transform", "translate(" + marginLeft + ", 0)")
// remove the lines & text, these are replaced with <p> elements later
.selectAll(".domain, .tick line, .tick text").remove()
// Making bars for incomplete. These are just the full width of the chart tbh, no scale needed.
svg.selectAll(".incBars")
.data(dataset)
.enter()
.append("rect")
.attr("class", "incBars")
.attr("x", function(d) {
return marginLeft;
})
.attr("y", function(d) {
return yScale(d.name);
})
.attr("width", chartWidth)
// function(d) {
// return xScale(d.incomplete);})
.attr("height", yScale.bandwidth())
.attr("rx", function() {return yScale.bandwidth() / 2.5})
.style("fill", function () {return colours[1];});
// Add <p> elements with d.name for each incomplete bar (left of chart) instead of axis stuff
d3.select("body")
.selectAll(".bar-label")
.data(dataset)
.enter()
.append("p")
.attr("class", "bar-label")
.style("position", "absolute")
.style("left", "0px")
.style("top", (d) => `${yScale(d.name) + 8}px`)
.style("width", `${marginLeft}px`)
.style("height", `${yScale.bandwidth()}px`)
.style("margin", "0")
.style("text-align", "right")
.style("display", "flex")
.style("align-items", "center")
.style("justify-content", "flex-end")
.text(d => d.name);
// The complete bars use the percentage complete, using the perCom function
svg.selectAll(".comBars")
.data(dataset)
.enter()
.append("rect")
.attr("class",
function(d) {
return percentage(d) === 1 ? "completedBars" : "comBars";
})
.attr("x", function(d) {
return marginLeft + yScale.bandwidth() * edge;
})
.attr("y", function(d) {
if (percentage(d) * chartWidth === 0) return null;
return yScale(d.name) + yScale.bandwidth() * edge;
})
.attr("width", function(d) {
var width = percentage(d) * chartWidth - (yScale.bandwidth() * (edge * 2));
if (percentage(d) * chartWidth === 0) return 0;
return width < minBarSize ? minBarSize : width;
})
.attr("height", function(d) {
if (percentage(d) * chartWidth === 0) return 0;
return yScale.bandwidth() - (yScale.bandwidth() * (edge * 2));
})
.attr("rx", function() { return yScale.bandwidth() / 2.5 })
.style("fill", function (d) {
if (percentage(d) === 1) return colours[2];
return colours[0];
});
// Adding the little faces according to completion rates
svg.selectAll(".smile-icon")
.data(dataset.filter(d => percentage(d) === 1))
.enter()
.append("g")
.attr("class", "smile-icon")
.attr("transform", function(d) {
var x = (chartWidth + marginLeft) - 55 ;
var y = yScale(d.name) + yScale.bandwidth() / 2 - 20; // Center icon (icon height is 41)
return `translate(${x},${y})`;
})
.each(function(d) {
d3.select(this).html(smileSVG);
});
svg.selectAll(".meh-icon")
.data(dataset.filter(d => percentage(d) > 0.5 && percentage(d) < 1))
.enter()
.append("g")
.attr("class", "meh-icon")
.attr("transform", function(d) {
var x = ((percentage(d) * chartWidth) + marginLeft) - 55 ;
var y = yScale(d.name) + yScale.bandwidth() / 2 - 20; // Center icon (icon height is 41)
return `translate(${x},${y})`;
})
.each(function(d) {
d3.select(this).html(mehSVG);
});
svg.selectAll(".sad-icon")
.data(dataset.filter(d => percentage(d) > 0.17 && percentage(d) < 0.5))
.enter()
.append("g")
.attr("class", "sad-icon")
.attr("transform", function(d) {
var x = ((percentage(d) * chartWidth) + marginLeft) - 55;
var y = yScale(d.name) + yScale.bandwidth() / 2 - 20; // Center icon (icon height is 41)
return `translate(${x},${y})`;
})
.each(function(d) {
d3.select(this).html(sadSVG);
});
// Attempting to style a shadow on the completedBars
svg.append("defs")
.append("filter")
.attr("id", "completedBarShadow")
.attr("x", "-20%")
.attr("y", "-20%")
.attr("width", "140%")
.attr("height", "140%")
.html(`
<feDropShadow dx="0" dy="0" stdDeviation="5" flood-color="white" flood-opacity="1"/>
<feDropShadow dx="0" dy="0" stdDeviation="10" flood-color="#FFF789" flood-opacity="1"/>
<feDropShadow dx="0" dy="0" stdDeviation="23" flood-color="#FFFFC1" flood-opacity="1"/>
`);
// COPILOT DONE THE SPARKLES
// Add animated sparkles to completed bars
const sparkleCount = 7;
const sparkleSVG = `
<g class="sparkle">
<circle r="2.5" fill="#FFF789" opacity="0.8"/>
<circle r="1" fill="#FFFFC1" opacity="0.9"
<circle r="1.2" fill="#fff" opacity="0.6"/>
<polygon points="5,0 6.5,3.5 10,4 7,6.5 8,10 5,8 2,10 3,6.5 0,4 3.5,3.5" fill="#fff" opacity="0.2"/>
</g>
`;
svg.selectAll(".completedBars")
.each(function(d, i) {
// Only add sparkles to fully completed bars
if (percentage(d) === 1) {
const bar = d3.select(this);
const barX = +bar.attr("x");
const barY = +bar.attr("y");
const barWidth = +bar.attr("width");
const barHeight = +bar.attr("height");
// Add sparkles group
const sparklesGroup = svg.append("g")
.attr("class", "sparkles-group");
for (let s = 0; s < sparkleCount; s++) {
// Random position within the bar
const sx = barX + Math.random() * (barWidth ) + 2;
const sy = barY + Math.random() * (barHeight ) + 2;
// Random animation duration and delay
const duration = 1.2 + Math.random() * 1.2;
const delay = Math.random() * 2;
// Append sparkle
const sparkle = sparklesGroup.append("g")
.attr("transform", `translate(${sx},${sy})`)
.html(sparkleSVG);
// Animate sparkle (scale and fade)
sparkle.select("circle")
.transition()
.delay(delay * 1000)
.duration(duration * 1000)
.ease(d3.easeCubicInOut)
.attrTween("transform", function() {
return function(t) {
const scale = 0.7 + 0.8 * Math.abs(Math.sin(t * Math.PI));
return `scale(${scale})`;
};
})
.attrTween("opacity", function() {
return function(t) {
return 0.7 + 0.3 * Math.abs(Math.sin(t * Math.PI));
};
})
.on("end", function repeat() {
d3.select(this)
.transition()
.duration(duration * 1000)
.attrTween("transform", function() {
return function(t) {
const scale = 0.7 + 0.8 * Math.abs(Math.sin(t * Math.PI));
return `scale(${scale})`;
};
})
.attrTween("opacity", function() {
return function(t) {
return 0.7 + 0.3 * Math.abs(Math.sin(t * Math.PI));
};
})
.on("end", repeat);
});
// Animate polygon (rotate and scale)
sparkle.select("polygon")
.transition()
.delay(delay * 1000)
.duration(duration * 1000)
.ease(d3.easeCubicInOut)
.attrTween("transform", function() {
// Center of the polygon is at (5,5) based on points
return function(t) {
const angle = 360 * t;
const scale = 0.7 + 0.5 * Math.abs(Math.sin(t * Math.PI));
// Rotate and scale around (5,5)
return `translate(5,5) rotate(${angle}) scale(${scale}) translate(-5,-5)`;
};
})
.attrTween("opacity", function() {
return function(t) {
return 0.5 + 0.5 * Math.abs(Math.sin(t * Math.PI));
};
})
.on("end", function repeat() {
d3.select(this)
.transition()
.duration(duration * 1000)
.attrTween("transform", function() {
return function(t) {
const angle = 360 * t;
const scale = 0.7 + 0.5 * Math.abs(Math.sin(t * Math.PI));
return `translate(5,5) rotate(${angle}) scale(${scale}) translate(-5,-5)`;
};
})
.attrTween("opacity", function() {
return function(t) {
return 0.5 + 0.5 * Math.abs(Math.sin(t * Math.PI));
};
})
.on("end", repeat);
});
}
}
});
d3.selectAll("rect.completedBars")
.attr("filter", "url(#completedBarShadow)");
// Now to add little icons for the type of study / course. These are stored as Svg code at the top of the script.
svg.selectAll (".studyIcon")
.data(dataset)
.enter()
.append("g")
.attr("class", "studyIcon")
.attr("transform", function(d) {
var x = marginLeft + chartWidth + 7;
var y = yScale(d.name) + yScale.bandwidth() / 2 - 20; // Center icon (icon height is 41)
return `translate(${x},${y})`;
})
.each(function(d) {
if (d.type === "Online course") {
d3.select(this).html(compute);
} else if (d.type === "Book") {
d3.select(this).html(book);
}
});
} // End of drawGraphic
// *********************************
// ** LOAD DATA AND DO THE THINGS **
// *********************************
// Load data and check it in console log
// Call all the functions to do things with the data here
// Function so D3 knows which bits are numbers, not strings
var rowConverter = function(d) {
return {
name: d.name,
completed: parseFloat(d.completed),
incomplete: parseFloat(d.incomplete),
type: d.type,
}};
d3.csv("learnyWormieData.csv", rowConverter).then(function(data) {
const dataset = data;
drawGraphic(dataset);
});