Skip to content

Instantly share code, notes, and snippets.

@Kamalabot
Last active June 23, 2022 16:53
Show Gist options
  • Save Kamalabot/4d6bfe4fe2bd7149abefd4dd3c9e2407 to your computer and use it in GitHub Desktop.
Save Kamalabot/4d6bfe4fe2bd7149abefd4dd3c9e2407 to your computer and use it in GitHub Desktop.
FCC: D3 Tree Map
{
"scripts": [],
"styles": [],
"layout": "splitBottom"
}
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<a href="?data=videogames">Video Game Data Set</a> |
<a href="?data=movies">Movies Data Set</a> |
<a href="?data=kickstarter">Kickstarter Data Set</a>
<h1 id="title"></h1>
<div id="description"></div>
<svg id="tree-map" width="960" height="570"></svg>
<svg id="legend" width="500"></svg>
<script src="script.js"></script>
</body>
</html>
const projectName = 'tree-map';
const DATASETS = {
videogames: {
TITLE: 'Video Game Sales',
DESCRIPTION: 'Top 100 Most Sold Video Games Grouped by Platform',
FILE_PATH:
'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/video-game-sales-data.json'
},
movies: {
TITLE: 'Movie Sales',
DESCRIPTION: 'Top 100 Highest Grossing Movies Grouped By Genre',
FILE_PATH:
'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/movie-data.json'
},
kickstarter: {
TITLE: 'Kickstarter Pledges',
DESCRIPTION:
'Top 100 Most Pledged Kickstarter Campaigns Grouped By Category',
FILE_PATH:
'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/kickstarter-funding-data.json'
}
};
var urlParams = new URLSearchParams(window.location.search);
const DEFAULT_DATASET = 'videogames';
const DATASET = DATASETS[urlParams.get('data') || DEFAULT_DATASET];
// const dataSelector = document.getElementById("data-selector");
// dataSelector.innerHTML = '<a>' + DATASETS[0].TITLE + '</a>' + '/' + '<a>' + DATASETS[1].TITLE + '</a>' + '/' + '<a>' + DATASETS[2].TITLE + '</a>';
document.getElementById('title').innerHTML = DATASET.TITLE;
document.getElementById('description').innerHTML = DATASET.DESCRIPTION;
// Define body
var body = d3.select('body');
// Define the div for the tooltip
var tooltip = body
.append('div')
.attr('class', 'tooltip')
.attr('id', 'tooltip')
.style('opacity', 0);
var svg = d3.select('#tree-map'),
width = +svg.attr('width'),
height = +svg.attr('height');
var fader = function (color) {
return d3.interpolateRgb(color, '#fff')(0.2);
};
var color = d3.scaleOrdinal().range(
// Recreate .schemeCategory20
[
'#1f77b4',
'#aec7e8',
'#ff7f0e',
'#ffbb78',
'#2ca02c',
'#98df8a',
'#d62728',
'#ff9896',
'#9467bd',
'#c5b0d5',
'#8c564b',
'#c49c94',
'#e377c2',
'#f7b6d2',
'#7f7f7f',
'#c7c7c7',
'#bcbd22',
'#dbdb8d',
'#17becf',
'#9edae5'
].map(fader)
);
var treemap = d3.treemap().size([width, height]).paddingInner(1);
d3.json(DATASET.FILE_PATH)
.then((data) => {
var root = d3
.hierarchy(data)
.eachBefore(function (d) {
d.data.id = (d.parent ? d.parent.data.id + '.' : '') + d.data.name;
})
.sum(sumBySize)
.sort(function (a, b) {
return b.height - a.height || b.value - a.value;
});
treemap(root);
var cell = svg
.selectAll('g')
.data(root.leaves())
.enter()
.append('g')
.attr('class', 'group')
.attr('transform', function (d) {
return 'translate(' + d.x0 + ',' + d.y0 + ')';
});
cell
.append('rect')
.attr('id', function (d) {
return d.data.id;
})
.attr('class', 'tile')
.attr('width', function (d) {
return d.x1 - d.x0;
})
.attr('height', function (d) {
return d.y1 - d.y0;
})
.attr('data-name', function (d) {
return d.data.name;
})
.attr('data-category', function (d) {
return d.data.category;
})
.attr('data-value', function (d) {
return d.data.value;
})
.attr('fill', function (d) {
return color(d.data.category);
})
.on('mousemove', function (event, d) {
tooltip.style('opacity', 0.9);
tooltip
.html(
'Name: ' +
d.data.name +
'<br>Category: ' +
d.data.category +
'<br>Value: ' +
d.data.value
)
.attr('data-value', d.data.value)
.style('left', event.pageX + 10 + 'px')
.style('top', event.pageY - 28 + 'px');
})
.on('mouseout', function () {
tooltip.style('opacity', 0);
});
cell
.append('text')
.attr('class', 'tile-text')
.selectAll('tspan')
.data(function (d) {
return d.data.name.split(/(?=[A-Z][^A-Z])/g);
})
.enter()
.append('tspan')
.attr('x', 4)
.attr('y', function (d, i) {
return 13 + i * 10;
})
.text(function (d) {
return d;
});
var categories = root.leaves().map(function (nodes) {
return nodes.data.category;
});
categories = categories.filter(function (category, index, self) {
return self.indexOf(category) === index;
});
var legend = d3.select('#legend');
var legendWidth = +legend.attr('width');
const LEGEND_OFFSET = 10;
const LEGEND_RECT_SIZE = 15;
const LEGEND_H_SPACING = 150;
const LEGEND_V_SPACING = 10;
const LEGEND_TEXT_X_OFFSET = 3;
const LEGEND_TEXT_Y_OFFSET = -2;
var legendElemsPerRow = Math.floor(legendWidth / LEGEND_H_SPACING);
var legendElem = legend
.append('g')
.attr('transform', 'translate(60,' + LEGEND_OFFSET + ')')
.selectAll('g')
.data(categories)
.enter()
.append('g')
.attr('transform', function (d, i) {
return (
'translate(' +
(i % legendElemsPerRow) * LEGEND_H_SPACING +
',' +
(Math.floor(i / legendElemsPerRow) * LEGEND_RECT_SIZE +
LEGEND_V_SPACING * Math.floor(i / legendElemsPerRow)) +
')'
);
});
legendElem
.append('rect')
.attr('width', LEGEND_RECT_SIZE)
.attr('height', LEGEND_RECT_SIZE)
.attr('class', 'legend-item')
.attr('fill', function (d) {
return color(d);
});
legendElem
.append('text')
.attr('x', LEGEND_RECT_SIZE + LEGEND_TEXT_X_OFFSET)
.attr('y', LEGEND_RECT_SIZE + LEGEND_TEXT_Y_OFFSET)
.text(function (d) {
return d;
});
})
.catch((err) => console.log(err));
function sumBySize(d) {
return d.value;
}
const projectName = 'tree-map';
const DATASETS = {
videogames: {
TITLE: 'Video Game Sales',
DESCRIPTION: 'Top 100 Most Sold Video Games Grouped by Platform',
FILE_PATH:
'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/video-game-sales-data.json'
},
movies: {
TITLE: 'Movie Sales',
DESCRIPTION: 'Top 100 Highest Grossing Movies Grouped By Genre',
FILE_PATH:
'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/movie-data.json'
},
kickstarter: {
TITLE: 'Kickstarter Pledges',
DESCRIPTION:
'Top 100 Most Pledged Kickstarter Campaigns Grouped By Category',
FILE_PATH:
'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/kickstarter-funding-data.json'
}
};
console.log(DATASETS)
var urlParams = new URLSearchParams(window.location.search);
const DEFAULT_DATASET = 'videogames';
const DATASET = DATASETS[urlParams.get('data') || DEFAULT_DATASET];
// const dataSelector = document.getElementById("data-selector");
// dataSelector.innerHTML = '<a>' + DATASETS[0].TITLE + '</a>' + '/' + '<a>' + DATASETS[1].TITLE + '</a>' + '/' + '<a>' + DATASETS[2].TITLE + '</a>';
console.log(DATASET.TITLE)
document.getElementById('title').innerHTML = DATASET.TITLE;
document.getElementById('description').innerHTML = DATASET.DESCRIPTION;
// Define body
var body = d3.select('body');
// Define the div for the tooltip
var tooltip = body
.append('div')
.attr('class', 'tooltip')
.attr('id', 'tooltip')
.style('opacity', 0);
var svg = d3.select('#tree-map'),
width = +svg.attr('width'),
height = +svg.attr('height');
var fader = function (color) {
return d3.interpolateRgb(color, '#fff')(0.2);
};
console.log(fader)
var color = d3.scaleOrdinal().range(
// Recreate .schemeCategory20
[
'#1f77b4',
'#aec7e8',
'#ff7f0e',
'#ffbb78',
'#2ca02c',
'#98df8a',
'#d62728',
'#ff9896',
'#9467bd',
'#c5b0d5',
'#8c564b',
'#c49c94',
'#e377c2',
'#f7b6d2',
'#7f7f7f',
'#c7c7c7',
'#bcbd22',
'#dbdb8d',
'#17becf',
'#9edae5'
].map(fader)
);
var treemap = d3.treemap().size([width, height]).paddingInner(1);
d3.json(DATASET.FILE_PATH)
.then((data) => {
var root = d3
.hierarchy(data)
.eachBefore(function (d) {
d.data.id = (d.parent ? d.parent.data.id + '.' : '') + d.data.name;
})
.sum(sumBySize)
.sort(function (a, b) {
return b.height - a.height || b.value - a.value;
});
treemap(root);
var cell = svg
.selectAll('g')
.data(root.leaves())
.enter()
.append('g')
.attr('class', 'group')
.attr('transform', function (d) {
return 'translate(' + d.x0 + ',' + d.y0 + ')';
});
cell
.append('rect')
.attr('id', function (d) {
return d.data.id;
})
.attr('class', 'tile')
.attr('width', function (d) {
return d.x1 - d.x0;
})
.attr('height', function (d) {
return d.y1 - d.y0;
})
.attr('data-name', function (d) {
return d.data.name;
})
.attr('data-category', function (d) {
return d.data.category;
})
.attr('data-value', function (d) {
return d.data.value;
})
.attr('fill', function (d) {
return color(d.data.category);
})
.on('mousemove', function (event, d) {
tooltip.style('opacity', 0.9);
tooltip
.html(
'Name: ' +
d.data.name +
'<br>Category: ' +
d.data.category +
'<br>Value: ' +
d.data.value
)
.attr('data-value', d.data.value)
.style('left', event.pageX + 10 + 'px')
.style('top', event.pageY - 28 + 'px');
})
.on('mouseout', function () {
tooltip.style('opacity', 0);
});
cell
.append('text')
.attr('class', 'tile-text')
.selectAll('tspan')
.data(function (d) {
return d.data.name.split(/(?=[A-Z][^A-Z])/g);
})
.enter()
.append('tspan')
.attr('x', 4)
.attr('y', function (d, i) {
return 13 + i * 10;
})
.text(function (d) {
return d;
});
var categories = root.leaves().map(function (nodes) {
return nodes.data.category;
});
categories = categories.filter(function (category, index, self) {
return self.indexOf(category) === index;
});
var legend = d3.select('#legend');
var legendWidth = +legend.attr('width');
const LEGEND_OFFSET = 10;
const LEGEND_RECT_SIZE = 15;
const LEGEND_H_SPACING = 150;
const LEGEND_V_SPACING = 10;
const LEGEND_TEXT_X_OFFSET = 3;
const LEGEND_TEXT_Y_OFFSET = -2;
var legendElemsPerRow = Math.floor(legendWidth / LEGEND_H_SPACING);
var legendElem = legend
.append('g')
.attr('transform', 'translate(60,' + LEGEND_OFFSET + ')')
.selectAll('g')
.data(categories)
.enter()
.append('g')
.attr('transform', function (d, i) {
return (
'translate(' +
(i % legendElemsPerRow) * LEGEND_H_SPACING +
',' +
(Math.floor(i / legendElemsPerRow) * LEGEND_RECT_SIZE +
LEGEND_V_SPACING * Math.floor(i / legendElemsPerRow)) +
')'
);
});
legendElem
.append('rect')
.attr('width', LEGEND_RECT_SIZE)
.attr('height', LEGEND_RECT_SIZE)
.attr('class', 'legend-item')
.attr('fill', function (d) {
return color(d);
});
legendElem
.append('text')
.attr('x', LEGEND_RECT_SIZE + LEGEND_TEXT_X_OFFSET)
.attr('y', LEGEND_RECT_SIZE + LEGEND_TEXT_Y_OFFSET)
.text(function (d) {
return d;
});
})
.catch((err) => console.log(err));
function sumBySize(d) {
return d.value;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.4/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-scale-chromatic@3.0.0/dist/d3-scale-chromatic.min.js"></script>
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
html,
body {
margin: 0;
padding: 0;
}
svg {
font: 10px sans-serif;
text-align: center;
display: block;
margin: auto;
}
a {
text-decoration: none;
}
body {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
}
.tile-text {
cursor: default;
}
#title {
font-size: 45px;
margin-bottom: 10px;
}
#description {
padding-bottom: 1.5rem;
}
div.tooltip {
position: absolute;
padding: 10px;
font: 12px Arial;
background: rgba(255, 255, 204, 0.95);
box-shadow: 1px 1px 10px rgba(128, 128, 128, 0.6);
border: 0;
border-radius: 2px;
pointer-events: none;
}
#legend {
font-size: 15px;
text-align: center;
display: block;
margin: auto;
padding-bottom: 5px;
margin-bottom: 2rem;
margin-top: 1rem;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment