can't get globe to rotate in d3 - svg

I'm trying to get a d3 globe to rotate to a particular country when you click that country in a list. To start out, I'm trying to get the following example working (I got it from http://bl.ocks.org/KoGor/5994804), but it throws an error TypeError: world is undefined from line 100. Can anyone help, please?:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Earth globe</title>
<script src="./d3/d3.v3.min.js"></script>
<script src="./d3/topojson.v1.min.js"></script>
<script src="./d3/queue.v1.min.js"></script>
</head>
<style type="text/css">
.water {
fill: #00248F;
}
.land {
fill: #A98B6F;
stroke: #FFF;
stroke-width: 0.7px;
}
.land:hover {
fill:#33CC33;
stroke-width: 1px;
}
.focused {
fill: #33CC33;
}
select {
position: absolute;
top: 20px;
left: 580px;
border: solid #ccc 1px;
padding: 3px;
box-shadow: inset 1px 1px 2px #ddd8dc;
}
.countryTooltip {
position: absolute;
display: none;
pointer-events: none;
background: #fff;
padding: 5px;
text-align: left;
border: solid #ccc 1px;
color: #666;
font-size: 14px;
font-family: sans-serif;
}
</style>
<body>
<script>
var width = 600,
height = 500,
sens = 0.25,
focused;
//Setting projection
var projection = d3.geo.orthographic()
.scale(245)
.rotate([0, 0])
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path()
.projection(projection);
//SVG container
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Adding water
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "water")
.attr("d", path);
var countryTooltip = d3.select("body").append("div").attr("class", "countryTooltip"),
countryList = d3.select("body").append("select").attr("name", "countries");
queue()
.defer(d3.json, "http://bl.ocks.org/KoGor/raw/5685937/world-110m.json")
.defer(d3.tsv, "http://bl.ocks.org/KoGor/raw/5685937/world-110m-country-names.tsv")
.await(ready);
//Main function
function ready(error, world, countryData) {
var countryById = {},
countries = topojson.feature(world, world.objects.countries).features;
//Adding countries to select
countryData.forEach(function(d) {
countryById[d.id] = d.name;
option = countryList.append("option");
option.text(d.name);
option.property("value", d.id);
});
//Drawing countries on the globe
var world = svg.selectAll("path.land")
.data(countries)
.enter().append("path")
.attr("class", "land")
.attr("d", path)
//Drag event
.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: r[0] / sens, y: -r[1] / sens}; })
.on("drag", function() {
var rotate = projection.rotate();
projection.rotate([d3.event.x * sens, -d3.event.y * sens, rotate[2]]);
svg.selectAll("path.land").attr("d", path);
svg.selectAll(".focused").classed("focused", focused = false);
}))
//Mouse events
.on("mouseover", function(d) {
countryTooltip.text(countryById[d.id])
.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px")
.style("display", "block")
.style("opacity", 1);
})
.on("mouseout", function(d) {
countryTooltip.style("opacity", 0)
.style("display", "none");
})
.on("mousemove", function(d) {
countryTooltip.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px");
});
//Country focus on option select
d3.select("select").on("change", function() {
var rotate = projection.rotate(),
focusedCountry = country(countries, this),
p = d3.geo.centroid(focusedCountry);
svg.selectAll(".focused").classed("focused", focused = false);
//Globe rotating
(function transition() {
d3.transition()
.duration(2500)
.tween("rotate", function() {
var r = d3.interpolate(projection.rotate(), [-p[0], -p[1]]);
return function(t) {
projection.rotate(r(t));
svg.selectAll("path").attr("d", path)
.classed("focused", function(d, i) { return d.id == focusedCountry.id ? focused = d : false; });
};
})
})();
});
function country(cnt, sel) {
for(var i = 0, l = cnt.length; i < l; i++) {
if(cnt[i].id == sel.value) {return cnt[i];}
}
};
};
</script>
</body>
</html>

Related

D3 change map colors on select from dropdown

I am new to d3js and I need some help for my project. I want to my map to change color on dropdown change. I have added two maps in my code, default would be data/world-map.json, and I want to change to data/2000.json when 2000 is selected from dropdown menu. Any help would be appreciated.
Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<title>Project</title>
<style>
.country {
stroke: white;
stroke-width: 0.5px;
}
.country:hover{
stroke: #fff;
stroke-width: 1.5px;
}
.text{
font-size:10px;
text-transform:capitalize;
}
#container {
margin:10px 10%;
border:2px solid #000;
border-radius: 5px;
height:100%;
overflow:hidden;
background: #F0F8FF;
}
.hidden {
display: none;
}
div.tooltip {
color: #222;
background: #fff;
padding: .5em;
text-shadow: #f5f5f5 0 1px 0;
border-radius: 5px;
box-shadow: 0px 0px 2px 0px #a6a6a6;
opacity: 0.9;
position: absolute;
}
.graticule {
fill: none;
stroke: #bbb;
stroke-width: .5px;
stroke-opacity: .5;
}
.equator {
stroke: #ccc;
stroke-width: 1px;
}
</style>
</head>
<body>
<h1 align="center">FIFA PLAYER OF THE YEAR 2000 - 2010</h1>
<select id="select_button" name="select_button">
<option value="0" selected="selected">Select year</option>
<option value="1">2000.</option>
<option value="2">2001.</option>
<option value="3">2002.</option>
<option value="4">2003.</option>
<option value="5">2004.</option>
<option value="6">2005.</option>
<option value="7">2006.</option>
<option value="8">2007.</option>
<option value="9">2008.</option>
<option value="10">2009.</option>
<option value="11">2010.</option>
</select>
<div id="container"></div>
<script src="js/d3.min.js"></script>
<script src="js/topojson.v1.min.js"></script>
<script>
d3.select(window).on("resize", throttle);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 9])
.on("zoom", move);
var width = document.getElementById('container').offsetWidth;
var height = width / 2;
var topo,projection,path,svg,g;
var currentKey = '0';
d3.select('#select_button').on('change', function(a) {
currentKey = d3.select(this).property('value');
draw(topo);
});
var graticule = d3.geo.graticule();
var tooltip = d3.select("#container").append("div").attr("class", "tooltip hidden");
setup(width,height);
function setup(width,height){
projection = d3.geo.mercator()
.translate([(width/2), (height/2)])
.scale( width / 2 / Math.PI);
path = d3.geo.path().projection(projection);
svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom)
.on("click", click)
.append("g");
g = svg.append("g");
}
d3.json("data/world-map.json", function(error, world) {
var countries = topojson.feature(world, world.objects.countries).features;
topo = countries;
draw(topo);
});
d3.json("data/2000.json", function(error, world) {
var countries2 = topojson.feature(world, world.objects.countries).features;
topo = countries2;
draw(topo);
});
d3.selectAll("path").remove();
function draw(topo) {
/* svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
g.append("path")
.datum({type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]]})
.attr("class", "equator")
.attr("d", path);
*/
var country = g.selectAll(".country").data(topo);
country.enter().insert("path")
.attr("class", "country")
.attr("d", path)
.attr("id", function(d,i) { return d.id; })
.attr("title", function(d,i) { return d.properties.name; })
.style("fill", function(d, i) { return d.properties.color; });
//offsets for tooltips
var offsetL = document.getElementById('container').offsetLeft+20;
var offsetT = document.getElementById('container').offsetTop+10;
//tooltips
country
.on("mousemove", function(d,i) {
var mouse = d3.mouse(svg.node()).map( function(d) { return parseInt(d); } );
tooltip.classed("hidden", false)
.attr("style", "left:"+(mouse[0]+offsetL)+"px;top:"+(mouse[1]+offsetT)+"px")
.html(d.properties.name);
})
.on("mouseout", function(d,i) {
tooltip.classed("hidden", true);
});
/*
//EXAMPLE: adding some capitals from external CSV file
d3.csv("data/country-capitals.csv", function(err, capitals) {
capitals.forEach(function(i){
addpoint(i.CapitalLongitude, i.CapitalLatitude, i.CapitalName );
});
}); */
}
function redraw() {
width = document.getElementById('container').offsetWidth;
height = width / 2;
d3.select('svg').remove();
setup(width,height);
draw(topo);
}
function move() {
var t = d3.event.translate;
var s = d3.event.scale;
zscale = s;
var h = height/4;
t[0] = Math.min(
(width/height) * (s - 1),
Math.max( width * (1 - s), t[0] )
);
t[1] = Math.min(
h * (s - 1) + h * s,
Math.max(height * (1 - s) - h * s, t[1])
);
zoom.translate(t);
g.attr("transform", "translate(" + t + ")scale(" + s + ")");
//adjust the country hover stroke width based on zoom level
d3.selectAll(".country").style("stroke-width", 0.5 / s);
}
var throttleTimer;
function throttle() {
window.clearTimeout(throttleTimer);
throttleTimer = window.setTimeout(function() {
redraw();
}, 200);
}
//geo translation on mouse click in map
function click() {
var latlon = projection.invert(d3.mouse(this));
console.log(latlon);
}
/*
//function to add points and text to the map (used in plotting capitals)
function addpoint(lat,lon,text) {
var gpoint = g.append("g").attr("class", "gpoint");
var x = projection([lat,lon])[0];
var y = projection([lat,lon])[1];
gpoint.append("svg:circle")
.attr("cx", x)
.attr("cy", y)
.attr("class","point")
.attr("r", 1.5);
//conditional in case a point has no associated text
if(text.length>0){
gpoint.append("text")
.attr("x", x+2)
.attr("y", y+2)
.attr("class","text")
.text(text);
}
}*/
</script>
</body>
</html>

How to align points on the outer edges of polygon for thicker border in svg?

My question is:
I have a polygon which is drawn by taking some points and I am showing the points on the edges/outline of polygon. When I increase the stroke-width for the polygon the points getting aligned in the center of the outline. How do we achieve the points alignment on the outside of the edge/border?
Actual how it come is like below picture, the points are center aligned across the border/stroke
working area link for this is
https://codepen.io/jinata92/pen/JjKZeqE
I am looking for a solution like as below picture, The points should be aligned on the outer edges of border/stroke
var height = 100,
width = 100;
var polygon;
var arrVertexes = [
[6, 6],
[94, 6],
[94, 94],
[6, 94]
];
var svg, gContainer;
function config() {
svg = d3
.select(".main")
.attr("width", width)
.attr("height", height)
.attr("viewBox", `0 0 ${width} ${height}`);
gContainer = svg.select("g");
}
function drawPolygon() {
polygon = gContainer
.append("polygon")
.attr("points", arrVertexes)
.attr("class", "segment");
}
function drawCircle() {
gContainer
.selectAll("circle")
.data(arrVertexes)
.enter()
.append("circle")
.attr("class", "vertex")
.classed("handle", true)
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", 4);
}
config();
drawPolygon();
drawCircle();
body {
background-color: grey;
}
svg {
position: absolute;
overflow: visible;
}
.resize-div {
position: relative;
overflow: visible;
}
.polygon {
stroke: yellow;
fill: transparent;
}
.vertex,
.dot {
fill: black;
stroke: none;
}
.segment {
stroke-width: 30;
stroke: yellow;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div style="width:100px;height:100px; left: 400px;
top:50px;" class="resize-div">
<svg class="main" height="100%" width="100%">
<g class="polygon"></g>
</svg>
</div>
Stroke-width is always applied to both sides, so the centre of the line is still at the required place. But there are some workarounds. One is to just draw the shape, but also apply a clip path equal to the element, but without the stroke-width:
var height = 100,
width = 100;
var polygon;
var arrVertexes = [
[6, 6],
[94, 6],
[94, 94],
[6, 94]
];
var svg, gContainer;
function config() {
svg = d3
.select(".main")
.attr("width", width)
.attr("height", height)
.attr("viewBox", `0 0 ${width} ${height}`);
gContainer = svg.select("g");
}
function drawPolygon() {
polygon = gContainer
.append("polygon")
.attr("clip-path", "url('#my-clip-path')")
.attr("points", arrVertexes)
.attr("class", "segment");
// Append a clip path
svg.append("defs")
.append("clipPath")
.attr("id", "my-clip-path")
.append("polygon")
.attr("points", arrVertexes)
.attr("class", "segment");
}
function drawCircle() {
gContainer
.selectAll("circle")
.data(arrVertexes)
.enter()
.append("circle")
.attr("class", "vertex")
.classed("handle", true)
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", 4);
}
config();
drawPolygon();
drawCircle();
body {
background-color: grey;
}
svg {
position: absolute;
overflow: visible;
}
.resize-div {
position: relative;
overflow: visible;
}
.polygon {
stroke: yellow;
fill: transparent;
}
.vertex,
.dot {
fill: black;
stroke: none;
}
.segment {
stroke-width: 30;
stroke: yellow;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div style="width:100px;height:100px; left: 400px;
top:50px;" class="resize-div">
<svg class="main" height="100%" width="100%">
<g class="polygon"></g>
</svg>
</div>
Alternatively, you could draw a polygon on top of the other, but inwards a little bit. Use the bounding box to calculate the centre of the outer polygon, and move the coordinates of the inner polygon inwards by some number of pixels:
var height = 100,
width = 100;
var polygon;
var arrVertexes = [
[6, 6],
[94, 6],
[94, 94],
[6, 94]
];
var svg, gContainer;
function config() {
svg = d3
.select(".main")
.attr("width", width)
.attr("height", height)
.attr("viewBox", `0 0 ${width} ${height}`);
gContainer = svg.select("g");
}
function drawPolygon() {
polygon = gContainer
.append("polygon")
.attr("points", arrVertexes)
.attr("class", "segment-border");
// Get the bounding box so you can calculate the centre
const boundingBox = polygon.node().getBBox();
const centre = {
x: boundingBox.x + boundingBox.width / 2,
y: boundingBox.y + boundingBox.height / 2,
};
const innerVertexes = arrVertexes.map(d => [
d[0] < centre.x ? d[0] + 15 : d[0] - 15,
d[1] < centre.y ? d[1] + 15 : d[1] - 15,
]);
polygon = gContainer
.append("polygon")
.attr("points", innerVertexes)
.attr("class", "segment");
}
function drawCircle() {
gContainer
.selectAll("circle")
.data(arrVertexes)
.enter()
.append("circle")
.attr("class", "vertex")
.classed("handle", true)
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", 4);
}
config();
drawPolygon();
drawCircle();
body {
background-color: grey;
}
svg {
position: absolute;
overflow: visible;
}
.resize-div {
position: relative;
overflow: visible;
}
.polygon {
stroke: yellow;
fill: transparent;
}
.vertex,
.dot {
fill: black;
stroke: none;
}
.segment-border {
fill: yellow;
}
.segment {
fill: white;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div style="width:100px;height:100px; left: 400px;
top:50px;" class="resize-div">
<svg class="main" height="100%" width="100%">
<g class="polygon"></g>
</svg>
</div>

Live graphing of data with d3 using real data

I have seen lots of great demos for live graphing of data using D3.
http://bl.ocks.org/simenbrekken/6634070 is one I like. However, all of the examples I have seen use random generated values. I want to graph live data, and display the most recent values as an updating numeric display. I use a python script which writes data from sensor readings to csv files. The csv is 3 values on each line: unixtime,sensor1_value,sensor2_value. Every 5 seconds there is a new line of data added to a ring buffer file which has 720 lines of data. When the web page is displayed I want to read the 720 lines in the buffer file then update the graph with each new value which is written onto the end of the file. I could also create a file with just the new line of csv every 5 seconds so that the update was performed by always reading a file with just 1 line of csv rather than manipulating the entire buffer.
Does anyone know of an example, or the right code to achieve this?
The code for the above cited example is:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.graph .axis {
stroke-width: 1;
}
.graph .axis .tick line {
stroke: black;
}
.graph .axis .tick text {
fill: black;
font-size: 0.7em;
}
.graph .axis .domain {
fill: none;
stroke: black;
}
.graph .group {
fill: none;
stroke: black;
stroke-width: 1.5;
}
</style>
</head>
<body>
<div class="graph"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var limit = 60 * 1,
duration = 750,
now = new Date(Date.now() - duration)
var width = 500,
height = 200
var groups = {
current: {
value: 0,
color: 'orange',
data: d3.range(limit).map(function() {
return 0
})
},
target: {
value: 0,
color: 'green',
data: d3.range(limit).map(function() {
return 0
})
},
output: {
value: 0,
color: 'grey',
data: d3.range(limit).map(function() {
return 0
})
}
}
var x = d3.time.scale()
.domain([now - (limit - 2), now - duration])
.range([0, width])
var y = d3.scale.linear()
.domain([0, 100])
.range([height, 0])
var line = d3.svg.line()
.interpolate('basis')
.x(function(d, i) {
return x(now - (limit - 1 - i) * duration)
})
.y(function(d) {
return y(d)
})
var svg = d3.select('.graph').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height + 50)
var axis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(x.axis = d3.svg.axis().scale(x).orient('bottom'))
var paths = svg.append('g')
for (var name in groups) {
var group = groups[name]
group.path = paths.append('path')
.data([group.data])
.attr('class', name + ' group')
.style('stroke', group.color)
}
function tick() {
now = new Date()
// Add new values
for (var name in groups) {
var group = groups[name]
//group.data.push(group.value) // Real values arrive at irregular intervals
group.data.push(20 + Math.random() * 100)
group.path.attr('d', line)
}
// Shift domain
x.domain([now - (limit - 2) * duration, now - duration])
// Slide x-axis left
axis.transition()
.duration(duration)
.ease('linear')
.call(x.axis)
// Slide paths left
paths.attr('transform', null)
.transition()
.duration(duration)
.ease('linear')
.attr('transform', 'translate(' + x(now - (limit - 1) * duration) + ')')
.each('end', tick)
// Remove oldest data point from each group
for (var name in groups) {
var group = groups[name]
group.data.shift()
}
}
tick()
</script>
</body>
</html>
Whereas the code I use for creating a static graph of one of the values (o2) from my csv versus a line:
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 900 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Set the ranges
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.o2); });
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.csv("./data/buffer.txt", function(error, data) {
data.forEach(function(d) {
d.time = +d.time;
d.o2 = +d.o2;
console.log(d.time);
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.o2; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>

Audio Midi API- How to remove change in pitch from audio samples?

I've been messing around with tutorial on midi and triggering samples. However I can't figure out how to remove the random pitch change of the samples in the code. Any idea how to remove the pitch change?
link to tutorial:http://www.keithmcmillen.com/blog/making-music-in-the-browser-web-midi-api/
<html>
<head>
<meta charset="UTF-8">
<title>Web MIDI API</title>
<style>
*:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
h4 {
margin: 0 0 5px 0;
}
p {
margin: 0 0 10px 0;
}
#content, #device_info {
max-width: 800px;
margin: 0 auto;
padding: 10px 0;
font-family: sans-serif;
font-size: 12px;
line-height: 12px;
letter-spacing: 1.5px;
}
#content, #key_data {
margin-top: 0px;
text-align: center;
}
#inputs, #outputs {
display: inline-block;
width: 49%;
margin-top: 10px;
vertical-align: top;
}
#outputs {
text-align: right;
}
.info {
padding: 20px;
border-radius: 3px;
box-shadow: inset 0 0 10px #ccc;
background-color: rgba(233,233,233,0.25);
}
.small {
border-bottom: 1px solid #ccc;
margin-left: 10px;
}
p:not(.small){
text-transform: uppercase;
font-weight: 800;
}
.button {
display: inline-block;
width: 100px;
height: 100px;
margin: 10px;
background-color: #00adef;
border-radius: 10px;
opacity: 1;
cursor: pointer;
border: 2px solid white;
transition: all 0.2s;
}
.button.active {
background-color: #9a6aad;
opacity: 0.25;
box-shadow: inset 0px 0px 30px orange;
border: 2px solid rgba(100,100,100,0.3);
animation: shake .2s ease-in-out;
}
#keyframes shake {
0% {
transform: translateX(0);
transform: translateY(0);
transform: scale(1,1);
}
20% {
transform: translateX(-10px);
transform: translateY(-100px);
transform: scale(0.5,0.75);
}
40% {
transform: translateX(10px);
transform: translateY(0px);
transform: scale(1.5,2);
}
60% {
transform: translateX(-10px);
transform: translateY(-50px);
transform: scale(0.5,0.75);
}
80% {
transform: translateX(10px);
transform: translateY(50px);
transform: scale(1.3,2);
}
100% {
transform: translateX(0);
transform: translateY(0);
}
}
</style>
</head>
<body>
<div id="content">
<div class="button" data-key="q" data-sound="audio/Slice1.mp3"></div>
<div class="button" data-key="w" data-sound="audio/dinky-snare.mp3"></div>
<div class="button" data-key="e" data-sound="audio/dinky-hat-2.mp3"></div>
<div class="button" data-key="r" data-sound="audio/dinky-cym.mp3"></div>
<div class="button" data-key="t" data-sound="audio/dinky-cym-noise.mp3"></div>
</div>
<div id="device_info">
<div id="key_data"></div>
<div id="inputs"></div>
<div id="outputs"></div>
</div>
<script>
//http://stackoverflow.com/questions/23687635/how-to-stop-audio-in-an-iframe-using-web-audio-api-after-hiding-its-container-di
(function(){
var log = console.log.bind(console), keyData = document.getElementById('key_data'),
deviceInfoInputs = document.getElementById('inputs'), deviceInfoOutputs = document.getElementById('outputs'), midi;
var AudioContext = AudioContext || webkitAudioContext; // for ios/safari
var context = new AudioContext();
var activeNotes = [];
var btnBox = document.getElementById('content'), btn = document.getElementsByClassName('button');
var data, cmd, channel, type, note, velocity;
// request MIDI access
if(navigator.requestMIDIAccess){
navigator.requestMIDIAccess({sysex: false}).then(onMIDISuccess, onMIDIFailure);
}
else {
alert("No MIDI support in your browser.");
}
// add event listeners
document.addEventListener('keydown', keyController);
document.addEventListener('keyup', keyController);
for(var i = 0; i < btn.length; i++){
btn[i].addEventListener('mousedown', clickPlayOn);
btn[i].addEventListener('mouseup', clickPlayOff);
}
// prepare audio files
for(var i = 0; i < btn.length; i++){
addAudioProperties(btn[i]);
}
var sampleMap = {
key60: 1,
key61: 2,
key62: 3,
key63: 4,
key64: 5
};
// user interaction
function clickPlayOn(e){
e.target.classList.add('active');
e.target.play();
}
function clickPlayOff(e){
e.target.classList.remove('active');
}
function keyController(e){
if(e.type == "keydown"){
switch(e.keyCode){
case 81:
btn[0].classList.add('active');
btn[0].play();
break;
case 87:
btn[1].classList.add('active');
btn[1].play();
break;
case 69:
btn[2].classList.add('active');
btn[2].play();
break;
case 82:
btn[3].classList.add('active');
btn[3].play();
break;
case 84:
btn[4].classList.add('active');
btn[4].play();
break;
default:
//console.log(e);
}
}
else if(e.type == "keyup"){
switch(e.keyCode){
case 81:
btn[0].classList.remove('active');
break;
case 87:
btn[1].classList.remove('active');
break;
case 69:
btn[2].classList.remove('active');
break;
case 82:
btn[3].classList.remove('active');
break;
case 84:
btn[4].classList.remove('active');
break;
default:
//console.log(e.keyCode);
}
}
}
// midi functions
function onMIDISuccess(midiAccess){
midi = midiAccess;
var inputs = midi.inputs.values();
// loop through all inputs
for(var input = inputs.next(); input && !input.done; input = inputs.next()){
// listen for midi messages
input.value.onmidimessage = onMIDIMessage;
listInputs(input);
}
// listen for connect/disconnect message
midi.onstatechange = onStateChange;
showMIDIPorts(midi);
}
function onMIDIMessage(event){
data = event.data,
cmd = data[0] >> 4,
channel = data[0] & 0xf,
type = data[0] & 0xf0, // channel agnostic message type. Thanks, Phil Burk.
note = data[1],
velocity = data[2];
// with pressure and tilt off
// note off: 128, cmd: 8
// note on: 144, cmd: 9
// pressure / tilt on
// pressure: 176, cmd 11:
// bend: 224, cmd: 14
log('MIDI data', data);
switch(type){
case 144: // noteOn message
noteOn(note, velocity);
break;
case 128: // noteOff message
noteOff(note, velocity);
break;
}
//log('data', data, 'cmd', cmd, 'channel', channel);
logger(keyData, 'key data', data);
}
function onStateChange(event){
showMIDIPorts(midi);
var port = event.port, state = port.state, name = port.name, type = port.type;
if(type == "input")
log("name", name, "port", port, "state", state);
}
function listInputs(inputs){
var input = inputs.value;
log("Input port : [ type:'" + input.type + "' id: '" + input.id +
"' manufacturer: '" + input.manufacturer + "' name: '" + input.name +
"' version: '" + input.version + "']");
}
function noteOn(midiNote, velocity){
player(midiNote, velocity);
}
function noteOff(midiNote, velocity){
player(midiNote, velocity);
}
function player(note, velocity){
var sample = sampleMap['key'+note];
if(sample){
if(type == (0x80 & 0xf0) || velocity == 0){ //needs to be fixed for QuNexus, which always returns 144
btn[sample - 1].classList.remove('active');
return;
}
btn[sample - 1].classList.add('active');
btn[sample - 1].play(velocity);
}
}
function onMIDIFailure(e){
log("No access to MIDI devices or your browser doesn't support WebMIDI API. Please use WebMIDIAPIShim " + e);
}
// MIDI utility functions
function showMIDIPorts(midiAccess){
var inputs = midiAccess.inputs,
outputs = midiAccess.outputs,
html;
html = '<h4>MIDI Inputs:</h4><div class="info">';
inputs.forEach(function(port){
html += '<p>' + port.name + '<p>';
html += '<p class="small">connection: ' + port.connection + '</p>';
html += '<p class="small">state: ' + port.state + '</p>';
html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
if(port.version){
html += '<p class="small">version: ' + port.version + '</p>';
}
});
deviceInfoInputs.innerHTML = html + '</div>';
html = '<h4>MIDI Outputs:</h4><div class="info">';
outputs.forEach(function(port){
html += '<p>' + port.name + '<br>';
html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
if(port.version){
html += '<p class="small">version: ' + port.version + '</p>';
}
});
deviceInfoOutputs.innerHTML = html + '</div>';
}
// audio functions
function loadAudio(object, url){
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(){
context.decodeAudioData(request.response, function(buffer){
object.buffer = buffer;
});
}
request.send();
}
function addAudioProperties(object){
object.name = object.id;
object.source = object.dataset.sound;
loadAudio(object, object.source);
object.play = function(volume){
var s = context.createBufferSource();
var g = context.createGain();
var v;
s.buffer = object.buffer;
s.playbackRate.value = randomRange(0.5, 2);
if(volume){
v = rangeMap(volume, 1, 127, 0.2, 2);
s.connect(g);
g.gain.value = v * v;
g.connect(context.destination);
}
else{
s.connect(context.destination);
}
s.start();
object.s = s;
}
}
// utility functions
function randomRange(min, max){
return Math.random() * (max + min) + min;
}
function rangeMap(x, a1, a2, b1, b2){
return ((x - a1)/(a2-a1)) * (b2 - b1) + b1;
}
//function frequencyFromNoteNumber( note ) {
// return 440 * Math.pow(2,(note-69)/12);
//}
function logger(container, label, data){
messages = label + " [channel: " + (data[0] & 0xf) + ", cmd: " + (data[0] >> 4) + ", type: " + (data[0] & 0xf0) + " , note: " + data[1] + " , velocity: " + data[2] + "]";
container.textContent = messages;
}
})();
</script>
</body>
</html>
There is a line in the addAudioProperties() function which sets the playback rate to random every time. You can remove this by setting the playback rate to 1:
s.playbackRate.value = randomRange(0.5, 2);
becomes
s.playbackRate.value = 1;

Creating clickable legends with D3

I'm trying to create a multi-line chart with D3, but I'm stuck on toggling the visibility of lines off. So far, there is only one line, whilst I try to figure it all out (still a bit of a beginner with this), but I can't get the legend to appear, so I can't test if it'll actually get rid of the line too. Here is the JavaScript code:
var BlackBird = [{
"population": "100",
"year": "1970"
}, {
"population": "100.8",
"year": "1971"
}, {
"population": "103.5",
"year": "1972"
}, {
"population": "95.6",
"year": "1973"
}, {
"population": "101.7",
"year": "1974"
}, {
"population": "102",
"year": "1975"
}
];
var vis = d3.select("#visualisation"),
WIDTH = 1110,
HEIGHT = 580,
MARGINS = {
top: 30,
right: 20,
bottom: 20,
left: 50
},
xScale = d3.scale.linear()
.range([MARGINS.left, WIDTH - MARGINS.right])
.domain([1970,2008]),
yScale = d3.scale.linear()
.range([HEIGHT - MARGINS.top, MARGINS.bottom])
.domain([0,300]),
xAxis = d3.svg.axis()
.scale(xScale)
.ticks(25)
.tickFormat(d3.format('0f')),
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(12);
vis.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis);
vis.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
var lineGen = d3.svg.line()
.x(function(d) {
return xScale(d.year);
})
.y(function(d) {
return yScale(d.population);
})
.interpolate("basis");
vis.append('path')
.attr('d', lineGen(BlackBird))
.attr('stroke-width', 5)
.attr('fill', 'none')
.attr('opacity', '0.2')
.attr("id", "aline");
vis.append("text")
.attr("x", WIDTH + MARGINS.left +10)
.attr("y", 10)
.attr("class", "legend")
.style("fill", "steelblue")
.on("click", function(){
var active = aline.active ? false : true,
newOpacity = active ? 0 : 1;
d3.select("#aline").attr("opacity", newOpacity);
aline.active = active;
})
.text("Blue Line");
HTML:
<!DOCTYPE html> <html lang= "en"> <head> <meta charset="UTF-8"> <title>D3 Birds</title> <link rel="stylesheet" href="D3Bird2.css"> </head> <body> <svg id="visualisation" width="1140" height="600"></svg> <div id ="BlaBird"> <img src="Blackbird.png" alt="A Blackbird" class= "Birdie"> </div> <script src="d3.min.js" charset="utf-8"> </script> <script src="script2.js" charset="utf-8"></script> </body> </html>
CSS:
.axis path {
fill: none;
stroke: #777;
shape-rendering: crispEdges;
}
.axis text {
font-family: Lato;
font-size: 13px;
}
#aline {
stroke: #000;
opacity: 0.5;
transition: 0.5s;
}
#aline: hover {
opacity: 1;
transition: 0.5s;
}
.Birdie {
transition: 0.5s;
opacity: 0.5;
}
#BlaBird {
position: absolute;
left: 1150px;
top: 30px;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: start;
}
First, you didn't include your HTML markup, but I'm assuming #visualisation is an SVG element. Make sure you've assigned it height and width attributes.
Second, you don't assign a stroke color to your line. I'm assuming you do this in CSS somewhere.
Third, after fixing your variable names, this line:
.attr("x", width + margin.left +10)
is the problem. This pushes the text element outside of it's SVG.
Here's an example where I've fixed some of this up.
EDITS
New problem after looking at your CSS. You are setting the attribute opacity in code, the CSS sets the style opacity. When both are set the browser will use the style. So change your click handler to:
.on("click", function() {
var active = aline.active ? false : true,
newOpacity = active ? 0 : 1;
d3.select("#aline").style("opacity", newOpacity); //<-- set style, not attr
aline.active = active;
})
Example updated.

Resources