Skip to content
This repository was archived by the owner on Feb 28, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/assets/svgs/icon-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions src/components/SlideshowWork/SlideshowWork.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import SlideshowWork from "./SlideshowWork"
import API from "@/static/db.json"

export default {
title: "@dchiamp / SlideshowWork"
}

export const Default = () => ({
components: { SlideshowWork },
data() {
return {
images: API.images
}
},
template: `<slideshow-work :images="images"/>`
})
153 changes: 153 additions & 0 deletions src/components/SlideshowWork/SlideshowWork.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<template lang="html">
<slideshow
ref="slideshow"
class="slideshow-work"
:slides="images"
:swipe-events="true"
@update:activeIndex="updateIndex"
>
<wp-image
v-if="slide.sourceUrl"
slot="slide"
slot-scope="{ slide }"
class="slide-image"
:image="slide"
mode="fullbleed"
/>

<icon-arrow slot="nav-next" />
<icon-arrow slot="nav-prev" />

<div slot="after" class="dots">
<div
v-for="(dot, i) in images"
:class="['dot', { active: i == activeIndex }]"
@click="goTo(i)"
/>
</div>
</slideshow>
</template>

<script>
// Components
import Slideshow from "@/components/global/Slideshow"
import WpImage from "@/components/global/WpImage"
import IconArrow from "@/assets/svgs/icon-arrow.svg"

export default {
components: { Slideshow, WpImage, IconArrow },
props: {
images: {
type: Array,
default: () => []
}
},
data() {
return {
activeIndex: 0
}
},
computed: {},
methods: {
updateIndex(i) {
this.activeIndex = i
},
goTo(i) {
this.$refs.slideshow.goToSlide(i)
}
}
}
</script>

<style lang="scss" scoped>
.slideshow-work {
min-height: 300px;
height: var(--unit-100vh);

/deep/ .nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 100;
cursor: pointer;
background: none;
&:focus {
outline: none;
}

&.prev {
left: 30px;
}
&.next {
right: 30px;
svg {
transform: rotate(180deg);
}
}

svg {
width: 30px;
height: auto;
path {
fill: var(--color-grey);
transition: fill 0.4s ease-in-out;
}
}
}

/deep/ .slide {
width: unset;
height: unset;
top: 100px;
left: 100px;
right: 100px;
bottom: 100px;
}

.dots {
position: absolute;
bottom: 70px;
left: 50%;
z-index: 100;
transform: translateX(-50%);
width: 100%;
text-align: center;

.dot {
display: inline-block;
height: 8px;
width: 8px;
border-radius: 50%;
background-color: var(--color-orange);
opacity: 0.25;
margin: 5px;
transition: opacity 0.4s ease-in-out;
cursor: pointer;

&.active {
opacity: 1;
}
}
}

// Hovers
@media #{$has-hover} {
/deep/ .nav:hover svg path {
fill: var(--color-orange);
}
.dot:hover {
opacity: 1;
}
}
// Breakpoints
@media #{$lt-phone} {
/deep/ .slide {
left: 20px;
right: 20px;
}
/deep/ .nav {
display: none;
}
}
}
</style>
204 changes: 204 additions & 0 deletions src/components/global/Slideshow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
<template>
<div
class="slideshow"
tabindex="0"
@keyup.right="goToNext(true)"
@keyup.left="goToPrev(true)"
@swipe-right="goToPrev(true)"
@swipe-left="goToNext(true)"
>
<slot name="before" />

<transition
v-for="(slide, i) in slides"
:key="slide.id"
:name="transitionName"
:mode="transitionMode"
>
<div v-show="activeSlideLogic(i, internalIdx)" class="slide">
<slot :slide="slide" name="slide" />
</div>
</transition>

<button
v-if="$slots['nav-next']"
class="nav next"
@click="goToNext(true)"
>
<slot name="nav-next" />
</button>
<button
v-if="$slots['nav-prev']"
class="nav prev"
@click="goToPrev(true)"
>
<slot name="nav-prev" />
</button>

<slot name="after" :index="internalIdx" />
</div>
</template>

<script>
// import Vue from "vue"
import _clamp from "lodash/clamp"

// When used in a project update path for import
import { initSwipeEvents } from "@/utils/tools"

export default {
props: {
slides: {
type: Array,
required: true
},
// Determines if slideshow autoprogresses or not
paused: {
type: Boolean,
default: false
},
// Sets the time before slideshow autoprogresses
timeout: {
type: Number,
default: 4000
},
// Sets Vue transition on incomming/outgoing slides. If not set slide-right/left is used
nextTransitionName: {
type: String,
default: "slide-left"
},
prevTransitionName: {
type: String,
default: "slide-right"
},
transitionMode: {
type: String,
default: ""
},
// Determines if slideshow should loop from last to first slide and vice-versa
wrap: {
type: Boolean,
default: true
},
// Determines active slide by array index
// Use .sync on prop to sync changes in variable with parent component
activeIndex: {
type: Number,
default: 0
},
// Function for determining which slides are visible
// Can be used to show next and prev slides along with the current slide
activeSlideLogic: {
type: Function,
default: (idx, internalIdx) => idx == internalIdx
},
swipeEvents: {
type: Boolean,
default: false
}
},
data() {
return {
timer: null,
transitionName: "",
internalIdx: 0
}
},
watch: {
internalIdx(newIdx) {
// Emit event to update activeIndex prop
this.$emit("update:activeIndex", newIdx)
// Emit slide change event
this.$emit("slide-change", newIdx)
}
},
created() {
this.internalIdx = this.activeIndex
},
mounted() {
// Pause slideshow when in a different tab
document.addEventListener(
"visibilitychange",
this.handleVisibilityChange
)

if (!this.paused) this.play()
if (this.swipeEvents) initSwipeEvents(this.$el)
},
destroyed() {
document.removeEventListener(
"visibilitychange",
this.handleVisibilityChange
)
},
methods: {
handleVisibilityChange() {
if (document.visibilityState === "hidden") {
this.pause()
} else {
this.play()
}
},
getLoopedIdx(idx) {
if (this.wrap)
return (idx + this.slides.length) % this.slides.length
else return _clamp(idx, 0, this.slides.length - 1)
},
goToNext(clearTimer = true) {
if (clearTimer) this.pause()
this.transitionName = this.nextTransitionName
this.$nextTick(
() =>
(this.internalIdx = this.getLoopedIdx(this.internalIdx + 1))
)
},
goToPrev(clearTimer = true) {
if (clearTimer) this.pause()
this.transitionName = this.prevTransitionName
this.$nextTick(
() =>
(this.internalIdx = this.getLoopedIdx(this.internalIdx - 1))
)
},
pause() {
clearInterval(this.timer)
},
play() {
clearInterval(this.timer)
this.timer = setInterval(() => this.goToNext(false), this.timeout)
},
goToSlide(slideIdx, clearTimer = true) {
if (clearTimer) this.pause()
if (slideIdx > this.internalIdx)
this.transitionName = this.nextTransitionName
else this.transitionName = this.prevTransitionName
this.$nextTick(
() => (this.internalIdx = this.getLoopedIdx(slideIdx))
)
}
}
}
</script>

<style lang="scss">
.slideshow {
position: relative;
overflow: hidden;
z-index: 0;
outline: none;

> .slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.nav {
border: none;
padding: 0;
appearance: none;
z-index: 10;
}
}
</style>