Created
November 14, 2018 13:15
-
-
Save Roy-Ermers/6a9bbd10efe0bfe4762ef9ed134e6cba to your computer and use it in GitHub Desktop.
a custom webcomponent slider with previews of images on various screens.
This file contains 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
class slider extends HTMLElement { | |
constructor() { | |
super(); | |
this.images = []; | |
this.attachShadow({ | |
mode: "open" | |
}); | |
// #region HTML/css | |
this.shadowRoot.innerHTML = /*html*/ ` | |
<style> | |
.site-preview, | |
:host { | |
display: block; | |
position: relative; | |
min-height: 100px; | |
padding: 1em; | |
z-index: 1; | |
text-align: center; | |
} | |
.site-preview .mobile, | |
:host .mobile { | |
display: inline-block; | |
border: 5px solid black; | |
border-radius: 15px; | |
width: 10%; | |
padding-top: 21.6666667%; | |
position: relative; | |
filter: drop-shadow(0 0 24px rgba(0, 0, 0, 0.32)); | |
z-index: 1; | |
} | |
.site-preview .mobile::before, | |
:host .mobile::before { | |
content: ""; | |
z-index: 1; | |
background-color: #000; | |
position: absolute; | |
width: 65%; | |
height: 10px; | |
top: 0; | |
left: 50%; | |
transform: translate(-50%); | |
border-bottom-left-radius: 5px; | |
border-bottom-right-radius: 5px; | |
} | |
.site-preview .mobile .screen, | |
:host .mobile .screen { | |
border-radius: 8px; | |
} | |
.site-preview .tablet, | |
:host .tablet { | |
z-index: 1; | |
display: inline-block; | |
border: 15px solid black; | |
position: relative; | |
border-top-width: 25px; | |
border-bottom-width: 35px; | |
border-radius: 15px; | |
width: 17%; | |
padding-top: 22.6666666667%; | |
filter: drop-shadow(0 0 24px rgba(0, 0, 0, 0.32)); | |
} | |
.site-preview .tablet::before, | |
:host .tablet::before { | |
z-index: 1; | |
content: ""; | |
background-color: #333; | |
position: absolute; | |
top: -17px; | |
left: 50%; | |
transform: translate(-50%); | |
height: 5px; | |
width: 5px; | |
border-radius: 100%; | |
} | |
.site-preview .tablet::after, | |
:host .tablet::after { | |
z-index: 1; | |
content: ""; | |
background-color: #333; | |
position: absolute; | |
bottom: -25px; | |
left: 50%; | |
transform: translate(-50%); | |
height: 15px; | |
width: 15px; | |
border-radius: 100%; | |
} | |
.site-preview .desktop, | |
:host .desktop { | |
display: inline-block; | |
border: 15px solid black; | |
position: relative; | |
border-radius: 25px; | |
margin: 0 -50px; | |
border-bottom-width: 10px; | |
border-bottom-left-radius: 0; | |
border-bottom-right-radius: 0; | |
width: 50%; | |
padding-top: 28.125%; | |
margin-bottom: 100px; | |
z-index: 0; | |
} | |
.site-preview .desktop .screen, | |
:host .desktop .screen { | |
border-top-left-radius: 10px; | |
border-top-right-radius: 10px; | |
} | |
.site-preview .desktop::after, | |
:host .desktop::after { | |
z-index: 1; | |
position: absolute; | |
display: block; | |
content: ""; | |
width: calc(100% + 30px); | |
left: -15px; | |
bottom: -60px; | |
height: 50px; | |
background-color: #c2c2c2; | |
border-bottom-left-radius: 15px; | |
border-bottom-right-radius: 15px; | |
} | |
.site-preview .desktop::before, | |
:host .desktop::before { | |
z-index: 1; | |
position: absolute; | |
display: block; | |
content: ""; | |
background-color: #acacac; | |
width: 25%; | |
height: 50px; | |
bottom: -110px; | |
left: 50%; | |
transform: translate(-50%); | |
border: #c2c2c2 5px solid; | |
border-left: transparent 10px solid; | |
border-right: transparent 10px solid; | |
} | |
.site-preview .screen, | |
:host .screen { | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
display: block; | |
overflow: hidden; | |
background-color: #000; | |
} | |
.site-preview .screen .slide, | |
:host .screen .slide { | |
display: inline-block; | |
width: 100%; | |
height: 0; | |
transition: height 250ms ease-in-out; | |
object-fit: cover; | |
object-position: center center; | |
} | |
.site-preview .screen .slide.selected, | |
:host .screen .slide.selected { | |
height: 100%; | |
} | |
@media screen and (max-width: 500px) { | |
:host .mobile { | |
border-radius: 7px; | |
border-width: 2px; | |
} | |
:host .mobile .screen { | |
border-radius: 3px; | |
} | |
:host .mobile::before { | |
height: 5px; | |
} | |
:host .tablet { | |
border-width: 7px; | |
border-top-width: 12px; | |
border-bottom-width: 17px; | |
border-radius: 7px; | |
} | |
:host .tablet::before { | |
top: -10px; | |
height: 5px; | |
width: 5px; | |
} | |
:host .tablet::after { | |
bottom: -15px; | |
height: 10px; | |
width: 10px; | |
} | |
:host .desktop { | |
border-top-left-radius: 10px; | |
border-top-right-radius: 10px; | |
border-width: 10px; | |
margin: 0 -20px; | |
margin-bottom: 70px; | |
} | |
:host .desktop .screen { | |
border-top-left-radius: 2px; | |
border-top-right-radius: 2px; | |
} | |
:host .desktop::after { | |
bottom: -35px; | |
height: 25px; | |
width: calc(100% + 20px); | |
left: -10px; | |
} | |
:host .desktop::before { | |
bottom: -65px; | |
height: 35px; | |
} | |
} | |
</style> | |
<div class="tablet"> | |
<div class="screen"></div> | |
</div> | |
<div class="desktop"> | |
<div class="screen"> | |
</div> | |
</div> | |
<div class="mobile"> | |
<div class="screen"></div> | |
</div> | |
<slot></slot>`; | |
//#endregion | |
this.desktop = this.shadowRoot.querySelector(".desktop>.screen"); | |
this.tablet = this.shadowRoot.querySelector(".tablet>.screen"); | |
this.mobile = this.shadowRoot.querySelector(".mobile>.screen"); | |
this.slides = 0; | |
this.currentSlide = 0; | |
window.addEventListener("resize", () => this.WidthChanged(this)); | |
} | |
async connectedCallback() { | |
await new Promise((res) => requestAnimationFrame(res)); | |
let slides = this.shadowRoot.querySelector("slot").assignedElements(); | |
this.slides = Math.ceil(slides.length / 3); | |
if (slides.length > 0) { | |
slides.forEach(screenshot => { | |
if (!screenshot.hasAttribute("data-type")) return; | |
switch (screenshot.getAttribute("data-type")) { | |
case "desktop": | |
this.desktop.innerHTML += slider.slide.replace(`{url}`, screenshot.src); | |
break; | |
case "tablet": | |
this.tablet.innerHTML += slider.slide.replace(`{url}`, screenshot.src); | |
break; | |
case "mobile": | |
this.mobile.innerHTML += slider.slide.replace(`{url}`, screenshot.src); | |
break; | |
} | |
screenshot.remove(); | |
}); | |
this.desktop.firstElementChild.classList.add("selected"); | |
this.tablet.firstElementChild.classList.add("selected"); | |
this.mobile.firstElementChild.classList.add("selected"); | |
} else | |
this.desktop.innerHTML = "<h1>No Slides found.</h1>"; | |
setInterval(() => this.Scroll(this), this.getAttribute("data-interval") || 2500); | |
} | |
WidthChanged(elem) { | |
console.log(elem.clientWidth); | |
elem.classList.toggle("mobile", elem.clientWidth < 385); | |
} | |
async Scroll(elem) { | |
elem.currentSlide++; | |
if (elem.currentSlide >= elem.slides) elem.currentSlide = 0; | |
let screen = [elem.tablet, elem.desktop, elem.mobile]; | |
screen.forEach((screen, i) => { | |
setTimeout(() => { | |
let current = screen.querySelector(".selected"); | |
if (current && current.nextElementSibling && elem.currentSlide > 0) { | |
current.nextElementSibling.classList.add("selected"); | |
current.classList.remove("selected"); | |
} else { | |
screen.firstElementChild.classList.add("selected"); | |
if (current && this.slides > 0) | |
current.classList.remove("selected"); | |
} | |
}, (elem.getAttribute("data-delay") || 0) * i); | |
}); | |
} | |
} | |
slider.slide = /*html*/ `<img src="{url}" class="slide">`; | |
customElements.define("app-slider", slider); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment