Skip to content

Instantly share code, notes, and snippets.

@CodeMyUI
Created January 6, 2020 23:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CodeMyUI/dc8e011624928221a10d32dd4b75b149 to your computer and use it in GitHub Desktop.
Save CodeMyUI/dc8e011624928221a10d32dd4b75b149 to your computer and use it in GitHub Desktop.
Progress Button Microinteractions with VueJS
#app
zz-button-progress(
text="Upload",
ref="zzUpload",
v-on:progress="moveProgress"
v-on:progress-finished="endProgress"
)

Progress Button Microinteractions with VueJS

I rewritten the Javascript source code, from using jQuery library into using VueJS framework.

Also I made it into a component, so this may be reused easily.

There is few changes in CSS, specially in transition. Then in Javascript, I changed some of the logic for changing the classes, and animations.

This is also a good opportunity for me to learn/practice the $emit, callback, and $refs of VueJS.

Quoted from old description: "Recently, I've been interested more in studying microinteraction, so I made an example button for uploading with microinteraction."

Here is the link for the old pen: https://codepen.io/takaneichinose/pen/KjBBOZ

A Pen by Takane Ichinose on CodePen.

License.

// ----- The component
Vue.component('zz-button-progress', {
props: ['text'],
data: function() {
return {
progress: null,
active: false,
done: false,
check: false
}
},
methods: {
/**
* Changes the value of the attribute 'zz-button-progress'
* Changing its value will change the percentage of the
* progress of the button.
*/
moveProgress: function(progress) {
this.progress = progress;
},
/**
* Reset the button progress to its original state.
*/
resetProgress: function() {
this.progress = null;
this.active = false;
this.done = false;
this.check = false;
},
/**
* Event handler for click event
*/
progressClickEvent: function() {
if (this.active === false && this.done === false) {
this.active = true;
}
},
/**
* Event handler for transitionend event
*/
progressTransitionEndEvent: function(evt) {
if (this.progress === null && this.active === true) {
this.progress = 0;
this.$emit('progress');
}
else if (this.done === true) {
this.check = true;
}
},
/**
* Event handler for animationend event
*/
progressAnimationEndEvent: function(evt) {
if (evt.animationName == 'progress-done-pre') {
this.done = true;
this.active = false;
this.progress = null;
}
else if (evt.animationName == 'progress-done-post') {
this.$emit('progress-finished');
}
}
},
template: `
<button
v-bind:zz-button-progress="this.progress"
v-bind:class="['zz-button', {'active': active, 'zz-button-progress-done': done, 'zz-button-progress-done-active': check}]"
v-on:click="progressClickEvent"
v-on:transitionend="progressTransitionEndEvent"
v-on:animationend="progressAnimationEndEvent"
>
{{ this.text }}
</button>
`
});
// ----- VueJS VM
new Vue({
el: '#app',
methods: {
moveProgress: function(progress = 0) {
// This is just an example progress.
// In real-world application, return from ajax process may be used
var progressTimeout = setTimeout(() => {
clearTimeout(progressTimeout);
if (progress < 100) {
let newProgress = progress + 1;
this.$refs.zzUpload.moveProgress(newProgress);
this.moveProgress(newProgress);
}
}, 10);
},
endProgress: function() {
var endProgressTimeout = setTimeout(() => {
clearTimeout(endProgressTimeout);
this.$refs.zzUpload.resetProgress();
}, 5000);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
$base-color: white;
$bg-full: #2f8aff;
$bg-empty: #085dc9;
$bg-done: #52b500;
$default-font-size-label: 32px;
$default-width: 250px;
$default-height: 100px;
$active-font-size-label: 18px;
$active-font-size-progress: 48px;
$active-width-height: 175px;
$done-check-thickness: 10px;
$done-check-width: 50px;
$done-check-height: 20px;
$animation-time: 750ms;
$animation-style: cubic-bezier(0.7, -0.55, 0.3, 1.55);
@import url("https://fonts.googleapis.com/css?family=Raleway:400,400i,700");
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
body {
font-family: Raleway, sans-serif;
display: flex;
align-items: center;
justify-content: center;
background-color: #424242;
}
.zz-button {
color: $base-color;
font-family: inherit;
font-size: $default-font-size-label;
font-weight: bold;
background-color: $bg-full;
width: $default-width;
height: $default-height;
border: none;
cursor: pointer;
outline: none;
border-radius: 20% / 50%;
transition: color $animation-time ease-in-out,
font-size $animation-time $animation-style,
background-color $animation-time ease-in-out,
width $animation-time $animation-style,
height $animation-time $animation-style,
border-radius $animation-time $animation-style;
}
.zz-button:after {
content: '0%';
font-size: 0;
display: block;
transition: font-size $animation-time ease-in-out;
}
.zz-button.active {
font-size: $active-font-size-label;
background-color: $bg-empty;
width: $active-width-height;
height: $active-width-height;
border-radius: 50%;
}
.zz-button.active:after {
font-size: $active-font-size-progress;
}
@for $i from 0 through 100 {
.zz-button[zz-button-progress='#{$i}'] {
background-image: linear-gradient(to top, $bg-full $i * 1%, $bg-empty $i * 1%);
}
.zz-button[zz-button-progress='#{$i}']:after {
content: '#{$i}%';
}
}
.zz-button[zz-button-progress='100'] {
animation: progress-done-pre $animation-time ease-out;
}
@keyframes progress-done-pre {
0% { transform: scale(1); }
35% { transform: scale(1.15); }
75% { transform: scale(0.9); }
90% { transform: scale(1.05) }
100% { transform: scale(1); }
}
.zz-button-progress-done {
color: transparent;
background-color: $bg-done;
position: relative;
}
.zz-button-progress-done:before {
content: '';
width: 0px;
height: 0px;
display: block;
}
.zz-button-progress-done.zz-button-progress-done-active:before {
width: $done-check-width;
height: $done-check-height;
border: solid $done-check-thickness $base-color;
border-top: none;
border-right: none;
position: absolute;
top: ($default-height / 2);
left: ($default-width / 2) - ($done-check-width / 2);
transform: rotate(-45deg);
transform-origin: top left;
animation: progress-done-post $animation-time $animation-style;
}
@keyframes progress-done-post {
0% { width: 0px; height: 0px; }
50% { width: 0px; height: $done-check-height; }
100% { width: $done-check-width; height: $done-check-height; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment