“Cinemagraphs” are animated photographs that feature repeated and subtle movement. Early experiments by Kevin Burg and Jamie Beck in fashion photography led to the format’s modern popularity.
But GIFs - the usual source for cinegraphs - aren’t a video format, and can’t be controlled very well. In addition, creating a true cinemagraph can require a lot of work. Over the weekend I realized that for some images the same effect could be created using CSS 3D transforms. As an alternative to the expense of shooting full video, this could be a valuable, performant and efficient technique for product demos and visualizations.
Setting The Stage
We start with the still portion of the image: in this case I’ve used a photograph from the previously mentioned startupstockphotos, used as a background in a div:
<div id="backgroundcontainer">
</div>
The CSS sets the image to cover the width of the element, and sets the height of the element to the background image:
div#backgroundcontainer {
background-image: url(macbook-background.jpg);
background-image: image-set("macbook-background.jpg" 1x,
"macbook-background-2x.jpg" 2x);
background-size: contain;
background-repeat: no-repeat;
max-width: 750px;
margin: 0 auto;
padding-top: 53.4%;
position: relative;
}
Placing The Player
Even if they’re familiar with CSS 3D, most web developers don’t realise that 3D transforms can be applied to any element… even <iframe>
`, meaning that YouTube videos and other sources are open to manipulation in three dimensions.
Videos from YouTube aren’t automatically responsive, but I’ve previously addressed how to solve that problem, and will use the same solution here:
<div id="backgroundcontainer">
<div id="ivideo">
<iframe frameborder="0"
src="https://youtube.com/embed/V7be0UEJDdo…">
</iframe>
</div>
</div>
The embedded video will autoplay; the next step is to set it on the page.
The Light Angle
To set the position of the video correctly, several things need to happen. The first is that the outer <div>
element needs the correct perspective
values applied:
div#backgroundcontainer {
perspective: 472px;
perspective-origin: 103% 8%;
}
We’ll use the fact that this outer container has relative positioning to set the absolute position of the inner <div>
containing the video:
#ivideo {
position: absolute;
top: 0;
right: 2.1%;
width: 41.6%;
padding-bottom: 21%;
}
The combination of absolute positioning together with width
and padding-bottom
makes the video responsive, if it is combined with the following declaration:
#ivideo iframe {
position: absolute;
width: 100%;
height: 100%;
}
The values I used to set the correct angle for this video were:
#ivideo {
transform: rotateX(10deg) rotateY(-7deg) translateY(74%);
}
Finding these values is a combination of experience, experimentation and testing. I knew that the vanishing point set by the perspective-origin
value corresponded to the top right of the background image: you can see the vertical edges of the Macbook screen narrowing towards that location. iframe
starts off in the same approximate location due to the positioning of its surrounding <div>
, so moving it down (using translateY
was the first order of operations. Then, I had to rotate it in X and Y. This was helped by using a small trick in Chrome’s Element inspector: placing your cursor in a CSS value and using the up and down cursor keys will raise and lower the value by 1.
Cut & Print
Using this live experimentation, I gained the correct values to move the video to the right place. However, you may find that the perspective isn’t perfect at every viewport size. To fix this, I introduced a @media
query:
@media screen and (max-width: 600px) {
div#container {
perspective: 250px;
}
}
I have plans for at least one other variation of this technique: if you enjoyed it, I hope you’ll come back for more. To be alerted for new updates and more web development news, I’d suggest following me on Twitter… or, if you’re old school, subscribing to this site’s RSS feed.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/yeqMpO