A Pen by Andreas Borgen on CodePen.
Last active
March 14, 2024 16:02
-
-
Save Sphinxxxx/d0f91aff183474404c0e82407b0f2c5f to your computer and use it in GitHub Desktop.
Vue bar chart race
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
console.clear(); | |
//Super-duper-lightweight tweening "library": | |
//https://gist.github.com/Sphinxxxx/fc9bcc601a2f903ff167112855d0437c | |
function TWEEN(b,f,h,k,c){function g(a){if(d){var e=(a-d)/h;a=1>e?b+l*c(e):f;if(1<=e)return}else d=a,a=b;k(a);requestAnimationFrame(g)}c=c||function(a){return a*(2-a)};var l=f-b,d;requestAnimationFrame(g)}; | |
</script> | |
<script src="//unpkg.com/vue@2"></script> | |
<div id="app"> | |
<h1>Vue bar chart race</h1> | |
<!-- https://vuejs.org/v2/guide/transitions.html#List-Transitions --> | |
<transition-group tag="div" name="bar-race" class="chart"> | |
<bar v-for="(item, index) in orderedItems" :key="item.id" :item="item" :max="maxVal"></bar> | |
</transition-group> | |
</div> | |
<script id="bar-template" type="text/x-template"> | |
<div class="bar-wrapper"> | |
<div class="bar" :id="item.id" :style="styleObj" :data-value="item.value">{{ item.name }}</div> | |
</div> | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Vue.component('bar', { | |
template: '#bar-template', | |
props: ['item', 'max'], | |
data() { | |
return { | |
animWidth: this.getWidth(), | |
}; | |
}, | |
computed: { | |
//https://stackoverflow.com/questions/42737034/vue-js-watch-multiple-properties-with-single-handler | |
targetWidth() { | |
return this.getWidth(); | |
}, | |
styleObj() { | |
return { | |
background: this.item.color, | |
//It *almost* works to just use getWidth() here, and use standard CSS `transition` for "tweening", | |
//but sometimes a bar will just jump to its new with without any transition. | |
//Therefore we need to use a tweening library to animate the width (`animWidth`) the hard way.. | |
//https://vuejs.org/v2/guide/transitioning-state.html#Animating-State-with-Watchers | |
// | |
// width: this.getWidth(), | |
width: this.animWidth + '%', | |
}; | |
} | |
}, | |
watch: { | |
//Won't react to `max` changes if there's a step where the value doesn't change.. | |
// 'item.value': function(newVal, oldVal) { | |
targetWidth: function(newW, oldW) { | |
//Calculate and slowly animate the new width when we go to a new step: | |
TWEEN(oldW, newW, 1000, | |
val => this.animWidth = val); | |
} | |
}, | |
methods: { | |
getWidth(value) { | |
const val = value || this.item.value, | |
w = this.max ? ((val / this.max) * 100) : 0; | |
return w; | |
}, | |
} | |
}); | |
(function() { | |
const items = [ | |
{ | |
id: 'aaa', | |
name: 'Aardvark', | |
value: 0, | |
color: 'tomato', | |
}, | |
{ | |
id: 'bbb', | |
name: 'Baboon', | |
value: 0, | |
color: 'deepskyblue', | |
}, | |
{ | |
id: 'ccc', | |
name: 'Chihuahua', | |
value: 0, | |
color: 'lime', | |
}, | |
{ | |
id: 'ddd', | |
name: 'Dodo', | |
value: 0, | |
color: 'gold', | |
}, | |
]; | |
new Vue({ | |
el: '#app', | |
data() { | |
return { | |
items: items, | |
}; | |
}, | |
computed: { | |
orderedItems() { | |
console.log('Reordering...'); | |
return this.items.slice().sort((a, b) => b.value - a.value); | |
}, | |
maxVal() { | |
const max = Math.max.apply(Math, this.items.map(x => x.value)); | |
//console.log('Max', max); | |
return max; | |
} | |
}, | |
mounted() { | |
}, | |
methods: { | |
} | |
}); | |
const INTERVAL = 1500, | |
STEPS = 100; | |
function rnd(max) { | |
return Math.floor(Math.random() * (max + 1)); | |
} | |
let stepCount = 0; | |
function nextStep() { | |
stepCount++; | |
items.forEach((x, i) => { | |
x.value += rnd(stepCount * 2); | |
}); | |
if(stepCount < STEPS) { | |
setTimeout(nextStep, INTERVAL); | |
} | |
} | |
nextStep(); | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: Georgia, sans-serif; | |
} | |
.chart { | |
display: flex; | |
flex-flow: column; | |
margin-top: 1em; | |
padding-right: 3em; | |
.bar-wrapper { | |
margin: .5em 0; | |
} | |
.bar { | |
position: relative; | |
padding: 1em 0; | |
white-space: nowrap; | |
text-indent: 1em; | |
//Needed if normal `transition` would work reliably on bar widths (see comment in styleObj()). | |
// transition: all 1s; | |
&::after { | |
content: attr(data-value); | |
position: absolute; | |
left: 100%; | |
} | |
} | |
} | |
//https://vuejs.org/v2/guide/transitions.html#List-Transitions | |
.bar-race { | |
//Smooth transition on add & shuffle: | |
&-move { | |
transition: transform 1s; | |
} | |
////Smooth transition on remove: | |
//&-leave-active { | |
// position: absolute; | |
// opacity: 0; | |
//} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment