Last active
August 3, 2024 04:07
-
-
Save JoshuaGrams/2e5d3029dc48edf8770abb4a6b682ff2 to your computer and use it in GitHub Desktop.
Line-by-line text reveal: Twine/SugarCube.
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
:: 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 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 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() | |
} | |
}) |
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
.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
Sorry, I'm not sure what you're saying. The continue marker doesn't go away when you reveal the last one?