Last active
December 17, 2017 03:11
-
-
Save Zhenmao/ee2fdf7d79f595f577f46fb72b38d551 to your computer and use it in GitHub Desktop.
Area chart with audio control
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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