javascript - CSS:如何在每次悬停时不重置悬停动画

标签 javascript html css

我有一个简单的悬停 CSS 动画,可以在图像之间进行幻灯片转换。
当用户悬停在第 1 节上并且在动画结束之前悬停在第 2 节上时,动画重新开始并进行滞后移动。
我的代码:

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>

有什么解决方案可以防止这个滞后问题吗?
也许有什么解决方案可以解决它并使这个动画流畅吗?
我正在寻找类似动画的东西 on this website .

最佳答案

更新后的版本
您可以使用 gsap 对其进行简化版本。除非您在 gsap 库中使用 css,否则最好不要将纯 css 与 gsap 混合太多。因为gsap会操纵一些 Prop 。例如。转变。并且使用变换比使用左/上更好,因为它是硬件加速的。
我对之前发布的代码做了一些改进。现在看起来更流畅了。此外,我还添加了一点缩放和水平移动效果——类似于引用网站上的动画。此外,动画现在从底部开始。
动画在引用的页面上做得很好。它是用 WebGL 完成的。这不是你的日常动画,需要相当多的努力才能让它发挥作用——至少对于不是设计师的人来说是这样。它使用 3d 变换矩阵和一些其他效果一起使用。

<!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>

旧答案

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>

关于javascript - CSS:如何在每次悬停时不重置悬停动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70504511/

相关文章:

javascript - SPA 中用户数据存储在哪里

javascript - 从函数 javascript 返回数组

javascript - JavaScript 方法中的 "Uncaught TypeError: Illegal Invocation"

php - div 展开和折叠在我的 PHP 代码中不起作用

javascript - execCommand h1 样式仅选定文本

javascript - jQuery – 单击 "Read More"后 "Read Less"span 不再出现

PHP 在每 4 条记录后循环添加 <div "style="clear"> </div>

javascript - Three.js - 获取旋转的顶点

html - 我希望我的 div 重叠

html - 为什么 margin 在 block 内不起作用?