<div id="app" :class="progressClasses"> <div class="progress__bg"></div> <template v-for="(step, index) in steps"> <div :class="stepClasses(index)"> <div class="progress__indicator"> <i class="fa fa-check"></i> </div> <div class="progress__label"> {{step.label}} </div> </div> </template> <div class="progress__actions"> <div class="btn" v-on:click="nextStep(false)" > Back </div> <div class="btn" v-on:click="nextStep" > Next </div> <div> Step: {{currentStep ? currentStep.label : "Start"}} </div> </div></div>
Para simplificar, o progress__actions
que controlam a direção da viagem estão aninhados na própria barra de progresso.
O CSS (SCSS)
É aqui que fazemos o trabalho pesado. As classes definidas aqui serão aplicadas dinamicamente pelo JS com base na etapa atual.
Primeiro, vamos selecionar algumas cores para trabalhar:
$gray: #E5E5E5;$gray2: #808080;$blue: #2183DD;$green: #009900;$white: #FFFFFF;
Agora defina o .progress
classe: o contêiner que mantém o conteúdo da barra de progresso junto.
.progress { position: absolute; top: 15vh; width: 0%; height: 10px; background-color: $blue; transition: width .2s;}
Nossa barra de progresso precisa de um .progress__bg
que as etapas de progresso correrão como uma trilha. Ele ficará cinza, coberto pela barra colorida conforme avança para a próxima etapa.
.progress__bg { position: absolute; width: 100vw; height: 10px; background-color: $gray; z-index: -1;}
Cada .progress__step
contém a etapa circular que será destacada e preenchida conforme a barra de progresso avança.
.progress__step { position: absolute; top: -8px; left: 0; display: flex; flex-direction: column; align-items: center; text-align: center; @for $i from 1 through 5 { &.progress__step--#{$i} { left: calc(#{$i * 20}vw - 9px); } }}
Ele também contém a rodada .progress__indicator
e texto do rótulo .progress__label
. Seus estilos padrão são definidos fora do .progress__step
.
.progress__indicator { width: 25px; height: 25px; border: 2px solid $gray2; border-radius: 50%; background-color: $white; margin-bottom: 10px; .fa { display: none; font-size: 16px; color: $white; }}.progress__label { position: absolute; top: 40px;}
Vamos agora continuar a aninhar dentro .progress__step
novamente e definir a etapa em seu ativo Estado.
&.progress__step--active { color: $blue; font-weight: 600;}
Em seguida, defina a etapa em seu completo Estado. Nota: os estilos padrão para .progress__indicator
e .progress__label
são substituídos quando no estado completo.
&.progress__step--complete { .progress__indicator { background-color: $green; border-color: $blue; color: $white; display: flex; align-items: center; justify-content: center; } .progress__indicator .fa { display: block; } .progress__label { font-weight: 600; color: $green; }}
O JavaScript
Conforme mencionado anteriormente, isso será diferente com base em como você implementa a lógica de etapas, o contexto mais amplo em que é implementado, quais estruturas e padrões você usa e assim por diante.
Este exemplo usa um componente Vue para demonstrar:
- cálculo de classes para a barra de progresso com base no estado atual.
- cálculo de classes para cada etapa com base no estado atual.
var app = new Vue({ el: '#app', data: { currentStep: null, steps: [ {"label": "one"}, {"label": "two"}, {"label": "three"}, {"label": "complete"} ] }, methods: { nextStep(next=true) { const steps = this.steps const currentStep = this.currentStep const currentIndex = steps.indexOf(currentStep) // handle back if (!next) { if (currentStep && currentStep.label === 'complete') { return this.currentStep = steps[steps.length - 1] } if (steps[currentIndex - 1]) { return this.currentStep = steps[currentIndex - 1] } return this.currentStep = { "label": "start" } } // handle next if (this.currentStep && this.currentStep.label === 'complete') { return this.currentStep = { "label": "start" } } if (steps[currentIndex + 1]) { return this.currentStep = steps[currentIndex + 1] } this.currentStep = { "label": "complete" } }, stepClasses(index) { let result = `progress__step progress__step--${index + 1} ` if (this.currentStep && this.currentStep.label === 'complete' || index < this.steps.indexOf(this.currentStep)) { return result += 'progress__step--complete' } if (index === this.steps.indexOf(this.currentStep)) { return result += 'progress__step--active' } return result } }, computed: { progressClasses() { let result="progress " if (this.currentStep && this.currentStep.label === 'complete') { return result += 'progress--complete' } return result += `progress--${this.steps.indexOf(this.currentStep) + 1}` } }})
Conclusão
No final de tudo, você tem isso:
Confira o CodePen para um exemplo ao vivo.
Se você achar meus artigos úteis, por favor, considere se tornar um membro de meu patreon 🙂
Ou se você só quer me comprar café (eu amo café):