I'm Dudley Storey, the author of Pro CSS3 Animation. This is my blog, where I talk about web design and development with HTML, CSS and SVG. To receive more information, including news, updates, and tips, you should follow me on Twitter or add me on Google+.

my books

Pro CSS3 Animation, Apress, 2013

Using SVG with CSS3 and HTML5, O'Reilly, 2017

my other blogs

Massive Head Canon: Intelligent discussion of movies, books, games, and technology.

my projects

The New Defaults — A Sass color keyword system for designers. Replaces CSS defaults with improved hues and more memorable, relevant color names.

CSSslidy — an auto-generated #RWD image slider. 3.8K of JS, no JQuery.

Animated 3D Card Scatter Gallery with HTML5 Video

Scattering videos like leaves

Some web developers assume that HTML5 video is somehow sacrosanct from manipulation by CSS. As you can see by moving your mouse over the header above and clicking on the revealed images, that’s not true in the slightest.

Inspired by the closing credits of Monsters University, which uses an animated theme of “Scarer Trading Cards”, this small piece has very simple markup, using videos kindly supplied by photographer Alexander Wagner:

<div id="scatter">
<figure id="desk">
<a href="#" id="estee"> 
<video poster="estee.png">
<source src="estee.mp4" type="video/mpeg">
<source src="estee.webm" type="video/webm">
</video>
</a>
<a href="#" id="indre"> 
<video poster="indre.png">
<source src="indre.mp4" type="video/mpeg">
<source src="indre.webm" type="video/webm">
</video>
</a>
</figure>
</div>

The outer <div> supplies the 3D perspective, while the <figure> element within creates the plane for the videos. Each video has two sources for cross-browser compatibility, and a poster frame to act as a stand in before the video is played.

The CSS controlling this is slightly more complex. (Vendor prefixes have been removed to simplify the code)

figure#desk {
transform: rotateX(60deg); position: relative; width: 200%;
height: 900px; background: url(woodgrain.png); 
margin-left: -150px; margin-top: -150px; transition: 1.2s 2s; 
}
div#scatter {
perspective: 700px; width: 100%; height: 500px; overflow: hidden; 
background: linear-gradient(#6c6769  0%, #0a0a0a 100%); 
}
figure#desk a  { 
position: absolute; top: 150px; border: 2vw solid #ffe;  
box-shadow: 0px 0px 6px 6px rgba(0,0,0,0.3); 
width: 25%; font-size: 0; background: #000; 
}
figure#desk a video { 
width: 100%; font-size: 0; transition: .6s; 
}
a#estee  {
transform: translateX(200%) rotateZ(60deg); transition: 2s; 
}
figure#desk:hover a#estee  {
transform: translateX(30%) rotateZ(700deg); 
}
a#indre {
top: 300px; transform: translateX(220%) rotateZ(48deg); transition: 2s .4s; 
}
figure#desk:hover a#indre {
transform: translateX(100%) rotateZ(360deg); 
}
figure#desk:hover { transform: rotateX(10deg); }
figure#desk a:hover video { opacity: 0.5; }
figure a:hover:before { opacity: 1; }
figure#desk a:before { 
content: "play"; font-size: 30px; position: absolute; top: 45%; left: 45%;
color: #fff; opacity: 0; transition: .3s; 
}

The videos are brought onscreen by using rotate and translate, triggered by hovering over the containing <figure> element. Each video is surrounded by a link, which provides generated content for the “play” prompt, and fades the video on hover. (The <video> elements themselves can’t create generated content due to the fact that they are replaced elements).

There are no playback controls, so we must start the videos with a click on their parent elements via some JavaScript:

(function(){
var desk = document.getElementById('desk');
var links = desk.getElementsByTagName('a');
for(var i=0;i<links.length;i++){
links[i].addEventListener('click',handler,false);
};
function handler(e){
var x = e.target; 
var video = x.querySelector("video");
x.play();
x.parentNode.classList.add('playing');
};
})();

When a video is playing, we don’t want the “play” prompt to show, so we use CSS that works off the class we just applied with JavaScript:

a.playing:hover:before { content: none; opacity: 0; }
a.playing video { opacity: 1; }

There are many potential improvements that could be made here: the videos should be flagged with a “playable” status only when they are fully on-screen, and reset after they have reached their final frame. Arguably, the entire animation would be better predicated on keyframes, rather than transitions. But, crude as it is, I hope that it might inspire more work in CSS 3D manipulation of HTML5 video. Play with this code on CodePen

This site helps millions of visitors while remaining ad-free. For less than the price of a cup of coffee, you can help pay for bandwidth and server costs while encouraging further articles.