CSS Transition Visibility
The CSS visibility transition does not make elements appear
or disappear gradually (see 2 sections below), as one might expect. It is, however,
important
in combination
with a visual effect that is specified separately by other means
(see
below Why setting Visibility and
using Transition is often needed)
E.g. often just the opacity transition is used for a fade-in and fade-out effect.
This, however, has significant problems in specific situations or with certain browsers, as discussed
below.
In this article we give various sample visual effects for combination with the
visibility transition, to make an element appear
or disappear made with HTML5 features like
Sample HTML/CSS Text
Dummy text to replace by a text of your choice.
Use the X above to close.
CSS transitions or WebGL™.
Click on the effects listed below or on more to scroll to source code
and description below.
Animations run in compatibility mode without WebGL™
and so do not show up completely. Use another browser and/or computer to see the WebGL™ examples working completely.
Incomplete Workaround: Fade-In and -Out using Transitions on Opacity
Most often the opacity property is used instead of visibility,
which results in a fade-in and fade-out effect. The idea is that a completely
transparent element is very similar to an element with visibility:hidden.
<style > .cl0 { opacity : 0; transition:opacity 1s; } .cl0:hover { opacity : 1} </style> <div class="cl0"> Sample Text </div> <span class="hoverhere">Hover over the Line Above this Line</span> <br> fails on IE8 and still catches events while invisible |
Sample Text
Hover over the Line Above this Line
fails on IE8 and still catches events while invisible
|
However care has to be taken with that approach, because unlike an invisible element (with visibility:hidden)
a fully transparent element (opacity:0) still catches events and e.g. a transparent link still
functions. The blog article CSS Transition Opacity for Fade Effects has a detailed description
and examples of that problem. Also old browsers like IE8.0 do not understand the opacity property
and so this simple approach will never hide the element in IE 8.
The solution is to combine the opcity transition with a
transition on visibility. Before we discuss that in detail
after the next the section, the next section itself explains
the detailed workings of transitions on visibility.
Appearance of a CSS Transition on Visibility
Just as transitions on other properties you can define a transition on
visibility, e.g. using " transition:visibility 1s". This
example specifies a duration of 1 second. There are still just the
two states visible and hidden (for normal non-table elements).
While the transition is running, the element is visible regardless if
it is a transition from visible to hidden or vice versa from hidden to
visible (if using the default ease or the linear timing
function). Only after the transition visibility switches to its specified
target value.
So in other words, when you hide an element using a transition from
visibility:visible to visibility:hidden it takes
some time before the element is actually hidden. This is the time
when you want to play some visual effect. You can specify the time
as duration of the visibility-transition.
On the other hand, when you reveal an element using a transition from
visibility:hidden to visibility:visible then the
element becomes visible immediately regardless on the specified duration.
This might sound like a strange complicated behaviour, but it
nicely solves the problem of playing a visual effect to hide an
element:
Why the Transition on Visibility is often Needed
Often simply using "visibility:hidden" in a style sheet renders an
additional visual effect invisible and therefore does not work.
On the other hand leaving out the "visibility:hidden" can create
other unwanted effects as discussed in the next section.
The solution is delaying hiding the element via a transition
until playing the effect is finished. The specific semantics of the
transition makes sure that when playing an animation to let the
element appear, the visibility property is set to visible at
once, to make the animation show.
Fade-In and -Out by Combining Transitions on Visibility and Opacity
The opacity transition creates a nice fade-in and -out
effect. However fully transparent elements, unlike hidden elements,
still catch mouse events and older browsers fail completely
on opacity. A solution for these problems is to specify a
transition on both visibility and opacity. This
works fine and the following example shows how to do it:
<style > .cl1 { transition:visibility 1s, opacity 1s; visibility:hidden; opacity:0} .outer1:hover > div { visibility:visible; opacity:1} </style> <div class="outer1"> <div class="cl1"> Sample Text </div> Hover Here <br > </div> |
|
It also exposes and solves another
problem: if visibility is set to hidden the cl1 element does no longer act on
mouse events and so the cl0:hover as used in the first
example never applies. So we put another div of
class outer1 around the sample element and use a rule
.outer1:hover > div which means that whenever the mouse
hovers the outer element then the inner div should get the
specified format.
Other solutions for these problems are discussed in [1].
Fly In from Outside the Screen - CSS Transition on Left and Top
The idea of this approach is to move an element somewhere off the
screen and to so simulate a hidden visibility.
This works nicely and the transition shows the element
flying from outside the document to its normal position
and vice versa.
We modified the example code given above accordingly, leading to the example below.
The first thing we added was "width:80%; padding:10%; background-color:yellow;"
which gives the element a yellow background and some padding.
This is not really required but makes the example look better.
Then we gave the element a position outside of the screen using
position:relative; left : -2000px; top:-500px;, i.e. we moved the element
2000px to the left and 500px to the top from its normal position.
This effectively hides the element without actually using the visibility property.
Finally we define transition-property:all; transition-duration:1s;
to enable transitions on all properties (we could also have used just left and top).
On activation of the transition we would like move the element to its normal position
on the screen e.g. using left : 0px; top:40%. However a rule
as above like .cl2:hover { left : 0px; top:40% } will not work,
because initially the element is moved off the screen and you cannot move
the mouse there. So what we do it to put another div of class outer2
around the sample element. The outer div is normally invisible (we just gave
it a blue border for illustration). Then we can write a rule
.outer2:hover > div which means that whenever the mouse hovers
the outer element then the inner div should get the specified format,
{ left : 0px; top:40% } in the example.
<style > .cl2 {width:80%; padding:10%; background‑color:yellow; position:relative; left : ‑2100px; top:‑500px; transition‑property:all; transition‑duration:1s;} .outer2 {border : 1px blue solid; width:90%; height:100px;} .outer2:hover > div {left:0px; top:40%} </style> <div class="outer2"> <div class="cl2"> Sample Text </div> Hover Here </div> |
|
You can also implement a similar transition using
another new HTML5 technique called HTML5 canvas 3D or WebGL™
and the taccgl™ javascript library.
The example script below first finds the HTML element with id=demo.
Then from(-600,4000,0) declares the corresponding fly in transition
from coordinates (x=-600,y=4000,z=0). The vEnd(0,0,0)
gradually slows down the motion, which corresponds to the default
timing function of the transition example.
taccgl.actor("demo",null,"visible") .from(-600,4000,0). vEnd(0,0,0) . start() | RUN |
The taccGL/WebGL™ example can easily be extended to display a curved surface showing a kind of page flip effect.
taccgl.actor("demo",taccgl.flexiBorder,"visible") .from(-600,4000,0). Flip(0,0). Rect1(). vEnd(0,0,0) . start() | RUN |
Also a shadow can be included.
In the example below a first line for the shadows and a z-coordinate for the
source position of the element were added:
taccgl.a(document.body).color("white") .shadowOnly().showAfter() .start(); taccgl.actor("demo",null,"visible") .from(-400,2500,2000). vEnd(0,0,0).duration(3).start() | RUN |
Very similar examples are discussed in the WebGL-HTML5 PopUp Animations
article or see our WebGL™ webdesign homepage for more
details on taccgl™.
Appear Inside - Clipping using Overflow:hidden and CSS transition on Top and Left
The idea of this workaround is to simulate hidden visibility by
using the overflow:hidden specification for the outer div
element. This specification hides the inner div while it is positioned
outside the outer one. Then the transition moves the inner div outside and inside
the outer div. It will not move across the document but vanishes as soon as it leaves
the outer div.
Main difference of the example below is the overflow:hidden specification
for the outer div element.
This means that whenever the inner div is positioned outside the outer one,
it is hidden. This is done using left : 150px; top:-150px;.
When hovering over the outer div,
the .outer3:hover > div { left : 0px; top:40% } rule applies
and the inner div moves to its normal position inside the outer div
and thereby appears.
<style > .cl3 { background‑color:yellow; width:80%; padding:10%; position:relative; left : 150px; top:‑150px; transition‑property:all; transition‑duration:1s;} .outer3 { width:90%; height:150px; overflow:hidden} .outer3:hover > div { left:0px; top:30% } </style> <div class="outer3"> <div class="cl3"> Sample Text </div> Hover Here </div> |
|
Clipping using Overflow:hidden and CSS Transition on Width
Basic idea of this work around for visibility:hidden is to set an elements
width (or height) to 0 and so to make the element disappear.
Then the transition turns the width to normal which should result in
an animation that gradually makes the element visible from left to right.
For this approach it is essential to use Overflow:hidden
on the element itself, because otherwise the elements content stays
visible even if the width is set to 0.
See below an example. Note that it is necessary to do
a transition for padding as well since otherwise the element does not
disappear completely. You might notice that there is a line break
appearing during the transition, which makes this approach not very attractive.
<style > .cl4 {background‑color:yellow; width:0px; padding:0px; overflow:hidden; transition‑property:all; transition‑duration:1s;} .outer4 {width:90%; height:150px; overflow:hidden} .outer4:hover > div {width:80%; padding:10%} </style> <div class="outer4"> <div class="cl4"> Sample Text </div> Hover Here </div> |
|
It is possible to improve this example using 3 nested
div-elements. Thereby the inner element is static containing the
actual content. The middle element is resizing using the transition.
It is also using the overflow:hidden to clip the inner
element. The outer element is needed for the hovering to work.
<style > .cl6 {background‑color:yellow; width:100px; padding:10px; } .middle6 {width:0px; overflow:hidden; transition‑property:all; transition‑duration:1s;} .outer6 {width:90%; height:150px; overflow:hidden} .outer6:hover > div {width:80%} </style> <div class="outer6"> <div class="middle6"> <div class="cl6"> Sample Text </div> </div> Hover Here </div> |
|
You can implement similar transitions using
HTML5 canvas 3D or WebGL™ and taccgl™. Here clipping is done using the
method. In addition there is the resize method to shrink and grow the element.
(a=taccgl.actor("demo",null,"visible")) . clipA(0.1,a.h,a.w,a.h) .start() | RUN |
(a=taccgl.actor("demo",null,"visible")) . from(a.x+a.w,a.y,0). clipA(0.1,a.h,a.w,a.h) .start() | RUN |
(a=taccgl.actor("demo",null,"visible")) . resize(0.1,a.h,a.w,a.h) .start() | RUN |
(a=taccgl.actor("demo",null,"visible")) . from(a.x+a.w/2,a.y+a.h/2,0). resize(1,1,a.w,a.h) .start() | RUN |
Using a feature called flexiBorder it is further possible to deform the HTML element during the transition:
(a=taccgl.actor("demo", taccgl.flexiBorder, "visible")) . resize(0.1,a.h,a.w,a.h) .Wave(40,40,-Math.PI,Math.PI) .Rect1() .start() | RUN |
(a=taccgl.actor("demo", taccgl.flexiBorder, "visible")) . from(a.x+a.w/2,a.y,0). resize(1,a.h,a.w,a.h) .Wave(40,40,-Math.PI,Math.PI) .Rect1() .start() | RUN |
(a=taccgl.actor("demo", taccgl.flexiBorder, "visible")) . resize(0.1,a.h,a.w,a.h) .ZWave(0.2,0.2,-Math.PI,Math.PI) .Rect1() .start() | RUN |
(a=taccgl.actor("demo", taccgl.flexiBorder, "visible")) . from(a.x+a.w/2,a.y,0). resize(1,a.h,a.w,a.h) .ZWave(0.2,0.2,-Math.PI,0) .Rect1() .blend(0,0,1,0) .start() | RUN |
Similar examples are discussed in the WebGL-HTML5 PopUp Animations
article.
Using transitions on the CSS Clip Property
Using clipping seems to work as well. The idea is simply to set the clip
parameters in a way that clip the complete element in order to simulate hidden visibility.
The transition then gradually removes the clipping.
Some browsers might have problems
with percent values and so you need to specify the actual pixel sizes which is a
disadvantage for dynamic or responsive layouts.
<style > .cl7 { background‑color:yellow; padding:10px; clip:rect(0px 0px 40px 0px); position:absolute; width:100px; transition‑property:all; transition‑duration:1s;} .outer7 { width:90%; height:150px; overflow:hidden} .outer7:hover > div { clip:rect(0px 140px 40px 0px)} </style> <div class="outer7"> <div class="cl7"> Sample Text </div> Hover Here </div> |
|
Circle Blends using transition of the border-radius Property
The next idea is to use a very high value of the border-radius property
instead of visibility:hidden. The transition then
turns the border-radius to 0 which shows a kind of circle blend.
The first try below is not working. If the border-radius is set
too high, the rounded corners do not clip the element but instead
the border-radius is just taken into account to the extend that the
content of the element stays visible.
This approach, however, works in the special case that the only
content of the inner div is a background-image.
<style > .cl8 { background‑color:yellow; padding:10px; border‑radius:100px; position:absolute; width:100px; transition‑property:all; transition‑duration:1s;} .outer8 { width:90%; height:150px; overflow:hidden} .outer8:hover > div { border‑radius:0px } </style> <div class="outer8"> Hover Here for a demo how not to do it. <div class="cl8"> Sample Text </div> </div> |
Hover Here for a demo how not to do it.
Sample Text
|
The problem can be overcome with d a middle element again. In the
following circle blend code the "Sample Text" is contained in an inner
div element, which in turn is contained in a middle div element. The
middle div initially has width:0px; height:0px; and so is
invisible. It also has overflow:hidden and so the inner div
element is invisible as well. Using the rule .outer10:hover > div
{ width:140px; height:140px; ... } the middle element grows on
hovering.
The middle element also has an initial border-radius:140px which becomes
a border-radius:0px on hover and so the middle element turns from
a circle to a square on hover.
Normally this would make the middle element grow starting from the top left corner.
We however would like to grow it inside out, i.e. the center of the
circle and the square should be fixed. Therefore the
middle element gets an position:absolute; margin-top:70px; margin-left:70px;
which moves the center point of the circle to the center point of the square.
Now we have a new problem, i.e. although the center point is fixed, the "Sample Text"
starts to move. To keep that fixed we need to add an initial
margin-top:-70px; margin-left:-70px; for the inner element
and a final { margin-top:0px; margin-left:0px} using the rule
.outer10:hover > div > div { margin-top:0px; margin-left:0px}.
<style > .cl10 { background‑color:yellow; padding:10px; padding‑top:60px; padding‑bottom:60px; height:20px; width:100px; margin‑top:‑70px; margin‑left:‑70px; transition‑property:all; transition‑duration:1s; } .outer10 { width:90%; height:150px;} .middle10 { width :0px; height:0px; overflow:hidden; border‑radius:140px; position:absolute; margin‑top:70px; margin‑left:70px; transition‑property:all; transition‑duration:1s;} .outer10:hover > div { width:140px; height:140px; border‑radius:0px; margin‑top:0px; margin‑left:0px} .outer10:hover > div > div { margin‑top:0px; margin‑left:0px} </style> <div class="outer10"> <div class="middle10"> <div class="cl10"> Sample Text </div> </div> Hover Here </div> |
|
With WebGL™ and taccgl™ further circle blends are possible that deform the
complete element :
(a=taccgl.actor("demo", taccgl.flexiBorder, "visible")) .from(a.x+(a.w-a.h),a.y+a.h/2,0) .to (a.x+(a.w-a.h)/2,a.y,0) .resize(1,1,a.h,a.h) .Circle() .start() .cont() .Rect1() .flyHome() .resize(a.h,a.h,a.w,a.h) .start() | RUN |
Negative Blends
Idea is to simulate hidden visibility by moving another element in
front covering the "Sample Text". The covering element gets
the background-color of the neighbor elements and so is
not realized by the viewer. There are many transition possibilities
to make the covering element appear and disappear, below we
give an example for a negative circle blend.
Note that in this example the "Sample Text" element is not contained
in the middle element, but instead both elements are contained
on the same level inside the outer element.
Using absolute and/or relative positioning both elements are
positioned on top of each other and using z-index the middle
element is put on top covering the "Sample Text" element.
Using border-radius the middle element is turned into
a circle. This circle is, however, invisible since it has
a white background color. And because it covers the "Sample Text" element
nothing is visible initially.
When you hover over the element, the middle element is resized to
zero using the transition to width:0px; height:0px;
and so the covering circle disappears and the "Sample Text" element
below appears.
<style > .cl9 { background‑color:yellow; width:100px; padding:10px; padding‑top:30px; padding‑bottom:30px; position:relative; top:32px; left:10px} .middle9 { border‑radius:100px; background‑color:white; z‑index:1; position:absolute; width:145px; height:145px; margin‑left:0px; margin‑top:0px; transition‑property:all; transition‑duration:1s;} .outer9 { width:90%; height:150px; overflow:hidden} .outer9:hover > div { width:0px; height:0px; margin‑left:67px; margin‑top:69px } </style> <div class="outer9"> Hover Here <div class="middle9"> </div> <p class="cl9"> Sample Text </p> </div> |
|
The additional transition on margin-left:67px; margin-top:69px
is needed to make the circle appear closing and opening from its center point.
In other words, while the circle is shrinking it is moved down and left,
so that its center point stays fixed.
Using CSS Transitions on the Transform Property to Fly In
Basic idea of this work around for visibility:hidden
is to use the CSS transition property to move the element
outside the document. This works nicely in newer browsers
and allows for interesting animations.
For older browsers you probably need workarounds as in [1].
It is clear that for browsers like IE 8 transitions do not work.
However most of the examples in the article still show and hide the elements
in IE8, just without animation. This is however not the case for the
two following examples (and also not for the first opacity example).
2D Example
The following examples show a fly in animation in 2D and then in 3D:
<style > .cl12 { width:80%; padding:10%; background‑color:yellow; transform:translate(‑1300px, 0px) rotate(180deg); transition‑property:all; transition‑duration:5s;} .outer12 { width:90%; height:100px;} .outer12:hover > div { transform:translate( 0px,0px); } </style> <div class="outer12"> <div class="cl12"> Sample Text </div> Hover Here </div> |
|
3D Example
<style > .cl13 { width:80%; padding:10%; background‑color:yellow; transform:translate3d(‑300px, 20px, 2000px) rotateY(‑180deg); transition‑property:all; transition‑duration:5s;} .outer13 { width:90%; height:100px; perspective:2300px;} .outer13:hover > div { transform:translate3d( 0px, 0px, 100px) } </style> <div class="outer13"> <div class="cl13"> Sample Text </div> Hover Here </div> |
|
Alternatively such an animation can be done with WebGL™ and taccgl™.
This has the additional advantage of automatic lighting and shadows.
Lighting simulates a virtual light source and calculates the color for every
pixel of the animated element. So for instance an element that directly
faces the light source appears brighter than an element that rotates away.
This leads to a much more realistic rotation effect.
taccgl.a(document.body) .color("white") .shadowOnly() .dur(4) .start(); a=taccgl.actor("demo", null, "visible");; a.posZ(100) .resizeZ() .from(-200,a.y-20,1400). rotateMiddle(0,1,0) .rotatePart(Math.PI,2*Math.PI) .vEnd(0,0,0) .dur(4) .start() | RUN |
More Animations
Similar animation examples making elements gradually appear and
disappear are found in the WebGL-HTML5 PopUp Animations blog
article (e.g. the box and parts example from above) and more complex
ones like the circle blend and rotating fadein are explained in the
Fragment Shaders tutorial page. In addition
the Parallax scrolling with 3D Acceleration article
has examples useful in the context of parallax scrolling.
Conclusion
The article gave various examples on
how to gradually make an HTML element visible or invisible. Some
examples used CSS transitions on various other properties besides
visibility and other more advanced examples were based on WebGL™.
If you are interested in the WebGL™ and taccgl™ technology,
we refer to our Home Page for more examples.
In order to understand the examples
in this article see the taccgl™ Tutorial
and the taccgl™ Manual.
July 2014, revised September 2015
[1] Transitions Using Visibility and Delay
WebGL™ is a trademark of the Khronos Group Inc.
|