# tapper-widget
This is widget that changes images when you tap or click it. It's basically a timeline sequence that just reacts to a tap or click.
Dependencies needed: node-sass and sass-loader.
# props
progressBarBackgroundColor String (optional) background color of progress bar
progressBarOrientation String (optional) horizontal/vertical. Location of where the progress bar will be placed, either underneath image or on the side using CSS Grid. default: horizontal
slides Array (required) Image slides.
Example: {
active: true,
src: '/assets/modules/brain-1.svg',
message: 'This is the message'
}
tapsPerSlide Number (optional) How many taps needed to advance to the next slide. Default: 1
resetButtonOnComplete Boolean (optional) Show refresh button on widget completion. default: false
debug Boolean (optional) Show outline around the widget for CSS debugging. default: false
# Events
@onComplete emits when last slide shows
# Example
<template>
<div>
<TapperWidget
:progressBarOrientation="'vertical'"
:slides="[
{
active: true,
src: '/assets/modules/brain-1.svg',
message: 'Tap (and keep tapping) to see how nicotine affects the brain and rewires us for addiction.'
},
{
active: false,
src: '/assets/modules/brain-2.svg',
message: 'Nicotine salts make it easier to take bigger hits.'
},
{
active: false,
src: '/assets/modules/brain-3.svg',
message: ' This puts vapers at risk for extremely high doses of nicotine with every hit.'
},
{
active: false,
src: '/assets/modules/brain-4.svg',
message: 'It also makes it easier to hit a vape more frequently.'
},
{
active: false,
src: '/assets/modules/brain-5.svg',
message: 'Putting vapers at high risk for nicotine addiction.'
}
]" />
</div>
</template>
<script>
import { TapperWidget } from '@rescue/vue-modules';
export default {
components: {
TapperWidget
}
};
</script>
# Source Code
<template>
<div
:class="[{ debug }, { vertical: progressBarOrientation === 'vertical' }]"
class="tapper-widget">
<div class="grid">
<div class="slides">
<button @click="onClick">
<img
v-for="(slide, i) in slides"
v-show="slide.active"
:key="i"
:src="slide.src" />
</button>
</div>
<div class="progress-bar">
<div
v-if="progressBarOrientation === 'horizontal'"
class="horizontal">
<span :style="`
width: ${ getProgress };
background: ${ progressBarBackgroundColor }`">
</span>
</div>
<div
v-if="progressBarOrientation === 'vertical'"
class="vertical">
<span :style="`
height: ${ getProgress };
background: ${ progressBarBackgroundColor }`">
</span>
</div>
</div>
</div>
<button
v-show="resetButtonOnComplete && showResetButton"
@click="reset"
class="reset-button">
<i class="fas fa-redo"></i>
</button>
<div
v-if="typeof activeSlide.message === 'string'"
v-html="activeSlide.message"
class="message">
</div>
<div
v-if="typeof activeSlide.instructions === 'string'"
v-html="activeSlide.instructions"
class="instructions">
</div>
</div>
</template>
<script>
export default {
props: {
progressBarBackgroundColor: {
default: "rgb(100, 100, 100)",
type: String
},
progressBarOrientation: {
default: "horizontal",
type: String
},
slides: {
default: [],
type: Array
},
tapsPerSlide: {
default: 2,
type: Number
},
resetButtonOnComplete: {
default: true,
type: Boolean
},
debug: {
default: false,
type: Boolean
}
},
computed: {
getProgress() {
return ((this.activeIndex / (this.slides.length - 1)) * 100) + "%";
},
activeSlide() {
return this.slides[this.activeIndex];
}
},
data() {
return {
numberOfTaps: 0,
activeIndex: 0,
showResetButton: false
}
},
methods: {
onClick() {
this.numberOfTaps++;
if (this.numberOfTaps >= this.tapsPerSlide) {
this.nextSlide();
this.numberOfTaps = 0;
}
},
nextSlide() {
for (let i = 0; i < this.slides.length; i++) {
if (this.slides[i].active && i < this.slides.length - 1) {
this.slides[i].active = false;
this.slides[i + 1].active = true;
this.activeIndex = i + 1;
if (this.activeIndex === this.slides.length - 1) {
// Widget Completed
this.$emit("onComplete");
if (this.resetButtonOnComplete) {
this.showResetButton = true;
}
}
break;
}
}
this.$forceUpdate();
},
reset() {
this.slides.forEach(slide => {
slide.active = false;
});
this.slides[0].active = true;
this.activeIndex = 0;
this.numberOfTaps = 0;
this.showResetButton = false;
}
}
}
</script>
<style lang="scss" scoped>
.tapper-widget {
position : relative;
&.debug * {
outline : 1px solid red;
}
&.vertical {
.grid {
display : grid;
grid-template-columns : 1fr 30px;
grid-gap : 15px;
}
}
.grid {
.slides {
button {
background : none;
border : none;
cursor : pointer;
&:focus,
&:hover,
&:active {
outline : none;
}
&:active {
transform : scale(0.975);
}
}
}
.progress-bar {
.horizontal {
margin-top : 15px;
width : 100%;
height : 30px;
border : 1px solid rgb(39, 39, 39);
span {
height : 100%;
display : block;
width : 0%;
transition : width 0.15s ease-in-out;
}
}
.vertical {
height : 100%;
width : 30px;
border : 1px solid rgb(39, 39, 39);
transform : rotate(180deg);
span {
width : 100%;
display : block;
height : 0%;
transition : height 0.15s ease-in-out;
}
}
}
}
.reset-button {
position : absolute;
bottom : 0px;
left : 0px;
width : 30px;
height : 30px;
border-radius : 15px;
background : white;
font-size : 0.8em;
box-shadow : 0px 4px 10px rgba(0, 0, 0, 0.1);
cursor : pointer;
&:focus,
&:hover,
&:active {
outline : none;
}
&:active {
transform : scale(0.975);
}
i {
top : 1px;
position : relative;
}
}
.message {
position : absolute;
top : 105%;
}
.instructions {
position : absolute;
top : 105%;
}
}
</style>