Skip to content

Instantly share code, notes, and snippets.

@muthu32
Last active May 3, 2019 02:04
Show Gist options
  • Save muthu32/a9401cd1179684202ca9c263aba83fb5 to your computer and use it in GitHub Desktop.
Save muthu32/a9401cd1179684202ca9c263aba83fb5 to your computer and use it in GitHub Desktop.
Quasar Bar
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Ajax Bar: Basic - Quasar Playground</title>
<link rel='stylesheet' href='Quasar.css'>
</head>
<body>
<div id="q-app">
<div class="q-pa-md">
<q-ajax-bar
ref="bar"
position="top"
color="blue"
size="5px"
skip-hijack
></q-ajax-bar>
<button color="primary" label="Trigger" @click="trigger" >Test</button>
</div>
</div>
<script src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js'></script>
<script src="Quasar.js"></script>
</body>
</html>
.bg-blue {
background: #2196f3!important
}
.no-transition {
transition: none!important
}
.q-loading-bar {
position: fixed;
z-index: 9998;
transition: transform .5s cubic-bezier(0,0,.2,1),opacity .5s
}
.q-loading-bar--top {
left: 0;
right: 0;
top: 0;
width: 100%
}
.q-loading-bar--bottom {
left: 0;
right: 0;
bottom: 0;
width: 100%
}
.q-loading-bar--right {
top: 0;
bottom: 0;
right: 0;
height: 100%
}
.q-loading-bar--left {
top: 0;
bottom: 0;
left: 0;
height: 100%
}
.q-loading {
color: #000;
position: fixed !important;
}
.q-loading:before {
content: '';
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: currentColor;
opacity: 0.5;
z-index: -1;
}
.q-loading > div {
margin: 40px 20px 0;
max-width: 450px;
text-align: center;
}
.q-pa-md {
padding: 16px 16px;
}
function between (v, min, max) {
return max <= min
? min
: Math.min(max, Math.max(min, v))
}
const
xhr = XMLHttpRequest,
send = xhr.prototype.send,
stack = { start: [], stop: [] }
let highjackCount = 0;
function translate ({ p, pos, active, horiz, reverse, dir }) {
let x = 1, y = 1
if (horiz) {
if (reverse) { x = -1 }
if (pos === 'bottom') { y = -1 }
return { transform: `translate3d(${x * (p - 100)}%,${active ? 0 : y * -200}%,0)` }
}
if (reverse) { y = -1 }
if (pos === 'right') { x = -1 }
return { transform: `translate3d(${active ? 0 : dir * x * -200}%,${y * (p - 100)}%,0)` }
}
function inc (p, amount) {
if (typeof amount !== 'number') {
if (p < 25) {
amount = Math.random() * 3 + 3
}
else if (p < 65) {
amount = Math.random() * 3
}
else if (p < 85) {
amount = Math.random() * 2
}
else if (p < 99) {
amount = 0.6
}
else {
amount = 0
}
}
return between(p + amount, 0, 100)
}
function highjackAjax (start, stop) {
stack.start.push(start)
stack.stop.push(stop)
highjackCount++
if (highjackCount > 1) { return }
function endHandler () {
stack.stop.map(fn => { fn() })
}
xhr.prototype.send = function (...args) {
stack.start.map(fn => { fn() })
this.addEventListener('abort', endHandler, false)
this.addEventListener('readystatechange', () => {
if (this.readyState === 4) { endHandler() }
}, false)
send.apply(this, args)
}
}
function restoreAjax (start, stop) {
stack.start = stack.start.filter(fn => fn !== start)
stack.stop = stack.stop.filter(fn => fn !== stop)
highjackCount = Math.max(0, highjackCount - 1)
if (!highjackCount) {
xhr.prototype.send = send
}
}
var qajaxbar = Vue.extend({
name: 'QAjaxBar',
props: {
position: {
type: String,
default: 'top',
validator (val) {
return ['top', 'right', 'bottom', 'left'].includes(val)
}
},
size: {
type: String,
default: '2px'
},
color: {
type: String,
default: 'red'
},
skipHijack: Boolean,
reverse: Boolean
},
data () {
return {
calls: 0,
progress: 0,
onScreen: false,
animate: true
}
},
computed: {
classes () {
return [
`q-loading-bar--${this.position}`,
`bg-${this.color}`,
this.animate ? '' : 'no-transition'
]
},
style () {
const active = this.onScreen
let o = translate({
p: this.progress,
pos: this.position,
active,
horiz: this.horizontal,
reverse: this.reverse,
dir: 1
})
o[this.sizeProp] = this.size
o.opacity = active ? 1 : 0
return o
},
horizontal () {
return this.position === 'top' || this.position === 'bottom'
},
sizeProp () {
return this.horizontal ? 'height' : 'width'
}
},
methods: {
start (speed = 300) {
this.calls++
if (this.calls > 1) { return }
clearTimeout(this.timer)
this.$emit('start')
if (this.onScreen) { return }
this.progress = 0
this.onScreen = true
this.animate = false
this.timer = setTimeout(() => {
this.animate = true
this.__work(speed)
}, 100)
},
increment (amount) {
this.calls > 0 && (this.progress = inc(this.progress, amount))
},
stop () {
this.calls = Math.max(0, this.calls - 1)
if (this.calls > 0) { return }
clearTimeout(this.timer)
this.$emit('stop')
const end = () => {
this.animate = true
this.progress = 100
this.timer = setTimeout(() => {
this.onScreen = false
}, 1000)
}
if (this.progress === 0) {
this.timer = setTimeout(end, 1)
}
else {
end()
}
},
__work (speed) {
if (this.progress < 100) {
this.timer = setTimeout(() => {
this.increment()
this.__work(speed)
}, speed)
}
}
},
mounted () {
if (!this.skipHijack) {
this.hijacked = true
highjackAjax(this.start, this.stop)
}
},
beforeDestroy () {
clearTimeout(this.timer)
this.hijacked && restoreAjax(this.start, this.stop)
},
render (h) {
return h('div', {
staticClass: 'q-loading-bar',
class: this.classes,
style: this.style
})
}
});
Vue.component('QAjaxBar',qajaxbar);
new Vue({
el: '#q-app',
methods: {
// we manually trigger it (this is not needed if we
// don't skip Ajax calls hijacking)
trigger () {
const bar = this.$refs.bar
bar.start()
this.timer = setTimeout(() => {
if (this.$refs.bar) {
this.$refs.bar.stop()
}
}, Math.random() * 3000 + 1000)
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment