Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

392
Vistas
CSS: How to don't reset onhover animation on every hover

I have a simple on-hover CSS animation which makes slide transition between images.

When the user makes the hover on SECTION ONE and before the animation ends make hover on SECTION two, animation restart and make lagging move.

MY CODE:

var $circle = $('#circle');

function moveCircle(e) {
    TweenLite.to($circle, 0.8, {
    css: {
      left: e.pageX,
      top: e.pageY
    }
  });
}

$(window).on('mousemove', moveCircle);
@import "compass/css3";

@keyframes in {
    from {
        transform: translateY(-100%);
    }
    to {
        transform: translateY(0);
    }
}
@keyframes out {
    from {
        transform: translateY(0);
    }
    to {
       transform: translateY(100%);
    }
}

html {
  background: #0E3741;
}

#circle {
  position: absolute;
  pointer-events : none;
  width: 400px;
  height: 200px;
  top: 50%;
  left: 50%;
  margin: -50px 0 0 -50px;
}

#circle .circle-wrapper {
    overflow: hidden;
    width: 400px;
    height: 200px;
    position: relative;
  }
  
#circle img {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 400px;
    height: 200px;
    object-fit: cover;
    overflow: hidden;
  }

#wrapper {
  display: flex;
  flex-direction: column;
}

.special-element {
  width: 100%;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
}

#one {
  background: blue;
}

#two {
  background: red;
}

#one:hover ~ #circle .circle-wrapper #imgOne {
  animation: in 1s ease-in-out;
  z-index: 2;
}

#one:hover ~ #circle .circle-wrapper #imgTwo {
  animation: out 1s ease-in-out;
}

#two:hover ~ #circle .circle-wrapper #imgTwo {
  animation: in 1s ease-in-out;
  z-index: 2;
}

#two:hover ~ #circle .circle-wrapper #imgOne {
  animation: out 1s ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenMax.min.js"></script>

<section id="wrapper">
  <section class="special-element" id="one">
    section one
  </section>

  <section class="special-element" id="two">
    section two
  </section>
  
  <div id="circle">
    <div class="circle-wrapper">
      <img id="imgOne" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
    <img id="imgTwo" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
    </div>
  </div>
</section>

Is there any solution how I can prevent this lagging issue?

Maybe is there any solution to how I can solve it and make this animation smooth?

I'm looking for something like animation on this website.

over 4 years ago · Santiago Trujillo
4 Respuestas
Responde la pregunta

0

I think that problem is because "moving circle function". Moving dom element with Left and right is not good for performance. You should move the circle with "transform". transform runs with GPU acceleration and it performs better and make move smooth.

Try this code.

function moveCircle(e) {
    TweenLite.to($circle, 0.8, {
    css: {
      transform: `translate(${e.pageX}px, ${e.pageY}px)`
    }
  });
}

over 4 years ago · Santiago Trujillo Denunciar

0

Updated version

You can do a simplified version of it with gsap. It is probably best not to mix plain css too much with the gsap, unless you use css inside the gsap library. Because gsap will manipulate some of the props. E.g. the transformation. And it is better to use transform than just left/top because it is hardware accelerated.

I've done some improvements to the code I've posted before. It looks smoother now. In addition, I have added a little zoom and horizontal-shift effect - similar to the animation on the referenced website. Also, the animation now starts from the bottom.

The animation is really well done on the referenced page. It is done with WebGL. This is not your every-day animation and requires quite a bit of effort to make it work - at least for someone who is not a designer. It uses a 3d transformation matrix and some other effects together.

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset-css@5.0.1/reset.min.css" />
  <script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
  <script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/CSSPlugin.min.js"></script>
  <style type="text/css">
    .section {
      display: block;
      width: 100%;
      height: 300px;
      border-bottom: 1px solid red;
    }
    
    .overlay {
      position: absolute;
      top: 0;
      left: 0;
      display: none;
      background: transparent;
      z-index: -1;
    }
    
    .stack {
      position: relative;
      min-width: 300px;
      min-height: 300px;
      width: 480px;
      height: 320px;
      max-width: 480px;
      max-height: 320px;
      overflow: hidden;
      z-index: 1;
    }
    
    .img {
      position: absolute;
      top: 0;
      left: 0;
      width: auto;
      height: auto;
      max-width: 100%;
      object-fit: contain;
      z-index: -1;
    }
  </style>
</head>

<body>
  <main class="main">
    <section class="section section-1" data-img="img-1">section 1</section>
    <section class="section section-2" data-img="img-2">section 2</section>
    <div class="overlay">
      <div class="stack">
        <img id="img-1" class="img" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
        <img id="img-2" class="img" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
      </div>
    </div>
  </main>

  <script type="application/javascript">
    window.onload = () => {
      const overlay = document.querySelector(".overlay");
      const stack = document.querySelector(".stack");
      const s1 = document.querySelector(".section-1");
      const s2 = document.querySelector(".section-2");
      const main = document.querySelector(".main");

      const overlaySize = {
        width: 480,
        height: 320
      };
      const easeFunc = "sine.inOut";
      const easeDuration = 0.5;

      let animation;
      let activeSection;
      let currentTarget;

      function createAnimation() {
        //console.log('create animation');
        t1 = gsap.timeline({
          paused: true
        });
        t1.to(currentTarget, {
          zIndex: 2,
          display: "block"
        }, 0);
        t1.fromTo(currentTarget, {
          y: "100%"
        }, {
          y: 0,
          duration: easeDuration,
          ease: easeFunc
        }, 0);
        t1.to(currentTarget, {
          scale: 1.25,
          transformOrigin: "center",
          duration: easeDuration,
          ease: easeFunc
        }, 0);
        stack.querySelectorAll(".img").forEach((it) => {
          if (it !== currentTarget) {
            t1.to(it, {
              zIndex: -1
            }, 0);
            t1.to(it, {
              scale: 1,
              transformOrigin: "center"
            }, 0);
            t1.to(it, {
              display: "none"
            }, easeDuration);
          }
        });
        return t1;
      }

      function onMouseLeave(e) {
        const target = e.target;
        //console.log("leave", e.target);
        if (target === activeSection) {
          gsap.set(overlay, {
            display: "none"
          });
          currentTarget = null;
        }
      }

      function onMouseEnter(e) {
        currentTarget = stack.querySelector(`#${e.target.dataset.img}`);
        gsap.set(overlay, {
          display: "block"
        });
        if (!animation) {
          //console.log("undefined animation")
          animation = createAnimation();
          animation.play();
        } else if (animation.isActive()) {
          //console.log("still active");
          animation.timeScale(10); // fast forward the rest of the animation
          animation = createAnimation();
          animation.timeScale(1).play();
        } else {
          //console.log("no longer active");
          animation = createAnimation();
          animation.play();
        }
      }

      function onMouseMove(e) {
        const hoveredEl = document.elementFromPoint(e.pageX, e.pageY);
        if (hoveredEl.classList.contains("section")) {
          if (activeSection !== hoveredEl) {
            activeSection = hoveredEl;
          }
        } else if (hoveredEl.classList.contains("overlay") || hoveredEl.classList.contains("stack") || hoveredEl.classList.contains("pointer")) {
          // do nothing
        } else {
          if (activeSection) {
            activeSection = null;
          }
        }

        if (currentTarget) {
          // update overlay
          gsap.set(overlay, {
            x: e.pageX - overlaySize.width / 2,
            y: e.pageY - overlaySize.height / 2
          });

          // add a little horizontal-shift effect
          const dx = window.innerWidth / 2 - e.pageX;
          const offsetX = dx / window.innerWidth / 2 * 100;
          gsap.to(currentTarget, {
            x: offsetX * 2,
            duration: 2
          }, 0);
        }
      }

      gsap.set(overlay, {
        x: 0,
        y: 0
      });
      stack.querySelectorAll('.img').forEach((it) => gsap.set(it, {
        x: 0,
        y: "100%"
      }));

      window.addEventListener("mousemove", onMouseMove);
      s1.addEventListener("mouseleave", onMouseLeave);
      s2.addEventListener("mouseleave", onMouseLeave);
      s1.addEventListener("mouseenter", onMouseEnter);
      s2.addEventListener("mouseenter", onMouseEnter);
    }
  </script>
</body>

</html>

Old answer

I have been playing around a little bit with the gsap library today. I've honestly never done anything with or like it. Tried to do it with the x and y params that you may pass to gsap. It will take care of the transformations - also the TimeLine stuff
is quite handy. The result is not that great, also the animations look like it could be done better, but maybe it might still help you out. You could also improve some of the logic and animation probably. At least it runs quite stable - performance wise.

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset-css@5.0.1/reset.min.css" />
  <script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
  <script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/CSSPlugin.min.js"></script>
  <style type="text/css">
    .section {
      display: block;
      width: 100%;
      height: 200px;
      border-bottom: 1px solid red;
    }
    
    .overlay {
      position: absolute;
      top: 0;
      left: 0;
      display: none;
      border: none; // 1px dashed black;
      background: transparent; // lavender;
      overflow: hidden;
    }
    
    .stack {
      position: relative;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      min-width: 300px;
      min-height: 300px;
      width: 480px;
      height: 320px;
      z-index: 0;
    }
    
    .anim-img {
      position: absolute;
      top: 0;
      left: 0;
      width: auto;
      height: auto;
      max-width: 100%;
      object-fit: contain;
      z-index: 1;
    }
  </style>
</head>

<body>
  <main class="main">
    <section class="section section-1">section 1</section>
    <section class="section section-2">section 2</section>
    <div class="overlay">
      <div class="stack">
        <img id="img-1" class="anim-img" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
        <img id="img-2" class="anim-img" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
      </div>
    </div>
  </main>

  <script type="application/javascript">
    window.onload = () => {
      const overlay = document.querySelector(".overlay");
      const img1 = document.getElementById("img-1");
      const img2 = document.getElementById("img-2");
      const s1 = document.querySelector(".section-1");
      const s2 = document.querySelector(".section-2");
      const main = document.querySelector(".main");

      let anim;

      let isS1active = false;
      let isS2active = false;

      let showEl;
      let hideEl;

      let leaveTimeout;

      function reverseFadeInOut(showEl, hideEl) {
        console.log("create reverse timeline anim -> ", {
          showEl,
          hideEl
        });
        const tl = gsap.timeline({
          paused: true
        });
        tl
          .to(showEl, {
            zIndex: 1
          }, 0)
          .to(hideEl, {
            zIndex: 10
          }, 0)
          .to(hideEl, {
            y: "-100%",
            duration: 0.375
          }, 0)
          .to(hideEl, {
            display: "none"
          }, 0.375)
          .to(hideEl, {
            zIndex: 1
          }, 0.375)
          .to(showEl, {
            display: "block",
            zIndex: 10
          }, 0.375)
          .fromTo(showEl, {
            y: "-100%"
          }, {
            y: 0,
            duration: .375
          }, 0.375)
          .to(hideEl, {
            display: "none"
          });
        return tl;
      }

      function fadeInOut(showEl, hideEl) {
        console.log("create timeline anim -> ", {
          showEl,
          hideEl
        });
        const tl = gsap.timeline({
          paused: true
        });
        tl
          .to(hideEl, {
            zIndex: 1
          }, 0)
          .to(showEl, {
            display: "block",
            zIndex: 10
          }, 0)
          .fromTo(showEl, {
            y: "-100%"
          }, {
            y: 0,
            duration: .75
          }, 0)
          .fromTo(hideEl, {
            y: 0
          }, {
            y: "-100%",
            duration: .75
          }, 0)
          .to(hideEl, {
            display: "none"
          }, 0.75);
        return tl;
      }

      function animateImage() {
        if (isS1active || isS2active) {
          if (isS1active) {
            showEl = img1;
            hideEl = img2;
          } else if (isS2active) {
            showEl = img2;
            hideEl = img1;
          }

          if (!anim) {
            console.log("create new animation");
            anim = fadeInOut(showEl, hideEl);
            anim.play();
          } else {
            console.log("anim active:", anim.isActive());
            if (anim.isActive()) {
              console.log("reverse");
              anim.kill();
              anim = reverseFadeInOut(showEl, hideEl);
              anim.play();
            } else {
              anim = fadeInOut(showEl, hideEl);
              anim.play();
            }
          }
        }
      }

      function moveOverlay(e) {
        e.preventDefault();
        e.stopPropagation();
        gsap.set(overlay, {
          x: e.pageX + 15,
          y: e.pageY + 15,
          display: isS1active || isS2active ? "block" : "none"
        });
      }

      function mouseOver(e, el, isEntering) {
        e.preventDefault();
        e.stopPropagation();
        el.classList.toggle("active");
        isS1active = s1.classList.contains("active");
        isS2active = s2.classList.contains("active");
        if (isEntering) {
          clearTimeout(leaveTimeout);
          animateImage();
        } else {
          leaveTimeout = setTimeout(() => {
            if (anim) {
              console.log("kill anim");
              anim.kill();
              anim = null;
            }
            gsap.timeline({
              onComplete: () => {
                console.log("clear props");
                gsap.set(".anim-img", {
                  clearProps: true
                });
              }
            });
          }, 500);
        }
      }

      gsap.set(overlay, {
        x: "0",
        y: "0"
      });
      gsap.set(img1, {
        x: "0",
        y: "-100%"
      });
      gsap.set(img2, {
        x: "0",
        y: "-100%"
      });
      window.addEventListener("mousemove", moveOverlay);
      s1.addEventListener("mouseenter", (e) => {
        mouseOver(e, s1, true);
      });
      s1.addEventListener("mouseleave", (e) => {
        mouseOver(e, s1, false);
      });
      s2.addEventListener("mouseenter", (e) => {
        mouseOver(e, s2, true);
      });
      s2.addEventListener("mouseleave", (e) => {
        mouseOver(e, s2, false);
      });
    }
  </script>
</body>

</html>

over 4 years ago · Santiago Trujillo Denunciar

0

Yes, there is, by modifying the value of the second parameter of TweenLite.to, because that's the duration, see more here: http://www.tud.ttu.ee/im/Jaak.Henno/FlashDevelop/greensock-as3/greensock-as3/docs/com/greensock/TweenLite.html#to()

enter image description here

var $circle = $('#circle');

function moveCircle(e) {
    TweenLite.to($circle, 0.1, {
    css: {
      left: e.pageX,
      top: e.pageY
    }
  });
}

$(window).on('mousemove', moveCircle);
@import "compass/css3";

@keyframes in {
    from {
        transform: translateY(-100%);
    }
    to {
        transform: translateY(0);
    }
}
@keyframes out {
    from {
        transform: translateY(0);
    }
    to {
       transform: translateY(100%);
    }
}

html {
  background: #0E3741;
}

#circle {
  position: absolute;
  pointer-events : none;
  width: 400px;
  height: 200px;
  top: 50%;
  left: 50%;
  margin: -50px 0 0 -50px;
}

#circle .circle-wrapper {
    overflow: hidden;
    width: 400px;
    height: 200px;
    position: relative;
  }
  
#circle img {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 400px;
    height: 200px;
    object-fit: cover;
    overflow: hidden;
  }

#wrapper {
  display: flex;
  flex-direction: column;
}

.special-element {
  width: 100%;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
}

#one {
  background: blue;
}

#two {
  background: red;
}

#one:hover ~ #circle .circle-wrapper #imgOne {
  animation: in 1s ease-in-out;
  z-index: 2;
}

#one:hover ~ #circle .circle-wrapper #imgTwo {
  animation: out 1s ease-in-out;
}

#two:hover ~ #circle .circle-wrapper #imgTwo {
  animation: in 1s ease-in-out;
  z-index: 2;
}

#two:hover ~ #circle .circle-wrapper #imgOne {
  animation: out 1s ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenMax.min.js"></script>

<section id="wrapper">
  <section class="special-element" id="one">
    section one
  </section>

  <section class="special-element" id="two">
    section two
  </section>
  
  <div id="circle">
    <div class="circle-wrapper">
      <img id="imgOne" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
    <img id="imgTwo" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
    </div>
  </div>
</section>

over 4 years ago · Santiago Trujillo Denunciar

0

The website is using canvas for the moving slideshow and they've used webgl for the smooth kind of reveal effects.

<div class="site-footer">
 <div class="js-scroll-height"></div>
 <canvas width="780" height="624" class="js-webgl" style="width: 1041px;height: 833px;opacity: 1;border: 1px solid brown;"></canvas>
</div>


We can create similar reveal effect using simple animation and z-index manipulation:

var $cursor = $('#cursor');

function movecursor(e) {
  TweenLite.to($cursor, 0.8, {
    css: {
      left: e.pageX,
      top: e.pageY
    }
  });
}
$(window).on('mousemove', movecursor);

let topImg = null;
$('.special-element').mouseenter((e) => {
  //make all images at same level
  $('.img-wrapper').css({ zIndex: '1' });

  //make last focused image at second highest level
  if (topImg) topImg.css({ zIndex: '2' });

  //make current image as topImage 
  let atr = $(e.target).attr('data-img');
  topImg = $('.img-wrapper[data-img="' + atr + '"]');
  //make it topmost
  topImg.css({ zIndex: '3' });
});
:root {
  --cursor-img-height: 30vh;
  --cursor-img-width: 30vw;
}

html,
body {
  background: #0E3741;
}

#wrapper {
  display: flex;
  flex-direction: column;
}

.special-element {
  width: 100%;
  height: 33vh;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 2rem;
}

.special-element:nth-child(1) {
  background: lightblue;
}

.special-element:nth-child(2) {
  background: lightcoral;
}

.special-element:nth-child(3) {
  background: lightgreen;
}

#cursor {
  position: absolute;
  pointer-events: none;
  margin: 0;
  margin-top: calc(var(--cursor-img-height) * -0.5);
  margin-left: calc(var(--cursor-img-width) * -0.5);
  overflow: hidden;
  width: var(--cursor-img-width);
  height: var(--cursor-img-height);
}

.img-wrapper {
  position: absolute;
  bottom: 0;
  left: 0;
  width: var(--cursor-img-width);
  height: var(--cursor-img-height);
  overflow: hidden;
}

.img-wrapper>img {
  position: absolute;
  bottom: 0;
  left: 0;
  width: var(--cursor-img-width);
  height: var(--cursor-img-height);
  object-fit: fill;
  z-index: 1;
}

.special-element[data-img="one"]:hover~#cursor [data-img="one"],
.special-element[data-img="two"]:hover~#cursor [data-img="two"],
.special-element[data-img="three"]:hover~#cursor [data-img="three"] {
  animation: slide .8s ease-in-out;
}

@keyframes slide {
  from {
    height: 0px;
    transform: scale(1.2);
  }
  to {
    height: (--cursor-img-height);
    transform: scale(1);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenMax.min.js"></script>

<section id="wrapper">
  <section class="special-element" data-img="one">food</section>
  <section class="special-element" data-img="two">animal</section>
  <section class="special-element" data-img="three">night</section>

  <div id="cursor">
    <div class="img-wrapper" data-img='one'>
      <img src="https://picsum.photos/id/674/400/200">
    </div>
    <div class="img-wrapper" data-img='two'>
      <img src="https://picsum.photos/id/433/400/200">
    </div>
    <div class="img-wrapper" data-img='three'>
      <img src="https://picsum.photos/id/901/400/200">
    </div>
  </div>
</section>


View in full page mode.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda