I have a very sublet keyframe animation. I'd like for it to play every single time the page has been scrolled - not just once when coming in the viewpoint. Each time the scroll is touched. All I can find is running once in viewport - which is fine, but it needs to play every time the scroll is touched in the viewport. Any ideas?
/* CSS */
.aha-section .pixels .light {
margin-top: 4px;
margin-left: 33px;
animation: slide 2s 1;
}
#keyframes slide {
from {
margin-left: 33px;
}
50% {
margin-left: 45px;
}
to {
margin-left: 33px;
}
}
You will need to use some JavaScript... then you will listen to the scroll event to do the desired function...
To trigger a animation set a class to it:
.scrolled .aha-section .pixels .light {
animation: slide 2s 1;
}
and let the javascript add this class when the event scroll happens:
document.body.className += ' scrolled';
hope it helps to show you the direction.
let animated = false;
window.addEventListener("scroll", (event) => {
animateOnScroll();
});
function animateOnScroll(){
if(animated == false){
animated = !animated;
document.body.className += ' scrolled';
setTimeout(function() {
animated = false;
document.body.classList.remove('scrolled');
}, 2000); //match whatever time set on css animation
}
}
.aha-section {
position: fixed;
}
.aha-section .pixels .light {
margin-top: 4px;
margin-left: 33px;
}
.scrolled .aha-section .pixels .light {
animation: slide 2s 1;
}
#keyframes slide {
from {
margin-left: 33px;
}
50% {
margin-left: 45px;
}
to {
margin-left: 33px;
}
}
.scroll-emulator {
height: 2500px;
}
<div class="aha-section">
<div class="pixels">
<div class="light">Light</div>
</div>
</div>
<div class="scroll-emulator"><div>
Related
this code changes the background of the div every time a button is pressed but the new background is supposed to fade in so I set an animation. If you press red, and then another color, it makes the transition the first time, but not from the second on. The background will just appear without animation.
function changit(color) {
document.getElementById('cont').classList = color;
}
#keyframes appear {
0% {opacity:0;}
100% {opacity:1;}
}
#cont {
position: relative;
width: 100px;
height: 100px;
display: block;
}
#cont::before {
width: 93%;
height: 100%;
position: sticky;
content: "";
display: block;
}
.red::before, .blue::before, .black::before { transition: opacity 2s ease;animation:appear 2s; }
.red::before {
background: url(https://cdn.sstatic.net/Img/teams/teams-illo-free-sidebar-promo.svg) no-repeat;
}
.blue::before {
background: url(https://www.gravatar.com/avatar/24780fb6df85a943c7aea0402c843737?s=64&d=identicon&r=PG) no-repeat;
}
.black::before {
background:url(https://cdn.sstatic.net/Sites/stackoverflow/Img/subcommunities/intel.svg?v=0371bf2f3b96) no-repeat;
}
<div id="cont">
</div>
<button onclick="changit('red');">make red</button>
<button onclick="changit('blue');">make blue</button>
<button onclick="changit('black');">make black</button>
isnt the animation supposed to play again if the element changes class? How can I reset the animation to start again every time the element changes class to achieve this effect?
The animation property value actually never changes, with all the classes it's always the same and there is no point in time where it's unset, so it won't fire again.
You need to force the CSS engine sees that it did change. For this you can remove the class altogether force what is called a "reflow", which is when the CSS engine recalculates all the element's boxes in the page, and then only, set the class back:
function changit(color) {
const elem = document.getElementById('cont');
elem.classList = "";
elem.offsetWidth; // force reflow
elem.classList = color;
}
function changit(color) {
const elem = document.getElementById('cont');
elem.classList = "";
elem.offsetWidth; // force reflow
elem.classList = color;
}
#keyframes appear {
0% {opacity:0;}
100% {opacity:1;}
}
#cont {
position: relative;
width: 100px;
height: 100px;
display: block;
}
#cont::before {
width: 93%;
height: 100%;
position: sticky;
content: "";
display: block;
}
.red::before, .blue::before, .black::before { transition: opacity 2s ease;animation:appear 2s; }
.red::before {
background: url(https://cdn.sstatic.net/Img/teams/teams-illo-free-sidebar-promo.svg) no-repeat;
}
.blue::before {
background: url(https://www.gravatar.com/avatar/24780fb6df85a943c7aea0402c843737?s=64&d=identicon&r=PG) no-repeat;
}
.black::before {
background:url(https://cdn.sstatic.net/Sites/stackoverflow/Img/subcommunities/intel.svg?v=0371bf2f3b96) no-repeat;
}
<div id="cont">
</div>
<button onclick="changit('red');">make red</button>
<button onclick="changit('blue');">make blue</button>
<button onclick="changit('black');">make black</button>
Or, since you're using JS anyway, use the WebAnimations API:
function changit(color) {
const elem = document.getElementById('cont');
elem.classList = color;
elem.animate(
[ { opacity: 0 }, { opacity: 1 } ],
{ duration: 2000, repeat: 1 }
);
}
function changit(color) {
const elem = document.getElementById('cont');
elem.classList = color;
elem.animate(
[ { opacity: 0 }, { opacity: 1 } ],
{ duration: 2000, repeat: 1 }
);
}
#cont {
position: relative;
width: 100px;
height: 100px;
display: block;
}
#cont::before {
width: 93%;
height: 100%;
position: sticky;
content: "";
display: block;
}
.red::before {
background: url(https://cdn.sstatic.net/Img/teams/teams-illo-free-sidebar-promo.svg) no-repeat;
}
.blue::before {
background: url(https://www.gravatar.com/avatar/24780fb6df85a943c7aea0402c843737?s=64&d=identicon&r=PG) no-repeat;
}
.black::before {
background:url(https://cdn.sstatic.net/Sites/stackoverflow/Img/subcommunities/intel.svg?v=0371bf2f3b96) no-repeat;
}
<div id="cont">
</div>
<button onclick="changit('red');">make red</button>
<button onclick="changit('blue');">make blue</button>
<button onclick="changit('black');">make black</button>
I've been searching for a couple of days to add a fade transition effect on the backgroundImage that I'm changing through a Vue app.
Here is the code snippet I've been testing on:
new Vue({
el: "#main",
data: {
images: [
"https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg",
"https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg",
"https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg"
],
current: 0,
show: false
},
methods: {
changeBG: function () {
if (this.current < this.images.length - 1) {
this.current = this.current + 1;
} else {
this.current = 0;
}
}
}
});
.main {
height: 800px;
width: 100%;
margin-left: auto;
margin-right: auto;
z-index: 0;
background-repeat: no-repeat;
background-size: cover;
background-position: center 0px;
}
button {
width: 200px;
height: 25px;
margin: 10px;
}
p.hello{
color: white;
margin: 10px;
font-size: 50px;
}
.fade-enter-active,
.fade-leave-active {
transition: all 2s linear;
}
.fade-enter-to,
.fade-leave {
opacity: 0;
}
.fade-enter,
.fade-leave-to {
opacity: 1;
}
/* hello example transition */
.slide-fade-enter-active {
transition: all 1s ease;
}
.slide-fade-leave-active {
transition: all 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<transition name="fade">
<div v-if="changeBG" class="main" id="main" :style="{ backgroundImage: 'url(\'' + images[current] + '\')' }">
<button v-on:click="changeBG">
changeBG
</button>
<div id="testFromGuide">
<button #click="show = !show">
toggleHello
</button>
<transition name="slide-fade">
<p class="hello" v-if="show">all your base are belong to us</p>
</transition>
</div>
</div>
</transition>
My first question if this is simply possible? The reason I'm using backgroundImage is because the website I'm using this on has a background that is most easy to handle responsively through this (always covering, no repeat and keeping it center). And my scond question would be, if not, is there a possibility to make it work with a background set as described here?
In the codepen I've added an example of the vue guide to make sure it works and nothing is else is wrong. And the example works perfectly. Can't seem to find the answer for my example but I've been beginning to suspect it is simply not possible or I can't seem to find why vue isn't detecting something is changing.
For Vue Transitions to work, you need to change the DOM elements. So this way would work if you were changing actual images out. In your example, you're only changing an attribute value. The DOM doesn't trigger a transition since its the same element.
However, you can use the :key attribute to convince VUE to replace the element thus giving you a transition between 2 elements.
You can also set the image with inline CSS like you're doing in the example. You'll still have to create the transition in your CSS.
Here's an example using Vue Transition
new Vue({
el: "#main",
data: {
currentID: 0,
images: [
'https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg',
'https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg',
'https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg'
]
},
methods: {
toggleImage: function(){
if(this.currentID < this.images.length-1){
this.currentID +=1
} else {
this.currentID = 0
}
}
}
});
body {
overflow: hidden;
}
.main {
position: relative;
}
img {
width: 100%;
height: auto;
display: block;
position: absolute;
-webkit-transition: all 3s ease;
transition: all 3s ease;
}
button {
z-index: 100;
position: relative;
width: 200px;
height: 25px;
margin: 20px;
}
/* prefix with transition name */
.slide-fade-enter-active {
opacity: 1;
z-index: 10;
}
.slide-fade-leave-active {
opacity: 1;
}
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="main" id="main">
<transition name="slide-fade">
<!-- SRC comes from the array of images the :key is important for vue to believe its a 'new' DOM element and do the transition -->
<img v-bind:src="images[currentID]" v-bind:key="currentID" />
</transition>
<button #click="toggleImage">
Toggle Image
</button>
</div>
However, you don't get a lot of control over that since it uses image tags. Instead, it might be better to use a background image like this:
new Vue({
el: "#main",
data: {
currentID: 0,
images: [
'https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg',
'https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg',
'https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg'
]
},
methods: {
toggleImage: function(){
if(this.currentID < this.images.length-1){
this.currentID +=1
} else {
this.currentID = 0
}
}
}
});
.main {
/* make this the size of the window */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.theImage {
width: 100%;
height: 100%;
position: absolute;
background-color: #333;
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
-webkit-transition: all 3s ease;
transition: all 3s ease;
}
button {
z-index: 100;
position: relative;
width: 200px;
height: 25px;
margin: 20px;
}
/* prefix with transition name */
.slide-fade-enter-active {
opacity: 1;
z-index: 10;
}
.slide-fade-leave-active {
opacity: 1;
}
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="main" id="main">
<transition name="slide-fade">
<!-- SRC comes from the array of images the :key is important for vue to believe its a 'new' DOM element and do the transition -->
<div class="theImage" v-bind:style="{'background-image': 'url(' + images[currentID] + ')'}" v-bind:key="currentID"></div>
</transition>
<button #click="toggleImage">
Toggle Image
</button>
</div>
The answer was indeed to forget about vue transitions and let css do the work. A working example can be found here:
new Vue({
el: "#main",
data: {
show: false,
BG1: true,
BG2: false,
BG3: false
},
methods: {
changeBG: function(){
if (this.BG1 == true){
this.BG1 = false;
this.BG2 = true;
this.BG3 = false;
} else if (this.BG2 == true) {
this.BG1 = false;
this.BG2 = false;
this.BG3 = true;
} else if (this.BG3 == true) {
this.BG1 = true;
this.BG2 = false;
this.BG3 = false;
}
},
showBG1: function(){
if (this.BG1 == true){
return "";
} else {
return "transparent";
}
},
showBG2: function(){
if (this.BG2 == true){
return "";
} else {
return "transparent";
}
},
showBG3: function(){
if (this.BG3 == true){
return "";
} else {
return "transparent";
}
}
}
});
.main {
}
#bgs img.transparent {
opacity:0;
transform: translateY(-0.0px);
}
#bgs img{
/* Set rules to fill background */
min-height: 100%;
min-width: 1024px;
/* Set up proportionate scaling */
width: 100%;
height: auto;
/* Set up positioning */
position: fixed;
top: 0;
left: 0;
z-index: -1;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
}
#media screen and (max-width: 1024px) { /* Specific to this particular image */
#bgs img{
left: 50%;
margin-left: -512px; /* 50% */
}
}
button {
z-index: 100;
position: relative;
width: 200px;
height: 25px;
margin: 20px;
}
p.hello{
color: white;
margin: 40px;
font-size: 50px;
}
.fade-enter-active {
transition: all 1s ease;
}
.fade-leave-active {
transition: all 1s ease;
}
.fade-enter, .fade-leave-to{
transform: translateY(-5px);
opacity: 0;
}
/* hello example transition */
.slide-fade-enter-active {
transition: all 1s ease;
}
.slide-fade-leave-active {
transition: all 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateY(-5px);
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="main" id="main">
<div id="bgs">
<img :class="showBG1()" src="https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg">
<img :class="showBG2()" src="https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg">
<img :class="showBG3()" src="https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg">
</div>
<button #click="changeBG">
changeBG
</button>
<div id="testFromGuide">
<button #click="show = !show">
toggleHello
</button>
<transition name="slide-fade">
<p class="hello" v-if="show">all your base are belong to us</p>
</transition>
</div>
</div>
It's not perfect yet as for every extra background I need to add a function and add an extra if else loop to the changeBG function. It would be more elegant if this could be done with a list variable but I've not found a way to make this work with the :class method. I hope to look into this at a later time.
I am using purely CSS to create a dynamically opening and closing sidebar based on the view-port with of the page. I have a couple issues with my code however:
How can I prevent an animation when the screen first loads? That is, I simply want the sidebar to be opened or closed on first loading, and then animated when the view-port is adjusted.
Why do I have to have two separate animations? Notice I have two identical keyframes toggle and toggle1, which are used for closing and opening respectively. If I try to use just toggle for both animations, the animation occurs instantly. Any workaround without duplicated code?
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
}
#media (min-width: 500px) {
#sidebar {
animation: toggle 200ms ease-in 1 reverse forwards;
}
}
#media (max-width: 500px) {
#sidebar {
animation: toggle1 200ms ease-out 1 normal forwards;
}
}
#keyframes toggle {
0% {
left: 0px;
}
100% {
left: -200px;
}
}
#keyframes toggle1 {
from {
left: 0px;
}
to {
left: -200px;
}
}
<div id="sidebar"></div>
Just use a simple transition instead of animation, and 1 media query
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
transition:left 500ms ease;
left:0;
}
#media (max-width: 500px) {
#sidebar {
left: -200px;
}
}
https://jsfiddle.net/ufoste1y/3/
You can also use transform:translateX, which should provide better performance.
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
transition:transform 500ms ease;
}
#media (max-width: 500px) {
#sidebar {
transform: translateX(-200px);
}
}
https://jsfiddle.net/ufoste1y/8/
In order to force the sidebar to be opened or closed by default, that cannot be done with raw CSS. You'd need JavaScript to wait until the page has finished loading, and then dynamically update the element to have a class or similar that the CSS animation applies to.
As for the duplicate code, you can certainly just use toggle. It not working for you was probably a result of you forgetting to also change the #media animation reference from toggle1 to toggle.
Here's an example showing the sidebar staying open by default with the aid of JavaScript. The animation is now triggering only on the .loaded class, and the JavaScript applies the loaded class to the element 1000 milliseconds after the page has loaded, meaning that it won't trigger the animation initially.
Having said that, you'll probably only want to trigger the slide on some sort of condition anyway, and JavaScript would be much better suited to that :)
window.addEventListener("load", function() {
setTimeout(function() {
var sidebar = document.getElementById("sidebar");
sidebar.classList.add("loaded");
}, 1000);
});
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
}
#media (min-width: 500px) {
#sidebar.loaded {
animation: toggle 200ms ease-in 1 reverse forwards;
}
}
#media (max-width: 500px) {
#sidebar.loaded {
animation: toggle 200ms ease-out 1 normal forwards;
}
}
#keyframes toggle {
0% {
left: 0px;
}
100% {
left: -200px;
}
}
<div id="sidebar"></div>
So I made this: http://jsfiddle.net/RaV3u/5/
...but haven't been able to figure out how to make the slides loop -- that is, when the last slide finishes, it continues from the first, and the same for the first slide going backwards.
Does anyone have an idea how to do this?
/* THUMBNAIL SLIDER IMAGES
------------------------------------------- */
.slideshow {
height: 200px;
position: relative;
overflow: hidden;
}
.images {
position: absolute;
left: 0; /* Change this value to slide */
top: 0;
height: 100%;
width: 300%;
z-index: -1; /* send this layer behind the .circle layer */
}
/* THUMBNAIL SLIDER NAVIGATION
------------------------------------------- */
.thumb-slide-nav-wrapper {
margin: 70px 15px; /* position of the nav buttons */
overflow:auto;
}
.circle-left {
/* position of the nav buttons */
float:left;
}
.circle-right {
/* position of the nav buttons */
float:right;
}
.circle {
/* nav buttons */
z-index: 1;
border-radius: 50%;
width: 60px;
height: 60px;
background: green;
}
a .circle:hover {
background: lightgrey;
}
Thanks.
Demo
In Your JavaScript Just check whether the end (first or last image) is reached before sliding it.
To get total count of images use: var tot=$(".images").find("img").length;
and use a counter variable i to check the direction of sliding and end image reached or not condition.
So here is your changed JS/JQuery:
$(document).ready(function() {
var tot=$(".images").find("img").length; //total images in slider nav
var i=0; //acts as a counter
var thumbSliderPos = $(".images").css("left");
// Detect thumbnail nav button clicks
$('#circle-left').click(function() {
//Detect if first image is reached?
if(i<=0){
i=tot-2;
$(".images").animate({
"left": "-="+(400*i),
}, 400);
//Yes Show last image
}
else
{
$(".images").animate({
left: "+=400",
}, 400);
i--;
}
});
$('#circle-right').click(function() {
//Detect if last image is reached?
if(i>=tot-2)
{
$(".images").animate({
left: "0",
}, 400);
i=0;
//Yes Show first image
}
else
{
$(".images").animate({
left: "-=400",
}, 400);
i++;
}
});
});
To make it autoSlide after page loads Here is another:
Demo
Hope it Helps you. Cheers :) !
My code currently looks like this:
#keyframes lfade {
from {
opacity: 0;
margin-left: 45px;
}
to {
opacity: 1;
margin-left: 0px; //this line
}
}
Can I remove the margin-left line in the to block so the browser uses the default margin for the object I'm trying to animate instead of 0px?
It seems to work in Firefox and Opera, but I wasn't able to find any official sources.
Yeah you can but first you have to specify the original margin left in the stylsheet of what you are animating before taking it out like this :
#tag { width: 200; margin-left:45;}//you just do what you know overthere
then set your animation tags
#keyframes lfade {
from {
opacity: 0;
margin-left: 45px;
}
to {
opacity: 1;
margin-left: 0px; //this line
}
}
try this