Skip to content

Instantly share code, notes, and snippets.

@Zhenmao
Last active December 17, 2017 03:11
Show Gist options
  • Save Zhenmao/ee2fdf7d79f595f577f46fb72b38d551 to your computer and use it in GitHub Desktop.
Save Zhenmao/ee2fdf7d79f595f577f46fb72b38d551 to your computer and use it in GitHub Desktop.
Area chart with audio control
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script defer src="https://use.fontawesome.com/releases/v5.0.1/js/all.js"></script>
</head>
<body>
<div id="svg"></div>
<script>
var margin = {top: 50, right: 20, bottom: 50, left: 50},
width = 800 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom,
percent = 0, // Progress percent
method = "pause", // Track play or pause
svg = d3.select("#svg").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom),
g = svg.append("g")
.attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
var x = d3.scaleLinear()
.domain([0, 1])
.rangeRound([0, width]);
var y = d3.scaleLinear()
.domain([0, 1])
.rangeRound([height, 0]);
var area = d3.area()
.x(function(d) { return x(d.percentage); })
.y1(function(d) { return y(d.retention); })
.y0(y(0))
.curve(d3.curveCatmullRom);
var xTime = d3.scaleLinear()
.rangeRound([0, width]);
// Mock data
var data = [
{ percentage: 0, trackName: "Moon River", retention: 0.93 },
{ percentage: 0.1, trackName: "Moon River", retention: 0.64 },
{ percentage: 0.2, trackName: "Moon River", retention: 0.61 },
{ percentage: 0.3, trackName: "Moon River", retention: 0.58 },
{ percentage: 0.4, trackName: "Moon River", retention: 0.56 },
{ percentage: 0.5, trackName: "Moon River", retention: 0.54 },
{ percentage: 0.6, trackName: "Moon River", retention: 0.53 },
{ percentage: 0.7, trackName: "Moon River", retention: 0.52 },
{ percentage: 0.8, trackName: "Moon River", retention: 0.52 },
{ percentage: 0.9, trackName: "Moon River", retention: 0.51 },
{ percentage: 1.0, trackName: "Moon River", retention: 0.49 },
{ percentage: 0, trackName: "Galway Girl", retention: 0.94 },
{ percentage: 0.1, trackName: "Galway Girl", retention: 0.75 },
{ percentage: 0.2, trackName: "Galway Girl", retention: 0.73 },
{ percentage: 0.3, trackName: "Galway Girl", retention: 0.71 },
{ percentage: 0.4, trackName: "Galway Girl", retention: 0.70 },
{ percentage: 0.5, trackName: "Galway Girl", retention: 0.69 },
{ percentage: 0.6, trackName: "Galway Girl", retention: 0.67 },
{ percentage: 0.7, trackName: "Galway Girl", retention: 0.67 },
{ percentage: 0.8, trackName: "Galway Girl", retention: 0.66 },
{ percentage: 0.9, trackName: "Galway Girl", retention: 0.65 },
{ percentage: 1.0, trackName: "Galway Girl", retention: 0.61 }
];
var dataByTrack = d3.nest()
.key(function(d) { return d.trackName; })
.map(data);
var trackName = "Moon River";
var duration = 0;
var currentTrackData = dataByTrack.get(trackName);
var currentTrackMedia = d3.select("#svg")
.append("audio")
.attr("src", "http://k007.kiwi6.com/hotlink/tfaw4el9c5/Moon_River_Jazz.mp3")
.node();
currentTrackMedia.addEventListener("loadedmetadata", renderXAxisTicks);
currentTrackMedia.addEventListener("timeupdate", updateBarLine);
xTime.domain([0, currentTrackMedia.duration]);
// Song title
svg.append("text")
.text(trackName)
.attr("text-anchor", "start")
.attr("x", 20)
.attr("y", 30)
.style("font-size", 32)
.style("font-weight", "bold");
// Area chart
g.append("path")
.datum(currentTrackData)
.attr("fill", "#FEEED8")
.attr("stroke", "#FBD48B")
.attr("stroke-width", 2)
.attr("d", area);
// Axes
var xAxis = g.append("g")
.attr("transform", "translate(0, " + height + ")")
.attr("class", "x axis");
g.append("g")
.attr("class", "y axis")
.call(customYAxis);
// Progress bar
g.append("g")
.attr("transform", "translate(0," + height + ")")
.append("rect")
.attr("id", "progress-bar")
.attr("height", 8)
.attr("width", percent * width);
// Red line
g.append("line")
.attr("id", "pregress-line")
.attr("x1", percent * width)
.attr("x2", percent * width)
.attr("y1", 0)
.attr("y2", height)
.attr("stroke", "red")
.attr("stroke-width", 1)
.attr("display", "none");
// Icon
var icons = svg.append("g")
.attr("id", "media-buttons")
.attr("transform", "translate(" + (25) + ", " + (height + margin.top + 40) + ")")
.on("click", togglePlaying);
icons.append("text")
.attr("id", "play-button")
.attr("font-family", "FontAwesome")
.attr("text-anchor", "middle")
.style("font-size", 32)
.text("\uf144");
icons.append("text")
.attr("id", "pause-button")
.attr("font-family", "FontAwesome")
.attr("text-anchor", "middle")
.style("font-size", 32)
.text("\uf28b")
.attr("display", "none");
g.append("rect")
.attr("class", "drag-layer")
.attr("width", width)
.attr("height", height)
.attr("fill", "none")
.style("pointer-events", "all")
.call(d3.drag()
.on("start drag", dragging)
.on("end", updateBarLine));
function renderXAxisTicks() {
var duration = currentTrackMedia.duration;
xTime.domain([0, duration]);
xAxis.call(customXAxis);
}
function customXAxis(g) {
g.call(d3.axisBottom(xTime).ticks(10));
g.select(".domain").remove();
g.selectAll(".tick line").remove();
g.selectAll(".tick text")
.attr("dy", 30)
.attr("text-anchor", "start")
.text(function(d) { return convertTime(d); });
}
function customYAxis(g) {
g.call(d3.axisLeft(y).ticks(5, "%"));
g.select(".domain").remove();
g.selectAll(".tick line")
.attr("stroke", "#EEE")
.attr("x1", width)
.attr("x2", 0)
.attr("stroke-width", 1);
}
function togglePlaying() {
if (method === "play") { // currently playing, now pause
d3.select("#play-button").attr("display", "");
d3.select("#pause-button").attr("display", "none");
method = "pause";
} else { // curently not playing, now start
if (percent === 0) {
d3.select("#pregress-line").attr("display", "");
}
d3.select("#play-button").attr("display", "none");
d3.select("#pause-button").attr("display", "");
method = "play";
}
currentTrackMedia[method]();
}
function updateBarLine() {
var currentTime = currentTrackMedia.currentTime,
duration = currentTrackMedia.duration;
if (currentTime === duration) {
d3.select("#pregress-line")
.attr("x1", 0)
.attr("x2", 0)
.attr("display", "none");
percent = 0;
d3.select("#play-button").attr("display", "");
d3.select("#pause-button").attr("display", "none");
method = "pause";
}
percent = currentTime / duration;
d3.select("#progress-bar")
.attr("width", percent * width);
d3.select("#pregress-line")
.attr("x1", percent * width)
.attr("x2", percent * width);
}
function dragging() {
if (percent === 0) { // If not been played yet, no drag
return;
} else {
percent = x.invert(d3.event.x);
currentTrackMedia.currentTime = currentTrackMedia.duration * percent;
}
}
function convertTime(inputSeconds) {
var seconds = Math.floor(inputSeconds % 60);
if (seconds < 10) {
seconds = "0" + seconds;
}
var minutes = Math.floor(inputSeconds / 60);
return minutes + ":" + seconds;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment