├── README.md ├── part_1 ├── css │ └── style.css ├── index.html └── js │ ├── chartStyles.js │ └── script.js └── part_2 ├── css └── style.css ├── index.html └── js ├── chartStyles.js └── script.js /README.md: -------------------------------------------------------------------------------- 1 | # Webinar visualización de datos web con ChartJS 2 | 3 | Materiales del webinar sobre **visualización de datos con ChartJS** impartido desde Ironhack Madrid | 45 min - abr. 2020 4 | 5 | Explora algunas de las posibilidades de [Chartjs](https://www.chartjs.org/) conectándolo a una [API de montañas rusas](https://github.com/german-alvarez-dev/api-coasters). 6 | 7 | ## Grabaciones 8 | - Vídeo completo del webinar (parte 1) [en este enlace](https://youtu.be/1R1tNZMnMmU?t=815). 9 | - Vídeo completo del webinar (parte 2) [en este enlace](https://youtu.be/Hpa3Pdb7p5U) 10 | 11 | ## Instalación 12 | No requiere instalación. 13 | 14 | ## Ejecución 15 | Ejecutar archivo `index.html` en el navegador. 16 | -------------------------------------------------------------------------------- /part_1/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #181f38; 3 | font-family: 'Poppins', monospace; 4 | color: white; 5 | font-size: .7em; 6 | font-weight: 200; 7 | letter-spacing: 1px; 8 | } 9 | 10 | html { 11 | padding: 20px; 12 | font-weight: 300; 13 | } 14 | 15 | main { 16 | padding: 60px; 17 | } 18 | 19 | section { 20 | margin-bottom: 40px; 21 | } 22 | 23 | h1 { 24 | font-size: 3em; 25 | font-weight: 100; 26 | } 27 | 28 | h2, h3 { 29 | font-size: 1.4em; 30 | margin-bottom: 29px; 31 | border-bottom: 1px solid rgba(255,255,255,.2); 32 | padding-bottom: 15px; 33 | font-weight: 200; 34 | } 35 | 36 | hr { 37 | border-color: white; 38 | margin-bottom: 50px; 39 | } 40 | 41 | figure { 42 | background-color: rgba(255,255,255,.05); 43 | padding: 10px 10px 30px; 44 | } 45 | 46 | figure p { 47 | margin-bottom: 7px; 48 | font-size: .8em; 49 | } 50 | 51 | canvas { 52 | width: 100%; 53 | } 54 | 55 | 56 | /* grid */ 57 | 58 | [class*='cols'] { 59 | display: flex; 60 | flex-direction: row; 61 | flex-wrap: nowrap; 62 | } 63 | 64 | .cols-3 > * { 65 | width: 33.33%; 66 | margin: 10px 67 | } 68 | 69 | .cols-2 > * { 70 | width: 50%; 71 | margin: 10px 72 | } 73 | 74 | .cols-1 > * { 75 | width: 100%; 76 | margin: 10px 77 | } 78 | 79 | #chart6 { 80 | max-height: 300px; 81 | } 82 | 83 | section { 84 | opacity: 0; 85 | transition: opacity .3s; 86 | } 87 | 88 | .running section { 89 | opacity: 1 90 | } 91 | 92 | .loading { 93 | position: absolute; 94 | border: none; 95 | } 96 | 97 | .running .loading { 98 | display: none; 99 | } -------------------------------------------------------------------------------- /part_1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Visualización de datos con ChartJS 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |

Montañas rusas: los datos

17 |

Visualización de estadísticas globales

18 | 19 |
20 | 21 |

Esperando respuesta de la API...

22 | 23 |
24 |
25 |

Gráfico radial

26 |

Comparativa global

27 | 28 |
29 | 30 |
31 |

Gráfico de área polar

32 |

Montañas rusas por país

33 | 34 |
35 |
36 | 37 |
38 |
39 |

Gráfico lineal

40 |

Construcciones por año

41 | 42 |
43 |
44 | 45 |
46 |
47 |

Gráfico radial

48 |

Altura

49 | 50 |
51 | 52 |
53 |

Gráfico de donut

54 |

Modelos

55 | 56 |
57 | 58 |
59 |

Gráfico de barras

60 |

Fuerza G

61 | 62 |
63 |
64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /part_1/js/chartStyles.js: -------------------------------------------------------------------------------- 1 | const styles = { 2 | color: { 3 | solids: ['rgba(116, 72, 194, 1)', 'rgba(33, 192, 215, 1)', 'rgba(217, 158, 43, 1)', 'rgba(205, 58, 129, 1)', 'rgba(156, 153, 204, 1)', 'rgba(225, 78, 202, 1)'], 4 | alphas: ['rgba(116, 72, 194, .2)', 'rgba(33, 192, 215, .2)', 'rgba(217, 158, 43, .2)', 'rgba(205, 58, 129, .2)', 'rgba(156, 153, 204, .2)', 'rgba(225, 78, 202, .2)'] 5 | } 6 | } -------------------------------------------------------------------------------- /part_1/js/script.js: -------------------------------------------------------------------------------- 1 | // API Ajax call 2 | fetch('https://coasters-api.herokuapp.com/') 3 | .then(response => response.json()) 4 | .then(data => printCharts(data)) 5 | 6 | 7 | function printCharts(coasters) { 8 | 9 | // Remove loading message, show chart panels 10 | document.body.classList.add('running') 11 | 12 | // Call each chart function passing the coasters and DOM Canvas tag ID to be rendered 13 | compareRadialChart(coasters, 'chart2') 14 | modelDoughnutChart(coasters, 'chart4') 15 | heightRadarChart(coasters, 'chart3') 16 | 17 | } 18 | 19 | 20 | function compareRadialChart(coasters, id) { 21 | 22 | // Every ChartJS chart needs data with labels and datasets 23 | const data = { 24 | labels: ['EEUU', 'UK', 'España', 'Japón', 'China'], 25 | datasets: [ // datasets is an Array of Objects, each Object contains one set of info/styles to be shown. In many charts, multiple sets of info can be rendered if multiple Objets are passed to the datasets Array 26 | { 27 | data: [ 28 | coasters.filter(eachCoaster => eachCoaster.country === 'United States').length, 29 | coasters.filter(eachCoaster => eachCoaster.country === 'United Kingdom').length, 30 | coasters.filter(eachCoaster => eachCoaster.country === 'Spain').length, 31 | coasters.filter(eachCoaster => eachCoaster.country === 'Japan').length, 32 | coasters.filter(eachCoaster => eachCoaster.country === 'China').length 33 | ], 34 | borderWidth: 1, 35 | borderColor: styles.color.solids.map(eachColor => eachColor), 36 | backgroundColor: styles.color.alphas.map(eachColor => eachColor) 37 | } 38 | ] 39 | } 40 | 41 | // Every ChartJs chart can have multiple layout options 42 | const options = { 43 | scale: { 44 | gridLines: { 45 | color: '#444' 46 | }, 47 | ticks: { 48 | display: false 49 | } 50 | }, 51 | legend: { 52 | position: 'right', 53 | labels: { 54 | fontColor: '#fff' 55 | } 56 | } 57 | } 58 | 59 | // Every ChartJS chart receives two arguments: the Canvas id to place the chart, and an object with: chart type, data to show, layout options object (optional) 60 | new Chart(id, { type: 'polarArea', data, options }) 61 | } 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | function modelDoughnutChart(coasters, id) { 71 | 72 | const data = { 73 | labels: ['Propulsada', 'Hiper montaña', 'Giga montaña', 'Inversión', 'Sentado'], 74 | datasets: [ 75 | { 76 | data: [ 77 | coasters.filter(eachCoaster => eachCoaster.model === 'Accelerator Coaster').length, 78 | coasters.filter(eachCoaster => eachCoaster.model === 'Hyper Coaster').length, 79 | coasters.filter(eachCoaster => eachCoaster.model === 'Giga Coaster').length, 80 | coasters.filter(eachCoaster => eachCoaster.model === 'Multi Inversion Coaster').length, 81 | coasters.filter(eachCoaster => eachCoaster.model === 'Sitting Coaster').length 82 | ], 83 | borderColor: styles.color.solids.map(eachColor => eachColor), 84 | backgroundColor: styles.color.alphas.map(eachColor => eachColor), 85 | borderWidth: 1 86 | } 87 | ] 88 | } 89 | 90 | const options = { 91 | legend: { 92 | position: 'right', 93 | labels: { 94 | fontColor: '#fff' 95 | } 96 | } 97 | } 98 | 99 | new Chart(id, { type: 'doughnut', data, options }) 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | function heightRadarChart(coasters, id) { 109 | 110 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.height > 80) 111 | 112 | const data = { 113 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name), 114 | datasets: [ 115 | { 116 | label: 'Altura', 117 | data: selectedCoasters.map(eachCoaster => eachCoaster.height), 118 | borderColor: styles.color.solids[0], 119 | borderWidth: 1 120 | } 121 | ] 122 | } 123 | 124 | const options = { 125 | scale: { 126 | gridLines: { 127 | color: '#444' 128 | }, 129 | pointLabels: { 130 | fontColor: '#fff' 131 | }, 132 | ticks: { 133 | display: false 134 | } 135 | }, 136 | legend: { 137 | display: false 138 | } 139 | } 140 | 141 | new Chart(id, { type: 'radar', data, options }) 142 | } -------------------------------------------------------------------------------- /part_2/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #181f38; 3 | font-family: 'Poppins', monospace; 4 | color: white; 5 | font-size: .7em; 6 | font-weight: 200; 7 | letter-spacing: 1px; 8 | } 9 | 10 | html { 11 | padding: 20px; 12 | font-weight: 300; 13 | } 14 | 15 | main { 16 | padding: 60px; 17 | } 18 | 19 | section { 20 | margin-bottom: 40px; 21 | } 22 | 23 | h1 { 24 | font-size: 3em; 25 | font-weight: 100; 26 | } 27 | 28 | h2, h3 { 29 | font-size: 1.4em; 30 | margin-bottom: 29px; 31 | border-bottom: 1px solid rgba(255,255,255,.2); 32 | padding-bottom: 15px; 33 | font-weight: 200; 34 | } 35 | 36 | hr { 37 | border-color: white; 38 | margin-bottom: 50px; 39 | } 40 | 41 | figure { 42 | background-color: rgba(255,255,255,.05); 43 | padding: 10px 10px 30px; 44 | } 45 | 46 | figure p { 47 | margin-bottom: 7px; 48 | font-size: .8em; 49 | } 50 | 51 | canvas { 52 | width: 100%; 53 | } 54 | 55 | 56 | /* grid */ 57 | 58 | [class*='cols'] { 59 | display: flex; 60 | flex-direction: row; 61 | flex-wrap: nowrap; 62 | } 63 | 64 | .cols-3 > * { 65 | width: 33.33%; 66 | margin: 10px 67 | } 68 | 69 | .cols-2 > * { 70 | width: 50%; 71 | margin: 10px 72 | } 73 | 74 | .cols-1 > * { 75 | width: 100%; 76 | margin: 10px 77 | } 78 | 79 | #chart6 { 80 | max-height: 300px; 81 | } 82 | 83 | section { 84 | opacity: 0; 85 | transition: opacity .3s; 86 | } 87 | 88 | .running section { 89 | opacity: 1 90 | } 91 | 92 | .loading { 93 | position: absolute; 94 | border: none; 95 | } 96 | 97 | .running .loading { 98 | display: none; 99 | } -------------------------------------------------------------------------------- /part_2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Visualización de datos con ChartJS 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |

Montañas rusas: los datos

17 |

Visualización de estadísticas globales

18 | 19 |
20 | 21 |

Esperando respuesta de la API...

22 | 23 |
24 |
25 |

Gráfico radial

26 |

Comparativa global

27 | 28 |
29 | 30 |
31 |

Gráfico de área polar

32 |

Montañas rusas por país

33 | 34 |
35 |
36 | 37 |
38 |
39 |

Gráfico lineal

40 |

Construcciones por año

41 | 42 |
43 |
44 | 45 |
46 |
47 |

Gráfico radial

48 |

Altura

49 | 50 |
51 | 52 |
53 |

Gráfico de donut

54 |

Modelos

55 | 56 |
57 | 58 |
59 |

Gráfico de barras

60 |

Fuerza G

61 | 62 |
63 |
64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /part_2/js/chartStyles.js: -------------------------------------------------------------------------------- 1 | const styles = { 2 | color: { 3 | solids: ['rgba(116, 72, 194, 1)', 'rgba(33, 192, 215, 1)', 'rgba(217, 158, 43, 1)', 'rgba(205, 58, 129, 1)', 'rgba(156, 153, 204, 1)', 'rgba(225, 78, 202, 1)'], 4 | alphas: ['rgba(116, 72, 194, .2)', 'rgba(33, 192, 215, .2)', 'rgba(217, 158, 43, .2)', 'rgba(205, 58, 129, .2)', 'rgba(156, 153, 204, .2)', 'rgba(225, 78, 202, .2)'] 5 | } 6 | } -------------------------------------------------------------------------------- /part_2/js/script.js: -------------------------------------------------------------------------------- 1 | // Defaults 2 | Chart.defaults.global.defaultFontColor = '#fff' 3 | Chart.defaults.global.elements.line.borderWidth = 1 4 | Chart.defaults.global.elements.rectangle.borderWidth = 1 5 | Chart.defaults.scale.gridLines.color = '#444' 6 | Chart.defaults.scale.ticks.display = false 7 | 8 | 9 | 10 | fetch('https://coasters-api.herokuapp.com/') 11 | .then(response => response.json()) 12 | .then(data => printCharts(data)) 13 | 14 | 15 | function printCharts(coasters) { 16 | 17 | document.body.classList.add('running') 18 | 19 | compareRadialChart(coasters, 'chart2') 20 | modelDoughnutChart(coasters, 'chart4') 21 | heightRadarChart(coasters, 'chart3') 22 | GForceBarsChart(coasters, 'chart5') 23 | countriesRadarChart(coasters, 'chart1') 24 | yearsBarChart(coasters, 'chart6') 25 | 26 | } 27 | 28 | 29 | function compareRadialChart(coasters, id) { 30 | 31 | const data = { 32 | labels: ['EEUU', 'UK', 'España', 'Japón', 'China'], 33 | datasets: [ 34 | { 35 | data: [ 36 | coasters.filter(eachCoaster => eachCoaster.country === 'United States').length, 37 | coasters.filter(eachCoaster => eachCoaster.country === 'United Kingdom').length, 38 | coasters.filter(eachCoaster => eachCoaster.country === 'Spain').length, 39 | coasters.filter(eachCoaster => eachCoaster.country === 'Japan').length, 40 | coasters.filter(eachCoaster => eachCoaster.country === 'China').length 41 | ], 42 | borderWidth: 1, 43 | borderColor: styles.color.solids.map(eachColor => eachColor), 44 | backgroundColor: styles.color.alphas.map(eachColor => eachColor) 45 | } 46 | ] 47 | } 48 | 49 | const options = { 50 | legend: { 51 | position: 'right' 52 | } 53 | } 54 | 55 | new Chart(id, { type: 'polarArea', data, options }) 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | function modelDoughnutChart(coasters, id) { 66 | 67 | const data = { 68 | labels: ['Propulsada', 'Hiper montaña', 'Giga montaña', 'Inversión', 'Sentado'], 69 | datasets: [ 70 | { 71 | data: [ 72 | coasters.filter(eachCoaster => eachCoaster.model === 'Accelerator Coaster').length, 73 | coasters.filter(eachCoaster => eachCoaster.model === 'Hyper Coaster').length, 74 | coasters.filter(eachCoaster => eachCoaster.model === 'Giga Coaster').length, 75 | coasters.filter(eachCoaster => eachCoaster.model === 'Multi Inversion Coaster').length, 76 | coasters.filter(eachCoaster => eachCoaster.model === 'Sitting Coaster').length 77 | ], 78 | borderColor: styles.color.solids.map(eachColor => eachColor), 79 | backgroundColor: styles.color.alphas.map(eachColor => eachColor), 80 | borderWidth: 1 81 | } 82 | ] 83 | } 84 | 85 | const options = { 86 | legend: { 87 | position: 'right' 88 | } 89 | } 90 | 91 | new Chart(id, { type: 'doughnut', data, options }) 92 | } 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | function heightRadarChart(coasters, id) { 101 | 102 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.height > 80) 103 | 104 | const data = { 105 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name), 106 | datasets: [ 107 | { 108 | label: 'Altura', 109 | data: selectedCoasters.map(eachCoaster => eachCoaster.height), 110 | borderColor: styles.color.solids[0], 111 | borderWidth: 1 112 | } 113 | ] 114 | } 115 | 116 | const options = { 117 | legend: { 118 | display: false 119 | } 120 | } 121 | 122 | new Chart(id, { type: 'radar', data, options }) 123 | } 124 | 125 | 126 | 127 | function GForceBarsChart(coasters, id) { 128 | 129 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.gForce) 130 | 131 | const data = { 132 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name), 133 | datasets: [{ 134 | data: selectedCoasters.map(eachCoaster => eachCoaster.gForce), 135 | backgroundColor: styles.color.alphas, 136 | borderColor: styles.color.solids 137 | }] 138 | } 139 | 140 | const options = { 141 | legend: { 142 | display: false 143 | }, 144 | scales: { 145 | yAxes: [{ 146 | gridLines: { 147 | display: false 148 | }, 149 | ticks: { 150 | display: true 151 | } 152 | }] 153 | } 154 | } 155 | 156 | new Chart(id, { type: 'bar', data, options }) 157 | 158 | } 159 | 160 | 161 | 162 | function countriesRadarChart(coasters, id) { 163 | 164 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.gForce) 165 | 166 | const data = { 167 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name), 168 | datasets: [ 169 | { 170 | label: 'Altura', 171 | data: selectedCoasters.map(eachCoaster => eachCoaster.height), 172 | borderColor: styles.color.solids[0], 173 | backgroundColor: styles.color.alphas[0] 174 | }, 175 | { 176 | label: 'Longitud', 177 | data: selectedCoasters.map(eachCoaster => eachCoaster.length), 178 | borderColor: styles.color.solids[1], 179 | backgroundColor: styles.color.alphas[1], 180 | hidden: true 181 | }, 182 | { 183 | label: 'Inversiones', 184 | data: selectedCoasters.map(eachCoaster => eachCoaster.inversions), 185 | borderColor: styles.color.solids[2], 186 | backgroundColor: styles.color.alphas[2] 187 | }, 188 | { 189 | label: 'Velocidad', 190 | data: selectedCoasters.map(eachCoaster => eachCoaster.speed), 191 | borderColor: styles.color.solids[3], 192 | backgroundColor: styles.color.alphas[3] 193 | }, 194 | { 195 | label: 'Fuerza G', 196 | data: selectedCoasters.map(eachCoaster => eachCoaster.gForce), 197 | borderColor: styles.color.solids[4], 198 | backgroundColor: styles.color.alphas[4] 199 | } 200 | ] 201 | } 202 | 203 | const options = { 204 | legend: { 205 | position: 'left' 206 | } 207 | } 208 | 209 | new Chart(id, { type: 'radar', data, options }) 210 | } 211 | 212 | 213 | function yearsBarChart(coasters, id) { 214 | 215 | 216 | const data = { 217 | labels: ['1995-1997', '1998-2000', '2001-2003', '2004-2006', '2007-2009', '2013-2015', '2016-2018', '2019-2021'], 218 | datasets: [ 219 | { 220 | label: 'Montañas creadas', 221 | borderColor: styles.color.solids[5], 222 | data: [ 223 | coasters.filter(eachCoaster => eachCoaster.year >= 1995 && eachCoaster.year <= 1997).length, 224 | coasters.filter(eachCoaster => eachCoaster.year >= 1998 && eachCoaster.year <= 2000).length, 225 | coasters.filter(eachCoaster => eachCoaster.year >= 2001 && eachCoaster.year <= 2003).length, 226 | coasters.filter(eachCoaster => eachCoaster.year >= 2004 && eachCoaster.year <= 2006).length, 227 | coasters.filter(eachCoaster => eachCoaster.year >= 2007 && eachCoaster.year <= 2009).length, 228 | coasters.filter(eachCoaster => eachCoaster.year >= 2010 && eachCoaster.year <= 2012).length, 229 | coasters.filter(eachCoaster => eachCoaster.year >= 2013 && eachCoaster.year <= 2015).length, 230 | coasters.filter(eachCoaster => eachCoaster.year >= 2016 && eachCoaster.year <= 2018).length, 231 | coasters.filter(eachCoaster => eachCoaster.year >= 2019 && eachCoaster.year <= 2021).length 232 | ] 233 | }, 234 | { 235 | type: 'bar', 236 | label: 'Aceleración', 237 | borderColor: styles.color.solids[3], 238 | backgroundColor: styles.color.solids[3], 239 | data: [ 240 | coasters.filter(eachCoaster => eachCoaster.year >= 1995 && eachCoaster.year <= 1997 && eachCoaster.model === 'Hyper Coaster').length, 241 | coasters.filter(eachCoaster => eachCoaster.year >= 1998 && eachCoaster.year <= 2000 && eachCoaster.model === 'Hyper Coaster').length, 242 | coasters.filter(eachCoaster => eachCoaster.year >= 2001 && eachCoaster.year <= 2003 && eachCoaster.model === 'Hyper Coaster').length, 243 | coasters.filter(eachCoaster => eachCoaster.year >= 2004 && eachCoaster.year <= 2006 && eachCoaster.model === 'Hyper Coaster').length, 244 | coasters.filter(eachCoaster => eachCoaster.year >= 2007 && eachCoaster.year <= 2009 && eachCoaster.model === 'Hyper Coaster').length, 245 | coasters.filter(eachCoaster => eachCoaster.year >= 2010 && eachCoaster.year <= 2012 && eachCoaster.model === 'Hyper Coaster').length, 246 | coasters.filter(eachCoaster => eachCoaster.year >= 2013 && eachCoaster.year <= 2015 && eachCoaster.model === 'Hyper Coaster').length, 247 | coasters.filter(eachCoaster => eachCoaster.year >= 2016 && eachCoaster.year <= 2018 && eachCoaster.model === 'Hyper Coaster').length, 248 | coasters.filter(eachCoaster => eachCoaster.year >= 2019 && eachCoaster.year <= 2021 && eachCoaster.model === 'Hyper Coaster').length 249 | ] 250 | } 251 | ] 252 | } 253 | 254 | const options = { 255 | maintainAspectRatio: false, 256 | scaleFontColor: '#fff', 257 | scales: { 258 | yAxes: [{ 259 | ticks: { 260 | display: true 261 | } 262 | }], 263 | xAxes: [{ 264 | barPercentage: 0.4, 265 | ticks: { 266 | display: true 267 | } 268 | }] 269 | } 270 | } 271 | 272 | new Chart(id, { type: 'line', data, options }) 273 | 274 | } 275 | --------------------------------------------------------------------------------