I have a menu form. To add and remove items from this menu, I use React Transition Group
ReactJS:
<TransitionGroup>
{
menu.map(meal =>
<CSSTransition
key={meal.id}
timeout={500}
classNames="meMeals"
>
<Meal meal={meal} deleteFromMenu={deleteMealFromMenu}/>
</CSSTransition>
)
}
</TransitionGroup>
CSS:
.meMeals-enter {
opacity: 0;
transform: translateY(-30px);
}
.meMeals-enter-active {
opacity: 1;
transform: translateY(0);
transition: all 500ms ease-in;
}
.meMeals-exit {
opacity: 1;
transform: translateY(0);
}
.meMeals-exit-active {
opacity: 0;
transform: translateY(-30px);
transition: all 500ms ease-in;
}
and I am completely satisfied with the behavior of the menu items.
Now I want the background element (grey) as well as the add button to move smoothly as the menu item appears or disappears. How can i do this?
I solved the problem by writing a method that is not directly related to the TransitionGroup, but works in parallel. I also set my window:
transition: 0.5s;
whatever the animation
Now I call this method every time the list changes....
function replaceMenuSize(value) {
const menuSize = menuEditorRef.current.getBoundingClientRect().height
if (value > 0) {
menuEditorRef.current.setAttribute("style", "height: " + (menuSize + 41) + "px")
} else {
menuEditorRef.current.setAttribute("style", "height: " + (menuSize - 41) + "px")
}
}
Is it possible for an element that is being animated using keyframes to animate to a fixed value for that property? My use case is a draggable element whose scale is pulsing until clicked, but when clicked it should have a fixed size. I would like the element to animate to that new size.
This example uses styled-components.
const pulse = keyframes`
from {
transform: scale(1);
}
to {
transform: scale(1.05);
}
`
const AnimatedThing = styled(div)`
${({ isDragging }) =>
isDragging ?
'transform: scale(1.2);' :
`animation: ${pulse} 0.5s alternate infinite;`
}
`
As you can see in the code, when isDragging is true, scale is set to 1.2. Right now it just jumps to that value, but I want it to animate to that value.
I tried using css variables and animating those, and couldn't get that to work either. That code is a bit more complex but you can see it here
const pulse = keyframes`
from {
transform: scale(var(--pulse-start-scale));
}
to {
transform: scale(var(--pulse-end-scale))
}
`
const AnimatedGroup = styled(div)`
#property --pulse-start-scale {
syntax: '<number>';
inherits: false;
initial-value: 1;
}
#property --pulse-end-scale {
syntax: '<number>';
inherits: false;
initial-value: 1.05;
}
transition: --pulse-start-scale --pulse-end-scale 1s;
animation: ${pulse} 0.5s alternate infinite;
${({ $active }) =>
$active &&
`
--pulse-end-scale: 1.2;
--pulse-start-scale: 1.2;
`}
`
In my navbar, I have a "Cart" item with a <v-badge /> on it that displays how many items are in the cart. When a user adds or removes to the cart, the number correctly increments and decrements. On state change of that number however, I'd like to be able to "bounce" the badge to provide the user with feedback that the item has been added or removed from the cart. I've been looking at the Vue docs for animations and transitions, I'm just not quite understanding how I'd go about achieving this.
I've attempted wrapping the badge in a <transition /> element and applying some keyframes animations I found on CSS Tricks, however it's still not working.
html:
<v-tabs
class="hidden-sm-and-down"
optional>
<v-tab
v-for="(item, i) in items"
:key="i"
:exact="item.title === 'Home'"
:to="item.to"
:ripple="false"
active-class="text--primary"
class="font-weight-bold nav-link"
min-width="96"
nuxt
text>
<transition
name="ballmove"
enter-active-class="bouncein"
leave-active-class="rollout">
<v-badge
v-if="item.badge && hasCartItems"
color="red"
:content="cartItems"
:value="cartItems"
class="default-badge"
overlap>
{{ item.title }}
</v-badge>
<span v-else>{{ item.title }}</span>
</transition>
</v-tab>
</v-tabs>
scss:
#mixin ballb($yaxis: 0) {
transform: translate3d(0, $yaxis, 0);
}
#keyframes bouncein {
1% { #include ballb(-400px); }
20%, 40%, 60%, 80%, 95%, 99%, 100% { #include ballb() }
30% { #include ballb(-80px); }
50% { #include ballb(-40px); }
70% { #include ballb(-30px); }
90% { #include ballb(-15px); }
97% { #include ballb(-10px); }
}
#keyframes rollout {
0% { transform: translate3d(0, 300px, 0); }
100% { transform: translate3d(1000px, 300px, 0); }
}
#keyframes ballroll {
0% { transform: rotate(0); }
100% { transform: rotate(1000deg); }
}
.rollout {
width: 60px;
height: 60px;
animation: rollout 2s cubic-bezier(0.55, 0.085, 0.68, 0.53) both;
div {
animation: ballroll 2s cubic-bezier(0.55, 0.085, 0.68, 0.53) both;
}
}
.bouncein {
animation: bouncein 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
.ballmove-enter {
#include ballb(-400px);
}
So I ended up going more of an old-school way. I took out the <transition /> wrapper around the <v-badge />, then added a watch function as such:
watch: {
cartItems: function(newValue, oldValue) {
const badge = document.querySelector('.v-badge__badge');
if (newValue !== oldValue) {
badge.classList.add('bounce');
this.delay(500).then(() => {
badge.classList.remove('bounce');
});
}
}
},
and my scss looks like:
#mixin ballb($yaxis: 0) {
transform: translate3d(0, $yaxis, 0);
}
#keyframes bouncein {
0%, 50% { #include ballb(-3px); }
25%, 75%, 100% { #include ballb() }
}
.bounce {
animation: bouncein 500ms cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
This gives the little "shake" of the badge I was looking for, however if someone has a more native Vue way of handling it, I'd absolutely love to see it.
In Vue 2.6.11, here is how I got a bit of a bounce, with some help from this answer from #Bill Criswell.
From his answer I got that I needed the :key on the badge element to force a re-render. The rest is straight out of Vue.js example here.
<transition name="bounce">
<v-badge
:key="item.comments.length"
v-if="item.comments && item.comments.length > 0"
>
<span slot="badge">
{{ item.comments.length }}
</span>
<v-icon>mdi-comment </v-icon>
</v-badge>
</transition>
And the associated SCSS just copied from the Vue example above:
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
#keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
I wrote some pieces of codes in angular about rotating an icon by pressing a toggle button in angular.
When I run it in chrome everything is OK but in Edge rotation process works properly but final rotation status is 180 degree reverse.
Here is my snippets:
my .ts file:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-menutest',
templateUrl: './menutest.component.html',
styleUrls: ['./menutest.component.scss']
})
export class MenutestComponent implements OnInit {
public classChangesRotation: {[key: string]: string} = {};
public expandeds: {[key: string]: boolean} = {};
private zero: string;
constructor() {
this.zero = 'zero';
this.expandeds[this.zero] = true;
this.classChangesRotation[this.zero] = 'rotate-expanded';
}
expansion_click(divName: string) {
this.expandeds[divName] = !this.expandeds[divName];
if (this.expandeds[divName]) {
this.classChangesRotation[divName] = 'rotate-expanded';
} else {
this.classChangesRotation[divName] = 'rotate-collapsed';
}
}
}
my html file:
<button mat-raised-button (click)="expansion_click('zero')">
Expand and Collapse
<mat-icon [ngClass]="classChangesRotation['zero']">
chevron_left
</mat-icon>
</button>
and my scss file contains :
.rotate-expanded {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
transition: -webkit-transform 150ms;
transition: transform 150ms;
transition: transform 150ms,-webkit-transform 150ms;
transition-timing-function: ease-in-out;
}
.rotate-collapsed {
-webkit-transform: rotate(0);
transform: rotate(0);
transition: -webkit-transform 150ms;
transition: transform 150ms;
transition: transform 150ms,-webkit-transform 150ms;
transition-timing-function: ease-in-out;
}
As I mentioned above when I run these codes in chrome everything is OK and normal but in Edge the animation process is fine but final rotation differs 180 degrees!
Do you have any idea?
Thank you for your attentions.
Pay attention please:
You should know that direction of this code is right to left and result in LTR direction is OK but the problem is in RTL direction. I understand it right now and added it to my question.
I am quite new to react and I am trying to build a react app. I want a login box to fade in on the page when the login in the navbar is clicked. In my App.js I set up the CSSTransitionGroup like this:
<CSSTransitionGroup
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
transitionAppear={true}
transitionAppearTimeout={500}
transitionName="fade">
{loginPopup}
</CSSTransitionGroup>
The login popup is shown based on state:
let loginPopup;
if (display_login) {
loginPopup = (<LoginPopup key={1} />)
}
Now the css I use for the transition looks like this:
//fade
.fade-appear {
opacity: 0.01;
}
.fade-appear-active {
opacity: 1;
transition: opacity 300ms ease-in;
}
.fade-leave {
opacity: 1;
}
.fade-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}
My problem is...that the fadeout works as intended, but the fadein does not work. It just pops up (no transition).
I also tried using .fade-enter - But I figured that enter was wrong as the LoginPopup component is spawned based on state.
Any ideas why the fade in don't work?
Edit:
This is the gist of the code: https://gist.github.com/xenoxsis/6345eb6897aedf228662ffaf64f65053