How to setup initial position of target element in anime.js? - vue.js

Target
On click "Open menu" button:
Dim overlay appearing with fade-in animation
Once dim overlay animation done, from the top, dim overlay is appearing with the sliding animation from the top to bottom:
Solution attempt and problem
<template>
<transition #enter="animateOpening" #leave="animateClosing">
<div
class="SearchProductsPane-DimUnderlay"
v-if="isOpen"
:ref="elementsReferencesIDs.overlay"
>
<div
class="SearchProductsPane-Body"
:ref="elementsReferencesIDs.body"
>
<!-- ... -->
</div>
</div>
</transition>
</template>
import { Vue, Component } from "vue-property-decorator";
import Animation from "animejs";
#Component
export default class SearchProductsPane extends Vue {
private elementsReferencesIDs: Record<"overlay" | "body", string> = {
overlay: "Overlay", body: "Body"
};
private animateOpening(_element: Element, done: () => {}): void {
Animation
.timeline({
easing: "linear",
duration: 500,
complete: done
})
.add({
targets: this.$refs[this.elementsReferencesIDs.overlay],
opacity: [0, 1]
})
.add({
targets: this.$refs[this.elementsReferencesIDs.body],
translateY: "100%"
})
}
private animateClosing(): void {
}
}
With current solution, the .SearchProductsPane-Body will move from the normal position to the downward outside of the screen. Instead of it, I need it move from the upward outside of the screen to it normal position.
I tried reach it by adding of below class:
.SearchProductsPane-Body__InitialPosition {
transform: translateY(-100%);
}
However, the animejs animation starts from the 0%, not -100%. How to change it?

You can use From Value in animeJs, which allows you to define you'r animation origin:
anime.stagger(100, {from: 'center'})
you'r options are:
first: animation starts from the first element(Default from value)
last: animation starts from the last element
center: animation starts from the center
index: you choose which element the animation starts from
here is the docs.
Or you can use From to which forces the animation to start at a specified value.
In you'r code:
anime({
targets: '.SearchProductsPane-DimUnderlay'
});
docs here

Related

Cannot make VueMapbox 100% of parent

I am using a VueMapbox with markers and trying to display it in a parent container. I added this CSS because without this the map has 0 height and width:
.mapboxgl-map {
position: relative !important;
}
I want the width to be 100% of the parent, so I added this which DOES work:
.mapboxgl-canvas {
height: 100% !important;
width: 100% !important;
}
HOWEVER, when you drag the map or zoom in, the markers move around as if the map is the default size. I've tried messing around with the CSS and I haven't had any success.
I've also tried calling map.resize() after the map gets loaded. The function gets called but doesn't do anything and once you zoom in to the map, the background disappears. Here is my component:
<template>
<MglMap :accessToken="accessToken" :mapStyle="mapStyle"
:center="coordinates"
#load="onMapLoaded"
>
<MglMarker v-for="team in teams"
:key="team.id"
:coordinates="[team.lng, team.lat]"
>
</MglMarker>
</MglMap>
</template>
<script>
import Mapbox from "mapbox-gl";
import { MglMap, MglGeojsonLayer, MglMarker, MglPopup } from "vue-mapbox";
export default {
components: {
MglMap,
MglGeojsonLayer,
MglMarker,
MglPopup
},
mixins: [teamHelper],
data() {
return {
accessToken: ...,
mapStyle: "mapbox://styles/mapbox/streets-v11",
coordinates: [-50.549668, 39.014],
map: null,
mapbox: null
}
},
props: {
teams: []
},
created() {
this.mapbox = Mapbox
},
methods: {
onMapLoaded(event) {
this.map = event.map;
this.map.resize(); // does not work
},
}
};
</script>
Once you resize the window it works as expected, so if there were a way to trigger that properly, then I feel like it should work. I've also read all of the similar questions on this I could find, and none helped, AND I read the documentation which does not mention anything about this.
I'm using Vue ^2.5.17 and vue-mapbox ^0.4.1
Alright well I found a somewhat hacky workaround. You just have to render the map component inside an IFrame, set the size of the IFrame to 100% of the parent and you're good to go.
If you need to pass data from the parent component to the child, you can do something like this: http://blog.pixelastic.com/2017/09/12/sending-data-to-an-iframe-with-vue-js/
If you have a better solution or know why the resizing wasn't working then please respond.

enter and leave classes of vue transition doesn't work

I created this codepen, which is a simple flip card and it works fine in codepen, but when I add this project in my vue project created with cli, everything works fine; upon clicking a card, it shows back of the card, but it doesn't apply that transition so user can visually see that it is rotating. It rotates very fast, sounds like transition is not effecting.
This is the template code
<div v-for="card in cards" #click="toggleCard(card)" :key="card.id">
<transition name="flip">
<div
v-bind:key="card.flipped"
v-html="card.flipped ? card.back : card.front"
></div>
</transition>
</div>
and the script code
export default {
name: "FlipCard",
data() {
return {
cards: [
// cards here
],
};
},
methods: {
toggleCard: function (card) {
const isFlipped = card.flipped;
this.cards.forEach((strategy) => {
strategy.flipped = false;
});
isFlipped === true ? (card.flipped = false) : (card.flipped = true);
},
},
};
and css code:
.flip-enter-active {
transition: all 2s ease;
}
.flip-leave-active {
display: none;
}
.flip-enter,
.flip-leave {
transform: rotateY(180deg) !important;
opacity: 0;
}
can anyone help why in vue cli project the transition is so fast or maybe not applying?
Thank you in advance
The codepen you provided uses Vue 2. Your question is tagged Vue 3, so I assume you are using Vue 3.
Vue 3 made changes to transition class names - https://v3-migration.vuejs.org/breaking-changes/transition.html#_2-x-syntax.
-enter and -leave are now -enter-from and -leave-from.

How to disable transition-group only on page load?

I have a transition-group that renders a div, and within that is a component with a v-for attribute that renders several items. I then have a button that adds a new item to the beginning of the array. That transition works perfectly.
The only thing I don't like, is that the entire list loads with the transition on page load, and I'd like to disable it only on page load. I've searched Stack and Google but couldn't find a way. Is there a way to do this, so that transitions still works on button click, but is disabled for page load?
<transition-group
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
.item-list-enter-active,
.item-list-leave-active,
.item-list-move {
transition : 250ms cubic-bezier(0.59, 0.12, 0.34, 0.95);
transition-property: opacity, transform;
}
.item-list-enter {
opacity : 0;
transform: translateX(50px) scaleY(0.5);
}
.item-list-enter-to {
opacity : 1;
transform: translateX(0) scaleY(1);
}
.item-list-leave-active {
position: absolute;
}
.item-list-leave-to {
opacity : 0;
transform : scaleY(0);
transform-origin: center top;
}
I wish I could've found a more "Vue-y" way of handling this, however I ended up going this route. Essentially I added a class to the body and removed all transitions. Then on the created lifecycle of my component, I remove that class. This removes the transition on page load, but still keeps the transition on click of the button like I want.
You can dynamically change the name value of the transition-group. Maybe on page load have a value different from the value that has the correct class name that the CSS targets. Then in the mounted lifecycle hook you can change it back to the correct class name.
You need to bind the duration for transition-group
template:
<transition-group
:duration="duration"
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
script:
data() {
return {
duration: 0,
items: [
{id: 1},
{id: 2}
]
}
},
methods: {
add() {
if(this.duration===0) this.duration = 250
this.items.push({id: 'xxx'})
}
}
In case anyone comes across this like I did.
I ended up achieving this by having a transitionsOn flag added to the data (didn't seem to matter what it was initialised to), and a computed name for the transition, i.e.
<transition-group :name="transitionName">
in computed, I then had, for a transition called 'flash'
computed: {
transitionName() {
return this.transitionsOn ? 'flash' : 'disabled';
},
},
I would then set this.transitionsOn = true when I wanted it to fire.
Took a lot of fiddling about to figure this out but it seems to work OK

How to do width transition in Tailwind CSS?

When I try to do a transition using the default "w-#" options in Tailwind, my transitions don't apply. When I hard code in my own classes for width, it works fine. Is there something weird with Tailwinds CSS and how it handles width that would cause this?
Here's the HTML text. The main part here is the dynamic class "sidebarWidth" which switches when the button is clicked. The transition-all, slowest and ease are all things I extended in Tailwind.
<nav class="text-white absolute md:relative flex-col min-h-full bg-black mt-24 md:mt-12 transition-all transition-slowest ease" :class="sidebarWidth">
Here's the JS code in the computed properties of the Vue component
sidebarWidth: function() {
if (this.$store.getters.isSidebarCollapsed) {
return "w-14 invisible md:visible";
} else {
return "w-64";
}
}
If I swap out w-14 and w-64 for the following classes, it works great.
<style scoped>
.width1 {
width: 100px;
}
.width2 {
width: 400px;
}
</style>
I basically want my sidebar nav to slide in when I click a button. In mobile, the sidebar nav is hidden and I want it to slide out. In the desktop, it should be a small nav and then slide out to a full screen nav. It works, but the slide transition doesn't work. Also, the margin change between mobile and desktop does animate properly.
You need to perform a few steps to activate the behaviour you are looking for.
The first one is about extending you Tailwind theme via tailwind.config.js. You need to add the transitionProperty for width.
module.exports = {
...
theme: {
...
extend: {
...
transitionProperty: {
'width': 'width'
},
},
},
}
The changes above create the transition-width class. Simply apply this one to your nav tag. In your specific case you can overwrite the transition-all class.
<nav class="text-white absolute md:relative flex-col min-h-full bg-black mt-24 md:mt-12 transition-width transition-slowest ease" :class="sidebarWidth">
The last step is quite easy: ensure that Tailwind is recreating the CSS. Afterwards you should see a smooth width transition in your project.
Background to the Problem
By default, the width and height transitions are not activated in Tailwind CSS. Here is the CSS that will be applied when using transition class.
transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
Like you can see width and height are missing in transition-property.
You can find more information about it in the Tailwind documentation.
You can make your own transition property like in tailwind.config.js :
Multiple properties :
module.exports = {
theme: {
extend: {
transitionProperty: {
multiple: "width , height , backgroundColor , border-radius"
}
}
}
One property :
module.exports = {
theme: {
extend: {
transitionProperty: {
width: "width"
}
}
}

Vue.js transition mode not working?

I am currently working on a slider component but it seems that my transition mode is not working. I have used:
<transition name="fade" mode="out-in">
To my understanding this should force the element going out to first complete it's entire animation before the element in is being rendered.
This is an example of my slider component:
https://www.webpackbin.com/bins/-KfMMcLvpUA6LGOFL3vM
I could fix it by overlaying images absolute but that is obviously not something that I want to do when there could be a neater solution.
The transition CSS is included in the header of the index page:
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
I have it up the as easily usable as possible, if somebody could take a look that would be awesome.
Your understanding that one transition will complete before another starts is wrong. It might be true if you were changing out an element within a single transition tag, but it is not true when each element is wrapped in its own transition tag.
The easiest thing you could do is use setTimeout to make the assignment of the next activeSlide wait for the transition.
methods: {
prevSlide() {
const nextSlide = this.activeSlide === this.firstSlide ? this.lastSlide : this.activeSlide - 1;
this.activeSlide = null;
setTimeout(() => {
this.activeSlide = nextSlide;
}, 500);
},
nextSlide() {
const nextSlide = this.activeSlide === this.lastSlide ? this.firstSlide : this.activeSlide + 1;
this.activeSlide = null;
setTimeout(() => {
this.activeSlide = nextSlide;
}, 500);
}
}
That requires that your timeout value and your CSS transition length be kept in sync.
Somewhat more work would be to use the transition hooks to emit events when a leave transition started and ended.
<transition name="fade" mode="out-in" #leave="transitionStarted" #after-leave="transitionComplete">
You would catch them in the top-level code, (not in slider, because slot events don't propagate to the slot container)
<slide src="http://lorempixel.com/196/196" alt="" #transition-started="holdUp" #transition-complete="okGo"></slide>
In the handlers (holdUp and okGo) you would set a boolean data item that you would pass to slider as a prop. In slider, nextSlide and prevSlide would calculate what the next value of activeSlide will be, but will not set it. It would get set when the prop indicated that the transition was finished.