-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1185 from uccser/issue/1182/data-bias
Recreate Data Bias interactive
- Loading branch information
Showing
11 changed files
with
412 additions
and
0 deletions.
There are no files selected for viewing
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Data Bias interactive | ||
|
||
**Created by:** | ||
|
||
- Sofia DiGirolamo | ||
- Minji Kong | ||
- Korey Mitchell | ||
- Mark Wolgin | ||
|
||
The original can be found [here](https://github.com/NCC74656/Bias-In-Big-Data-Interactive). | ||
|
||
**Rebuilt by:** Courtney Bracefield | ||
|
||
This interactive demonstrates how small changes in selection and perception can change the result of a study or competition. | ||
The user will learn how to better view data by altering their perspective. | ||
|
||
## The interactive | ||
|
||
The user is presented with a number of coloured circles placed on a coloured background and is asked to click the visible circles. | ||
Some circles are the same colour as the background and therefore near impossible to find. | ||
The interactive then reveals the hidden circles and allows the user to change the background colour via a slider. | ||
|
||
## Licences | ||
|
||
The licence of the original interactive can be found in `LICENCE-THIRD-PARTY` with a full copy available in the `third-party-licences` directory. |
122 changes: 122 additions & 0 deletions
122
csfieldguide/static/interactives/data-bias/css/data-bias.scss
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
@import "node_modules/nouislider/distribute/nouislider"; | ||
@import "node_modules/bootstrap/scss/functions"; | ||
@import "node_modules/bootstrap/scss/variables"; | ||
@import "node_modules/bootstrap/scss/mixins"; | ||
|
||
.circle { | ||
border-radius: 50%; | ||
width: 3.125rem; | ||
height: 3.125rem; | ||
position: absolute; | ||
} | ||
|
||
#circles-area { | ||
position: relative; | ||
border-radius: 1rem; | ||
} | ||
|
||
#data-bias-container { | ||
padding: 4rem !important; | ||
} | ||
|
||
.noUi-pips { | ||
padding-top: 0.3125rem; | ||
} | ||
|
||
.noUi-target { | ||
border: none; | ||
background: linear-gradient( | ||
to right, | ||
hsl(0,100%,50%), | ||
hsl(60,100%,50%), | ||
hsl(120,100%,50%), | ||
hsl(180,100%,50%), | ||
hsl(240,100%,50%), | ||
hsl(300,100%,50%), | ||
hsl(360,100%,50%) | ||
); | ||
} | ||
|
||
.noUi-handle { | ||
border: 0.125rem white solid; | ||
height: 1.75rem !important; | ||
width: 1.75rem !important; | ||
border-radius: 50%; | ||
top: -0.375rem !important; | ||
} | ||
|
||
.noUi-handle:before, | ||
.noUi-handle:after { | ||
content: none; | ||
} | ||
|
||
.noUi-connect { | ||
background: none; | ||
} | ||
|
||
.glow { | ||
box-shadow: 0 0 1rem 0.5rem black; | ||
} | ||
|
||
.red { | ||
background: red; | ||
} | ||
|
||
.lime { | ||
background: lime; | ||
} | ||
|
||
.blue { | ||
background: blue; | ||
} | ||
|
||
.yellow { | ||
background: yellow; | ||
} | ||
|
||
.purple { | ||
background: #9d03fc; | ||
} | ||
|
||
.darkorange { | ||
background: darkorange; | ||
} | ||
|
||
.fuchsia { | ||
background: fuchsia; | ||
} | ||
|
||
.deepskyblue { | ||
background: deepskyblue; | ||
} | ||
|
||
.grey { | ||
background: grey !important; | ||
} | ||
|
||
@include media-breakpoint-down(sm) { | ||
#instruction-area, | ||
#background-colour-slider-container { | ||
padding: 0rem !important; | ||
} | ||
|
||
#data-bias-container { | ||
padding: 1rem 3rem !important; | ||
} | ||
|
||
#instruction-text { | ||
margin: 1rem 0rem; | ||
} | ||
|
||
.circle { | ||
width: 2.5rem !important; | ||
height: 2.5rem !important; | ||
} | ||
} | ||
|
||
@include media-breakpoint-up(xl) { | ||
#circles-area { | ||
max-width: 60%; | ||
max-height: 80%; | ||
} | ||
} |
186 changes: 186 additions & 0 deletions
186
csfieldguide/static/interactives/data-bias/js/data-bias.js
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
const noUiSlider = require('nouislider'); | ||
const wNumb = require('wnumb'); | ||
|
||
const COLOURS = ['red', 'lime', 'blue', 'yellow', 'purple', 'darkorange', 'fuchsia', 'deepskyblue']; | ||
const START_TEXT = gettext("Click each dot that you see on the screen, then click 'Next stage' to reveal the answer!"); | ||
const MISSED_CIRCLES_TEXT = gettext('You seem to have missed some dots! <br><br>Forced perspective like this can be used in data representation and cause bias in the overall results.'); | ||
const SLIDER_TEXT = gettext('Click and drag the slider to change the background colour. <br><br>What do you notice is happening?') | ||
const SLIDER_MIN = 0; | ||
const SLIDER_MAX = 360; | ||
const PERENTAGE_BOUNDARY_UPPER = 85; // 85% | ||
const PERCENTAGE_ADJUSTMENT = 15; // 15% | ||
const PERENTAGE_BOUNDARY_LOWER = 5; // 5% | ||
const NUM_CIRCLES_TO_ADD = 8; // in addition to the 3 circles created that are the same colour as the background colour | ||
// below dictionary holds the 'H' value of the HSL colours. | ||
// these numbers correspond to the slider value of that colour, e.g blue is at value 240 on the slider. | ||
const SLIDER_COLOUR_VALUES = { | ||
'red': 0, | ||
'lime': 120, | ||
'blue': 240, | ||
'yellow': 60, | ||
'purple': 277, | ||
'darkorange': 33, | ||
'fuchsia': 300, | ||
'deepskyblue': 195 | ||
} | ||
var firstStage = true; | ||
var bgColourSlider = $('#background-colour-slider'); | ||
var sliderStartPos = 0; | ||
var startColour = 'red'; | ||
|
||
|
||
$(document).ready(function() { | ||
init(); | ||
$('#next-stage').click(loadNextStage); | ||
$('#start-again').click(restartInteractive); | ||
}); | ||
|
||
|
||
/** | ||
* Returns everything to the inital 'page loaded' state. | ||
*/ | ||
function init() { | ||
// get random background colour to start | ||
startColour = getRandomColour(); | ||
sliderStartPos = SLIDER_COLOUR_VALUES[startColour]; | ||
$('#circles-area').addClass(startColour); | ||
// make sure we have at least 2 circles that are the same as the background colour | ||
// a third circle is added later in loadNextStage to make sure the user NEVER finds all of the circles | ||
createCircle(startColour); | ||
createCircle(startColour); | ||
// generate 8 more randomly coloured circles | ||
for (i=0; i < NUM_CIRCLES_TO_ADD; i++) { | ||
createCircle(); | ||
} | ||
$('#next-stage').removeClass('d-none'); | ||
$('#start-again').addClass('d-none'); | ||
createSlider(); | ||
bgColourSlider[0].noUiSlider.on('update', updateSlider); | ||
} | ||
|
||
|
||
/** | ||
* Creates slider that controls background colour. | ||
*/ | ||
function createSlider() { | ||
noUiSlider.create(bgColourSlider[0], { | ||
start: sliderStartPos, | ||
step: 1, | ||
connect: "lower", | ||
orientation: "horizontal", | ||
range: { | ||
'min': SLIDER_MIN, | ||
'max': SLIDER_MAX | ||
}, | ||
format: wNumb({ | ||
decimals: 0 | ||
}) | ||
}); | ||
} | ||
|
||
|
||
/** | ||
* Updates background colour when slider is moved. | ||
*/ | ||
function updateSlider() { | ||
var value = bgColourSlider[0].noUiSlider.get(); | ||
hslColour = 'hsl(' + value + ', 100%, 50%)'; | ||
$('#circles-area').css('background', hslColour); | ||
$('.noUi-handle').css('background', hslColour); | ||
} | ||
|
||
|
||
/** | ||
* Returns a random position in the form of {top: ..., left: ...} to randomly place circles. | ||
*/ | ||
function getRandomPosition() { | ||
var circlesAreaHeight = $('#circles-area').height(); | ||
var circlesAreaWidth = $('#circles-area').width(); | ||
var randHeight = Math.floor((Math.random() * circlesAreaHeight)); | ||
var randWidth = Math.floor((Math.random() * circlesAreaWidth)); | ||
|
||
// convert px to % | ||
heightInPercentage = Math.floor((randHeight / circlesAreaHeight) * 100); | ||
widthInPercentage = Math.floor((randWidth / circlesAreaWidth) * 100); | ||
|
||
// reduces or increases percentage by 5% to prevent circles going outside of parent | ||
if (heightInPercentage >= PERENTAGE_BOUNDARY_UPPER) { | ||
heightInPercentage -= PERCENTAGE_ADJUSTMENT; | ||
} else if (heightInPercentage <= PERENTAGE_BOUNDARY_LOWER) { | ||
heightInPercentage += PERCENTAGE_ADJUSTMENT | ||
} | ||
if (widthInPercentage >= PERENTAGE_BOUNDARY_UPPER) { | ||
widthInPercentage -= PERCENTAGE_ADJUSTMENT; | ||
} else if (widthInPercentage <= PERENTAGE_BOUNDARY_LOWER) { | ||
widthInPercentage += PERCENTAGE_ADJUSTMENT; | ||
} | ||
|
||
return { | ||
top: heightInPercentage + '%', | ||
left: widthInPercentage + '%' | ||
}; | ||
} | ||
|
||
|
||
/** | ||
* Creates a circle div and adds it to the page. | ||
*/ | ||
function createCircle(colour) { | ||
var colour = colour || getRandomColour(); | ||
var $circle = $("<div>").addClass('circle ' + colour); | ||
$circle.css(getRandomPosition()); | ||
// so overlapping circles don't give away the hidden circles | ||
// brings non hidden circles up 1 layer | ||
if (colour !== startColour) { | ||
$circle.css('z-index', 1); | ||
} | ||
$('#circles-area').append($circle); | ||
$circle.click(function() { | ||
// toggle glow around clicked circle | ||
$circle.toggleClass('glow'); | ||
}); | ||
} | ||
|
||
|
||
/** | ||
* Loads the next stage which is either revealing the hidden circles or adjusting the background colour with the slider. | ||
*/ | ||
function loadNextStage() { | ||
if (firstStage) { | ||
// add a sneaky extra circle in the off chance they find all of the hidden circles :P | ||
createCircle(startColour); | ||
$('#circles-area').removeClass(startColour); | ||
$('#circles-area').addClass('grey'); | ||
$('#instruction-text').html(MISSED_CIRCLES_TEXT); | ||
firstStage = false; | ||
} else { | ||
$('#instruction-text').html(SLIDER_TEXT); | ||
$('.circle').removeClass('glow'); | ||
$('#circles-area').removeClass('grey'); | ||
$('#circles-area').addClass(startColour); | ||
$('#next-stage').addClass('d-none'); | ||
$('#start-again').removeClass('d-none'); | ||
$('#background-colour-slider-container').removeClass('d-none'); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Resets the interactive and calls init() to return page to the 'page loaded' state. | ||
*/ | ||
function restartInteractive() { | ||
$('.circle').remove(); | ||
$('#background-colour-slider-container').addClass('d-none'); | ||
firstStage = true; | ||
$('#instruction-text').html(START_TEXT); | ||
bgColourSlider[0].noUiSlider.destroy(); | ||
init(); | ||
} | ||
|
||
|
||
/** | ||
* Returns a random colour from the COLOURS array. | ||
*/ | ||
function getRandomColour() { | ||
return COLOURS[Math.floor(Math.random() * COLOURS.length)]; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"name": "csfg-interactive-data-bias", | ||
"version": "1.0.0", | ||
"private": true, | ||
"dependencies": { | ||
"nouislider": "13.1.5", | ||
"wnumb": "1.2.0" | ||
} | ||
} |
Oops, something went wrong.