I am using CSS Animations to create animations for a vertical image reel in my React app, however, the animation uses a variable that gets set in CSS by Javascript using the setProperty function.
So essentially, I have a fixed size reel with 10 slides and each slide has it's own animation (this is to handle where the image starts).
$margin: 25px;
$max-images-in-view: 10;
$speed: 90;
$offset: calc($speed * 1000ms);
.slide {
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: #{$speed}s;
#for $i from 1 through 10 {
&:nth-child(#{$i}) {
animation-delay: calc(
1000ms * $speed/$max-images-in-view * (#{$i} + 1) - $offset
);
animation-name: slide-animation-#{$i};
top: 100%;
#keyframes slide-animation-#{$i} {
from {
top: 100%;
opacity: 1;
}
to {
top: calc(
100% - $max-images-in-view * (var(--image-reel-image-height) + $margin)
);
opacity: 1;
}
}
}
}
}
The --image-reel-image-height variable gets set by the react component upon load and after the screen size updates.
const calculateCSSImageHeight = () => {
const imgEl = document.getElementsByClassName(Styles.imageContainer).item(0)
if (isBrowser)
document.documentElement.style.setProperty(
'--image-reel-image-height',
`${imgEl?.clientHeight ?? 0}px`
)
}
useEffect(() => {
calculateCSSImageHeight()
if (typeof window !== 'undefined') window.addEventListener('resize', calculateCSSImageHeight)
return () => {
window.removeEventListener('resize', calculateCSSImageHeight)
}
})
This seems to work perfectly fine for Chrome, but Safari doesn't seem to regenerate/update the animation after the component updates the CSS variable. For example, if I set --image-reel-height to '300px' as a default value, it animates as though the image height is 300px but the animation never updates after the variable has been updated.
Why isn't Safari picking up the CSS variable changes?
Related
So I'm making a music website with a playhead that I've animated to move along the screen and loop back.
.playhead {
position: absolute;
border-left: 0.2em solid;
min-height: calc(100% - var(--display-border-width));
border-color: red;
animation: play-animation 4s linear infinite;
}
#keyframes play-animation {
0% {
left: 0%;
}
100% {
left: 100%
}
}
Now while the playhead is moving, I want to access its position value, or how far along the screen it is (offsetX), so I can play the notes or whatever at that position.
So if it halfway across the screen, and the screen is 1000px wide, I want to access the 500px value during the animation.
I've tried using useEffect and useRef together, but that only gets called when the animation stops or starts, but not while it is running. So the value stays the same during the animation
I've tried setting up a seperate setInterval for calculating the position only, like so:
useEffect(() => {
if (props.play) {
const markers = (playTime * 1000) / frameRate;
const percentToAdd = 100 / markers;
const id = setInterval(() => {setProgress((prev) => (prev + percentToAdd) % 100)}, frameRate);
setIntervalID(id);
} else {
if (intervalID !== 0) {
clearInterval(intervalID);
}
}
}, [props.play]);
But this is out of sync for some reason. And plus, I'd rather the position values come from the playhead directly.
Is there any way to do this? Thanks!
I want to fadein a component after ajax call completes and jquery has rebuilt DOM.
I have this setup:
index.html:
<head>
<style>
body {
opacity: 0;
transition: opacity 2s;
}
</style>
</head>
<body onload="document.body.style.opacity='1'">
<div class="content">
<!-- Markup for content -->
</div>
</body>
main.css
.content {
opacity: 0;
transition: opacity 6s;
transition: opacity 2s;
-webkit-transition: opacity 6s;
-moz-transition: opacity 6s;
}
main.js
$(document).ready(function () {
const contentEl = document.querySelector(".content");
$(".submit").on("click", async function (e) {
e.preventDefault();
console.log(contentEl.style.opacity);
if (contentEl.style.opacity == 1) {
contentEl.style.opacity = 0;
console.log("Style opacity is in if and = %s", contentEl.style.opacity);
}
// Do Ajax and update DOM via jQuery
contentEl.style.opacity = 1;
}
The first time thru .content fades in as expected as well as fade in of whole page on initial render. However subsequent times thru there is no transition effect. Logging shows that I am changing style.opacity from 1 -> 0 and back to 1 after initial iteration. Any CSS guru's versed in CSS's dark secrets input advice appreciated.
$(document).ready(function () {
const contentEl = document.querySelector(".content");
contentEl.style.opacity = 1; // Define initial opacity (starting val)
$(".submit").on("click", async function (e) {
e.preventDefault();
contentEl.style.opacity = 0; // on click set opacity to 0?
FadeIn(); // Let's Fade from 0 to 1!
});
let FadeIn = () => { // Function
if (contentEl.style.opacity < 1) { // If opacity doesn't equal 1
contentEl.style.opacity += 0.2; // Let's add 0.2!
setTimeout(FadeIn(), 300); // Hell let's repeat that more 300ms
}
}; // Once we equal 1 we should be done
});
I don't play with the JQuery like that, I much rather too use CSS entirely for the animation process of these types of things, it's cleaner (less jitter). I'm assuming this is what you're sort of after though, a simple set value and slowly loop till finished. Button will start out 1 opacity, when clicked jump to 0 and slowly climb its way back up to 1.
I am using this script to transition between pages:
window.transitionToPage = function (href) {
document.querySelector('body').style.animation = "fadeOut 1s ease-out 0s 1 normal"
document.querySelector('body').style.animationFillMode = "backwards";
setTimeout(() => window.location.href = href, 750)
}
And:
#keyframes fadeOut {
0% { opacity: 1; }
100% { opacity: 0; }
}
The problem is when I use the browser history to come back to a page, the opacity is 0 and I can no longer see the page. I tried changing te animationfillmode, but it does not work.
Could you pause the animation then set the opacity of body back to 1 just before changing the window location?
I currently have the message displaying and disappearing as I want but the transition isn't working, This is what I tried.
const alertMsg = document.querySelector('.alert');
contactForm.addEventListener('submit', formSubmitted);
function formSubmitted(e) {
//other stuff
alertMsg.style.display = 'block';
setTimeout(() => {
alertMsg.style.display = 'none';
}, 5000);
}
.alert {
transition: all 0.5s ease-out;
}
<div class="alert">Your message has been sent, I will get back to you as soon as possible.</div>
The message just instantly disappears and reappears, how can I use the transition currently to make some sort of animation?
This is my first question so sorry if I missed any information out, I will add any more if needed. Thanks
You can't transition (or animate) the display property. the display property is either on or off there's nothing to transition or animate.
What you can do is animate opacity and alter the display property at start and end.
something like:
#keyframes showBlock {
from { display: block; opacity: 0; }
to { opacity: 1; }
}
#keyframes hideBlock {
from { opacity: 1; }
to { opacity: 0; display: none; }
}
I basically have some social media icons at the top of the page, that later become fixed.
I want them to fade in, I was using CSS. Everything I tried using JS did the same thing or didn't work.
Here is my JS:
jQuery(document).scroll(function() {
var y = jQuery(document).scrollTop(), //get page y value
social = jQuery(".socialnetworks"),
headerHeight = jQuery(".bg-cover").height();
if(y >= headerHeight + 500) {
social.css({opacity : 0});
social.addClass("fixedsocialnetworks");
} else {
social.removeClass("fixedsocialnetworks");
}
});
And my CSS
.fixedsocialnetworks {
position: fixed!important;
top: 200px!important;
z-index:10;
left:30px;
opacity:1!important;
transition: opacity 400ms;
}
So I set the opacity to 0, then add a class that sets the opacity to 1, and has the transition.
So it works, but it doesn't work the first time on scroll, it works every other time after the first. Why? How do I fix this?