Animated style-components perfomance issues - css

I found html/css snowfall animation. Html part of this animation is 200 divs with .snow class and here is the SCSS:
body {
background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
overflow-x: hidden;
filter: drop-shadow(0 0 10px white);
}
#function random_range($min, $max) {
$rand: random();
$random_range: $min + floor($rand * (($max - $min) + 1));
#return $random_range;
}
.snow {
$total: 200;
position: absolute;
width: 10px;
height: 10px;
background: white;
border-radius: 50%;
#for $i from 1 through $total {
$random-x: random() * 100vw;
$random-offset: random_range(-10, 10) * 1vw;
$random-x-end: $random-x + $random-offset;
$random-x-end-yoyo: $random-x + ($random-offset / 2);
$random-yoyo-time: random_range(3,8) / 10;
$random-yoyo-y: $random-yoyo-time * 100vh;
$random-scale: random();
$fall-duration: random_range(10, 30) * 1s;
$fall-delay: random(30) * -1s;
&:nth-child(#{$i}) {
opacity: random();
transform: translate($random-x, -10px) scale($random-scale);
animation: fall-#{$i} $fall-duration $fall-delay linear infinite;
}
#keyframes fall-#{$i} {
#{percentage($random-yoyo-time)} {
transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);
}
to {
transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);
}
}
}
}
I wanted to make a react component out of this animation, and I did it with styled-components:
const randomRange = (min: number, max: number) => {
return min + Math.random() * (max - min)
}
const DropAnimation = (docHeight: number, size:number) => {
const xstart = Math.random() * 100;
const xshift = randomRange(-10, 10);
const xmiddle = xstart + xshift;
const xend = xstart + xshift / 2;
const middlePercentage = randomRange(30, 80)
const animation = keyframes`{
0%{
transform: translate(${xstart}vw,-10px);
}
${middlePercentage}%{
transform: translate(${xmiddle}vw,${middlePercentage / 100 * docHeight}px);
}
100%{
transform: translate(${xend}vw,${docHeight - size}px);
}
}`
return animation
}
const StyledSnowflake = styled.div<StyledSnowflake>`
position: absolute;
width: ${p => p.size}px;
height: ${p => p.size}px;
border-radius: 50%;
background-color: ${p => p.color || 'white'};
animation: ${p => DropAnimation(p.docHeight, p.index,p.size)} ${p => randomRange(p.duration / 3, p.duration)}s
${p => -p.duration * Math.random()}s linear infinite;
filter: drop-shadow(0 0 10px ${p => p.color || 'white'});
opacity: ${p => p.opacity};
`
const SnowFlake: FC<Snowflake> = ({docHeight, duration, index, size}) => {
const opacity = Math.random();
const randomSize = Math.random() * size;
return <StyledSnowflake duration={duration} size={randomSize} opacity={opacity} docHeight={docHeight}
index={index}/>
}
Snowfall components renders 200 SnowFlake components. All I did is just copied css, but perfomance-wise my Snowfall component is much much worse, everything lagging quite hard. Why? What is the difference between rendering 200 divs from html and 200 styledComponent divs from react?
How can I make this work faster?
P.S. if this question is too stupid, please just leave me a few links where I could read about this. Honestly, I don't even know how to google this problem, because when I search for any kind of info I only see articles about efficient rendering in react, but I don't think this is what I actually need since I render my components once, but animation still performs poorly.
EDIT
I found out that creacting regular divs in react works fine like this:
<div>
{Array(200).fill(null).map((_item,i)=> <div key={i} className={styles.snow}></div>)}
</div>
But when I use styled-components and add animation for each of those everything starts lagging.

Related

Anymation changes color to black when it should keep the gradient

As a case study I was trying to replicate what hyperplexed brings up at the end of the video.
https://www.youtube.com/watch?v=owpaafxvkjU&ab_channel=Hyperplexed
i tried to apply the gradient to the words, however, by doing so the color of the word changes to back (even though the word split effect still happens)
I have tried to do it on hover, tried to change the colors, but still happens.
const enhance = (id) => {
const element = document.getElementById('split');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
const enhance1 = (id) => {
const element = document.getElementById('split1');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
const enhance2 = (id) => {
const element = document.getElementById('split2');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
const enhance3 = (id) => {
const element = document.getElementById('split3');
const text = element.innerText.split('');
element.innerText = '';
text.forEach((letter) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = letter;
element.appendChild(span);
});
};
enhance('split');
enhance('split1');
enhance2('split2');
enhance3('split3');
body {
background-color : #000;
margin : 0;
display : flex;
flex-direction : column;
align-items : center;
justify-content : center;
height : 100vh;
}
.line {
display : flex;
justify-content : space-between;
}
p,
a {
color : #fff;
font-size : 5vw;
font-family : 'Rubik', sans-serif;
margin : 0rem;
text-transform : uppercase;
}
a {
text-decoration : none;
}
#text:has(.fancy:hover) .word:not(.fancy:hover) {
opacity : 0.4;
}
.fancy > .letter {
display : inline-block;
transition : transform 350ms ease;
}
.fancy:hover > .letter {
transition-duration: 800ms;
}
.fancy:hover > .letter:nth-child(1) { transform : translate(-80%, 60%) rotate( 8deg); }
.fancy:hover > .letter:nth-child(2) { transform : translate(0%, 8%) rotate(-6deg); }
.fancy:hover > .letter:nth-child(3) { transform : translate(-10%, 60%) rotate(-6deg); }
.fancy:hover > .letter:nth-child(4) { transform : translate(0%, 8%) rotate(-8deg); }
.fancy:hover > .letter:nth-child(5) { transform : translate(0%, 60%) rotate(-3deg); }
.fancy:hover > .letter:nth-child(6) { transform : translate(0%, 20%) rotate(-3deg); }
.fancy:hover > .letter:nth-child(7) { transform : translate(0%, 80%) rotate(-5deg); }
#keyframes background-pan
{
from { background-position: 0% center; }
to { background-position: -200% center; }
}
p,
a,
.letter > .magic {
animation : background-pan 15s linear infinite;
background : linear-gradient(to right,#ecf2ff,#e3dffd,#e5d1fa,#fff4d2,#ecf2ff);
-webkit-background-clip : text;
-webkit-text-fill-color : transparent;
}
<div class="container" id="text">
<div class="line magic">
Home
</div>
<div class="line magic">
About
</div>
<div class="line magic">
Shop
</div>
<div class="line magic">
Contact
</div>
</div>
If anyone knows what it's causing the color to change to black please let me know, or point me towards some documentation so I can have a read through.
My issue was that I should be calling the gradient inside the .fancy>letter instead of getting the p,a and letter.
it now works as intended

Getting rid of white space after transform

I'm trying to translate an element such that it always stays on the screen, but it seems that it's leaving some whitespace on the bottom. I can't use position absolute as I want high performance on the animation. I have tried making the width and height 0 and changing the transform-origin property, but nothing has worked so far. Here is my website and the relevant code.
JSX:
useEffect(() => {
const onScroll = () => {
let test = document.getElementsByClassName('scrollToTop')[0];
test.style.transform =
'translate(calc(100vw - 36px - 10px),' +
(window.scrollY -
document.body.scrollHeight +
window.innerHeight -
10) +
'px)';
console.log(test.style.transform);
};
window.addEventListener('scroll', onScroll);
return () => window.removeEventListener('scroll', onScroll);
}, []);
CSS:
.scrollToTop {
/* opacity: 0; */
transform: translate(0, 0);
visibility: none;
background: var(--blue);
transition: opacity 0.5s ease-in-out;
display: inline-block;
}

i want make particle event in React component(current not animation)

i want make particle event in div element on React component using useRef
but it is not working animation.
codes
const particles = useRef<HTMLDivElement>(null!);
**set parent ref(paricles) and create child particle.
and set useCallback for set location child particle and animation**
const particleEffect = useCallback(() => {
console.log("???");
const np = particles.current.clientWidth / 70;
particles.current.innerHTML = "";
const childRe = [] as HTMLDivElement[];
for (let i = 0; i < np; i++) {
const child = document.createElement("div");
childRe.push(child);
}
for (let i = 0; i < np; i++) {
const w = particles.current.clientWidth;
const h = particles.current.clientHeight;
const rndw = Math.floor(Math.random() * w) + 1;
const rndh = Math.floor(Math.random() * h) + 1;
const widthpt = Math.floor(Math.random() * 12) + 3;
const opty = Math.floor(Math.random() * 5) + 2;
const anima = Math.floor(Math.random() * 12) + 8;
childRe[i].classList.add(`${classes(styles.particle)}`);
childRe[i].style.marginLeft = `${rndw}px`;
childRe[i].style.marginTop = `${rndh}px`;
childRe[i].style.width = `${widthpt}px`;
childRe[i].style.height = `${widthpt}px`;
childRe[i].style.background = "white";
childRe[i].style.opacity = `${opty}`;
childRe[i].style.animation = `move ${anima}s ease-in infinite `;
particles.current.appendChild(childRe[i]);
}
}, [particles.current]);
and then, i make eventHandler for call useCallback function
useEffect(() => {
if (!particles.current) {
return;
}
particleEffect();
window.addEventListener("resize", particleEffect);
window.addEventListener("load", particleEffect);
return () => {
window.removeEventListener("resize", particleEffect);
window.removeEventListener("load", particleEffect);
};
}, []);
in css
.particles {
position: absolute;
overflow: hidden;
width: 100%;
height: 100vh;
top: 0;
left: 0;
filter: blur(8px);
.particle{
border-radius: 50%;
position: absolute;
}
}
#keyframes move {
0%{
transform: translateX(0vh);
opacity: 0;
}
10%{
opacity: 1;
}
90%{
opacity: 1;
}
100%{
transform: translateX(45vh);
opacity: 0;
}
}
but animation css not working...
why is not working...?
what should i fix it?
example
https://codepen.io/ricardoolivaalonso/pen/agdRRB

How to generate a random number each time a component re-renders?

I want to use Less variables to generate a random number for conditionally class that gets added and removed each second. Here is the sample code of reactjs file
function App() {
const [date , setDate] = useState('')
const [circleBoolean, setCircleBoolean] = useState(false);
const [random_number, setRandom_number] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
doThings()
}, 1000);
return () => clearInterval(interval);
}, []);
function doThings() {
setCircleBoolean((prev)=>{
return !prev});
setDate(moment().format('MMMM Do YYYY, h:mm:ss a')) ;
setRandom_number(Math.random() * 100);
console.log("cirlce boolean :" + circleBoolean)
}
return (
<div className="App">
<div className="bg">
<div style={{ top:`${random_number}%`, left:`${random_number}%` }} className={circleBoolean ? "circle" : ""} />
<div className="card">
<p className="card-info">{date}</p>
</div>
</div>
</div>
);
}
and here is the sample code from css file
#color: blue;
#random: (round(`Math.random()`)) ;
.circle {
position: absolute;
border: solid 4px #color;
border-radius: 50%;
height: #random;
width: 3em;
background-color: #color;
animation: circleSize 1s ease-in-out;
}
#keyframes circleSize {
0% {
transform: scale(0, 0);
}
50% {
transform: scale(1,1);
}
100%{
transform: scale(0,0);
}
}
The random number is generated only during the first render and is not changed during subsequent re-renders. How do I go about it ?
My advice is use a 'css-in-js' approach.
React only re-renders components that changed and your css is static right now. If you define your styles in a js file and pass your random number to this file every time your App's interval runs - you'll get your re-renders.

React animate component when it render and when its state change

For animation i write the code like this but this code is not working for me. Whant to know this is a css problem or my code style needs update. or how to do animation on a component
render: function() {
var len = Object.keys(Interfaces.previewContainer.state.sourceImg).length;
var imgContainer = [];
for (var i = 0; i < len; i++) {
imgContainer.push(React.createElement(PreviewImgContainer, {
key: i,
flipParentClass: this.state.flipParentClass,
i_d: 'SelectedTemplate_' + i,
dataactionstring: 'selecttemplatetype-' + this.state.dataactionstring['src' + i],
sourceImg: this.state.sourceImg['src' + i],
ref: 'PreviewImgContainer',
alter: this.state.alter
}));
}
return (
React.createElement(
ReactCSSTransitionGroup, {
className:'flipParent',
transitionEnterTimeout: 250,
transitionLeaveTimeout: 250,
transitionName: 'flipped' // 'flipParent flipped' is a class name
},
React.createElement('div', {
className: 'previewContainer',
style: {
marginLeft: this.state.marginLeft
},
ref: 'previewContainer'
}, imgContainer)
)
);
}
please let me know where i am making a mistake or where i have to update the code
.flipParent {
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.5s ease 0s;
width: 100%;
}
/* .flipped is for flipping (x-axis) the element */
.flipped {
transform: rotateY(180deg);
}
Maybe you have to implement these
.flipped-enter
.flipped-enter.flipped-enter-active
.flipped-leave
.flipped-leave.flipped-leave-active
The below screenshot is from the official react guide

Resources