Revision of Twine: Sugarcane custom CSS passage transitions from Fri, 02/22/2013 - 00:36

I've modified my previous Sugarcane passage transition code to allow you to define your own transitions with CSS.

The code
First, include this code in a script passage:

History.prototype.display=function(d,b,a){var c=tale.get(d);this.history.unshift({passage:c,variables:clone(this.history[0].variables)});
this.history[0].hash=this.save();var e=c.render();e.style.visibility="visible";if(a!="offscreen"){var p=$("passages");
for(var i=0;i<p.childNodes.length;i+=1){var q=p.childNodes[i];q.classList.add("transition-out");
setTimeout(function(){p.removeChild(q);},1000);}e.classList.add("transition-in");
setTimeout(function(){e.classList.remove("transition-in");},1);p.appendChild(e);}if((a=="quietly")||(a=="offscreen")){e.style.visibility="visible";
}if(a!="offscreen"){document.title=tale.title;this.hash=this.save();document.title+=": "+c.title;
window.location.hash=this.hash;window.scroll(0,0);}return e;};

What this does is remove the default JavaScript transition from Sugarcane, and cause the outgoing passage to gain a "transition-out" class, and the incoming passage to instantly gain and lose a "transition-in" class. This enables you to define new passage transitions by styling these 3 classes using CSS.

CSS Examples

In order for there to be a transition, you must specify the transition property for your passage elements. A very basic example is as follows:

.passage {
	transition: 0.25s linear;
	-webkit-transition: 0.25s linear;
}
The transition property causes elements to transition between styles when they gain and lose them, in a specific way. In this case, it's a quarter-second, purely linear transition. (Other options than "linear" are listed here.) The maximum duration time before the departing passage is removed is 1 second.

The current versions of Opera, Firefox and IE support the plain transition property, but you need to also include the -webkit variant in order to work in Chrome and Safari (you can also include -khtml-transition for Konqueror users, few though they may be).

Now that you've defined the transition itself, you can define the styles that the passages can transition from and to.

A very basic example:

.transition-in {
	position:fixed;
	opacity:0;
}
When a new passage arrives, its style will transition from the "transition-in" style to the normal passage style. In this case, it starts invisible ("opacity:0") and fades in to visibility. Note the "position:fixed" - both "transition-in" and "transition-out" should have position:fixed if you want them to neatly appear above or below each other.
.transition-out {
	position:fixed;
	opacity:0;
}

Putting identical styles for "transition-out" means that departing passages will fade out of visibility as well. With all 3 of these CSS snippets, you've replicated the "dissolve" transition linked above.

A broad number of CSS attributes can be used with this technique, and it can also be combined with tag-based passage CSS to give different passages different transitions. Experiment!

Live examples
All examples are "The Sky in the Room" by Porpentine.

Fade in (the default transition)

.transition-in {
	opacity:0;
	position:fixed;
}
.passage:not(.transition-out) {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	position:fixed;
}

Specifying :not(.transition-out) means that the transition is applied only for the incoming passage - the departing one instantly gets opacity:0. Note that specifying passage.transition-in would not do anything, since it is by removing the transition-in class that the transition is initiated.

Retro 8-bit four-step fade in

.transition-in {
	opacity:0;
	position:fixed;
}
.passage:not(.transition-out) {
	transition: 0.8s steps(3);
	-webkit-transition: 0.8s steps(3);
}
.transition-out {
	opacity:0;
	position:fixed;
}

Fade out

.transition-in {
	opacity:0;
	position:fixed;
}
.passage {
	transition: 0s 1s;
	-webkit-transition: 0s 1s;
}
.passage.transition-out {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	position:fixed;
}

Zoom In
.transition-in {
	opacity:0;
	transform: scale(0.8,0.8);
	-webkit-transform: scale(0.8,0.8);
	position:fixed;
}
.passage {
	width: calc(100% - 12em); // Necessary to keep the zoom origin point consistent.
	width: -webkit-calc(100% - 12em);
	transition: 0.5s ease-out;
	-webkit-transition: 0.5s ease-out;
}
.transition-out {
	opacity:0;
	transform: scale(2,2);
	-webkit-transform: scale(2,2);
	position:fixed;
}

Fast scroll up
Ideally you would use "vh" (viewport height) units instead of "rem" (root em) units, but not enough browsers support it yet. ;_;

.transition-in {
	opacity:0;
	transform: translate(0,-100rem);
	-webkit-transform: translate(0,-100rem);
	position:fixed;
}
.passage {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	transform: translate(0,100rem);
	-webkit-transform: translate(0,100rem);
	position:fixed;
}

Garage door
This would work well with a story whose passage divs have a fixed width and height.

.transition-in {
	opacity:0;
	position:fixed;
}
.passage {
	background-color: #000;
	transition: 1s ease-in;
	-webkit-transition: 1s ease-in;
}
.transition-out {
	position:fixed;
	z-index:3;
	transform: translate(0,-200%);
	-webkit-transform: translate(0,-200%);
}

Focus

.transition-in {
	color:transparent;
	text-shadow: #fff 0 0 1em;
	position:fixed;
}
.passage:not(.transition-out) {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	position:fixed;
}

Blur Merge

.transition-in {
	color:transparent;
	text-shadow: #fff -4em 0 1em, #fff 4em 0 1em;
	position:fixed;
}
.passage:not(.transition-out) {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	position:fixed;
}

AttachmentSize
tsitr_fadein.html92.69 KB
tsitr_fadeout.html92.74 KB
tsitr_scrollup.html92.82 KB
tsitr_focus.html92.76 KB
tsitr_sillyrotate.html92.92 KB
tsitr_retrofadein.html92.71 KB
tsitr_spin5seconds.html92.98 KB
tsitr_blur_merge.html92.74 KB
tsitr_garagedoor.html92.78 KB
tsitr_zoomin2.html92.92 KB