Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cycling animation of rainbow-coloured text

CYCLING COLORS ON HOVER With JavaScript/jQuery

I'm trying to take a block of text, color each letter according to calculated position between HSL 0deg and 360deg, and on hover animate the colors to the right. I know this is weird, but bear with me. What I want is animated rainbow text on hover.

I've covered the logic of making all of this happen once, but can't get hover cycling behavior to work.

here is a link to the codepen.io: http://cdpn.io/txmlf

I've tried using JavaScript mouse events and jQuery's .hover(). My initial thought was to set an interval on mouse enter and clear it on exit.

I'd truly appreciate any help on this obviously very important project.

like image 445
Todd Avatar asked Oct 03 '13 17:10

Todd


2 Answers

You might wanna think about how this'll affect UX, but what about this: http://jsfiddle.net/7Xuep/6/

Ok, so rotating through all the colours of the rainbow is easy enough using CSS-animations. The issue is then linking them up to all your span tags such that the animation starts in the right place. (i.e. you need the green letter to start it's animation from the colour green etc.) To do this, we can use animation-delay:

https://developer.mozilla.org/en-US/docs/Web/CSS/animation-delay

which we can use to start the rainbow animation at the appropriate colour for each letter. By using a linear timing function, it's simple to determine at what time the animation will arrive at each colour. Therefore, it's just a matter of attaching the right animation-delay value to each <span> element. I do that by just taking your already generated HTML and adding in the CSS rules to each element's style attribute:

var animTime = 6, // time for the animation in seconds
    hueChange = 3, // the hue change from one span element to the next
    prefixes = ["", "-webkit-", "-moz-", "-o-"],
    numPrefixes = prefixes.length;

$('.unicorn').find('span').each(function (i) {
    for (var j = 0; j < numPrefixes; j++) {
        $(this).css(prefixes[j] + 'animation-delay', (animTime * ((i * hueChange) % 360) / 360) - animTime + 's');
    }
});

but you could do this at the same time as you're generating all your span elements. Then it's just a case of setting up the animation using CSS:

.unicorn:hover span {

    animation: colorRotate 6s linear 0s infinite;

}

@keyframes colorRotate {
    from {
        color: rgb(255, 0, 0);
    }
    16.6% {
        color: rgb(255, 0, 255);
    }
    33.3% {
        color: rgb(0, 0, 255);
    }
    50% {
        color: rgb(0, 255, 255);
    }
    66.6% {
        color: rgb(0, 255, 0);
    }
    83.3% {
        color: rgb(255, 255, 0);
    }
    to {
        color: rgb(255, 0, 0);
    }
}

All this gets us to here: http://jsfiddle.net/P6WVg/7/

Now, if you don't want the colours to reset once someone is no longer hovering over .unicorn then you can use animation-play-state:

https://developer.mozilla.org/en-US/docs/Web/CSS/animation-play-state

However, I was finding that Chrome has an issue with combining an initial value of -webkit-animation-play-state:paused; and a negative value of -webkit-animation-delay such that it just displayed the first frame (i.e. color: rgb(255,0,0); in this case). Therefore I had to use an event listener to add a class containing the animation CSS on the first hover, and this leads us to:

http://jsfiddle.net/7Xuep/6/

(that bug in chrome can be tracked here: https://code.google.com/p/chromium/issues/detail?id=269340)

like image 104
Ben Jackson Avatar answered Sep 27 '22 23:09

Ben Jackson


Why not keep it simple, (with only your HTML) this is all you need:

Live demo

var step = 4, // colorChage step, use negative value to change direction
    ms   = 10,  // loop every
    $uni = $('.unicorn'),
    txt  = $uni.text(),
    len  = txt.length,
    lev  = 360/len,
    newCont = "",
    itv;
alert(lev+' '+len);

for(var i=0; i<len; i++)newCont += "<span style='color:hsla("+ i*lev +", 100%, 50%, 1)'>"+ txt.charAt(i) +"</span>";

$uni.html(newCont); // Replace with new content
var $ch = $uni.find('span'); // character

function stop(){ clearInterval(itv); }
function anim(){
  itv = setInterval(function(){
    $ch.each(function(){
      var h = +$(this).attr('style').split(',')[0].split('(')[1]-step % 360;
      $(this).attr({style:"color:hsla("+ h +", 100%, 50%, 1)"});
    });
  }, ms); 
}

$uni.hover(anim,stop);

Tested in FF, Chrome, Opera

like image 27
Roko C. Buljan Avatar answered Sep 27 '22 21:09

Roko C. Buljan