Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
alan0buchanan

How to achieve 'floating' and mouse-tracking effects from this example?

Recommended Posts

Here's the site: http://mojobar.co.uk/

 

I believe the animations are build with GSAP based on what Wappalyzer tells me.

 

  1. When you hover over a tile, a skull becomes visible within the tile. The skull moves around in a 'floaty' way - yoyoing up and down and rotating slightly.
  2. When you hover over the skull, it tracks your mouse movement to an extent.

 

From dev tools I can see it's animating a combination of `transform: translate()` and `transform: matrix()`:
 5932891a9f75c_ScreenShot2017-06-03at11_01_20.png.e49096be5b9a3177c0f67997e673d509.png

 

I thought these animations were really neat. Any idea if there is a GSAP plugin/technique to achieve them?

Link to comment
Share on other sites

That's a cool effect, thanks for sharing.

GSAP is definitely being used for the animation, but there isn't a plugin or tool that is just going to create that entire effect for you.

 

I haven't been able to dissect or interpret all the code but I'm guessing they are using percentage based translations AND a matrix so that they can independently run percentage-based animations and px-based translations simultaneously. I further suspect that they are using 1 type for reacting to the mouse position and the other for the repetitious motion.

 

In GSAP when you set x:100 it will see that as a pixel value and use either a 2d or 3d matrix. When you specify % as units such as x:"100%"  or use the special xPercent/yPercent properties GSAP will create a separate translate() function as shown in your image above.

 

You read more about percentage-based translation here: https://greensock.com/gsap-1-13-1 and the CSSPlugin docs: https://greensock.com/docs/#/HTML5/GSAP/Plugins/CSSPlugin/

 

For some of the motion (most likely the mouse-following) they are using requestAnimationFrame to repeatedly figure out the distance to the mouse and then they are doing TweenLite.set() to offset the position. Blake like's using the ModifiersPlugin for mouse followers and gets great results: 

See the Pen qNPBpJ by osublake (@osublake) on CodePen

 

That's an advanced demo.

 

For the site you referenced, likewise it is an advanced effect, but I grabbed this chunk of code which I'm pretty sure is doing most of the heavy lifting.

 

function() {
    var t = $("#locations");
    if (t.length <= 0 || Modernizr.touch)
        return !1;
    var e = t.find(".location-panel:nth-child(2) .location-inner");
    e = {
        root: e,
        skull: e.find(".skull img")
    };
    var i = {
        working: null,
        index: 1,
        y: 0,
        z: 0,
        speed: 3,
        modify: 0,
        tween: !1,
        mouse: {
            x: site.width / 6,
            y: site.height / 2
        },
        max: {
            y: 10,
            z: 20
        },
        min: {
            y: -10,
            z: 0
        }
    }
      , n = function(n) {
        t.find(".location-inner").each(function(t) {
            var r = $(this)
              , o = r.find(".skull img")
              , s = r.find(".background")
              , a = r.parent().index();
            return n ? (r.off("mouseenter.landing"),
            void TweenMax.set([o, s], {
                clearProps: "all"
            })) : void r.on("mouseenter.landing", function(t) {
                if (!r.is(".active")) {
                    if (site.width >= setting.mediaQuery.medium) {
                        TweenMax.fromTo(o, .4, {
                            scale: 0
                        }, {
                            scale: 1,
                            ease: Back.easeOut,
                            delay: .1
                        });
                        var n = 1.2 * r.height();
                        TweenMax.set(s, {
                            top: "50%",
                            left: "50%",
                            borderRadius: "50%",
                            width: n,
                            height: n,
                            marginTop: 0 - n / 2,
                            marginLeft: 0 - n / 2
                        }),
                        TweenMax.fromTo(s, .4, {
                            scale: 0
                        }, {
                            scale: 1,
                            ease: Cubic.easeOut,
                            force3D: !0
                        })
                    }
                    e.root.removeClass("active"),
                    e = {
                        root: r,
                        skull: o
                    },
                    e.root.addClass("active"),
                    i.index = a
                }
            })
        })
    }
      , r = function() {
        var t = rangeToRange(i.mouse.y, site.height, 0, i.max.y, i.min.y)
          , n = 0 - rangeToRange(i.mouse.x, site.width / 3, 0, i.max.z, i.min.z);
        i.y += (t - i.y) / i.speed,
        i.z += (n - i.z) / i.speed,
        TweenMax.set(e.skull, {
            y: -50 + (i.modify + i.y) + "%",
            rotation: i.modify + i.z
        }),
        i.working && requestAnimationFrame(r)
    }
      , o = function(t) {
        t ? ($core.body.off("mousemove.landing"),
        i.tween && i.tween.kill()) : (i.tween = TweenMax.to(i, 2, {
            modify: 10,
            yoyo: !0,
            repeat: -1,
            ease: Cubic.easeInOut
        }),
        $core.body.on("mousemove.landing", function(t) {
            i.mouse.x = t.pageX - site.width / 3 * i.index,
            i.mouse.y = t.pageY
        }),
        r())
    };
    site.resize.after.landing = function() {
        var t = site.width >= setting.mediaQuery.medium;
        t !== i.working && (i.working = t,
        o(!t),
        e.root.toggleClass("active", t),
        n(!t))
    }

 

You could probably come up with something considerably simpler though. Perhaps start with a tween that moves something up and down using a percentage value and then try to offset using a pixel value based on distance to the mouse. 

  • Like 5
Link to comment
Share on other sites

Right here...

r = function() {
  var t = rangeToRange(i.mouse.y, site.height, 0, i.max.y, i.min.y)
  , n = 0 - rangeToRange(i.mouse.x, site.width / 3, 0, i.max.z, i.min.z);
  i.y += (t - i.y) / i.speed,
  i.z += (n - i.z) / i.speed,
  TweenMax.set(e.skull, {
    y: -50 + (i.modify + i.y) + "%",
    rotation: i.modify + i.z
  }),
i.working && requestAnimationFrame(r)

 

I see linear interpolation (lerp) being used...

i.y += (t - i.y) / i.speed
i.z += (n - i.z) / i.speed

 

 

See the Pen 578e170d729eaa97fc54b89c57ee380f by osublake (@osublake) on CodePen

 

 

  • Like 6
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×