Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
How can I use CSS animation to move an image (in this case a GIF) from left (outside the screen) to right (outside the screen)?
The only thing that is important to me is that you can choose the speed, the animation will always repeat AND you can define how long you want to wait before the next repetition.
NO EaseIn or EaseOut, just a normal animation.
I have done some research on this, but have not found a satisfying solution anywhere.
Since the delay part of an animation is used only to determine when an element should begin the animation, it cannot be reused after each iteration of the animation (only the first time). Therefore, we'll need JavaScript to do what you're asking. Instead of the delay being included in the CSS, it will now be included in the JavaScript.
We trigger the animation to begin when the page first loads listening to the DOMContentLoaded event.
We listen for the animation ending using the animationend event. When this happens, we set the animation to none and then back to an empty string. This will essentially restart the animation.
The code for the animationend handler, as well as the code for the DOMContentLoaded handler, both occur within setTimeout calls, with the delay assigned to a variable.
const img = document.querySelector(".my-img");
const delay = 2000; // ms=
const handleDOMReady = () => {
window.setTimeout(() => {
img.classList.add("on");
}, delay);
}
const handleAnimationEnd = () => {
img.style.animation = "none"
window.setTimeout(() => {
img.style.animation = "";
}, delay)
}
img.addEventListener("animationend", handleAnimationEnd);
window.addEventListener("DOMContentLoaded", handleDOMReady);
html, body {
padding: 0;
margin: 0;
}
.my-img {
transform: translateX(-100%);
}
.my-img.on {
--speed: 2s;
animation: move var(--speed) linear forwards;
}
#keyframes move {
to {
transform: translateX(100vw);
}
}
<img class="my-img" src="http://placekitten.com/200/300" alt="cat">
jsFiddle
Related
I have a weird issue where if I play an animation on one of my 3 monitors, YouTube videos on any other monitor crashes. I did fix this by disabling hardware acceleration in chrome://flags, but a new update in Chrome recently made the issue come back, and I haven't found a way to fix it yet. Animations occur on places like Facebook ("Someone is typing a comment...") or basically any website with a animation-duration CSS property on something (spinners are probably the most used form of animations I guess).
I can verify this simply by placing this CSS on any page:
* {
animation-duration: 0s !important;
}
Boom instantly all my videos play perfectly. No issues what so ever. I could add this to an userscript (or make a tiny extension), and I don't think it would mess up too much, but I'm more interested in knowing if there's a Chrome flag that can disable animations completely? I don't know if animation-duration works for any animation.
From what I know Chrome has no such option.
But, I was able to make something similar using the Tampermonkey extension.
Simply add the following script to the extension:
// ==UserScript==
// #name Animation Stopper
// #description Stop all CSS animations on every website.
// #author Ba2siK - https://stackoverflow.com/users/7845797
// #match *://*/*
// #grant GM_addStyle
// #run-at document-end
// ==/UserScript==
GM_addStyle(`
*, *:before, *:after {
transition-property: none !important;
transform: none !important;
animation: none !important;
}`
);
console.log("Animation Stopper ran successfully");
Make sure it's enabled at the extensions bar
Note: it won't work on iframe elements.
Btw, You can disable the window animation in chrome by adding the --wm-window-animations-disabled command-line flag.
* {
animation: none !important;
}
/* turn off animation on all elements*/
Allow me to answer my own question. Setting animation-duration to 0s !important seems to be working. However, I added animation-play-state: paused for good measure as well.
I made an userscript, and found that it doesn't target the Shadow DOM, so I have to traverse through every element, check if it's a shadow root, and then add the CSS. Since elements can be added to a page dynamically, I decided to do this every second. So far I cannot see a performance difference, even on pages with a lot of elements.
Install TamperMonkey (Chrome) or GreaseMonkey (Firefox) to use this:
// ==UserScript==
// #name Disable all animations
// #version 1.0
// #author mortenmoulder
// #include *
// #grant GM_addStyle
// #grant GM_addElement
// ==/UserScript==
//remove animations globally
GM_addStyle("* { animation-duration: 0s !important; animation-play-state: paused; }");
var ignoreElements = [];
//remove animations inside shadow DOM elements
function findShadowRoots(elements) {
for (var i = 0; i < elements.length; i++) {
if(elements[i].shadowRoot) {
if(ignoreElements.includes(elements[i].shadowRoot) == false) {
GM_addElement(elements[i].shadowRoot, 'style', { textContent: '* { animation-duration: 0s !important; animation-play-state: paused;' });
ignoreElements.push(elements[i].shadowRoot);
}
findShadowRoots(elements[i].shadowRoot.querySelectorAll("*"));
}
}
}
//remove animations every 1 second
setInterval(() => {
var allNodes = document.querySelectorAll('*');
findShadowRoots(allNodes);
}, 1000);
I want to create a smooth transition between 2 images with a legend.
The images come from an object-array of images.
Because works only on single tags and components, I've created a component to define the image+legend.
<transition>
<home-image :slide="slide" :key="slide"></home-image>
</transition>
The classes I define are like this
.v-enter-active,
.v-leave-active {
transition: opacity 2s ease-in-out;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
.v-enter,
.v-leave-to {
opacity: 0;
}
The new image is returned by a method
updateSlide() {
this.slide = this.entries[ Math.floor( Math.random() * this.entries.length ) ];
}
where entries is my array defined in data
this.slide is updated in regular intervals, every 10seconds like this, which is defined in the created() section
this.updateSlide();
this.uSlide = setInterval( this.updateSlide, 10000);
The code works, in the sense that a new image is loaded in this.slide every 10 seconds.
However, the transitions work only "half-way".
There is no transition fading out: the "old image" disappears and makes way for the new image fading in.
However, what I'd like is a smooth transition from one to the other.
I've tried more than a couple of ideas including using mode="out-in" and "in-out" but nothing works as I want.
What am I overlooking?
I'm creating a dashboard page which is full of CSS animations. From Bootstrap stuff (animated progress bars) to custom animations.
When you click some of the elements, a near full-screen modal is triggered, which overlaps all the animations, so I want to temporarily pause them all (because of possible performance issues) by adding/removing a class to one of the top elements, and using CSS to pause all animations when that class is set.
This solution would use only a single line of js, just to toggle the class on opening the modal.
My template looks somewhat like this:
<body>
<div class="modal">
<!-- Modal code -->
</div>
<div class="app">
<!-- Template -->
</div>
</div>
Is it possible to add a class to .app which pauses every CSS animation in every child element?
Note 1:
I know you can use the exact opposite of what I request: namely, have a default .animation-play class to one of the top elements, and prefix every child element with an animation with this class, and then remove this class to pause every animation. Just like:
app.animation-play .somediv .somediv .element {
// animation code
}
app.animation-play .somediv .element {
// animation code
}
app.animation-play .somediv .somediv .somediv .somediv .element {
// animation code
}
But then I have to edit a lot of CSS code, and it doesn't look very nice either.
Note 2:
I'm also open for a JS solution, but I would heavily prefer a pure CSS way of achieving this.
You can use a universal selector to target everything when a class of 'paused' is added to your app wrapper, however many CSS linters still warn against using these due to performance impacts.
To be honest the impact is probably minimal these days and many CSS resets for example use them.
You could use something like:
.app.paused * {
animation: none;
}
EDIT:
Looking through the comments above it seems as though the above selector doesn't have enough precedence to overwrite the animations so '!important' has been added.
.app.paused * {
animation: none !important;
transition: none !important;
}
However this is generally not a great idea, I always try to avoid using '!important' at all costs due to the difficulty in maintaining the stylesheet with these selectors in place. If you can overwrite the animations with a greater precedence then it would be better to do so rather than using '!important'.
EDIT 2:
As you mentioned you were open to JS solutions, here is some JS that should clear all the animations within a given selector. I'm not sure what the performance impact of doing it this way is but I added it here just in case someone else prefers to do it only using JS:
let stopAnimationsWrap = document.querySelector('.app');
let stoppedAnims = [];
// Stop animations
document.querySelector('.stop').addEventListener('click', () => {
let appAllEls = stopAnimationsWrap.querySelectorAll('*');
let allElsAr = Array.prototype.slice.call(appAllEls);
allElsAr.forEach((thisEl) => {
let elClass = thisEl.classList[0];
let cs = getComputedStyle(thisEl, null);
let thisAnimation = cs.getPropertyValue('animation-name');
if (thisAnimation !== 'none') {
stoppedAnims.push([elClass, {
'animationName': thisAnimation
}]);
thisEl.style.animationName = 'none';
}
});
});
// Start animations
document.querySelector('.start').addEventListener('click', () => {
stoppedAnims.forEach((thisEl) => {
let domEl = '.' + thisEl[0];
stopAnimationsWrap.querySelector(domEl).style.animationName = thisEl[1].animationName;
});
});
Fiddle:
https://jsfiddle.net/vu6javb2/14/
.app {
-webkit-animation-play-state: paused; /* Safari 4.0 - 8.0 */
animation-play-state: paused;
}
on hover:
.app:hover {
-webkit-animation-play-state: paused; /* Safari 4.0 - 8.0 */
animation-play-state: paused;
}
Is it possible to restate(override) css-transition after it has already triggered?
It seems down right impossible to override transition-delay after the css-transition has already taking effect.
(Especially considering the possible case when delay duration might have past already, delay value gets shortened and so on.)
However I failed to find concrete explanation.
I suppose css-transition has to be instructed at before or the exact same time when the change of target property (background-color in this case) is instructed?
(Sorry I had to edit numerous times to narrow down my question. If anyone wonder why I need such thing, I explained a mechanism I want below. It's already in simplified of my project.)
What I'm trying to do is
Before jQuery 'load' event, background-color: transparent is applied.
Then on jQuery 'load' event, apply background-color: red
(which is determined by css with class name loaded added by jQuery).
On the otherhand, transition-delay is determined separately by its class name which is provided by jQuery.
Clarification
Just to differentiate from other similar questions..
I only need transition-delay to be override conditionally.
Although interestingly, it would succeed when background-color is changed alongside.
Overriding happens conditionally, i.e: CSSOM remains constant but DOM's class name might change after its initial state. (To be specific to this case, class is added when the button is pressed.)
Override succeeds while DOM stays constant, but that's not what I want.
Here's fiddle to illustrate the situation
HTML
<div>
<!-- class name "loaded" will be given after load event. -->
<p class="delay delay-1s">
<!-- class name 'delay-1s' will be replaced with 'delay-5s' with button -->
This will be turning into red after loaded in..<br>
1.) 1 seconds without button or<br>
2.) 5 second with button
</p>
</div>
<button id="change-delay">Change delay to 5s</button>
CSS
/* Make P from transparent to red when loaded */
div p {
background-color: transparent;
}
div.loaded p {
background-color: red;
}
.delay.delay-1s {
transition-delay: 1s;
}
.delay.delay-5s {
transition-delay: 5s;
}
Javascript
$(window).on('load', function(){
$('div').addClass('loaded');
});
$('button#change-delay').click(function() {
$('.delay').removeClass('delay-1s').addClass('delay-5s');
});
I tested this on Chrome/Firefox updated to this day (Sep 2017), but both returned the same result.
edit1:
I'm pretty sure this has something to do with the time to provide class name.
If I add the class btn-pressed in HTML, i.e hardcode them on the document, override works as intended. Although, it wouldn't happen exclusively when the class is modified.
edit2:
Added clarification.
edit3:
Narrowed down question.
edit4:
adding timelapse of event happening on code:
Loads document.
P gets background-color: transparent and transition-delay: 1s;
Loading is done, adding class 'loaded' to DIV,
P gets background-color: red and fires css-transition
BUTTON gets clicked*, adding class 'btn-pressed' to DIV.
P gets transition-delay: 5s; assigned, overrides CSSOM; however, since css-transition from 2. is already in action, it doesn't take effect.
*Surely, if BUTTON has clicked before loading completed, it will take effect.
I was too lazy to read W3C documents but maybe that's what I should do in spare time..
Html code here
<div class="bg">
<p>test here</p>
</div>
<button class="btn">Activate</button>
Css code here
.bg {
min-height: 200px;
background-color: transparent;
}
Js code here
$(window).on('load', function(){
var delay = 5000;
If ( $('.bg').hasClass('pressed') ) {
delay = 2000; //set delay to 2 sec,
}
$('.btn').on('click', function() {
$('.bg').addClass('pressed');
});
$('.bg').animate({
'background-color': 'red'
}, delay);
});
I assume that you have jquery. If any issue plz let me know by putting a comment below.
I hope this is what you need.
Note - Make few changes as per you needs like class name and events.
I have two divs I want to transition across the screen to make it look like an infinite loop. I have worked with adjusting setTimeouts and setting an event listener (transitionend), but I keep getting a huge gap between the divs. Here is a link to the js fiddle using the event listener. Below is code I used without using the event listener.
Javascript
var boxWrap = $('#one');
var boxWrap2 = $('#two');
boxWrap.addClass('start')
boxWrap2.addClass('start')
setInterval(function () {
boxWrap.removeClass('start')
boxWrap[0].offsetTop
boxWrap.addClass('start')
}, 35000)
setInterval(function () {
boxWrap2.removeClass('start')
boxWrap2[0].offsetTop
boxWrap2.addClass('start')
}, 35000)
CSS
.boxWrapper.start {
transition: right linear 35s;
right: calc(-144vmin - 80vmin - 200vmin);
}
.boxWrapper.boxWrapper2.start {
transform: translateX(-224vmin);
transition: right linear 35s;
right: calc(-144vmin - 80vmin - 200vmin);
}
Tell me if there is anything you need.