Skip to content

Instantly share code, notes, and snippets.

@gabrielcornish
Forked from JoshuaGrams/reveal.tw
Created May 16, 2022 15:13
Show Gist options
  • Select an option

  • Save gabrielcornish/8780e3862023b389b87d01c33038ce9a to your computer and use it in GitHub Desktop.

Select an option

Save gabrielcornish/8780e3862023b389b87d01c33038ce9a to your computer and use it in GitHub Desktop.
Line-by-line text reveal: Twine/SugarCube.
:: Some Passage
This will show at the start.
@@.hide;This will show after you click or press space/enter.@@
@@.hide;.nocontinue;This will show after you advance again, but will <em>not</em> have the continue marker.@@
<span class="hide">HTML tags work fine too, but the other notation is shorter.</span>
:: StoryInit
/* Remember how much of each passage has been revealed.
* If you would prefer a different variable name, do
* <<set setup.continueVar to '$myContinueCounts'>>
*/
<<set $continued to {}>>
// This whole thing goes in the Story JavaScript
// -----------------------------------------------------------------------------------
// If you prefer, you can delete these configuration commands from the JavaScript
// and <<set ...>> these three values in your StoryInit instead.
// These keys cause the text to advance.
setup.continueKeys = new Set([' ', 'Enter'])
// Ignore clicks/keypresses on anything in one of these CSS selectors.
// Any selector should work, so also things like '.myClass' or '#myID'
setup.noContinue = new Set(['a', 'button', 'input', 'textarea'])
// SugarCube only: variable telling how many sections have been
// revealed on various passages.
// Be sure to <<set $continued to {}>> in StoryInit
// (or whatever you call the variable).
setup.continueVar = '$continued'
// ------------------------------------------------------------------------------
function isInSelector(elt, selector) {
while(elt && elt.matches) {
if(elt.matches(selector)) return true
elt = elt.parentNode
}
return false
}
function revealFirstHidden(ev) {
// Ignore these elements.
if(ev) for(const selector of setup.noContinue) {
if(isInSelector(ev.target, selector)) return
}
// Remove any existing continue indicators.
let shown = $('.passage .continue')
if(shown) shown.removeClass('continue')
// Get any hidden items.
const hidden = $('.passage .hide')
if(hidden.length > 0) {
// We're doing the special thing, prevent the default behavior
ev && ev.preventDefault()
// If more are hidden, add the continue indicator.
let show = hidden.first()
if(hidden.length > 1 && !show.hasClass('nocontinue')) {
show.addClass('continue')
}
// Set the first to fade in and then unhide it.
show.addClass('fadein')
show.removeClass('hide')
// If scrollIntoView() exists (maybe not on IE?), do it.
if(show[0].scrollIntoView) {
show[0].scrollIntoView({behavior: 'smooth', block: 'nearest'})
}
// If we're being called directly (not from an event),
// and we're in SugarCube...
if(ev && State && typeof State.getVar === 'function') {
// and the variable exists,
const count = State.getVar(setup.continueVar)
// increment the number of things we've revealed.
if(count) count[passage()] = (count[passage()] || 0) + 1
}
}
}
// When you open up the story in your browser, add keyboard and mouse handlers.
$(document).ready(function() {
$('html').on('click', revealFirstHidden)
$('html').on('keypress', function(ev) {
const modifiers = ev.ctrlKey || ev.altKey || ev.shiftKey
if(setup.continueKeys.has(ev.key) && !modifiers) {
revealFirstHidden(ev)
}
})
})
// If you want to remember how much was already revealed, include this bit,
// which happens every time a new passage is displayed.
$(document).on(':passagedisplay', function() {
// If we're in SugarCube
if(State && typeof State.getVar === 'function') {
// $continued[passage()] tells how many hidden items were revealed.
const count = State.getVar(setup.continueVar)
const n = count && count[passage()] || 0
for(let i=0; i<n; ++i) revealFirstHidden()
}
})
.hide { display: none; }
.fadein {
opacity: 1;
animation-name: fadeInOpacity;
animation-iteration-count: 1;
animation-timing-function: ease-in;
animation-duration: 0.5s;
}
@keyframes fadeInOpacity {
0% { opacity: 0; }
100% { opacity: 1; }
}
.continue:after {
content: "\A➔";
white-space: pre;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment