Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move each character of a div based on mouse movement

I'm developing a site and I don't know how to create a javascript animation that looks like this:

I have a div that have some text on it, and when the user moves his mouse over this text, I want each character to move independently of each other, in order to maintain a certain distance from it (the mouse). Also, I want this animation to have rotation, but it isn't that important now. Here's an image explanation:

explanation

Here's what I did so far:

HTML:

<div class="div1">Hello World</div>

Javascript:

var chars = $(".div1").html().split('');
$(".div1").empty();
for(var i = 0; i < chars.length; i++){
    $(".div1").append("<span class='letter'>"+chars[i]+"</span>");
}

JSFiddle


Can someone help me to achieve this effect? I don't know how to proceed and there's no site or answer that helped me. You can use jQuery or pure JavaScript but, please, keep it simple! Thank you.

like image 371
Tiago Marinho Avatar asked May 12 '14 03:05

Tiago Marinho


2 Answers

Oh here we go, I've found a solution for this.

What I did was using a different class name for each character (.letter + character number) and then created a way of moving the characters depending on the mouse position and distance compared to each character, so, for example, when the distance between the mouse and a character is less than X, and the mouse Y is less than the character Y, then the character will go down.

Thanks to adeneo and Derek

Here's the relevant code:

JavaScript:

var chars = $(".div1").html().split('');
$(".div1").empty();
for (var i = 0; i < chars.length; i++) {
    $(".div1").append("<span class='letter" + i + "'>" + chars[i] + "</span>");
    $(".letter" + i).css({
        "position":"relative",
    });
    $(".letter" + i).css({
        "transition": "0.5s"
    });
}

$(document).on("mousemove", function (e) {
    for (var i = 0; i < chars.length; i++) {
        var x = e.pageX,
            y = e.pageY;
        var distx = x - $(".letter" + i).offset().left + ($(".letter" + i).width() / 2);
        var disty = y - $(".letter" + i).offset().top;

    if (Math.abs(distx) < 24 && Math.abs(disty) < 24) {
        if (distx > 6 || distx < -6) {
            if (x < $(".letter" + i).offset().left) {
                $(".letter" + i).css({
                    "left": + (24 / Math.abs(distx) * Math.abs(distx)),
                        "position": "relative"
                });
            } else {
                $(".letter" + i).css({
                    "left": - (24 / Math.abs(distx) * Math.abs(distx)),
                        "position": "relative"
                });
            }
        }

        if (disty > 12 || disty < -12) {
            if (y < $(".letter" + i).offset().top + 6) {
                $(".letter" + i).css({
                    "top": + (24 / Math.abs(disty) * Math.abs(disty)),
                        "position": "relative"
                });
            } else {
                $(".letter" + i).css({
                    "top":  - (24 / Math.abs(disty) * Math.abs(disty)),
                        "position": "relative"
                });
            }
        }
    }
    distx = 0;
    disty = 0;
}

});

HTML:

<div class="div1">Hello World</div>

Updated JSFiddle with CSS Transitions to improve smoothness

like image 124
Tiago Marinho Avatar answered Oct 14 '22 19:10

Tiago Marinho


Well since you say yo want to learn, i'll give a code to help you out, but you have to work your way through, i haven't test it, i just wrote it blindly so it propably won't work but might give you a good idea of what must be done.

Html:

<div class="container">
    <div id="coolDiv" class="scatterContainer">Hello World</div>
</div>

Css:

*{margin:0;}
span:hover{
    color:#0CF;
}
.scatterContainer{
    display: inline;
}
.container {
    margin: 30px auto;
}

Javascript

LetterScatterer = (function() {

  function LetterScatterer(id) {

    this.id = id
    this.$el = $('#' + this.id);
    this.rangeOfaction = 3; // Number of characters to affect
    this.maxVerticalMovement = 10; // Value in px
    this.minVerticalMovement = 2
    this.duration = 100; // In miliseconds



    // Event Listeners

    this.$el.on(mousemove((function(_this){

        return function(e){

            var x = e.pageX;
            var y = e.pageY;

            return _this.scatter(x, y);
        }

    })(this));

  }

  LetterScatterer.prototype.splitCharacters = function() {
    var nodes = [];
    var nodesQ = 0;
    var _this = this;
    this.chars = $el.text().split('');
    $el.empty();


    for(var i = 0; i < chars.length; i++){
        var markup = "<span class='letter'>"+chars[i]+"</span>";
        nodes.push(markup);
    }

    this.$nodes = $(nodes);

    this.nodesWidth = [];
    this.$nodes.each(function(){
        var width = $(this).outerWidth();
        _this.nodesWidth.push(width);
    });

    $el.append(this.$nodes);


  }

  LetterScatterer.prototype.scatter = function(x, y) {
    var epicenter;
    var offset = 0;
    var midPoint, farestLeft;

    for(var i = 0, len = this.nodesWidth.length; i < len; i++){
        offset += this.nodesWidth[i];
        if(x <= offset){
            epicenter = i;
            break;
        }
    }

    leftRange = (this.rangeOfaction - 1) / 2; // We remove one, this is our epicenter, then we get left and right halves


    farestLeft = epicenter - leftRange;
    for(var i = farestLeft; i < this.rangeOfaction; i++){
        this.animateY($node[i]);
    }


  }


  LetterScatterer.prototype.animateY = function(node, verticalDisplacement) {
    var $node = $(node);
    $node.animate({margin-top: verticalDisplacement + 'px'}, this.duration);
  }



  return LetterScatterer;

})();

letterScatterer = new LetterScatterer('coolDiv');

What you see in the code is a classlike function, first you pass it the id of the element containing the text that will be scattered. There are some config varaibles, range of action is lets say, if you mouse over one character, how many characters to the left and to the right (also including the current hovered element) should be animated, the max and min verticalMovement, determines how much should move the one that is hovered (max) and those further apart will use min, those in between should interpolate, but i didn't code that far.

We then got a mousemove listener, that calls the method scatter, this method finds which items is currently hovered by adding up each character widht, but now i think about it, it should be easier to just add a listener to the span, and get the current index of that element with the jQuery method index(), then based on that index you animate that one and those in the range. You must create the code that calculates the rotation, and x movement if you want to, but i think i gave you a lot to start, it took me a while to code it, so i hope it helps and this answer satisfies your question. :)

like image 23
Lu Roman Avatar answered Oct 14 '22 20:10

Lu Roman