Commit c390a069 authored by Konrad Mohrfeldt's avatar Konrad Mohrfeldt

improve ux for pull-to-refresh

pull-to-refresh now shows a little indicator when it’s used and animates
once it’s triggered.
parent cec5989c
Pipeline #2002 passed with stages
in 5 minutes and 56 seconds
<template>
<div :style="style">
<Refresh/>
<NavBar/>
<router-view :key="routerViewKey"></router-view>
<ProviderData/>
......@@ -12,6 +13,7 @@
import NavBar from './components/nav/NavBar'
import { oneOf } from './util'
import ProviderData from './components/nav/ProviderData'
import Refresh from './components/generic/Refresh'
function isTextField (field) {
if (!field) { return false }
......@@ -25,7 +27,12 @@
export default {
name: 'App',
components: { ProviderData, NavBar, Player },
components: {
NavBar,
Player,
ProviderData,
Refresh
},
data () {
return {
hasFocus: isTextField(document.activeElement)
......
<template>
<div class="refresh" role="presentation">
<div class="refresh-indicator" :style="indicatorStyle" @animationend="animation = null">
<RefreshCwIcon/>
</div>
</div>
</template>
<script>
import { RefreshCwIcon } from 'vue-feather-icons'
import { pullRefreshController as refresh } from '../../util/dom'
export default {
components: { RefreshCwIcon },
data () {
return {
refresh,
animation: null
}
},
computed: {
indicatorStyle () {
return {
transition: !this.animation ? 'transform .05s' : null,
transform: !this.animation ? `translateY(${-100 + refresh.triggerProgress * 1.75}%)` : null,
animation: this.animation ? `${this.animation} 1s linear forwards` : null
}
}
},
watch: {
'refresh.isRefreshing' (isRefreshing) {
if (isRefreshing) {
this.animation = 'refresh-spin'
}
}
}
}
</script>
<style lang="scss">
@import '../../styles/variables';
.refresh {
display: flex;
justify-content: center;
position: fixed;
top: 0;
left: 0;
right: 0;
height: auto;
pointer-events: none;
z-index: 1000;
}
.refresh-indicator {
background: $color-surface;
width: 44px;
height: 44px;
color: white;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 4px rgba(0, 0, 0, .20);
}
@keyframes refresh-spin {
0% {
transform: translateY(75%) rotate(0);
}
50% {
transform: translateY(25%) rotate(360deg);
}
100% {
transform: translateY(-100%) rotate(360deg);
}
}
</style>
import { debounce } from 'throttle-debounce'
import Vue from 'vue'
export const pullRefreshController = (() => {
let startY
const TRIGGER_OFFSET_MAX = 150
let startY = null
const refresh = debounce(350, false, () => {
for (const subscriber of subscribers) {
subscriber.refresh()
}
const notifySubscribers = debounce(350, false, () => {
return Promise.all(subscribers.map(s => s.refresh()))
.finally(() => { refresh.isRefreshing = false })
})
document.body.addEventListener('touchstart', e => {
......@@ -15,13 +16,27 @@ export const pullRefreshController = (() => {
document.body.addEventListener('touchmove', e => {
const y = e.touches[0].pageY
if (!startY || refresh.isRefreshing || subscribers.length === 0) return
if (document.scrollingElement.scrollTop === 0 && y > startY) {
refresh()
const triggerOffset = y - startY
refresh.triggerProgress = (triggerOffset / TRIGGER_OFFSET_MAX) * 100
if (triggerOffset > TRIGGER_OFFSET_MAX) {
refresh.isRefreshing = true
refresh.triggerProgress = 0
notifySubscribers()
}
}
}, { passive: true })
document.body.addEventListener('touchend', e => {
refresh.triggerProgress = 0
startY = null
}, { passive: true })
const subscribers = []
return {
const refresh = Vue.observable({
triggerProgress: 0,
isRefreshing: false,
subscribe (subscriber) {
subscribers.push(subscriber)
},
......@@ -31,5 +46,6 @@ export const pullRefreshController = (() => {
subscribers.splice(index, 1)
}
}
}
})
return refresh
})()
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