Built with blockbuilder.org
forked from tomshanley's block: Line chart labels using textPath
forked from tomshanley's block: Line chart labels using textPath
xxxxxxxxxx
<head>
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Oxygen" rel="stylesheet">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="data.js"></script>
<style>
body {
font-family: 'Oxygen', sans-serif;
font-size: 12px
margin: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #222222
}
.line-app {
fill: none;
stroke-width: 4px;
}
</style>
</head>
<body>
<script>
const formatDate = d3.timeFormat("%b %y") // Mar 17
const parseDate = d3.timeParse("%d/%m/%Y") // 1/03/2017
function formatPercentage(n) { return Math.round(n * 100) + "%"; }
appData.forEach(function (d) {
d.parsedDate = parseDate(d.date)
})
let nestedData = d3.nest()
.key(function (d) { return d.app; })
.entries(appData)
//////////////////////////////////////////////////////////////////
const width = 700
const height = 500
const margin = { "top": 50, "bottom": 50, "left": 50, "right": 150, }
let xScale = d3.scaleTime()
.domain(d3.extent(appData, function (d) { return d.parsedDate; }))
.range([0, width])
let yScale = d3.scaleLinear()
.domain([0, 0.5])
.range([height, 0])
let xAxis = d3.axisBottom(xScale)
let yAxis = d3.axisLeft(yScale)
.ticks(5)
let colour = d3.scaleOrdinal()
.domain(["Twitter", "Snapchat", "Facebook", "Instagram"])
.range(["#00aced", "#fffc00", "#3b5998", "#cd486b"])
let backgroundColour = "#222222"
let line = d3.line()
.x(function (d) { return xScale(d.parsedDate); })
.y(function (d) { return yScale(d.percentage); })
.curve(d3.curveCardinal.tension(0.5));
let svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
let g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
//////////////////////////////////////////////////////////////////
let yAxixBoxDate = new Date("1/1/2017")
let yAxixBoxWidth = 30
let yAxixBoxHeight = 20
let axes = g.append("g").attr("class", "axes")
let yAxisG = axes.append("g")
.attr("transform", "translate(0,0)")
.call(yAxis)
let xAxisG = axes.append("g")
.attr("transform", "translate(0," + height + ")")
let xTicks = xAxisG.selectAll(".tick")
.data(nestedData[0].values )
.enter()
.append("g")
.attr("class", "tick")
.attr("transform", function(d){ return "translate(" + xScale(d.parsedDate) + ",0)"; })
xTicks.append("text")
.text(function (d) { return formatDate(d.parsedDate); })
.attr("y", 18)
.style("text-anchor", "middle")
xTicks.append("line")
.attr("y1", -height)
.attr("y2", 1)
yAxisG.selectAll(".tick").selectAll("text")
.text(function (d) { return formatPercentage(d); })
.attr("x", xScale(new Date("1/1/2017")))
.style("text-anchor", "middle")
yAxisG.selectAll(".tick").selectAll("line")
.attr("x1", 0)
.attr("x2", width)
yAxisG.selectAll(".tick").append("rect")
.attr("x", xScale(yAxixBoxDate) - (yAxixBoxWidth/2))
.attr("y", -(yAxixBoxHeight/2))
.attr("width", yAxixBoxWidth)
.attr("height", yAxixBoxHeight)
.style("fill", backgroundColour)
yAxisG.selectAll(".tick").selectAll("text")
.text(function (d) { return formatPercentage(d); })
.attr("x", xScale(new Date("1/1/2017")))
.style("text-anchor", "middle")
.raise()
axes.selectAll(".domain").remove()
axes.selectAll(".tick").selectAll("line")
.style("stroke", "grey")
.style("stroke-dasharray", "2,2")
.style("stroke-linecap", "round")
axes.selectAll(".tick").selectAll("text")
.style("fill", "grey")
//////////////////////////////////////////////////////////////////
let lines = g.append("g").attr("class", "lines")
let app = lines.selectAll("g")
.data(nestedData)
.enter()
.append("g")
.attr("id", function (d) { return d.key })
lines.select("#Snapchat").raise()
app.append("path")
.datum(function (d) { return d.values; })
.attr("d", line)
.attr("class", "line-app")
.attr("id", function(d){ return "line-app-" + d[0].app })
.style("stroke", backgroundColour)
.style("stroke-width", "18")
.style("fill", "none")
let textPaths = app.append("text")
.attr("dy", 5)
.append("textPath")
.text(function (d) {
let thisID = "#line-app-" + d.key
let pathLength = d3.select(thisID).node().getTotalLength()
let label = ""
let n = (pathLength / d.key.length) / 10
for (var i = 0; i < n; i++) { label = label + " " + d.key }
return label.toUpperCase();
})
.attr("xlink:href", function(d){ return "#" + "line-app-" + d.key; })
.style("fill", function (d) {
return d3.rgb(colour(d.key)).darker();
})
app.selectAll("circle")
.data(function (d) { return d.values; })
.enter()
.append("circle")
.attr("cx", function(d){ return xScale(d.parsedDate) })
.attr("cy", function(d){ return yScale(d.percentage) })
.attr("r", 9)
.style("fill", function (d) { return colour(d.app); })
.style("stroke", backgroundColour)
.style("stroke-width", 3)
//////////////////////////////////////////////////////////////////
let duration = 200
var animatePathLabel = setInterval(updatePathLabel, duration);
function updatePathLabel() {
textPaths.text(function(d) {
let t = d3.select(this).text()
let l = t.length - 1
let lastChar = t.slice(-1)
let newLabel = lastChar + t.slice(0, l)
return newLabel
})
}
</script>
</body>
https://d3js.org/d3.v4.min.js