Transition from sentence to one word

Viewed 369

What I am trying to achieve is to animate an element width few words in the way that hovered word leaves in the center of the element and the rest smoothly goes out of the bounds. I would also keep it as clear as possible in HTML and not to use fixed pixel amount of margins/widths to position elements.

The very lousy sketch of what I have on my mind is here:

* {
  transition: all 1.5s;
}

div {
  min-width: 150px;
  width: 30%;
  height: 25px;
  line-height: 25px;
  background: orange;
  text-align: center;
  overflow: hidden;
}

div:hover {
  background: white;
  word-spacing: 300px;
}

a:hover::after,
a:hover::before {
  content: ' ';
}
<div>
  <a class="afirst" href="">First</a> & <a class="asecond" href="">Second</a>
</div>

Each word on hover should go to center (possibly not with those 'jumps' visible now when other words disapear). Do you have any ideas? I'm pretty sure that the way I try to follow with word-spacing is wrong.

3 Answers

The issue is that when increasing word spacing the text goes to new line and create this jump thing. So you may add white-space: nowrap and you can also use padding-left to push text and make it in the center :

div {
  min-width: 150px;
  width: 180px;
  height: 25px;
  line-height: 25px;
  background: orange;
  text-align: center;
  overflow: hidden;
  transition: all 0.5s;
  box-sizing: border-box;
  white-space: nowrap;
}

div:hover {
  word-spacing: 80px;
  padding-left: 80px;
  background: white;
}
<div>
  <a class="afirst" href="">First</a> & <a class="asecond" href="">Second</a>
</div>

Actually the word spacing is applied to the div so you cannot apply hover on word. It's also easier to apply this technique on the first word as the second one will be hide with overflow, but am not sure how you can do the same with the second word with the use of word-spacing.


Here is another idea on how you can do without word-spacing. I used some padding animation and also pseudo element to hide the first word when hovering the second one.

div {
  min-width: 150px;
  width: 180px;
  height: 25px;
  line-height: 25px;
  background: orange;
  text-align: center;
  overflow: hidden;
  transition: all 0.5s;
  box-sizing: border-box;
  white-space: nowrap;
}

.afirst,
.asecond {
  position:relative;
  display: inline-block;
  transition: all 0.5s;
}

.afirst:hover {
  padding: 0 44%;
  background: white;
}
.asecond:hover {
  padding: 0 50% 0 0;
  background: white;
}
.asecond:hover::before {
  content:" ";
  position:absolute;
  left:-50%;
  width:50%;
  top:0;
  bottom:0;
  z-index:99;
  background:#fff;
}
<div>
  <a class="afirst" href="">First</a> & <a class="asecond" href="">Second</a>
</div>

I think you can generalize this solution by using :before element in the left and :after element in the right to hide everything else.

Here is an example with multiple word (but not giving center alignement correctly, still need improvement) :

div {
  min-width: 150px;
  height: 25px;
  line-height: 25px;
  background: orange;
  text-align: center;
  overflow: hidden;
  transition: all 0.5s;
  box-sizing: border-box;
  white-space: nowrap;
}

.word {
  position: relative;
  display: inline-block;
  transition: all 0.5s;
}

.word:hover {
  background: white;
  padding: 0 40%;
}

.word:hover::before {
  content: " ";
  position: absolute;
  left: -50%;
  width: 50%;
  top: 0;
  bottom: 0;
  z-index: 99;
  background: #fff;
}

.word:hover::after {
  content: " ";
  position: absolute;
  right: -50%;
  width: 50%;
  top: 0;
  bottom: 0;
  z-index: 99;
  background: #fff;
}
<div>
  <a class="word" href="">First</a> &
  <a class="word" href="">Second</a> &
  <a class="word" href="">third</a> &
  <a class="word" href="">Fourth</a>
</div>

Another solution with perfect centering but less animation for the other words :

div {
  position:relative;
  height: 25px;
  width:500px;
  margin:0 auto;
  line-height: 25px;
  background: orange;
  text-align: center;
  overflow: hidden;
  transition: all 0.5s;
  box-sizing: border-box;
  white-space: nowrap;
}

.word {
  position: relative;
  z-index:9;
  display: inline-block;
  transition: all 0.5s;
}

.word:hover {
  position:absolute;
  right:0;
  left:0;
  top:0;
  bottom:0;
  text-align:center;
  background: white;
  z-index:99;
}
<div>
  <a class="word" href="">First</a> &
  <a class="word" href="">Second</a> &
  <a class="word" href="">third</a> &
  <a class="word" href="">Fourth</a>
</div>

This version uses Flex Box, accommodates multiple items, and does not use fixed widths.

The trouble I kept having playing around with this was centering items without using fixed widths. Especially those that were later in line and trying to push items to the left.

Solution: flex box order.

You can set the order of flex items numerically, so simply setting it to -1 on hover puts the hovered item first in the list so you eliminate the issue of flexing items leftward.

The animation may not be as fluid as you're looking for because you can only transition order by integer. So it's immediately set along with the width, and then the other properties transition.

div.outer {
  width: 50vw;
  height: 30px;
  background: orange;
  text-align: center;
  overflow: hidden;
  position: relative;
  padding: 0;
}

div.inner {
  position: relative;
  margin: 0 auto;
  height: 30px;
  width: 50vw;
  display: flex;
  justify-content: center;
  align-items: center;
  background: transparent;
  flex-wrap: wrap;
}

span {
   order: 1;
   padding: 0 3px;
   flex-shrink:1;
}

a {
  z-index: 0;
  order: 1;
  height: 30px;
  line-height: 30px;
  flex-grow: 0;
  flex-shrink: 1;
  transition: background .5s, flex-grow .25s, flex-shrink .25s;
 }

a:hover {
  z-index: 10;
  width: 50vw;
  order: -1;
  flex-grow: 1;
  background: #eee;
}
<div class="outer">
  <div class="inner">
    <a href="">First</a>
    <span>&amp;</span>
    <a href="">Second</a>
    <span>&amp;</span>
    <a href="">Third</a>  
  </div>
</div>

This isn't perfect, but I think it comes pretty close to achieving what you were looking for. Using a little bit of jQuery was necessary for math (but not the animations themselves).

Hope this gets you further!

function reset() {
  // reset all spans
  $('.animate span').css('transform', '');
  $('.animate span.active').removeClass('active');
}

$('.animate').on('mouseover', 'span:not(.active)', function() {
  reset();

  // set current hovered word to active
  $(this).addClass('active');

  // slide over previous/next words
  $(this).prevAll().css('transform', 'translateX(-100vw)');
  $(this).nextAll().css('transform', 'translateX(100vw)');

  // determine offset for center of hovered word
  var center = (window.innerWidth / 2) - ($(this).offset().left + ($(this).width() / 2));
  // slide current word to center
  $(this).css('transform', 'translateX(' + center + 'px)');
});

// when leaving animate section, reset
$('.animate').mouseleave(reset);
.animate {
  background-color: orange;
  color: white;
  font-family: sans-serif;
  text-align: center;
  transition: .5s background-color, .5s color;
}

.animate:hover {
  background-color: white;
  color: orange;
}

span {
  display: inline-block;
  transition: 3s transform;
}

a {
  color: inherit;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="animate">
  <span><a href="#">First</a></span> <span>&amp;</span> <span><a href="#">Second</a></span> <span>&amp;</span> <span><a href="#">Third</a></span> <span>and</span> <span><a href="#">Fourth</a></span>
</div>

Related