{"id":281735,"date":"2019-01-29T08:24:49","date_gmt":"2019-01-29T15:24:49","guid":{"rendered":"http:\/\/css-tricks.com\/?p=281735"},"modified":"2019-01-29T08:24:49","modified_gmt":"2019-01-29T15:24:49","slug":"slide-an-image-to-reveal-text-with-css-animations","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/slide-an-image-to-reveal-text-with-css-animations\/","title":{"rendered":"Slide an Image to Reveal Text with CSS Animations"},"content":{"rendered":"

I want to take a closer look at the CSS animation property<\/a> and walk through an effect that I used on my own portfolio website<\/a>: making text appear from behind a moving object. Here\u2019s an isolated example<\/a> if you\u2019d like to see the final product. <\/p>\n

<\/p>\n

Here\u2019s what we’re going to work with:<\/p>\n

\nSee the Pen
\nRevealing Text Animation Part 4 – Responsive<\/a> by Jesper Ekstrom (
@jesper-ekstrom<\/a>)
\non
CodePen<\/a>.<\/span>\n<\/p>\n

Even if you\u2019re not all that interested in the effect itself, this will be an excellent exercise to expand your CSS knowledge and begin creating unique animations of your own. In my case, digging deep into animation helped me grow more confident in my CSS abilities and increased my creativity, which got me more interested in front-end development as a whole.<\/p>\n

Ready? Set. Let\u2019s go!<\/p>\n

Step 1: Markup the main elements<\/h3>\n

Before we start with the animations, let’s create a parent container that covers the full viewport. Inside it, we’re adding the text and the image, each in a separate div so it\u2019s easier to customize them later on. The HMTL markup will look like this:<\/p>\n

<!-- The parent container -->\r\n<div class=\"container\"> \r\n  <!-- The div containing the image -->\r\n  <div class=\"image-container\">\r\n  <img src=\"https:\/\/jesperekstrom.com\/wp-content\/uploads\/2018\/11\/Wordpress-folder-purple.png\" alt=\"wordpress-folder-icon\">\r\n  <\/div>\r\n  <!-- The div containing the text that's revealed -->\r\n  <div class=\"text-container\">\r\n    <h1>Animation<\/h1>\r\n  <\/div>\r\n<\/div><\/code><\/pre>\n

We are going to use this trusty transform trick<\/a> to make the divs center both vertically and horizontally with a position: absolute;<\/code> inside our parent container, and since we want the image to display in front of the text, we’re adding a higher z-index<\/code> value to it.<\/p>\n

\/* The parent container taking up the full viewport *\/\r\n.container {\r\n  width: 100%;\r\n  height: 100vh;\r\n  display: block;\r\n  position: relative;\r\n  overflow: hidden;\r\n}\r\n\r\n\/* The div that contains the image  *\/\r\n\/* Centering trick: https:\/\/css-tricks.com\/centering-percentage-widthheight-elements\/ *\/\r\n.image-container {\r\n  position: absolute;\r\n  top: 50%;\r\n  left: 50%;\r\n  transform: translate(-50%,-50%);\r\n  z-index: 2; \/* Makes sure this is on top *\/\r\n}\r\n\r\n\/* The image inside the first div *\/\r\n.image-container img {\r\n  -webkit-filter: drop-shadow(-4px 5px 5px rgba(0,0,0,0.6));\r\n  filter: drop-shadow(-4px 5px 5px rgba(0,0,0,0.6));\r\n  height: 200px;\r\n}\r\n\r\n\/* The div that holds the text that will be revealed *\/\r\n\/* Same centering trick *\/\r\n.text-container {\r\n  position: absolute;\r\n  top: 50%;\r\n  left: 50%;\r\n  transform: translate(-50%,-50%);\r\n  z-index: 1; \/* Places this below the image container *\/\r\n  margin-left: -100px;\r\n}<\/code><\/pre>\n

We’re leaving vendor prefixes out the code examples throughout this post, but they should definitely be considered if using this in production environment.<\/p>\n

Here\u2019s what that gives us so far, which is basically our two elements stacked one on top of the other.<\/p>\n

\n See the Pen
\n Revealing Text Animation Part 1 – Mail Elements<\/a> by Jesper Ekstrom (
@jesper-ekstrom<\/a>)
\n on
CodePen<\/a>.<\/span>\n<\/p>\n

Step 2: Hide the text behind a block<\/h3>\n

To make our text start displaying from left to right, we need to add another div inside our .text-container<\/code>:<\/p>\n

<!-- ... -->\r\n\r\n  <!-- The div containing the text that's revealed -->\r\n  <div class=\"text-container\">\r\n    <h1>Animation<\/h1>\r\n    <div class=\"fading-effect\"><\/div>\r\n  <\/div>\r\n  \r\n<!-- ... --><\/code><\/pre>\n

…and add these CSS properties and values to it: <\/p>\n

.fading-effect {\r\n  position: absolute;\r\n  top: 0;\r\n  bottom: 0;\r\n  right: 0;\r\n  width: 100%;\r\n  background: white;\r\n}<\/code><\/pre>\n

As you can see, the text is hiding behind this block now, which has a white background color to blend in with our parent container.<\/p>\n

If we try changing the width of the block, the text starts to appear. Go ahead and try playing with it in the Pen:<\/p>\n

\nSee the Pen
\nRevealing Text Animation Part 2 – Hiding Block<\/a> by Jesper Ekstrom (
@jesper-ekstrom<\/a>)
\non
CodePen<\/a>.<\/span>\n<\/p>\n

There is another way of making this effect without adding an extra block with a background over it. I will cover that method later in the article. 🙂<\/p>\n

Step 3: Define the animation keyframes<\/h3>\n

We are now ready for the fun stuff! To start animating our objects, we’re going to make use of the animation property<\/a> and its @keyframes<\/code> function. Let\u2019s start by creating two different @keyframes<\/code>, one for the image and one for the text, which will end up looking like this:<\/p>\n

\/* Slides the image from left (-250px) to right (150px) *\/\r\n@keyframes image-slide {\r\n  0% { transform: translateX(-250px) scale(0); }\r\n  60% { transform: translateX(-250px) scale(1); }\r\n  90% { transform: translateX(150px) scale(1); }\r\n  100% { transform: translateX(150px) scale(1); }  \r\n}\r\n\r\n\/* Slides the text by shrinking the width of the object from full (100%) to nada (0%) *\/\r\n@keyframes text-slide {\r\n  0% { width: 100%; }\r\n  60% { width: 100%; }\r\n  75%{ width: 0; }\r\n  100% { width: 0; }\r\n}<\/code><\/pre>\n

I prefer to add all @keyframes<\/code> on the top of my CSS file for a better file structure, but it\u2019s just a preference.<\/p>\n

The reason why the @keyframes<\/code> only use a small portion of their percent value (mostly from 60-100%) is that I have chosen to animate both objects over the same duration instead of adding an animation-delay<\/code><\/a> to the class it\u2019s applied to. That\u2019s just my preference. If you choose to do the same, keep in mind to always have a value set for 0% and 100%; otherwise the animation can start looping backward or other weird interactions will pop up.<\/p>\n

To enable the @keyframes<\/code> to our classes, we\u2019ll call the animation name on the CSS property animation<\/code>. So, for example, adding the image-slide<\/code> animation to the image element, we\u2019d do this:<\/p>\n

.image-container img {\r\n  \/* [animation name] [animation duration] [animation transition function] *\/\r\n  animation: image-slide 4s cubic-bezier(.5,.5,0,1);\r\n}<\/code><\/pre>\n

The name of the @keyframes<\/code> works the same as creating a class. In other words the name doesn\u2019t really matter as long as it\u2019s called the same on the element where it\u2019s applied.<\/p>\n

If that cubic-bezier<\/code> part causes head scratching, then check out this post by Michelle Barker<\/a>. She covers the topic in depth. For the purposes of this demo, though, it\u2019s suffice to say that it is a way to create a custom animation curve for how the object moves from start to finish. The site cubic-bezier.com<\/a> is a great place to generate those values without all the guesswork.<\/p>\n

We talked a bit about wanting to avoid a looping animation. We can force the object to stay put once the animation reaches 100% with the animation-fill-mode<\/code> sub-property:<\/p>\n

.image-container img {\r\n  animation: image-slide 4s cubic-bezier(.5,.5,0,1);\r\n  animation-fill-mode: forwards;\r\n}<\/code><\/pre>\n

So far, so good!<\/p>\n

\nSee the Pen
\nRevealing Text Animation Part 3 – @keyframes<\/a> by Jesper Ekstrom (
@jesper-ekstrom<\/a>)
\non
CodePen<\/a>.<\/span>\n<\/p>\n

Step 4: Code for responsiveness<\/h3>\n

Since the animations are based on fixed (pixels) sizing, playing the viewport width will cause the elements to shift out of place, which is a bad thing when we\u2019re trying to hide and reveal elements based on their location. We could create multiple animations on different media queries to handle it (that\u2019s what I did at first), but it\u2019s no fun managing several animations at once. Instead, we can use the same animation and change its properties at specific breakpoints.<\/p>\n

For example:<\/p>\n

@keyframes image-slide {\r\n  0% { transform: translatex(-250px) scale(0); }\r\n  60% { transform: translatex(-250px) scale(1); }\r\n  90% { transform: translatex(150px) scale(1); }\r\n  100% { transform: translatex(150px) scale(1); }\r\n}\r\n\r\n\/* Changes animation values for viewports up to 1000px wide *\/\r\n@media screen and (max-width: 1000px) {\r\n  @keyframes image-slide {\r\n    0% { transform: translatex(-150px) scale(0); }\r\n    60% { transform: translatex(-150px) scale(1); }\r\n    90% { transform: translatex(120px) scale(1); }\r\n    100% { transform: translatex(120px) scale(1); }\r\n  }\r\n}<\/code><\/pre>\n

Here we are, all responsive!<\/p>\n

\nSee the Pen
\nRevealing Text Animation Part 4 – Responsive<\/a> by Jesper Ekstrom (
@jesper-ekstrom<\/a>)
\non
CodePen<\/a>.<\/span>\n<\/p>\n

Alternative method: Text animation without colored background<\/h3>\n

I promised earlier that I\u2019d show a different method for the fade effect, so let\u2019s touch on that. <\/p>\n

Instead of using creating a whole new div — <div class=\"fading-effect\"><\/code> — we can use a little color trickery to clip the text and blend it into the background:<\/p>\n

.text-container {\r\n  background: black;\r\n  -webkit-background-clip: text;\r\n  -webkit-text-fill-color: transparent;\r\n}<\/code><\/pre>\n

This makes the text transparent which allows the background color behind it to bleed in and effectively hide it. And, since this is a background, we can change the background width and see how the text gets cut by the width it\u2019s given. This also makes it possible to add linear gradient colors to the text or even a background image display inside it. <\/p>\n

The reason I didn’t go this route in the demo is because it isn’t compatible with Internet Explorer (note those -webkit<\/code> vendor prefixes). The method we covered in the actual demo makes it possible to switch out the text for another image or any other object.<\/p>\n


\n

Pretty neat little animation, right? It\u2019s relatively subtle and acts as a nice enhancement to UI elements. For example, I could see it used to reveal explanatory text or even photo captions. Or, a little JavaScript could be used to fire the animation on click or scroll position to make things a little more interactive.<\/p>\n

Have questions about how any of it works? See something that could make it better? Let me know in the comments!<\/p>\n","protected":false},"excerpt":{"rendered":"

I want to take a closer look at the CSS animation property and walk through an effect that I used on my own portfolio website: making text appear from behind a moving object. Here\u2019s an isolated example if you\u2019d like to see the final product.<\/p>\n","protected":false},"author":256071,"featured_media":282012,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"A little CSS animation to make one element peek-a-boo out from under another.","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[708],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/01\/3542A5F8-FB64-4C8E-BE76-2D4C64D64B77.jpeg?fit=1800%2C900&ssl=1","jetpack-related-posts":[],"featured_media_src_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/01\/3542A5F8-FB64-4C8E-BE76-2D4C64D64B77.jpeg?fit=1024%2C512&ssl=1","_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/281735"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/256071"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=281735"}],"version-history":[{"count":11,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/281735\/revisions"}],"predecessor-version":[{"id":281779,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/281735\/revisions\/281779"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/282012"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=281735"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=281735"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=281735"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}