Commit 0f936915 authored by Evan You's avatar Evan You

loading indicator

parent eb6450f8
<template>
<svg class="spinner" :class="{ show: show }" width="44px" height="44px" viewBox="0 0 44 44">
<circle class="path" fill="none" stroke-width="4" stroke-linecap="round" cx="22" cy="22" r="20"></circle>
</svg>
</template>
<script>
export default {
props: ['show']
}
</script>
<style>
.spinner {
opacity: 0;
transition: opacity .3s ease;
animation: rotator 1.4s linear infinite;
animation-play-state: paused;
}
.spinner.show {
opacity: 1;
animation-play-state: running;
}
@keyframes rotator {
0% { transform: scale(0.5) rotate(0deg); }
100% { transform: scale(0.5) rotate(270deg); }
}
.spinner .path {
stroke: #4fc08d;
stroke-dasharray: 126;
stroke-dashoffset: 0;
transform-origin: center;
animation: dash 1.4s ease-in-out infinite;
}
@keyframes dash {
0% { stroke-dashoffset: 126; }
50% {
stroke-dashoffset: 32;
transform:rotate(135deg);
}
100% {
stroke-dashoffset: 126;
transform:rotate(450deg);
}
}
</style>
...@@ -9,7 +9,7 @@ import About from '../views/About.vue' ...@@ -9,7 +9,7 @@ import About from '../views/About.vue'
export default new Router({ export default new Router({
mode: 'history', mode: 'history',
routes: [ routes: [
{ path: '/news/:page', component: News }, { path: '/news/:page(\\d+)', component: News },
{ path: '/about', component: About }, { path: '/about', component: About },
{ path: '*', redirect: '/news/1' } { path: '*', redirect: '/news/1' }
] ]
......
...@@ -8,7 +8,7 @@ const inBrowser = typeof window !== 'undefined' ...@@ -8,7 +8,7 @@ const inBrowser = typeof window !== 'undefined'
// if in browser, use pre-fetched state injected by SSR // if in browser, use pre-fetched state injected by SSR
const state = (inBrowser && window.__INITIAL_STATE__) || { const state = (inBrowser && window.__INITIAL_STATE__) || {
storiesPerPage: 30, storiesPerPage: 20,
topStoryIds: [], topStoryIds: [],
items: {} items: {}
} }
......
<template> <template>
<div> <div>
<h2>News</h2> <h2>News <spinner :show="loading"></spinner></h2>
<ul> <ul>
<li> <li>
<router-link v-if="page > 1" :to="'/news/' + (page - 1)">prev</router-link> <router-link v-if="page > 1" :to="'/news/' + (page - 1)">prev</router-link>
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
</li> </li>
</ul> </ul>
<transition :name="transition"> <transition :name="transition">
<div class="news-list" :key="$route.params.page"> <div class="news-list" v-if="!loading" :key="$route.params.page">
<transition-group tag="ul" name="item"> <transition-group tag="ul" name="item">
<news-item v-for="item in news" :key="item.id" :item="item"> <news-item v-for="item in news" :key="item.id" :item="item">
</news-item> </news-item>
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
</template> </template>
<script> <script>
import Spinner from '../components/Spinner.vue'
import NewsItem from '../components/NewsItem.vue' import NewsItem from '../components/NewsItem.vue'
const fetchData = store => { const fetchData = store => {
...@@ -35,6 +36,7 @@ export default { ...@@ -35,6 +36,7 @@ export default {
name: 'news', name: 'news',
prefetch: fetchData, prefetch: fetchData,
components: { components: {
Spinner,
NewsItem NewsItem
}, },
data () { data () {
...@@ -49,20 +51,27 @@ export default { ...@@ -49,20 +51,27 @@ export default {
page () { page () {
return Number(this.$route.params.page) return Number(this.$route.params.page)
}, },
hasMore () { maxPage () {
const { storiesPerPage, topStoryIds } = this.$store.state const { storiesPerPage, topStoryIds } = this.$store.state
return this.page < Math.ceil(topStoryIds.length / storiesPerPage) return Math.floor(topStoryIds.length / storiesPerPage)
},
hasMore () {
return this.page < this.maxPage
},
loading () {
return this.news.length < this.$store.state.storiesPerPage
} }
}, },
created () { mounted () {
if (typeof window !== 'undefined') {
fetchData(this.$store) fetchData(this.$store)
if (this.page > this.maxPage) {
this.$router.push('/')
} }
}, },
watch: { watch: {
'$route' (to, from) { '$route' (to, from) {
this.$store.dispatch(`FETCH_NEWS`) this.$store.dispatch(`FETCH_NEWS`)
this.transition = to.params.page > from.params.page this.transition = Number(to.params.page) > Number(from.params.page)
? 'slide-left' ? 'slide-left'
: 'slide-right' : 'slide-right'
} }
...@@ -77,12 +86,10 @@ export default { ...@@ -77,12 +86,10 @@ export default {
} }
.slide-left-enter, .slide-right-leave-active { .slide-left-enter, .slide-right-leave-active {
opacity: 0; opacity: 0;
-webkit-transform: translate(30px, 0);
transform: translate(30px, 0); transform: translate(30px, 0);
} }
.slide-left-leave-active, .slide-right-enter { .slide-left-leave-active, .slide-right-enter {
opacity: 0; opacity: 0;
-webkit-transform: translate(-30px, 0);
transform: translate(-30px, 0); transform: translate(-30px, 0);
} }
.item-move, .item-enter-active, .item-leave-active { .item-move, .item-enter-active, .item-leave-active {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment