Я работал над сайд-проектом, где меня попросили создать сайт с анимацией вокруг него. Я решил начать с React (чтобы изучить новую структуру и расширить свои знания).
Одна важная анимация, которую я отслеживаю (или пытаюсь воспроизвести), взята с сайта Apple .
Когда вы запускаете веб-страницу и прокручиваете ее вниз, текст отображается плавно, переместившись на 60 пикселей и изменив прозрачность. Как только он находится в окне просмотра.
Я пытался сделать это с классами. Тем не менее, у меня возникли бы проблемы с классами, так как они иногда повторно отображали бы мой элемент, или компонент не был бы виден при обновлении.
Я также пытался использовать fadeIn
с setInterval
, однако каждый раз, когда он активируется, он работает по-разному.
Мой компонент, который использует анимацию:
import React, { Component } from "react";
import "./SuccessStory.css";
class SuccessStory extends Component {
state = {
componentAppearsScrollUp: false,
componentDisappearsScrollUp: false,
componentAppearsScrollDown: false,
componentDisappearsScrollDown: false,
showComponent: false,
translateMove: 60,
opacity: 0
};
fadingIn() {
const timer = setInterval(() => {
if (this.state.opacity >= 1) {
clearInterval(timer);
return;
}
this.setState({
opacity: this.state.opacity + 0.05
});
}, 100);
}
fadingOut() {
const timer = setInterval(() => {
if (this.state.opacity <= 0) {
clearInterval(timer);
}
this.setState({
opacity: this.state.opacity - 0.05
});
}, 100);
}
componentDidUpdate(prevProps, prevState) {
if (this.props.currentY < prevProps.currentY) {
if (
this.props.intersectionRatio > prevProps.intersectionRatio &&
this.props.isIntersecting
) {
if (this.props.intersectionRatio > 0.6) {
console.log("Scrolling down enter");
this.fadingIn();
this.setState({
componentAppearsScrollUp: false,
componentDisappearsScrollUp: false,
componentAppearsScrollDown: true,
componentDisappearsScrollDown: false,
showComponent: true
});
}
} else {
if (
this.props.intersectionRatio < 0.8 &&
this.props.intersectionRatio > 0.6
) {
console.log("Scrolling down leave");
this.setState({
componentAppearsScrollUp: false,
componentDisappearsScrollUp: false,
componentAppearsScrollDown: false,
componentDisappearsScrollDown: true,
showComponent: true
});
this.fadingOut();
}
}
} else if (
this.props.currentY > prevProps.currentY &&
this.props.isIntersecting
) {
if (this.props.intersectionRatio < prevProps.intersectionRatio) {
if (this.props.intersectionRatio < 0.9) {
console.log("Scrolling up leave");
this.fadingOut();
this.setState({
componentAppearsScrollUp: false,
componentDisappearsScrollUp: true,
componentAppearsScrollDown: false,
componentDisappearsScrollDown: false,
showComponent: true
});
}
} else {
if (
this.props.intersectionRatio > 0.6 &&
this.props.intersectionRatio < 0.8
) {
console.log("Scrolling up enter");
this.setState({
componentAppearsScrollUp: true,
componentDisappearsScrollUp: false,
componentAppearsScrollDown: false,
componentDisappearsScrollDown: false,
showComponent: true
});
this.fadingIn();
}
}
} else if (
this.props.isIntersecting &&
this.props.intersectionRatio > 0.9
) {
this.fadingIn();
}
}
render() {
return (
<div
className={
(this.state.componentAppearsScrollDown
? " story-appear-scroll-down "
: "") +
(this.state.componentDisappearsScrollDown
? " story-disappear-scroll-down "
: "") +
(this.state.componentAppearsScrollUp
? "story-appear-scroll-up "
: "") +
(this.state.componentDisappearsScrollUp
? " story-disappear-scroll-up "
: "")
}
style={{
opacity: this.state.opacity
}}
>
<h1 className="success-story">The success story</h1>
<h2
className="success-story-text-one"
// style={
// this.props.isIntersecting && this.props.intersectionRatio > 0.8
// ? { opacity: 1 }
// : { opacity: 0 }
// }
>
MobileLife brought together over 100 dedicated people based in
Copenhagen and Vilnius to deliver innovative mobile solutions to the
banking customers in Nordic markets.
</h2>
</div>
);
}
}
export default SuccessStory;
Мой родительский компонент, который передает значения наблюдателя пересечения дочернему компоненту:
import React, { Component } from "react";
import "./SecondPage.css";
import SuccessStory from "./SuccessStory";
import { InView } from "react-intersection-observer";
// import SuccessStoryText1 from "./SuccessStoryText1";
import SuccessStoryText2 from "./SuccessStoryText2";
class SecondPage extends Component {
state = {
isIntersecting: false,
intersectionRatio: "",
currentY: "",
isIntersectingSecondText: false,
intersectionRatioSecondText: "",
currentYSecondText: ""
};
render() {
return (
<div className="second-page">
<div className="navigation-second" style={{ position: "sticky" }} />
<div style={{ paddingTop: "25%", height: "60vh" }}>
<InView
threshold={[0.1, 0.2, 0.3, 0.4, 0.6, 0.75, 0.8]}
onChange={(inView, ref) => {
this.setState({
currentY: ref.boundingClientRect.y,
isIntersecting: ref.isIntersecting,
intersectionRatio: ref.intersectionRatio
});
// console.log("Inview:", inView, ref);
}}
>
{({ inView, ref }) => (
<div ref={ref} style={{ position: "sticky", top: "30%" }}>
<SuccessStory
isIntersecting={this.state.isIntersecting}
intersectionRatio={this.state.intersectionRatio}
currentY={this.state.currentY}
/>
</div>
)}
</InView>
</div>
<InView
threshold={[0.1, 0.2, 0.3, 0.4, 0.6, 0.75, 0.8]}
onChange={(inView, ref) => {
if (ref.intersectionRatio > 0.1) {
this.setState({
currentYSecondText: ref.boundingClientRect.y,
isIntersectingSecondText: ref.isIntersecting,
intersectionRatioSecondText: ref.intersectionRatio
});
}
// console.log("Inview:", inView, ref);
}}
>
{({ inView, ref }) => (
<div ref={ref}>
<SuccessStoryText2
isIntersecting={this.state.isIntersectingSecondText}
intersectionRatio={this.state.intersectionRatioSecondText}
currentY={this.state.currentYSecondText}
/>
</div>
)}
</InView>
</div>
);
}
}
//
export default SecondPage;
А вот Css, который я использую для анимации:
.story-appear-scroll-down {
animation: successStoryTextAppearScrollDown 0.3s linear;
animation-fill-mode: forwards;
}
@keyframes successStoryTextAppearScrollDown {
from {
transform: translate3d(0, 60px, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}
.story-disappear-scroll-down {
animation: successStoryTextDisappearScrollDown 0.3s linear;
animation-fill-mode: forwards;
}
@keyframes successStoryTextDisappearScrollDown {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}
.story-appear-scroll-up {
animation: successStoryTextAppearScrollUp 0.3s linear;
animation-fill-mode: forwards;
}
@keyframes successStoryTextAppearScrollUp {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}
.story-disappear-scroll-up {
animation: successStoryTextDisappearScrollUp 0.3s linear;
animation-fill-mode: forwards;
}
@keyframes successStoryTextDisappearScrollUp {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(0, 60px, 0);
}
}
Я думал, что будет правильным способом активировать такую анимацию, которую я пытаюсь воспроизвести, и она будет выглядеть одинаково при каждой активации (как на веб-странице Apple). Однако, поскольку я уже потратил на это слишком много времени, я не могу получить желаемые результаты.
Я ценю вашу помощь, и любые советы или предложения приветствуются, так как это мой первый пост.