I try make an text Animation with framer motion, React
The Animation work fine still.
Since I use text Animation after switch route. So after change path.
But will be lot of components.
So I try solving things inside the component.
Not animation when path change but when the props change.
Now the Animation also work but not perfect
The essence:
The First Animation Fine with "A A"
The second and third Animation not appear one by one but at once
I guess based on other post something problem with stagger children. Bit I dont know exatly what should i do that working text animation one by one when props change
More simple if I show you on codesandbox:
Data.js
const data = [
{
id: 0,
maintext: "A A",
answerButtons: [{ answerText: "Next", isCorrect: true }],
image: "",
},
{
id: 1,
maintext: "B B B B",
answerButtons: [{ answerText: "Next", isCorrect: true }],
},
{
id: 2,
maintext: "C C C C C C",
answerButtons: [{ answerText: "Next", isCorrect: true }],
},
];
export default data;
MobilBase.js
import React, { useState, useEffect } from "react";
import SpeechBubble from "../../../components/SpeechBubble/SpeechBubble";
import Data from "../../../utils/data";
import "./MobilBase.css";
const MobilBase = () => {
const [counter, setCounter] = useState(0);
const [data, setData] = useState(Data);
const nextHandler = (i) => {
if (Data[counter].answerButtons[i].isCorrect === true) {
setCounter((prevState) => {
return prevState + 1;
});
}
};
return (
<div className="base base-page-grid">
<div className="counter">
<h2>
{counter} / {data.length - 1}
</h2>
</div>
<div className="speech-bubble-outer">
<SpeechBubble maintext={data[counter].maintext} />
</div>
{data[counter].answerButtons.map((answerbutton, i) => (
<div key={i} className="base-buttons-outer">
<div>
<button
onClick={() => {
nextHandler(i);
}}
>
<strong>{answerbutton.answerText}</strong>
</button>
</div>
</div>
))}
</div>
);
};
export default MobilBase;
SpeechBubble.js
import React, { useState, useEffect } from "react";
import Animation from "./Animation";
export default function SpeechBubble(props) {
return (
<div className="frame-speech-bubble">
<div className="zumzum-animation-grid-frame">
<Animation maintext={props.maintext} />
</div>
</div>
);
}
Animation.js
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion/dist/framer-motion";
import AnimatedText from "./AnimatedText";
import "./Animate.css";
export default function Animation(props) {
// Placeholder text data, as if from API
const placeholderText = [{ type: "heading1", text: props.maintext }];
const container = {
visible: {
transition: {
staggerChildren: 0.025,
},
},
};
return (
<motion.div
className="animation-frame"
initial="hidden"
animate="visible"
variants={container}
>
<div className="container-animated-text">
{placeholderText.map((item, index) => {
return <AnimatedText {...item} key={index} />;
})}
</div>
</motion.div>
);
}
AnimatedText.js
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion/dist/framer-motion";
// Word wrapper
const Wrapper = (props) => {
// We'll do this to prevent wrapping of words using CSS
return <span className="word-wrapper">{props.children}</span>;
};
// Map API "type" vaules to JSX tag names
const tagMap = {
paragraph: "p",
heading1: "h1",
heading2: "p"
};
// AnimatedCharacters
// Handles the deconstruction of each word and character to setup for the
// individual character animations
const AnimatedCharacters = (props) => {
// Framer Motion variant object, for controlling animation
const item = {
hidden: {
x: "-200%",
color: "#0055FF",
transition: { ease: [0.455, 0.03, 0.515, 0.955], duration: 0.85 }
},
visible: {
x: 0,
color: "red",
transition: { ease: [0.455, 0.03, 0.515, 0.955] }
}
};
// Split each word of props.text into an array
const splitWords = props.text.split(" ");
// Create storage array
let words = [];
// Push each word into words array
for (const item of splitWords) {
words.push(item.split(""));
}
// Add a space ("\u00A0") to the end of each word
words.map((word) => {
return word.push("\u00A0");
});
// Get the tag name from tagMap
const Tag = tagMap[props.type];
return (
<Tag>
{words.map((word, index) => {
console.log(`return: word ${word} index: ${index}`);
return (
// Wrap each word in the Wrapper component
<Wrapper key={word.id}>
{words[index].flat().map((element, index) => {
return (
<span
style={{
overflow: "hidden",
display: "inline-block"
}}
key={word.id}
>
<motion.span
className="animatedtext spell"
style={{ display: "inline-block" }}
variants={item}
>
{element}
</motion.span>
</span>
);
})}
</Wrapper>
);
})}
</Tag>
);
};
export default AnimatedCharacters;