I am using css transitions and a little javascript to make elements slide into the page from the left or right.
It works perfectly fine from the right side (starting from transform: translateX(100vw);).
But from the left side (negative translate value: transform: translateX(-100vw);) it does not work at all. There is no error, but the element pops up at the end of the transition without any "animated" movement.
Using a px value instead of vw for the negative/ left side also works fine. I found several examples where people used negative vw values and did not seem to have problems with it...
I also tried transform: translateX(calc(0px - 100vw));. No luck...
(I am not using any libraries etc.)
<!-- HTML -->
<div class="container">
<div class="slide-in l"></div>
<div class="slide-in r"></div>
</div>
/* CSS */
.slide-in {
transition: transform 0.8s ease;
}
.slide-in.l {
transform: translateX(-100vw); /* this is the line does not work */
}
.slide-in.r {
transform: translateX(100vw);
}
.slide-in.show {
transform: translateX(0);
}
/* JavaScript */
const slider = document.querySelectorAll('.slide-in');
window.addEventListener("load", () => {
slider.forEach(s => {
s.classList.add('show');
})
});
Any ideas what could be the problem?
The translateX(-100vw); should work fine.
When you run this snippet you should see both rectangles sliding into view,
the green one from the left and the right one from the right.
const slider = document.querySelectorAll('.slide-in');
window.addEventListener("load", () => {
slider.forEach(s => {
s.classList.add('show');
})
});
/* CSS */
.slide-in {
transition: transform 0.8s ease;
width: 100px;
height: 100px;
}
.slide-in.l {
transform: translateX(-100vw);
/* this is the line does not work */
background-color: green;
}
.slide-in.r {
transform: translateX(100vw);
background-color: blue;
}
.slide-in.show {
transform: translateX(0);
}
<!-- HTML -->
<div class="container">
<div class="slide-in l">a</div>
<div class="slide-in r">b</div>
</div>
Related
Here is my bug demo:
https://jsbin.com/gijabuseca/edit?html,css,js,output
bug img
Is it a browser bug?
I solved this problem by replacing transform: translateX (100%) with left: 100%
However, using left to change the position performance is much lower than transform. If insist on using transform, is there a way to solve this gap problem?
A hack around this bug can be changing slightly the movement of div 1.
Offsetting a litle bit the timing function from what the standard ease value is, we adjust it closer to div2.
I have set the transition slower so that it is easier to see if it fails
setTimeout(function(){
document.querySelector('.demo1').classList.add('right');
document.querySelector('.demo2').classList.add('right');
});
body {
background: green;
}
.wrapper {
position: relative;
width: 100px;
}
.demo1, .demo2 {
position: absolute;
width: 100%;
height: 100px;
background: #ddd;
transition: 4s;
}
.demo1 {
transform: translateX(0);
transition-timing-function: cubic-bezier(.23, 0.12, .25, 1.05);
}
.demo2 {
transform: translateX(100%);
}
.demo1.right {
transform: translateX(100%)
}
.demo2.right {
transform: translateX(200%);
}
<div class="wrapper">
<div class="demo1"></div>
<div class="demo2"></div>
</div>
i'm working on a project where i have to render some components with an enter and leave animation, when a component enters the screen it has to enter form the bottom, and when it leaves, it has to do it going upwards, the desired behavior is that when i change the :is property of the component tag, the current component goes upwards and the next one comes from the bottom, the code looks like this:
<template>
<div class="home">
<transition name="section">
<component :is="activeSection"></component>
</transition>
</div>
</template>
<script>
import comp1 from './comp1';
import comp2 from './comp2';
export default {
components: {
comp1,
comp2
},
data() {
activeSection: 'comp1'
}
</script>
<style scoped>
.section-enter {
top: 100vh;
}
.section-enter-to {
top: 0vh;
}
.section-enter-active {
animation-name: 'slideIn';
animation-duration: 1s;
}
.section-leave {
top: 0vh;
}
.section-leave-active {
animation-name: 'slideOut';
animation-duration: 1s;
}
.section-leave-to {
top: -100vh;
}
#keyframes slideIn {
from {
top: 100vh;
}
to {
top: 0
}
}
#keyframes slideOut {
from {
top: 0vh;
}
to {
top: -100vh;
}
}
</style>
but the actual behavior is that the first component goes upwards but the second appears inmediatly after without animation.
if i render one at a time (not destructing one and rendering another with the same action) everything works perfectly. I dont know what is happening.
There are a few problems in your CSS.
CSS Transitions and CSS Animations
A transition can be implemented using either CSS Transitions or CSS Animations. Your CSS incorrectly mixes the two concepts in this case.
In particular, the slideIn keyframes and .section-enter/.section-enter-to rules are effectively performing the same task of moving .section into view. However, this is missing a transition rule with a non-zero time, required to animate the change, so the change occurs immediately. The same issue exists for the slideOut keyframes and leave rules.
.section-enter {
top: 100vh;
}
.section-enter-to {
top: 0;
}
.section-enter-active {
transition: .5s; /* MISSING RULE */
}
.section-leave {
top: 0;
}
.section-leave-to {
top: -100vh;
}
.section-leave-active {
transition: .5s; /* MISSING RULE */
}
Removing the keyframes, and adding the missing rules (as shown above) would result in a working CSS Transition.
demo 1
Using CSS Animations
Alternatively, you could use keyframes with CSS Animations, where the animation is applied only by the *-active rules, and no *-enter/*-leave rules are used. Note your question contained unnecessary quotes in animation-name: 'slideIn';, which is invalid syntax and would be silently ignored (no animation occurs). I use a simpler shorthand in the following snippet (animation: slideIn 1s;).
.section-enter-active {
animation: slideIn 1s;
}
.section-leave-active {
animation: slideOut 1s;
}
#keyframes slideIn {
from {
top: 100vh;
}
to {
top: 0;
}
}
#keyframes slideOut {
from {
top: 0;
}
to {
top: -100vh;
}
}
demo 2
Optimizing CSS Transitions
You could also tweak your animation performance by using translateY instead of transitioning top.
/* top initially 0 in .wrapper */
.section-leave-active,
.section-enter-active {
transition: .5s;
}
.section-enter {
transform: translateY(100%);
}
.section-leave-to {
transform: translateY(-100%);
}
demo 3
Use a Mixin
Thanks for the explanation #tony19
please use a mixin for this so the logic can be repeated easily.
Also, your slideIn and slideOut can be combined by using reverse:
#mixin animationmixin($type:'animation', $style:'', $duration:1s) {
#keyframes #{$type}-#{$style} { // register animation
0% { opacity: 1; transform: none; } // reset style
100% { #content; } // custom style
}
.#{$style} { // add '.section'
&-enter-active, &-leave-active { // add '.section-enter-active', ...
transition: #{$duration};
}
&-enter, &-leave-to {
animation: #{$type}-#{$style} #{$duration}; // use animation
}
&-leave, &-enter-to {
animation: #{$type}-#{$style} #{$duration} reverse; // use animation in reverse
}
}
}
Use it like this:
#include animationmixin($style:'section') { // set custom styling
transform: translateY(100%);
};
And like this:
#include animationmixin($style:'fade') {
opacity: 0;
transform: scale(0.9);
};
I have a punching bag "game" where I want to add an animation class one every click event. And when the health meter is down to zero, I am replacing the bag picture with an image of a burst bag.
This works ok as long I don't try to add the animation classes. When I try to add the animations, neither functionality is working.
The link is here: https://codepen.io/damianocel/pen/mqXady
This is the HTML:
<div id="vue-app-one">
<!-- the bag images -->
<div id="bag" v-bind:class="[animationToggle ? activeClass : 'swingLeft',
'noSwing']" v-bind:class="{ burst: ended }"></div>
<!-- health meter -->
<div id="bag-health">
<div v-bind:class="[danger ? activeClass : 'dangerous', 'safe']" v-
bind:style="{ width: health + '%'}"></div>
</div>
<!-- the game buttons -->
<div id="controls">
<button v-on:click="punch" v-show="!ended">Punch it!</button>
<button v-on:click="restart">Restart game</button>
</div>
</div>
The problematic part is this:
<div id="bag" v-bind:class="[animationToggle ? activeClass : 'swingLeft',
'noSwing']" v-bind:class="{ burst: ended }"></div>
// Above I try to bind the noSwing CSS class as default and change it to swingLeft if the animationToggle property changes. However, this adds both classes when I check dev tools, and no animation is happening.Can I have 2 class bindings on 1 element like that?
// Further, I bind the ended property to the burst CSS class, this only works if I remove the animationToggle binding and all the relevant CSS.
The instance looks like this:
var one = new Vue({
el: '#vue-app-one',
data: {
health: 100, //init health bar, this works
ended: false, // init ended state, works partially
punched: false, //init punched, don't need for now
danger: false, // this works
animationToggle: false, // there is a problem with this
activeClass: "" // have to init or I get the errors in the console
},
methods: {
punch: function(){
this.health -=10; //works
this.animationToggle= true; // is set on click
if(this.health <= 0){
this.ended = true; // works partially, the background img change is not working ,though
}
if(this.health <= 20){
this.danger = true; // works
}
else{
this.danger = false;
}
setTimeout(function () {
this.animationToggle = false // I am not sure this ever works, give no error, but I am still not sure
}.bind(this),500);
},
restart: function(){
this.health =100;
this.ended = false; // works partially, no img change when health is 0, though
}
}
});
The relevant CSS:
#bag.noSwing {
width: 300px;
height: 500px;
margin: -80px auto;
background: url("https://3.imimg.com/data3/VM/TI/MY-18093/classical-heavy-
bag-250x250.png") center no-repeat;
background-size: 70%;
-webkit-animation-name: swingRight;
-webkit-animation-duration:1s;
-webkit-animation-iteration-count: 1;
-webkit-transition-timing-function: cubic-bezier(.11,.91,.91,.39);
-webkit-animation-fill-mode: forwards;
-webkit-transform-origin: center;
transform-origin: center;
}
#bag.swingLeft {
width: 300px;
height: 500px;
margin: -80px auto;
background: url("https://3.imimg.com/data3/VM/TI/MY-18093/classical-heavy-
bag-250x250.png") center no-repeat;
background-size: 70%;
-webkit-animation-name: swingLeft;
-webkit-animation-duration:1s;
-webkit-animation-iteration-count: 1;
-webkit-transform-origin: right;
transform-origin: right;
-webkit-transition-timing-function: cubic-bezier(.91,.11,.31,.69);
}
#keyframes swingLeft {
0% { -webkit-transform: rotate (0deg); transform: rotate (0deg); }
20% { -webkit-transform: rotate (-20deg); transform: rotate (-20deg); }
50% { -webkit-transform: rotate (20deg); transform: rotate (20deg); }
70% { -webkit-transform: rotate (-10deg); transform: rotate (-10deg); }
100% { -webkit-transform: rotate (0deg); transform: rotate (0deg); }
}
#keyframes swingRight {
0% { -webkit-transform: rotate (0deg); transform: rotate (0deg); }
20% { -webkit-transform: rotate (20deg); transform: rotate (20deg); }
50% { -webkit-transform: rotate (-20deg); transform: rotate (-20deg); }
70% { -webkit-transform: rotate (10deg); transform: rotate (10deg); }
100% { -webkit-transform: rotate (0deg); transform: rotate (0deg); }
}
#bag.burst {
background: url("http://i.imgur.com/oRUzTNx.jpg") center no-repeat;
background-size: 70%;
}
#bag-health {
width: 200px;
border: 2px solid #004;
margin: -80px auto 20px auto;
}
#bag-health div.safe {
height: 20px;
background: #44c466;
}
#bag-health div.dangerous {
background: #00ffff;
}
So why are the animations not applied when the "punch it" button is clicked, why does it add both the noSwing and swingLeft class? And it overrides the functionality which changes the background image to a burst bad when the health meter reaches a value of zero.
First of all you can't have 2 class bindings on the same element, nor should you need them.
You have quite a lot of logic regarding adding/removing classes, so i would suggest extracting it to a computed:
computed: {
className () {
let classes= [];
if (this.animationToggle){
classes.push(this.activeClass)
}
else{
classes.push('swingLeft')
}
return classes;
}
}
}
<div id="bag" :class="className"></div>
I am trying to get a container to come down when i click on an item in my menu. The animation downwards works fine. But the moment i click a different item in my menu, it doesnt animate upwards.
Css:
.card{
width: 100%;
background: blue;
transform: translateY(-100px);
opacity: 0;
height:0;
min-height: 0;
transition-timing-function: cubic-bezier(.175,.885,.32,1.275);
transition-property: opacity,transform;
transition-duration: 1s;
}
.card-appeared{
margin-top: 0;
opacity: 1;
transform: translateY(0);
min-height: 300px;
transition-delay: 1s;
height:auto;
width: 100%;
}
Html:
<div id="aboutme" class="container card ">
About me
</div>
<div id="gallery" class="container card card-appeared">
Gallery
</div>
Basic javascript for adding and removing classes
function appear(child){
parent.classList.remove("card-appeared");
let others = document.getElementsByClassName("card-appeared");
for(var i = 0; i < others.length;i++){
others[i].classList.remove("card-appeared");
}
child.classList.add("card-appeared");
}
function dissapear(child) {
child.classList.remove("card-appeared");
parent.classList.add("card-appeared");
}
others is the list of other cards in the page and the parent is the very first container.
If you need any other code, please let me know. I cannot seem to get the upwards animation working but the animation down does work.
Thank you.
Since an explicit height is only specified when the class card-appeared is added, with the property min-height, the expected behaviour cannot be observed when this class is removed again, since the inherit state of the element in question has no explicit height defined. So it just "pops" back up.
To resolve this, consider the below:
.card {
width: 100%;
background: blue;
transform: translateY(-600px); /* adjusted */
opacity: 0;
height: 0;
min-height: 300px; /* added */
transition-timing-function: cubic-bezier(.175, .885, .32, 1.275);
transition-property: opacity, transform;
transition-duration: 1s;
}
.card-appeared {
margin-top: 0;
opacity: 1;
transform: translateY(-300px); /* adjusted */
transition-delay: 1s;
height: auto;
width: 100%;
}
Breakdown: Since static positioning is being used here, elements with y-positioning offsets will still occupy space in the DOM. In order to account for this, the values of the transform: translateY() properties must be adjusted accordingly now that the elements in question always have a minimum height defined.
For Consideration: A better solution to this may be utilizing absolute positioning; this will remove the elements in question from the natural flow of the document, meaning you will not have to account for space occupied in the DOM by these elements, so transform: translateY() property values can remain intuitive.
I am building a custom wizard form with waypoints. Something interesting is happening and I can't figure it out for the life of me.
My sample CODEPEN is showing 2 pages of the wizard process to show you what I mean.
When you hit the forward action button (search in the first page of the wizard), the waypoints slide from the right and the next page or screen shows. That would repeat on-forward and backwards if I click on the backward action button. That is working.
The problem I see is with the initial horizontal scrollbar. It shows on page load, which it's a problem because the user could just scroll to the next screen by dragging the scrollbar. I thought of giving it an overflow-x but it didn't fix the issue. The interesting thing is, if I click on the search button and the waypoint slides, the scroll bar disappears and gives me the desired effect! What gives?
I built the CODEPEN as close as possible to the real environment so that you guys can catch any conflict with other elements instead of isolating the problem.
Here is the related code just in case:
HTML:
<div id="content" class="content">
<div class="row page">
<!-- First page content here -->
</div>
<div class="row page2">
<!-- Second page content here -->
</div>
</div>
CSS:
.page, .page2 {
position: absolute;
top: 20px;
left: 10px;
width: 100%;
-webkit-transition: -webkit-transform 0.8s;
transition: -webkit-transform 0.8s;
transition: transform 0.8s;
transition: transform 0.8s, -webkit-transform 0.8s
}
.page {
-webkit-transform: translateX(0%);
transform: translateX(0%)
}
.show-page2 .page {
-webkit-transform: translateX(-100%);
transform: translateX(-100%)
}
.page2 {
-webkit-transform: translateX(100%);
transform: translateX(100%)
}
.show-page2 .page2 {
-webkit-transform: translateX(0%);
transform: translateX(0%)
}
JS:
(function () {
var body = $('#content'),
nav = $('.btn-waypoint'),
panels = $('#content');
nav.on('click', function (e) {
e.preventDefault();
var dest = $(this).data('panel-link');
body
.removeClass(function (index, css) {
// remove only classes start with show-
// http://stackoverflow.com/questions/2644299/jquery-removeclass-wildcard
return (css.match(/\bshow-\S+/g) || []).join(' ');
})
.addClass('show-' + dest);
});
}());
The closest fix I've tried to solve this is making page2 display:none on page load to eliminate the scrollbar and then making it visible on button click. That almost did it except a funky look happens between the waypoint sliding effect and the css fade effect. Here is the code for that:
JS
$( document ).ready(function() {
$('.page2').css('display', 'none');
$('[data-panel-link]').on('click', function(){
$('.page2').css('display', 'block');
});
});
Here is the link to my CODEPEN
Thanks in advance!
As it plays out the root of the problem is the hard positioning. The waypoint divs are natively in vertical position which they would obviously not produce a horizontal scrollbar. They are being forced to be side by side by position:absolute and the transform: translateX(-100%) and this creates the horizontal scrollbar. If the mousewheel is disabled via jQuery the scrollbar goes away, but it goes away vertically as well. So instead of fighting that battle, a better alternative is to use a different transition that looks good but doesn't require a side by side animation. A fade will do just nice:
Simply replace the css effects from translateX to the following:
.page, .page2{
position: absolute;
width:100%;
transition: opacity 1s ease-in-out;
-moz-transition: opacity 1s ease-in-out;
-webkit-transition: opacity 1s ease-in-out;
}
.page {
opacity: 1;
}
.show-page2 .page {
opacity: 0;
}
.page2{
opacity: 0;
}
.show-page2 .page2{
opacity: 1;
}