Built with blockbuilder.org
Created
February 8, 2020 12:40
-
-
Save mohamedkhanafer/8841192c50aa54b599049e444aba8fc5 to your computer and use it in GitHub Desktop.
making it work
This file contains hidden or 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
| license: mit |
This file contains hidden or 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> | |
| <head> | |
| <title>Time Use Simulation</title> | |
| <link rel="stylesheet" href="style/style.css" type="text/css" media="screen" /> | |
| <link rel="stylesheet" type="text/css" href="//cloud.typography.com/7626174/696048/css/fonts.css" /> | |
| <link href='https://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'> | |
| </head> | |
| <meta charset="utf-8"> | |
| <style> | |
| /*Defining text stylings*/ | |
| #h1 { | |
| font-size: 32px; | |
| margin: 0px 0px 2px 0px; | |
| font-family: 'Barlow', sans-serif; | |
| font-weight: 300; | |
| font-variant: small-caps slashed-zero; | |
| color: #f20675; | |
| } | |
| #h2 { | |
| font-size: 18px; | |
| margin: 0px 0px 0px 0px; | |
| font-family: 'Barlow', sans-serif; | |
| font-weight: 100; | |
| font-variant: small-caps slashed-zero; | |
| color: #1ce3cd; | |
| } | |
| #link { | |
| font-family: 'Barlow', sans-serif; | |
| font-size: 5px; | |
| font-variant: small-caps slashed-zero; | |
| margin: 10px 10px 2px 0px; | |
| color: #cecece; | |
| } | |
| a:link, a:visited, a:active { | |
| text-decoration: none; | |
| color: #cccccc; | |
| border-bottom: 0.7px dotted #cccccc; | |
| } | |
| body { | |
| background-color:#ffffff; | |
| } | |
| </style> | |
| <body> | |
| <!-- Connecting with D3 library--> | |
| <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
| <div id="main-wrapper"> | |
| <div id="sidebar"> | |
| <div id="current_time">Pre-baby</div> | |
| <div id="speed"> | |
| <div class="togglebutton slow current" data-val="slow">Slow</div> | |
| <div class="togglebutton medium" data-val="medium">Medium</div> | |
| <div class="togglebutton fast" data-val="fast">Fast</div> | |
| <div class="clr"></div> | |
| </div> | |
| <div id="note"></div> | |
| <div id="cite"> | |
| This is an animation to highlight the different activities done by women before and after they give birth. | |
| </div> | |
| </div> | |
| <div id="chart"></div> | |
| <div class="clr"></div> | |
| </div> | |
| <!-- Connecting with D3 library--> | |
| <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
| <script> | |
| var USER_SPEED = "slow"; | |
| var width = 780, | |
| height = 800, | |
| padding = 1, | |
| maxRadius = 3; | |
| // color = d3.scale.category10(); | |
| var sched_objs = [], | |
| curr_minute = 0; | |
| var act_codes = [ | |
| {"index": "1", "short": "Sleeping", "desc": "Sleeping"}, | |
| {"index": "2", "short": "Work/Commute", "desc": "Work/Commute"}, | |
| {"index": "3", "short": "Free Time", "desc": "Free Time"}, | |
| {"index": "4", "short": "Nursing / Pumping", "desc": "Nursing / Pumping"}, | |
| {"index": "5", "short": "Take Care of Baby", "desc": "Taking Care of Baby"} | |
| ]; | |
| /// here we are defining how fast are the 3 different options: | |
| var speeds = { "slow": 100, "medium": 70, "fast": 20 }; | |
| var time_notes = [ | |
| { "start_minute": 1, "stop_minute": 60, "note": "The animation starts with data based on a graph by @beeonaposy." }, | |
| { "start_minute": 90, "stop_minute": 180, "note": "In the pre-baby phase, the routine is normally split between work, sleep, and free time." }, | |
| { "start_minute": 240, "stop_minute": 320, "note": "The baby has arrived!" }, | |
| { "start_minute": 360, "stop_minute": 440, "note": " The typical day activities are now more devoted to the baby." }, | |
| { "start_minute": 480, "stop_minute": 540, "note": "In this month, the baby requires a little less nursing." }, | |
| { "start_minute": 580, "stop_minute": 640, "note": "Do not forget that in Month 1 and 2, the mother is on maternal leave." }, | |
| { "start_minute": 720, "stop_minute": 780, "note": "Time to get back to work." }, | |
| { "start_minute": 850, "stop_minute": 910, "note": "Older baby, a bit more free time." }, | |
| { "start_minute": 960, "stop_minute": 1030, "note": "Back to a routine with a baby in the equation." }, | |
| ]; | |
| var notes_index = 0; | |
| // Activity to put in center of circle arrangement | |
| var center_act = "Sleeping", | |
| center_pt = { "x": 380, "y": 365 }; | |
| // Coordinates for activities | |
| var foci = {}; | |
| act_codes.forEach(function(code, i) { | |
| if (code.desc == center_act) { | |
| foci[code.index] = center_pt; | |
| } else { | |
| var theta = 2 * Math.PI / (act_codes.length-1); | |
| foci[code.index] = {x: 250 * Math.cos(i * theta)+380, y: 250 * Math.sin(i * theta)+365 }; | |
| } | |
| }); | |
| // Start the SVG | |
| var svg = d3.select("#chart").append("svg") | |
| .attr("width", width) | |
| .attr("height", height); | |
| //Data loading | |
| d3.csv('https://gist.githubusercontent.com/mohamedkhanafer/c98be5b6981264a6d8054a0ae0436898/raw/ef9513e446b4f510cf6466fd4083c703987d36fc/gistfile1.txt', function(data) { | |
| data.forEach(function(d) { | |
| var day_array = d.day.split(","); | |
| var activities = []; | |
| for (var i=0; i < day_array.length; i++) { | |
| // Duration | |
| if (i % 2 == 1) { | |
| activities.push({'act': day_array[i-1], 'duration': +day_array[i]}); | |
| } | |
| } | |
| sched_objs.push(activities); | |
| }); | |
| // Used for percentages by minute | |
| var act_counts = {"1": 0, "2": 0, "3": 0, "4": 0, "5": 0}; | |
| // A node for each person's schedule | |
| var nodes = sched_objs.map(function(o,i) { | |
| var act = o[0].act; | |
| act_counts[act] += 1; | |
| var init_x = foci[act].x + Math.random(); | |
| var init_y = foci[act].y + Math.random(); | |
| return { | |
| act: act, | |
| radius: 6, | |
| x: init_x, | |
| y: init_y, | |
| color: color(act), | |
| moves: 0, | |
| next_move_time: o[0].duration, | |
| sched: o, | |
| } | |
| }); | |
| var force = d3.layout.force() | |
| .nodes(nodes) | |
| .size([width, height]) | |
| // .links([]) | |
| .gravity(0) | |
| .charge(0) | |
| .friction(.9) | |
| .on("tick", tick) | |
| .start(); | |
| var circle = svg.selectAll("circle") | |
| .data(nodes) | |
| .enter().append("circle") | |
| .attr("r", function(d) { return d.radius; }) | |
| .style("fill", function(d) { return d.color; }); | |
| // .call(force.drag); | |
| // Activity labels | |
| var label = svg.selectAll("text") | |
| .data(act_codes) | |
| .enter().append("text") | |
| .attr("class", "actlabel") | |
| .attr("x", function(d, i) { | |
| if (d.desc == center_act) { | |
| return center_pt.x; | |
| } else { | |
| var theta = 2 * Math.PI / (act_codes.length-1); | |
| return 340 * Math.cos(i * theta)+380; | |
| } | |
| }) | |
| .attr("y", function(d, i) { | |
| if (d.desc == center_act) { | |
| return center_pt.y; | |
| } else { | |
| var theta = 2 * Math.PI / (act_codes.length-1); | |
| return 340 * Math.sin(i * theta)+365; | |
| } | |
| }); | |
| label.append("tspan") | |
| .attr("x", function() { return d3.select(this.parentNode).attr("x"); }) | |
| // .attr("dy", "1.3em") | |
| .attr("text-anchor", "middle") | |
| .text(function(d) { | |
| return d.short; | |
| }); | |
| label.append("tspan") | |
| .attr("dy", "1.3em") | |
| .attr("x", function() { return d3.select(this.parentNode).attr("x"); }) | |
| .attr("text-anchor", "middle") | |
| .attr("class", "actpct") | |
| .text(function(d) { | |
| return act_counts[d.index] + "%"; | |
| }); | |
| // Update nodes based on activity and duration | |
| function timer() { | |
| d3.range(nodes.length).map(function(i) { | |
| var curr_node = nodes[i], | |
| curr_moves = curr_node.moves; | |
| // Time to go to next activity | |
| if (curr_node.next_move_time == curr_minute) { | |
| if (curr_node.moves == curr_node.sched.length-1) { | |
| curr_moves = 0; | |
| } else { | |
| curr_moves += 1; | |
| } | |
| // Subtract from current activity count | |
| act_counts[curr_node.act] -= 1; | |
| // Move on to next activity | |
| curr_node.act = curr_node.sched[ curr_moves ].act; | |
| // Add to new activity count | |
| act_counts[curr_node.act] += 1; | |
| curr_node.moves = curr_moves; | |
| curr_node.cx = foci[curr_node.act].x; | |
| curr_node.cy = foci[curr_node.act].y; | |
| nodes[i].next_move_time += nodes[i].sched[ curr_node.moves ].duration; | |
| } | |
| }); | |
| force.resume(); | |
| curr_minute += 1; | |
| // Update percentages | |
| label.selectAll("tspan.actpct") | |
| .text(function(d) { | |
| return readablePercent(act_counts[d.index]); | |
| }); | |
| // Update time | |
| var true_minute = curr_minute % 1440; | |
| d3.select("#current_time").text(minutesToTime(true_minute)); | |
| // Update notes | |
| // var true_minute = curr_minute % 1440; | |
| if (true_minute == time_notes[notes_index].start_minute) { | |
| d3.select("#note") | |
| .style("top", "0px") | |
| .transition() | |
| .duration(600) | |
| .style("top", "20px") | |
| .style("color", "#000000") | |
| .text(time_notes[notes_index].note); | |
| } | |
| // Make note disappear at the end. | |
| else if (true_minute == time_notes[notes_index].stop_minute) { | |
| d3.select("#note").transition() | |
| .duration(1000) | |
| .style("top", "300px") | |
| .style("color", "#ffffff"); | |
| notes_index += 1; | |
| if (notes_index == time_notes.length) { | |
| notes_index = 0; | |
| } | |
| } | |
| setTimeout(timer, speeds[USER_SPEED]); | |
| } | |
| setTimeout(timer, speeds[USER_SPEED]); | |
| function tick(e) { | |
| var k = 0.04 * e.alpha; | |
| // Push nodes toward their designated focus. | |
| nodes.forEach(function(o, i) { | |
| var curr_act = o.act; | |
| // Make sleep more sluggish moving. | |
| if (curr_act == "0") { | |
| var damper = 0.6; | |
| } else { | |
| var damper = 0.5; | |
| } | |
| o.color = color(curr_act); | |
| o.y += (foci[curr_act].y - o.y) * k * damper; | |
| o.x += (foci[curr_act].x - o.x) * k * damper; | |
| }); | |
| circle | |
| .each(collide(.5)) | |
| .style("fill", function(d) { return d.color; }) | |
| .attr("cx", function(d) { return d.x; }) | |
| .attr("cy", function(d) { return d.y; }); | |
| } | |
| // Resolve collisions between nodes. | |
| function collide(alpha) { | |
| var quadtree = d3.geom.quadtree(nodes); | |
| return function(d) { | |
| var r = d.radius + maxRadius + padding, | |
| nx1 = d.x - r, | |
| nx2 = d.x + r, | |
| ny1 = d.y - r, | |
| ny2 = d.y + r; | |
| quadtree.visit(function(quad, x1, y1, x2, y2) { | |
| if (quad.point && (quad.point !== d)) { | |
| var x = d.x - quad.point.x, | |
| y = d.y - quad.point.y, | |
| l = Math.sqrt(x * x + y * y), | |
| r = d.radius + quad.point.radius + (d.act !== quad.point.act) * padding; | |
| if (l < r) { | |
| l = (l - r) / l * alpha; | |
| d.x -= x *= l; | |
| d.y -= y *= l; | |
| quad.point.x += x; | |
| quad.point.y += y; | |
| } | |
| } | |
| return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; | |
| }); | |
| }; | |
| } | |
| // Speed toggle | |
| d3.selectAll(".togglebutton") | |
| .on("click", function() { | |
| if (d3.select(this).attr("data-val") == "slow") { | |
| d3.select(".slow").classed("current", true); | |
| d3.select(".medium").classed("current", false); | |
| d3.select(".fast").classed("current", false); | |
| } else if (d3.select(this).attr("data-val") == "medium") { | |
| d3.select(".slow").classed("current", false); | |
| d3.select(".medium").classed("current", true); | |
| d3.select(".fast").classed("current", false); | |
| } | |
| else { | |
| d3.select(".slow").classed("current", false); | |
| d3.select(".medium").classed("current", false); | |
| d3.select(".fast").classed("current", true); | |
| } | |
| USER_SPEED = d3.select(this).attr("data-val"); | |
| }); | |
| }); // @end d3.tsv | |
| function color(activity) { | |
| var colorByActivity = { | |
| "0": "#e0d400", | |
| "1": "#1c8af9", | |
| "2": "#51BC05", | |
| "3": "#FF7F00", | |
| "4": "#DB32A4", | |
| "5": "#00CDF8", | |
| "6": "#E63B60", | |
| "7": "#8E5649", | |
| "8": "#68c99e", | |
| "9": "#a477c8", | |
| "10": "#5C76EC", | |
| "11": "#E773C3", | |
| "12": "#799fd2", | |
| "13": "#038a6c", | |
| "14": "#cc87fa", | |
| "15": "#ee8e76", | |
| "16": "#bbbbbb", | |
| } | |
| return colorByActivity[activity]; | |
| } | |
| var d3Graph = d3.select('svg') | |
| var dropShadowFilter = d3Graph.append('svg:filter') | |
| .attr('id', 'drop-shadow') | |
| .attr('filterUnits', "userSpaceOnUse") | |
| .attr('width', '250%') | |
| .attr('height', '250%'); | |
| dropShadowFilter.append('svg:feGaussianBlur') | |
| .attr('in', 'SourceGraphic') | |
| .attr('stdDeviation', 2) | |
| .attr('result', 'blur-out'); | |
| dropShadowFilter.append('svg:feColorMatrix') | |
| .attr('in', 'blur-out') | |
| .attr('type', 'hueRotate') | |
| .attr('values', 180) | |
| .attr('result', 'color-out'); | |
| dropShadowFilter.append('svg:feOffset') | |
| .attr('in', 'color-out') | |
| .attr('dx', 3) | |
| .attr('dy', 3) | |
| .attr('result', 'the-shadow'); | |
| dropShadowFilter.append('svg:feBlend') | |
| .attr('in', 'SourceGraphic') | |
| .attr('in2', 'the-shadow') | |
| .attr('mode', 'normal'); | |
| var simpleGradient = d3Graph.append('defs') | |
| .append('radialGradient') | |
| .attr('id', 'fake-shadow'); | |
| simpleGradient.append('stop') | |
| .attr('offset', "80%") | |
| .attr('stop-color', '#01AFAF'); | |
| simpleGradient.append('stop') | |
| .attr('offset', "100%") | |
| .attr('stop-color', "#01AFAF00"); | |
| // Output readable percent based on count. | |
| function readablePercent(n) { | |
| var pct = 100 * n / 100; | |
| if (pct < 1 && pct > 0) { | |
| pct = "<1%"; | |
| } else { | |
| pct = Math.round(pct) + "%"; | |
| } | |
| return pct; | |
| } | |
| // Minutes to time of day. Data is minutes from 4am. | |
| function minutesToTime(m) { | |
| var minutes = (m + 4*60) % 1440; | |
| var hh = Math.floor(minutes / 60); | |
| var ampm; | |
| if (hh > 12) { | |
| hh = hh - 12; | |
| ampm = "pm"; | |
| } else if (hh == 12) { | |
| ampm = "pm"; | |
| } else if (hh == 0) { | |
| hh = 12; | |
| ampm = "am"; | |
| } else { | |
| ampm = "am"; | |
| } | |
| var mm = minutes % 60; | |
| if (mm < 10) { | |
| mm = "0" + mm; | |
| } | |
| run=hh + ampm | |
| if(run=="4am" || run=="5am" || run=="6am"|| run=="7am") | |
| return "Pre-pregnancy" | |
| else if (run=="8am" || run=="9am" || run=="10am" ||run=="11am") | |
| return "1 Month old Baby"; | |
| else if (run=="12pm" || run=="1pm" || run=="2pm" || run=="3pm") | |
| return "2 Months old Baby"; | |
| else if (run=="4pm" || run=="5pm" || run=="6pm" || run=="7pm") | |
| return "3 Months old Baby"; | |
| else if (run=="8pm" || run=="9pm" || run=="10pm" || run=="11pm") | |
| return "4 Months old Baby"; | |
| else if (run=="12am" || run=="1am" || run=="2am" || run=="3am") | |
| return "5-6 Months old Baby" | |
| } | |
| </script> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment