jsffidle
<div class="container">
<input placeholder="find PLEASE" v-model="search" />
<transition-group name="fade">
<div v-for="(item, index) in filteredItems" :key="index" class="container__item">{{ item }}</div>
.container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
new Vue({
el: '.container',
data() {
return {
search: '',
items: ['lorem opas', 'opas loram ', 'mushroms so good']
}
},
computed: {
filteredItems() {
return this.items.filter(item => item.indexOf(this.search) > -1)
}
}
})
Why transition-group animation not worked? all items have a unique key, also I get the name for transition-group.
Your transition is in fact working, however you haven't specified any style for it.
From the official documentation:
When an element wrapped in a transition component is inserted or
removed, this is what happens:
Vue will automatically sniff whether the target element has CSS
transitions or animations applied. If it does, CSS transition classes
will be added/removed at appropriate timings.
To make your example work, you can have like so:
new Vue({
el: '.container',
data() {
return {
search: '',
items: [
'lorem opas',
'opas loram ',
'mushroms so good'
]
}
},
computed: {
filteredItems() {
return this.items.filter(item =>
item.indexOf(this.search) > -1
)
}
}
})
.container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div class="container">
<input placeholder="find PLEASE" v-model="search" />
<transition-group name="fade">
<div class="container__item"
v-for="(item, index) in filteredItems"
:key="index"
>
{{ item }}
</div>
</transition-group>
</div>
All that was missing were the CSS transition classes.
<transition-group name="fade">
In this case, the specified name should be the prefix of all the css classes. Currently there are 6 transition classes that vue uses, which for the example above, would be the following:
fade-enter
fade-enter-active
fade-enter-to
fade-leave
fade-leave-active
fade-leave-to
Also note that you can share the same classes with multiple instances of this transition-group in your app.
Related
I have this TabPanel where I am rendering multiple tabs. Each Panel inside TabPanel uses/extends Panel where I am using slots. However, during tab switch content of Tabs are getting merged.
<template>
<div class>
<div class>
<ul class>
<li
style="display: inline-block; padding:10px; margin: 10px; border: 1px solid #ccc"
v-for="(panel, index) in panels"
:key="index"
#click="selectTab(index)"
:ref="'panel' + index"
>{{ panel.title }}</li>
</ul>
</div>
<div class>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "TabPanel",
data() {
return {
panels: [],
selectedIndex: 0
};
},
methods: {
selectTab(panelIndex) {
this.selectedIndex = panelIndex;
this.panels.forEach((panel, index) => {
panel.isActive = index === panelIndex;
});
}
},
created() {
this.panels = this.$children;
},
mounted() {
this.selectTab(0);
}
};
</script>
Panel code
<template>
<div v-show="isActive">
<slot></slot>
</div>
</template>
<script>
export default {
name: "Panel",
props: {
title: {
type: String,
required: true
},
panelId: {
type: String,
required: true
}
},
data() {
return {
isActive: true
};
}
};
</script>
Is there anyway to fix this issue. This is the CodeSandbox link to demonstrate this issue since its easier to visualize problem that way I think.
Dashboard.vue and RosePanel.vue changes isActive data but they are not doing nothing whit this.
One option is to set a v-show/v-if to each own <Panel>:
<Panel v-show="isActive" :title="title" :panelId="panelId">Content from Dashboard</Panel>
CodeSandbox: https://codesandbox.io/s/charming-hamilton-jz1og
The simplest way to fix the issue is to wrap all components in <Panel>-s:
<TabPanel>
<Panel title="Dashboard" panel-id="dashboard">
<Dashboard></Dashboard>
</Panel>
<Panel title="Test" panel-id="test">Content from Panel</Panel>
<Panel title="Rose Panel" panel-id="rose-panel">
<RosePanel></RosePanel>
</Panel>
</TabPanel>
https://codesandbox.io/s/eager-brown-6ethk?file=/src/App.vue
But this is probably not what you had in mind.
From what I see, you simply forgot to propagate the "is-active" property to the embedded panel:
<Panel :title="title" :panelId="panelId" :is-isActive="isActive">Content from Dashboard</Panel>
(e.x in the Dashboard Component)
I am using the vuetify framework and I am running into this issue where I am not sure how I can add an item from the list multiple times. I have a dropdown list and I would like to add the option foo or any option multiple times on select. Here is a link to the demo codepen.
So right now if I select foo or any other option and then select it again from the dropdown list, it goes away, instead I want another chip with same option
added into it?
new Vue({
el: '#app',
data() {
return {
items: [{
text: 'Foo',
value: 'foo'
},
{
text: 'Bar',
value: 'bar'
},
{
text: 'biz',
value: 'buzz'
},
{
text: 'buzz',
value: 'buzz'
}
],
}
}
})
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<v-app id="inspire">
<v-container>
<v-combobox :items="items" label="Add Multiple Chips" multiple small-chips solo deletable-chips>
<template v-slot:item="{ index, item }">
<v-list-tile-content>
{{item.text}}
</v-list-tile-content>
</template>
<template v-slot:selection="{ index, item }">
<v-chip close dark color="info">
{{ item.text }}
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
If anyone has any clue on how to achieve this. It will be much appreciated. Thank you
A couple of small adjustments,
put a .stop on the item click to prevent Vuetify from processing after your handler
tell the combo-box to use arr for :value
add a delete click handler to v-chip and corresponding method (NB this works on Vuetify 2.1.0, but not on Vuetify 1.5.14 as used on the Codepen. If you don't need that specific version, install the latest.
Codepen Vuetify v1.5.14
CodeSandbox Vuetify v2.1.0
<template>
<div id="app">
<v-app id="inspire">
<v-container>
<v-combobox
:items="items"
label="Add Multiple Chips"
multiple
small-chips
solo
deletable-chips
:value="arr"
>
<template v-slot:item="{ index, item }">
<v-list-tile-content #click.stop="multipleSelection(item)">{{item.text}}</v-list-tile-content>
</template>
<template v-slot:selection="{ index, item }">
<v-chip close dark color="info"
#click:close="deleteChip(item)" >{{ item.text }}</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
</template>
<script>
export default {
name: "playground",
data: () => ({
arr: [],
items: [
{
text: "Foo",
value: "foo"
},
{
text: "Bar",
value: "bar"
},
{
text: "biz",
value: "buzz"
},
{
text: "buzz",
value: "buzz"
}
]
}),
methods: {
multipleSelection(item) {
this.arr.push({...item});
console.log(this.arr);
},
deleteChip(item) {
this.arr = this.arr.filter(x => x !== item);
console.log(this.arr);
}
}
};
</script>
Addressing the problem of long selection lists being obscured by the dropdown menu,
The dropdown menu looks like this at runtime (via chrome developer tools)
<div class="v-menu__content theme--light menuable__content__active v-autocomplete__content"
style="max-height: 304px; min-width: 357px; top: 149px; left: 12px; transform-origin: left top; z-index: 8;">
<div role="listbox" tabindex="-1" class="v-list v-select-list v-sheet v-sheet--tile theme--light theme--light"
id="list-261">
<div tabindex="0" role="menuitem" id="list-item-267" class="v-list-item v-list-item--link theme--light">Foo</div>
<div tabindex="0" role="menuitem" id="list-item-268" class="v-list-item v-list-item--link theme--light">Bar</div>
<div tabindex="0" role="menuitem" id="list-item-269" class="v-list-item v-list-item--link theme--light">biz</div>
</div>
</div>
and has these styles
element.style {
max-height: 304px;
min-width: 357px;
top: 149px;
left: 12px;
transform-origin: left top;
z-index: 8;
}
Vuetify changes the top: 149px in it's selection handler, but since we turned that off we need to call updateMenuDimensions() in our own handler multipleSelection().
To do this, add a ref to the combobox,
<v-combobox
:items="items"
label="Add Multiple Chips"
multiple
small-chips
solo
deletable-chips
:value="arr"
ref="combobox"
>
...
</v-combobox>
Then add the call to updateMenuDimensions, inside a nextTick() to allow the selection box to settle.
methods: {
multipleSelection(item) {
this.arr.push({ ...item });
console.log(this.arr);
this.$nextTick(() => this.$refs.combobox.updateMenuDimensions())
},
deleteChip(item) {
this.arr = this.arr.filter(x => x !== item);
console.log(this.arr);
this.$nextTick(() => this.$refs.combobox.updateMenuDimensions())
}
}
Codepen Vuetify v1.5.14 (NB not deleting chips).
Preventing text from adding an item
There's another problem (for anyone following here but not seeing the chat room discussion). I've separated the answers for each issue, to make things easier to follow.
Feel free to vote on all answers :).
Problem
If the user types into the combobox, the expectation is that the list will be filtered, and it is.
But if the user presses enter after the text, an item is added and this is not desired.
We tried a permutations of event suppression, including,
#keydown.enter.prevent
#keypress.enter.prevent
#submit.prevent
#keydown.enter.prevent="enterItem" with event.preventDefault() in the handler
#blur.prevent
plus permutations with .stop, .capture.
None of the event suppression worked, so a hacky way to do it is to v-if the v-chip
<v-combobox
...various attributes as before
>
<template v-slot:item="{ index, item }">
...
</template>
<template v-slot:selection="{ index, item }">
<v-chip v-if="item.text" ...other attributes >{{ item.text }}</v-chip>
</template>
</v-combobox>
This works because the added item has no text (reason unknown), and the variable this.arr does not get modified when pressing the enter key as described.
Codepen
I'm trying to have the navigation on my Vue app to have a main menu up top populated by all the root level routes, and a side menu if the route has any children. Like so:
The current Design I have has the main navigation with routing links on the top with the router-view below. I have been able to get the side menu working so it it only shows up when I choose Travellers and updates the components/content correctly. However I'm having trouble routing things correctly, when I click on one of the links in the sub menu it does not append to the current path. So when I click on View when I'm in localhost/Traveler and click View the url changes to localhost/View/ instead of localhost/Traveler/View. Also the selection on the top menu gets unselected when I choose something in the child menu.
And I cannot get to the pages via something like localhost/Traveler/View only localhost/View
I started rereading the documentation on nested routes as I began making this post and I think I realized that I should be creating an new router at each level which is not something I have done in my code below.
I'm also not sure how to access the children of the current route. I've tried to display them like so:
<h2>Route: {{ $route.name }}</h2>
<ul id="example-1">
<li v-for="child in $route.children">
{{ child.name }}
</li>
</ul>
But I get nothing. Should they be passed as Params or something? Or are the not that easily accessible?
Any advice or help will be greatly appreciated.
Root
Contains top Menu-Nav with router links and router-view below.
<template>
<div id="app" class="container-fluid">
<div class="row">
<div style="width:100%">
<nav-menu params="route: route"></nav-menu>
</div>
</div>
<div class="row">
<div>
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import NavMenu from './nav-menu'
export default {
components: {
'nav-menu': NavMenu
},
data() {
return {}
}
}
</script>
Top Nav-Menu
Gets populated with the routes
<template>
<nav class="site-header sticky-top py-1">
<div class="container d-flex flex-column flex-md-row justify-content-between">
<a class="nav-item" v-for="(route, index) in routes" :key="index">
<router-link :to="{path: route.path, params: { idk: 1 }}" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ route.display }}</span>
</router-link>
</a>
</div>
</nav>
</template>
<script>
import { routes } from '../router/routes'
export default {
data() {
return {
routes,
collapsed: true
}
},
methods: {
toggleCollapsed: function (event) {
this.collapsed = !this.collapsed
}
}
}
</script>
Traveler Page/View
Currently the Traveller Page which has a side bar menu and another router view for the content:
<template>
<div id="app" class="container-fluid">
<div class="wrapper">
<traveler-menu params="route: route"></traveler-menu>
<div id="content">
<router-view name="travlerview"></router-view>
</div>
</div>
</div>
</template>
<script>
import TravelerMenu from './traveler-menu'
export default {
components: {
'traveler-menu': TravelerMenu
},
data() {
return {}
}
}
</script>
Side Bar/ Traveler Menu
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Route's Children:</h3>
</div>
<ul class="list-unstyled components">
<li>
<a class="nav-item" v-for="(route, index) in travelerroutes" :key="index">
<router-link :to="{path: route.path, params: { idk: 1 }}" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ route.display }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
import { travelerroutes } from '../../router/travelerroutes'
export default {
data() {
console.log(travelerroutes);
return {
travelerroutes,
collapsed: true
}
},
methods: {
toggleCollapsed: function (event) {
this.collapsed = !this.collapsed
}
}
}
</script>
Routes
import CounterExample from 'components/counter-example'
import FetchData from 'components/fetch-data'
import HomePage from 'components/home-page'
import TestPage from 'components/test-page'
import Travelers from 'components/Traveler/traveler-root'
import { travelerroutes } from './travelerroutes'
export const routes = [
{ name: 'home', path: '/', component: HomePage, display: 'Home', icon: 'home' },
{ name: 'counter', path: '/counter', component: CounterExample, display: 'Counter', icon: 'graduation-cap' },
{ name: 'fetch-data', path: '/fetch-data', component: FetchData, display: 'Fetch data', icon: 'list' },
{ name: 'test-page', path: '/test-page', component: TestPage, display: 'Test Page', icon: 'list' },
{
name: 'traveler-root', path: '/traveler', component: Travelers, display: 'Travelers', icon: 'list', children: travelerroutes
}
]
Traveler Routes (travelerroutes.js)
import TestPage from 'components/test-page'
import ViewTravelers from 'components/Traveler/TravelerPages/view-travelers'
export const travelerroutes = [{
name: 'View',
path: '/View',
display: 'View', icon: 'list',
components: {
travlerview: TestPage
}
},
{
name: 'Create',
path: '/Create',
display: 'Create', icon: 'list',
components: {
travlerview: ViewTravelers
}
},
{
name: 'Edit',
path: '/Edit',
display: 'Edit', icon: 'list',
components: {
travlerview: ViewTravelers
}
}];
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import { routes } from './routes'
Vue.use(VueRouter)
let router = new VueRouter({
mode: 'history',
routes
})
export default router
app.js
import Vue from 'vue'
import axios from 'axios'
import router from './router/index'
import store from './store'
import { sync } from 'vuex-router-sync'
import App from 'components/app-root'
import { FontAwesomeIcon } from './icons'
// Registration of global components
Vue.component('icon', FontAwesomeIcon)
Vue.prototype.$http = axios
sync(store, router)
const app = new Vue({
store,
router,
...App
})
export {
app,
router,
store
}
Let me know if you you need anymore details, context or code.
You don't need to create new router instances, instead watch the $route property and create the sidebar nav menu as it changes. You'll need to pull the child routes from the $router.options.routes. Here's an example:
const router = new VueRouter({
routes: [{
name: 'home',
path: '/',
component: {
template: '<div>Home</div>'
}
},
{
name: 'foo',
path: '/foo',
component: {
template: '<div>Foo<router-view></router-view></div>'
},
children: [{
name: 'foo.baz',
path: '/baz',
component: {
template: '<div>Baz</div>'
}
}, {
name: 'foo.tar',
path: '/tar',
component: {
template: '<div>Tar</div>'
}
}]
},
{
name: 'bar',
path: '/bar',
component: {
template: '<div>Bar<router-view></router-view></div>'
},
children: [{
name: 'bar.zim',
path: '/zim',
component: {
template: '<div>Zim</div>'
}
}, {
name: 'bar.zug',
path: '/zug',
component: {
template: '<div>Zug</div>'
}
}]
}
]
})
new Vue({
el: '#app',
router,
data() {
return {
children: []
}
},
watch: {
$route: function(current) {
const route = this.$router.options.routes.find(route => route.path === current.path)
if (route && Array.isArray(route.children)) {
this.children = route.children
} else if (route) {
this.children = []
}
}
}
})
* {
margin: 0;
padding: 0;
}
html,
body,
#app {
width: 100%;
height: 100%;
}
#top {
border: 1px solid black;
}
#top ul {
display: flex;
flex-direction: row;
justify-content: flex-start;
list-style-type: none;
}
li {
padding: 1rem;
text-align: center;
text-transform: uppercase;
}
li a {
text-decoration: none;
font-weight: bold;
color: black;
}
#sidebar {
height: 50%;
width: 100px;
border: 1px solid black;
}
#content {
width: 50%;
}
#content,
#content>div {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<div id="top">
<ul>
<li v-for="item in $router.options.routes" :key="item.path" :style="{ 'background-color': $route.name.includes(item.name) ? 'rgba(197,225,165,1)' : 'white' }">
<router-link :to="item.path">{{ item.name }}</router-link>
</li>
</ul>
</div>
<div style="display: flex; flex-direction: row; height: 100%; width: 100%;">
<div id="sidebar">
<ul>
<li v-for="item in children" :key="item.path" :style="{ 'background-color': $route.name === item.name ? 'rgba(197,225,165,1)' : 'white' }">
<router-link :to="item.path">{{ item.name.split('.').pop() }}</router-link>
</li>
</ul>
</div>
<div id="content">
<router-view></router-view>
</div>
</div>
</div>
I made some modifications to DigitalDrifter's answer where I find the children using the template syntax. I'm pretty sure my answer will stop working after another level of children so I'm going to say DigitalDrifter still has a better answer.
Also I'm not sure how preserve the sub menues route (localhost/Traveller) so it isn't searching the children of (locathost/Traveller/View) when I click on something like view. Right now I'm doing it in kind of a janky way:
v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.path||$route.path.includes(x.path))"
Thet full SFC without styling is here:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.path||$route.path.includes(x.path))">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="{path: route.path+'/'+child.path}" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.path }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
Edit: I know vue has the data I want associated with the routers views as shown here,
I'm just not sure how to access those properties.
Update 1/29/2019:
I figured out that all you need to do to get the path is by using $Route.matched[0].path you can learn more here.
Now I have been able to simplify the menu into an SFC like this:
<template>
<nav id="sidebar">
<div class="sidebar-header">
<h3>Bootstrap Sidebar</h3>
</div>
<ul class="list-unstyled components" v-for="(route, index) in $router.options.routes.filter(x=>x.path==$route.matched[0].path)">
<li v-for="child in route.children">
<a class="nav-item" :key="index">
<router-link :to="route.path+'/'+child.path" exact-active-class="active">
<icon :icon="route.icon" class="mr-2" /><span>{{ child.name }}</span>
</router-link>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>
CSS not included.
I am trying to create a splash screen (loading-screen) in Vue JS that after a few seconds fades away, revealing my defaut view. I have tried several approaches but just can't get any to work. The closest is this example on CodePen But ideally the component wouldn't be inside main.js and instead inside its own component. Despite that the below code won't work.
My main.js is as below:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
// FILTERS
Vue.filter('snippet', function(value) {
return value.slice(0,100);
});
Vue.component('loading-screen', {
template: '<div id="loading">Loading...</div>'
})
new Vue({
router,
store,
render: h => h(App),
data: {
isLoading: true
},
mounted () {
setTimeout(() => {
this.isLoading = false
}, 3000)
}
}).$mount("#app");
and my App.vue is as follows
<template>
<div id="app">
<loading-screen v-if="isLoading"></loading-screen>
<Top/>
<router-view/>
<PrimaryAppNav/>
</div>
</template>
<script>
import Top from './components/Top.vue'
import PrimaryAppNav from './components/PrimaryAppNav.vue'
export default {
name: 'app',
components: {
Top,
PrimaryAppNav
}
}
</script>
A LoadingScreen.vue component could look like this:
<template>
<div :class="{ loader: true, fadeout: !isLoading }">
Loading ...
</div>
</template>
<script>
export default {
name: "LoadingScreen",
props: ["isLoading"]
};
</script>
<style>
.loader {
background-color: #63ab97;
bottom: 0;
color: white;
display: block;
font-size: 32px;
left: 0;
overflow: hidden;
padding-top: 10vh;
position: fixed;
right: 0;
text-align: center;
top: 0;
}
.fadeout {
animation: fadeout 2s forwards;
}
#keyframes fadeout {
to {
opacity: 0;
visibility: hidden;
}
}
</style>
Be aware that the loader needs to be aware if loading is done to be able to fade out. You need to check that in your App as well, so it's not showing while it still needs to prepare data. Otherwise, information from the app part in the background could be leaking (for example scrollbars might be visible on the LoadingScreen). So App.vue could have a template like this:
<template>
<div id="app">
<LoadingScreen :isLoading="isLoading" />
<div v-if="!isLoading">
...your main content here...
</div>
</div>
</template>
If you want to have the LoadingScreen divs to disappear completely, you need to manage the state of the fadeout animation in the App.vue itself, making it a bit more complicated (I'd probably use two props for LoadingScreen then: isLoading and fadeout, where fadeout is a callback that gets called in LoadingScreen as soon as the fadeout animation is complete).
I have prepared a codesandbox for you with the state management inside the LoadingScreen.
This is a working App.vue with a splash screen:
<template>
<div id="app">
<v-app :light="!nav.dark" :dark="nav.dark">
<transition name="slide-fade" mode="out-in">
<router-view></router-view>
</transition>
</v-app>
<div v-if="loading" style="position:absolute; width: 100%; height:100%; top:0; left:0; z-index:10000; background-color:white">
<div style="margin-left: auto; margin-right: auto">
Loading...
</div>
</div>
</div>
</template>
<script>
export default {
name: "app",
data: () => ({
loading: true
}),
mounted() {
setTimeout(() => {
this.loading = false
}, 3000)
}
}
</script>
Note that there is a z-index trick and mounted is in App component.
You can of course create a component just for loading, so it will become:
App.vue
<template>
<div id="app">
<v-app :light="!nav.dark" :dark="nav.dark">
<transition name="slide-fade" mode="out-in">
<router-view></router-view>
</transition>
</v-app>
<loader v-if="loading"/>
</div>
</template>
<script>
import Loader from "./Loader"
export default {
name: "app",
data: () => ({
loading: true
}),
mounted() {
setTimeout(() => {
this.loading = false
}, 3000)
}
}
</script>
Loader.vue
<template>
<div style="position:absolute; width: 100%; height:100%; top:0; left:0; z-index:10000; background-color:white">
<div style="margin-left: auto; margin-right: auto">
Loading...
</div>
</div>
</template>
<script>
export default {
name: "loader"
}
</script>
After that, I strongly suggest that you use dynamic components for your router components, Top and PrimaryAppNav. Like that they will load during your splash screen. You will find how to do that very easily in my answer in this thread (only section 2 is relevant for you): here
At beginning show fullscreen splash (with class binding listens for loadedApp).
When vuejs mounted, (or your any other process completed then, change data loadedApp = true.
Then fadeoutHide style will run and hides your splash
<div class="fullscreen-splash" :class="{fadeoutHide:loadedApp}">
// splash logo etc
</div>
data() {
return {
loadedApp: false
}
},
mounted() {
this.loadedApp = true
}
.fadeoutHide {
animation: fadeoutHide .5s forwards;
}
#keyframes fadeoutHide {
to {
opacity: 0;
visibility: hidden;
}
}
I'm trying to code simple slide panel, like here http://anton.shevchuk.name/wp-demo/jquery-tutorials/simple-slide-panel.html
but on Vue.js. But panel doesn't slide it's wait 2 seconds then close without animation.
https://codepen.io/TogusaRusso/pen/dqoMLr
If I remove v-if, I can animate it, changing height of div.
https://codepen.io/TogusaRusso/pen/mGJEEJ
How can I animate removing of div?
<template>
<v-flex xs12 sm6 offset-sm3>
<transition name="slide" :duration="2000">
<div
class="slide-media"
:style="{height: '400px'}"
v-if="isOpen"
>Hello!</div>
</transition>
<v-btn
flat color="orange"
#click="isOpen=!isOpen"
>
{{isOpen?"Close":"Open"}}
</v-btn>
</v-flex>
</template>
<script>
export default {
name: 'SlidePanel',
data() {
return {
isOpen: false,
}
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.slide-media {
background-color: orangered;
overflow-y:hidden;
}
.slide-enter-active, .slide-leave-active {
transition: height 2s;
}
.slide-enter, .slide-leave-to {
height: 0px;
}
</style>
Check MDN: Css Specificity,
Inline styles added to an element (e.g., style="font-weight:bold")
always overwrite any styles in external stylesheets, and thus can be
thought of as having the highest specificity.
So removed the inline styles :style="{height: '400px'}", then add height:400px; inside .slide-media.
Below is one demo:
new Vue({
el: '#app',
data(){
return {
isOpen: false
}
},
methods: {
togglePanel: function () {
this.isOpen = !this.isOpen
}
}
})
.slide-media {
background-color: orangered;
overflow-y:hidden;
height:400px;
}
.slide-enter-active, .slide-leave-active {
transition: height 2s;
}
.slide-enter, .slide-leave-to {
height: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<button #click="togglePanel()">Toggle Panel</button>
<transition name="slide" :duration="2000">
<div
class="slide-media"
v-if="isOpen"
>Hello!</div>
</transition>
</div>