A letter scrambling and object constancy demo based on Mike Bostock's General Update Pattern III.
-
-
Save hyponymous/425e419cb52c415a6427 to your computer and use it in GitHub Desktop.
Letter Scrambles
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> | |
<link href="http://fonts.googleapis.com/css?family=Anonymous+Pro:400,700" rel="stylesheet" type="text/css"> | |
<meta charset="utf-8"> | |
<style> | |
text { | |
font-family: 'Anonymous Pro', monospace; | |
font-size: 32px; | |
font-weight: bold; | |
color: rgb(51, 51, 51); | |
} | |
</style> | |
<body> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script type="text/javascript"> | |
function ScramblerView(el) { | |
this.letterWidth = 17; | |
this.lineHeight = 45; | |
this.lettersPerLine = 34; | |
var width = this.letterWidth * (this.lettersPerLine + 2), | |
height = 4 * this.lineHeight; | |
this.svg = d3.select(el).append('svg') | |
.attr('width', width) | |
.attr('height', height) | |
.append('g') | |
.attr('transform', 'translate(' + this.letterWidth + ',' + this.lineHeight + ')'); | |
} | |
ScramblerView.prototype.render = function(model) { | |
var data = model.text.split('').map(function (letter, index) { | |
return { | |
l: letter, | |
i: index | |
}; | |
}); | |
switch (model.mode) { | |
case 'full': | |
data = d3.shuffle(data); | |
break; | |
case 'word': | |
// only shuffle inside of words | |
var wordData = model.text.split(' ').map(function (word) { | |
return [word, 0]; | |
}); | |
// calculate the start indices | |
wordData.reduce(function (startIndex, wordDatum) { | |
wordDatum[1] = startIndex; | |
return startIndex + wordDatum[0].length + 1; | |
}, 0); | |
// shuffle within each pair of indices | |
wordData.map(function (wordDatum) { | |
return [wordDatum[1] + 1, wordDatum[1] + wordDatum[0].length - 1]; | |
}).filter(function (indices) { | |
return indices[1] > indices[0]; | |
}).forEach(function (indices) { | |
d3.shuffle(data, indices[0], indices[1]); | |
}); | |
break; | |
case 'plain': | |
default: | |
break; | |
} | |
this.update(data); | |
}; | |
ScramblerView.prototype.update = function(data) { | |
var letterWidth = this.letterWidth; | |
var lettersPerLine = this.lettersPerLine; | |
var lineHeight = this.lineHeight; | |
var rows = Math.floor(data.length / lettersPerLine); | |
this.svg | |
.transition() | |
.duration(500) | |
.attr('transform', 'translate(' + this.letterWidth + ',' + (2 - rows / 2) * this.lineHeight + ')'); | |
function x(d, i) { | |
return letterWidth * (i % lettersPerLine); | |
} | |
function y(d, i) { | |
return lineHeight * Math.floor(i / lettersPerLine); | |
} | |
var text = this.svg.selectAll('text') | |
.data(data, function (d, i) { return d.l + '-' + d.i; }); | |
text | |
.transition() | |
.duration(500) | |
.attr('x', x) | |
.attr('y', y); | |
text.enter() | |
.append('text') | |
.attr('dy', '.35em') | |
.attr('y', -5 * this.lineHeight) | |
.attr('x', x) | |
.style('fill-opacity', 1e-6) | |
.text(function (d) { return d.l; }) | |
.transition() | |
.duration(500) | |
.attr('y', y) | |
.style('fill-opacity', 1); | |
text.exit() | |
.transition() | |
.duration(500) | |
.attr('y', 5 * this.lineHeight) | |
.style('fill-opacity', 1e-6) | |
.remove(); | |
}; | |
var scramblerView = new ScramblerView('body'); | |
var scramblerModel = { | |
text: null, | |
mode: null, | |
}; | |
// a simple state machine | |
var sentences = [ | |
'It\'s a familiar trope that a written message may be intelligible even after some of its letters have been scrambled', | |
'Yet scrambling all the letters of a sentence renders it unreadable' | |
]; | |
var transitions = { | |
full: 'word', | |
word: 'plain', | |
plain: 'full' | |
}; | |
function step() { | |
// initialize / update | |
scramblerModel.mode = scramblerModel.mode || 'full'; | |
if (scramblerModel.currentSentence == null) { | |
scramblerModel.currentSentence = 0; | |
} | |
scramblerModel.text = sentences[scramblerModel.currentSentence]; | |
scramblerView.render(scramblerModel); | |
setTimeout(function () { | |
// transition | |
scramblerModel.mode = transitions[scramblerModel.mode] || scramblerModel.mode; | |
if (scramblerModel.mode === 'full') { | |
scramblerModel.currentSentence = (scramblerModel.currentSentence + 1) % sentences.length; | |
} | |
step(); | |
}, 2500); | |
} | |
step(); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment