我有一个简单的悬停 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/