Skip to content

Instantly share code, notes, and snippets.

@headwinds
Last active April 16, 2016 20:23
Show Gist options
  • Save headwinds/8457979 to your computer and use it in GitHub Desktop.
Save headwinds/8457979 to your computer and use it in GitHub Desktop.
Orbitting Moons

experimenting with circles and pi

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.experiment {
font-family: Arial, sans-serif;
font-size: 16px;
}
div.container {
display: block;
position: absolute;
top: 0px;
left:0px;
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
}
svg {
display: block;
position: relative;
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
}
.circleText {
fill: #666;
font-family: SequentialistBB, Arial, sans-serif;
}
button {
width: 100px;
height: 34px;
border-radius: 4px;
border: none;
background-color: #ddd;
pointer-events: all;
cursor: pointer;
}
</style>
<body>
<div class="container">
<svg id="stage"></svg>
</div>
<script src="//d3js.org/d3.v4.0.0-alpha.28.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script>
var svg = d3.select("#stage");
// animation
var eases = ["sine", "quad", "cubic", "bounce","back"];
var curEase = eases[3];
var curScale = 1;
// dummy data
var centerX = $(window).width() / 2;
var centerY = $(window).height() / 2;
var mainTitle = "ripley".toUpperCase().split(' ')
var titleA = "bishop".toUpperCase().split(' ');
var titleB = "vasquez".toUpperCase().split(' ');
var titleC = "gorman".toUpperCase().split(' ');
var titleD = "hicks".toUpperCase().split(' ');
var titles = [titleA,titleB,titleC,titleD];
var bubble = {title: "", frequency: 0, trend: 0, x: 0, y: 200, angle: 0};
var mainBubble = _.clone(bubble);
mainBubble.title = mainTitle;
mainBubble.frequency = 600;
mainBubble.trend = 0;
mainBubble.x = 100;
var bubbleData = [mainBubble];
_.times(4, function(index){
var subBubble = _.clone(bubble);
subBubble.title = titles[index];
subBubble.frequency = Math.floor(Math.random() * 500 + 200);
subBubble.trend = index + 1;
subBubble.x = 300 * (index + 1 );
bubbleData.push(subBubble);
})
////////////////////////////// UTILITIES
var getRadius = function( d ){
return d.radius = Math.floor( d.frequency / 4.75 );
}
var getColor = function( trend ){
var colors = ["lightblue", "yellow", "orange", "lightgrey", "pink"];
return colors[trend];
}
var getEase = function(){
var index = Math.floor( Math.random() * eases.length);
return eases[index];
}
var getTextStyle = function (d, textColor) {
var that = this;
var magicNum = 1.7;
var textColor = (undefined !== textColor && null !== textColor) ? textColor : "#000";
//var bubbleRadius = (null !== selRadius ) ? ( selRadius + 20 ) : d.radius;
var longestStrNum = _.max( d.title, function(name){ return name.length; }).length;
// if its 5 or less letters... just use 5 so small words like CAT don't look oddly large
var longestSizeNum = ( longestStrNum <= 5 ) ? 5 : longestStrNum;
var fontSize = Math.round ( d.radius / ( longestSizeNum / magicNum ) );
var styleStr = "font-family: Arial, sans-serif; font-weight:bold; font-size: " + fontSize + "px; fill: " + textColor;
return styleStr;
};
var getFontSize = function( textArray ) {
var textSizeArray = [];
for (var i = 0; i < textArray.length; i++) {
textSizeArray.push( textArray[i].length );
}
return Math.max.apply(Math, textSizeArray);
}
var getDestinationVector = function( originVector, angle, distance) {
var destinationVector = {x: 0, y: 0};
destinationVector.x = Math.round(Math.cos(angle * Math.PI / 180) * distance + originVector.x);
destinationVector.y = Math.round(Math.sin(angle * Math.PI / 180) * distance + originVector.y);
return destinationVector;
}
var getDistance = function(d){
var mainBubbleRadius = bubbleData[0].radius;
var centerPoint = {x: centerX, y: centerY};
var targetRadius = d.radius;
var distance = mainBubbleRadius + targetRadius;
return distance;
}
var getSubBubblePoint = function(d,index){
var angles = [-45, -140, -235, -320];
var centerPoint = {x: centerX, y: centerY};
var distance = getDistance(d);
var angle = angles[index-1];
d.angle = angle;
var targetPoint = getDestinationVector(centerPoint, angle, distance);
return targetPoint;
}
var getOrbitPoint = function(d, direction){
if (direction === "negative") d.angle++;
else d.angle--;
if (d.angle > 360 || d.angle < -360) d.angle = 0;
var angle = d.angle;
var centerPoint = {x: centerX, y: centerY};
var distance = getDistance(d);
var targetPoint = getDestinationVector(centerPoint, angle, distance);
return targetPoint;
}
var getTranslation = function(d,index){
// move 0 to the center;
var bubbleGroupX;
var bubbleGroupY;
switch( d.trend) {
case 0 :
bubbleGroupX = centerX;
bubbleGroupY = centerY;
break;
default :
var subBubblePoint = getSubBubblePoint(d, index);
bubbleGroupX = subBubblePoint.x;
bubbleGroupY = subBubblePoint.y;
break;
}
d.x = bubbleGroupX;
d.y = bubbleGroupY;
return "translate(" + bubbleGroupX + "," + bubbleGroupY + ") scale(1)";
}
var getOrbitTranslation = function(d, index, direction) {
var vector = getOrbitPoint(d,direction);
return "translate(" + vector.x + "," + vector.y + ") scale(1)";
}
////////////////////////////// DRAW
var bubbles;
var orbitInterval;
var draw = function( bubbles ){
var onBubbleClickHandler = function(d,i){
clearInterval(orbitInterval);
orbit("positive");
}
bubbles = svg.selectAll("g myCircleText")
.data(bubbles)
.enter()
.append("g")
.attr("transform", function(d){return "translate("+d.x+","+d.y+")"});
bubbles
.each(function (d, i) {
if (i === 0 ) {
d3.select(this)
.on("click", onBubbleClickHandler)
.attr("pointer-events", "all")
.attr("cursor", "pointer");
}
});
bubbles
.append("svg:circle")
.attr("r", function(d){return getRadius(d)} )
.attr("stroke","black")
.attr("stroke-width","0")
.attr("fill", function(d){return getColor(d.trend)});
bubbles
.each(function (d, i) {
var container = d3.select(this);
var dy0 = -(d.title.length - 1) / 2;
var magicNum = 1.75;
for (var i = 0; i < d.title.length; i++)
{
container
.append('svg:text')
.attr('pointer-events', 'none')
.attr("text-anchor", "middle")
.attr("dy", (dy0 + i) + "em")
.attr("class", "circleText")
.attr("style", function(d) { return getTextStyle(d, null); })
.text(function(d) { return d.title[i]; })
;
}
});
bubbles
.transition()
.duration(2000)
.attr("transform", function(d, index){ return getTranslation(d, index)} );
var orbit = function(direction){
bubbles
.each(function (d, i) {
if (i !== 0 ) {
d3.select(this)
.attr("transform", function(d, index){ return getOrbitTranslation(d, index, direction)} );
//console.log("after: ", d)
}
});
}
var animateOrbit = function(){
orbitInterval = setInterval( function(){
orbit("negative");
}, 50);
}
setTimeout( function(){
animateOrbit();
}, 500);
}
draw(bubbleData);
</script>
var svg = d3.select("#stage");
var drawCycle = function(){
var createViz = function() {
var dateObj = new Date();
var timeStr = dateObj.toLocaleTimeString();
document.getElementById("drawing").innerHTML = "drawing interval: " + timeStr;
svg.selectAll("g").remove();
var ranFreqA = Math.floor((Math.random()*100)+1);
var ranFreqB = Math.floor((Math.random()*100)+1);
var ranFreqC = Math.floor((Math.random()*100)+1);
var titleA = "toronto walking tours".toUpperCase().split(' ');
var titleB = "san francisco hyperloop".toUpperCase().split(' ');
var titleC = "tokyo and back again".toUpperCase().split(' ');
var responseA = {title: titleA, frequency: ranFreqA, x: 75 };
var responseB = {title: titleB, frequency: ranFreqB, x: 175 };
var responseC = {title: titleC, frequency: ranFreqC, x: 300 };
var responseData = [responseA, responseB, responseC];
// font scaling has to be based on the longest string in the series
var getFontSize = function( textArray ) {
var textSizeArray = [];
for (var i = 0; i < textArray.length; i++) {
textSizeArray.push( textArray[i].length );
}
return Math.max.apply(Math, textSizeArray);
}
var elem = svg.selectAll("g myCircleText")
.data(responseData)
var group = elem.enter()
.append("g")
.attr("transform", function(d){return "translate("+d.x+", 150)"})
var circles = group.append("svg:circle")
.attr("r", function(d){return d.frequency } )
.attr("stroke","black")
.attr("stroke-width","4")
.attr("fill", "white")
group
.each(function (d, i) {
var container = d3.select(this);
var dy0 = -(d.title.length - 1) / 3;
var magicNum = 1.75; // my math isn't the greatest so I got this by trial and error
var longestStrSize = getFontSize(d.title);
for (var i = 0; i < d.title.length; i++)
{
container
.append('svg:text')
.attr('pointer-events', 'none')
.attr("text-anchor", "middle")
.attr("dy", (dy0 + i) + "em")
.attr("class", "circleText")
.style("fill", "black")
.style("font-size", function(d) { return ( d.frequency / ( longestStrSize / magicNum ) ) + "px" })
.text(function(d) { return d.title[i]; })
;
}
});
};
var cycleInterval = setInterval( function(){ createViz(); }, 500 );
//createViz();
}
drawCycle();
.experiment {
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
}
.circleText {
color: #D7BF00;
font-family: Georgia, serif;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment