apieceofbart
apieceofbart

Reputation: 2197

Reversible transition with delay

What I'm trying to do is to have on hover transition or animation (can be triggered via javascript with onmouseover or onmouseenter) that will also be reversible (so the opposite animation should happen on mouse leave) but

It's hard to describe without showing so please check this codepen that is pretty close to what I'm trying to achieve: http://codepen.io/anon/pen/xROOqO

There are two problems here:

Is this even possible using css transitions (perhaps keyframes animation) or should I stick to setting timers in javascript and leave out the delay from css?

Upvotes: 0

Views: 595

Answers (1)

qbolec
qbolec

Reputation: 5134

Not sure if what I'm going to present is simpler, but it seems to address some of your issues, and matches my taste.

The main idea is to admit that the problem is complicated due to multiple states, and address it using a state machine. This allows for a declarative approach like this one:

const TRANSITIONS = {
  'small-inside' : {
    'transitionend' : 'big-inside',
    'mouseover' : 'small-inside',
    'mouseout' : 'small-outside',
  },
  'small-outside' : {
    'transitionend' : 'small-outside',
    'mouseover' : 'small-inside',
    'mouseout' : 'small-outside',
  },
  'big-inside' : {
    'transitionend' : 'big-inside',
    'mouseover' : 'big-inside',
    'mouseout' : 'big-outside',
  },
  'big-outside' : {
    'transitionend' : 'small-outside',
    'mouseover' : 'big-inside',
    'mouseout' : 'big-outside',
  },
}

And quite simple handling of events:

function step(e){
  box.className = TRANSITIONS[box.className][e.type];
}
box.addEventListener('transitionend', step);
box.addEventListener('mouseover', step);
box.addEventListener('mouseout', step);

Another insight is that you can specify the delay using CSS transition-delay:3s property:

div.small-inside,
div.big-inside {
  width: 300px;
}
div.small-outside,
div.big-outside {
  width: 150px;
}
div.big-outside {
  transition-delay:3s;
}

The proof of concept is here: http://codepen.io/anon/pen/pNNMWM.

What I do not like about my solution is that it assumes that the initial state is small-outside while actually the mouse pointer could be well located within the div when the page loads. You've mentioned ability to trigger state transitions manually from JS. I believe this is possible as long as you keep track of two separate boolean variables: "is mouse inside?" and "does js asked to grow?". You can not mix them into a one state and expect correct "counting". As you see I already have 2*2=4 states because I'm trying to keep track of {small,big}x{inside,outside} - one could imagine extending it to {small,big}x{inside,outside}x{js-open,js-close} in similar manner, with some extra "events" like 'open' and 'close'.

Upvotes: 2

Related Questions