HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URL's added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by Skypack, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ES6 import
usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<script>
document.documentElement.className = "js";
var supportsCssVars = function() { var e, t = document.createElement("style"); return t.innerHTML = "root: { --tmp-var: bold; }", document.head.appendChild(t), e = !!(window.CSS && window.CSS.supports && window.CSS.supports("font-weight", "var(--tmp-var)")), t.parentNode.removeChild(t), e };
supportsCssVars() || alert("Please view this demo in a modern browser that supports CSS Variables.");
</script>
<body class="loading">
<svg class="hidden">
<symbol id="icon-arrow" viewBox="0 0 24 24">
<title>arrow</title>
<polygon points="6.3,12.8 20.9,12.8 20.9,11.2 6.3,11.2 10.2,7.2 9,6 3.1,12 9,18 10.2,16.8 " />
</symbol>
<symbol id="icon-drop" viewBox="0 0 24 24">
<title>drop</title>
<path d="M12,21c-3.6,0-6.6-3-6.6-6.6C5.4,11,10.8,4,11.4,3.2C11.6,3.1,11.8,3,12,3s0.4,0.1,0.6,0.3c0.6,0.8,6.1,7.8,6.1,11.2C18.6,18.1,15.6,21,12,21zM12,4.8c-1.8,2.4-5.2,7.4-5.2,9.6c0,2.9,2.3,5.2,5.2,5.2s5.2-2.3,5.2-5.2C17.2,12.2,13.8,7.3,12,4.8z" />
<path d="M12,18.2c-0.4,0-0.7-0.3-0.7-0.7s0.3-0.7,0.7-0.7c1.3,0,2.4-1.1,2.4-2.4c0-0.4,0.3-0.7,0.7-0.7c0.4,0,0.7,0.3,0.7,0.7C15.8,16.5,14.1,18.2,12,18.2z" />
</symbol>
<symbol id="icon-longarrow" viewBox="0 0 54 24">
<title>longarrow</title>
<path d="M.42 11.158L12.38.256c.333-.27.696-.322 1.09-.155.395.166.593.467.593.903v6.977h38.87c.29 0 .53.093.716.28.187.187.28.426.28.716v5.98c0 .29-.093.53-.28.716a.971.971 0 0 1-.716.28h-38.87v6.977c0 .416-.199.717-.592.903-.395.167-.759.104-1.09-.186L.42 12.62a1.018 1.018 0 0 1 0-1.462z" />
</symbol>
<symbol id="icon-navarrow" viewBox="0 0 408 408">
<title>navarrow</title>
<polygon fill="#fff" fill-rule="nonzero" points="204 0 168.3 35.7 311.1 178.5 0 178.5 0 229.5 311.1 229.5 168.3 372.3 204 408 408 204"></polygon>
</symbol>
</svg>
<main>
<div class="frame">
<header class="codrops-header">
<h1 class="codrops-header__title">Diagonal Slideshow</h1>
<div class="codrops-links">
<a class="github" href="https://github.com/codrops/DiagonalSlideshow/">GitHub</a>
<a class="codrops-icon codrops-icon--prev" href="https://tympanus.net/Development/SlideOutBoxMenu/" title="Previous Demo">
<svg class="icon icon--arrow">
<use xlink:href="#icon-arrow"></use>
</svg>
</a>
<a class="codrops-icon codrops-icon--drop" href="https://tympanus.net/codrops/?p=35765" title="Back to the article">
<svg class="icon icon--drop">
<use xlink:href="#icon-drop"></use>
</svg>
</a>
</div>
</header>
</div>
<div class="slideshow">
<div class="slideshow__deco"></div>
<div class="slide">
<div class="slide__img-wrap">
<div class="slide__img" style="background-image: url(https://tympanus.net/Development/DiagonalSlideshow/img/1.jpg);"></div>
</div>
<div class="slide__side">Memories & Thoughts</div>
<div class="slide__title-wrap">
<span class="slide__number">1</span>
<h3 class="slide__title">Automation</h3>
<h4 class="slide__subtitle">A tree needs to be your friend if you're going to paint him</h4>
</div>
</div>
<div class="slide">
<div class="slide__img-wrap">
<div class="slide__img" style="background-image: url(https://tympanus.net/Development/DiagonalSlideshow/img/2.jpg);"></div>
</div>
<div class="slide__side">Random Roam</div>
<div class="slide__title-wrap">
<span class="slide__number">2</span>
<h3 class="slide__title">Machines</h3>
<h4 class="slide__subtitle">This is probably the greatest thing to happen in my life</h4>
</div>
</div>
<div class="slide">
<div class="slide__img-wrap">
<div class="slide__img" style="background-image: url(https://tympanus.net/Development/DiagonalSlideshow/img/3.jpg);"></div>
</div>
<div class="slide__side">Arbitrary Words</div>
<div class="slide__title-wrap">
<span class="slide__number">3</span>
<h3 class="slide__title">Coexistence</h3>
<h4 class="slide__subtitle">The only guide is your heart</h4>
</div>
</div>
<div class="slide">
<div class="slide__img-wrap">
<div class="slide__img" style="background-image: url(https://tympanus.net/Development/DiagonalSlideshow/img/4.jpg);"></div>
</div>
<div class="slide__side">Haunted Drift</div>
<div class="slide__title-wrap">
<span class="slide__number">4</span>
<h3 class="slide__title">Bellamio</h3>
<h4 class="slide__subtitle">The only prerequisite is that it makes you happy</h4>
</div>
</div>
<div class="slide">
<div class="slide__img-wrap">
<div class="slide__img" style="background-image: url(https://tympanus.net/Development/DiagonalSlideshow/img/5.jpg);"></div>
</div>
<div class="slide__side">Fun Diverge</div>
<div class="slide__title-wrap">
<span class="slide__number">5</span>
<h3 class="slide__title">Pastures</h3>
<h4 class="slide__subtitle">Let's go up in here, and start having some fun</h4>
</div>
</div>
<div class="slide">
<div class="slide__img-wrap">
<div class="slide__img" style="background-image: url(https://tympanus.net/Development/DiagonalSlideshow/img/6.jpg);"></div>
</div>
<div class="slide__side">Hopes & Dreams</div>
<div class="slide__title-wrap">
<span class="slide__number">6</span>
<h3 class="slide__title">Focus</h3>
<h4 class="slide__subtitle">This is unplanned it really just happens</h4>
</div>
</div>
<button class="nav nav--prev">
<svg class="icon icon--navarrow-prev">
<use xlink:href="#icon-navarrow"></use>
</svg>
</button>
<button class="nav nav--next">
<svg class="icon icon--navarrow-next">
<use xlink:href="#icon-navarrow"></use>
</svg>
</button>
</div>
<div class="content">
<div class="content__item">
<span class="content__number">1</span>
<h3 class="content__title">Automation</h3>
<h4 class="content__subtitle">A tree needs to be your friend if you're going to paint him</h4>
<div class="content__text">Just let this happen. We just let this flow right out of our minds. Just relax and let it flow. That easy. Let's put some happy little clouds in our world. It's a very cold picture, I may have to go get my coat. It’s about to freeze me to death. This is gonna be a happy little seascape. Let's go up in here, and start having some fun The least little bit can do so much. Work on one thing at a time. Don't get carried away - we have plenty of time. Put your feelings into it, your heart, it's your world. These trees are so much fun. I get started on them and I have a hard time stopping.</div>
</div>
<div class="content__item">
<span class="content__number">2</span>
<h3 class="content__title">Machines</h3>
<h4 class="content__subtitle">This is probably the greatest thing to happen in my life</h4>
<div class="content__text">We're not trying to teach you a thing to copy. We're just here to teach you a technique, then let you loose into the world. Now, we're going to fluff this cloud. We don't have anything but happy trees here. Let's do that again. Use what you see, don't plan it. Let's go up in here, and start having some fun The least little bit can do so much. Work on one thing at a time. Don't get carried away - we have plenty of time. Put your feelings into it, your heart, it's your world. These trees are so much fun. I get started on them and I have a hard time stopping.</div>
</div>
<div class="content__item">
<span class="content__number">3</span>
<h3 class="content__title">Coexistence</h3>
<h4 class="content__subtitle">The only guide is your heart</h4>
<div class="content__text">Let's go up in here, and start having some fun The least little bit can do so much. Work on one thing at a time. Don't get carried away - we have plenty of time. Put your feelings into it, your heart, it's your world. These trees are so much fun. I get started on them and I have a hard time stopping. But we're not there yet, so we don't need to worry about it. Now let's put some happy little clouds in here. What the devil. A thin paint will stick to a thick paint. I'm going to mix up a little color. </div>
</div>
<div class="content__item">
<span class="content__number">4</span>
<h3 class="content__title">Bellamio</h3>
<h4 class="content__subtitle">The only prerequisite is that it makes you happy</h4>
<div class="content__text">See. We take the corner of the brush and let it play back-and-forth. This is unplanned it really just happens. I'm sort of a softy, I couldn't shoot Bambi except with a camera. I guess I'm a little weird. I like to talk to trees and animals. That's okay though; I have more fun than most people. We'll play with clouds today. Didn't you know you had that much power? You can move mountains. You can do anything. Let's go up in here, and start having some fun The least little bit can do so much. Work on one thing at a time. Don't get carried away - we have plenty of time. Put your feelings into it, your heart, it's your world. These trees are so much fun. I get started on them and I have a hard time stopping.</div>
</div>
<div class="content__item">
<span class="content__number">5</span>
<h3 class="content__title">Pastures</h3>
<h4 class="content__subtitle">Let's go up in here, and start having some fun</h4>
<div class="content__text">So often we avoid running water, and running water is a lot of fun. Everyone is going to see things differently - and that's the way it should be. A big strong tree needs big strong roots. Steve wants reflections, so let's give him reflections. We don't have to be committed. We are just playing here. Making all those little fluffies that live in the clouds. Let's go up in here, and start having some fun The least little bit can do so much. Work on one thing at a time. Don't get carried away - we have plenty of time. Put your feelings into it, your heart, it's your world. These trees are so much fun. I get started on them and I have a hard time stopping.</div>
</div>
<div class="content__item">
<span class="content__number">6</span>
<h3 class="content__title">Focus</h3>
<h4 class="content__subtitle">This is unplanned it really just happens</h4>
<div class="content__text">But we're not there yet, so we don't need to worry about it. Now let's put some happy little clouds in here. What the devil. A thin paint will stick to a thick paint. I'm going to mix up a little color. We’ll use Van Dyke Brown, Permanent Red, and a little bit of Prussian Blue. Let's go up in here, and start having some fun The least little bit can do so much. Work on one thing at a time. Don't get carried away - we have plenty of time. Put your feelings into it, your heart, it's your world. These trees are so much fun. I get started on them and I have a hard time stopping.</div>
</div>
<button class="content__close">
<svg class="icon icon--longarrow">
<use xlink:href="#icon-longarrow"></use>
</svg>
</button>
</div>
</main>
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}body{margin:0;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:0.67em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em;}pre{white-space:pre-wrap;}q{quotes:"\201C" "\201D" "\2018" "\2019";}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}img{border:0;}svg:not(:root){overflow:hidden;}figure{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}
*,
*::after,
*::before {
box-sizing: border-box;
}
body {
--color-text: #f1f1f1;
--color-bg: #0c0c0c;
--color-link: #1ab3de;
--color-link-hover: #f1f1f1;
--color-deco: #141414;
--color-side: #353535;
font-family: Futura, "futura-pt", sans-serif;
min-height: 100vh;
color: #57585c;
color: var(--color-text);
background-color: var(--color-bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-top: 10rem;
}
/* Page Loader */
.js .loading::before {
content: '';
position: fixed;
z-index: 100000;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--color-bg);
}
.js .loading::after {
content: '';
position: fixed;
z-index: 100000;
top: 50%;
left: 50%;
width: 60px;
height: 60px;
margin: -30px 0 0 -30px;
pointer-events: none;
border-radius: 50%;
opacity: 0.4;
background: var(--color-link);
animation: loaderAnim 0.7s linear infinite alternate forwards;
}
@keyframes loaderAnim {
to {
opacity: 1;
transform: scale3d(0.5,0.5,1);
}
}
a {
text-decoration: none;
color: var(--color-link);
outline: none;
}
a:hover,
a:focus {
color: var(--color-link-hover);
outline: none;
}
.hidden {
position: absolute;
overflow: hidden;
width: 0;
height: 0;
pointer-events: none;
}
.message {
position: relative;
z-index: 100;
display: none;
padding: 1em;
text-align: center;
color: var(--color-bg);
background: var(--color-text);
}
/* Icons */
.icon {
display: block;
width: 1.5em;
height: 1.5em;
margin: 0 auto;
fill: currentColor;
}
.frame {
position: fixed;
z-index: 10000;
top: 5rem;
left: 0;
width: 100%;
max-width: none;
min-height: 0;
height: 100vh;
padding: 1rem;
pointer-events: none;
}
.frame a {
pointer-events: auto;
}
/* Header */
.codrops-header {
position: relative;
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
}
.codrops-header__title {
font-size: 1rem;
font-weight: normal;
line-height: 1;
margin: 0;
}
/* Top Navigation Style */
.codrops-links {
position: relative;
display: flex;
justify-content: flex-end;
align-items: center;
white-space: nowrap;
}
.github {
display: block;
padding: 0.25em;
margin: 0 0.25rem;
}
.codrops-icon {
display: inline-block;
padding: 0.25em;
margin: 0.25em 0 0 0;
}
.slideshow {
position: relative;
overflow: hidden;
margin: 0;
height: 100vh;
width: 100%;
height: calc(100vh - 10rem);
display: grid;
grid-template-columns: 33% 33% 33%;
grid-column-gap: 0.5%;
grid-template-rows: 100%;
grid-template-areas: '... slide ...';
}
.slide {
width: 100%;
display: flex;
pointer-events: none;
cursor: pointer;
position: relative;
height: 100%;
grid-area: slide;
}
.slideshow--previewopen .slide {
cursor: default;
}
.slide--current {
pointer-events: auto;
}
.slide__img-wrap {
width: 100%;
overflow: hidden;
z-index: 100;
height: 80%;
top: 10%;
position: absolute;
}
.slideshow__deco {
grid-area: slide;
background: var(--color-deco);
width: 100%;
height: 80%;
align-self: center;
position: relative;
margin: -40px 0 0 0;
right: -20px;
}
.nav {
position: absolute;
background: none;
width: 3rem;
height: 3rem;
z-index: 1000;
border: 0;
padding: 0;
margin: 0;
pointer-events: none;
transition: transform 0.8s, opacity 0.8s;
transition-timing-function: cubic-bezier(0.7,0,0.3,1);
}
.nav--next {
bottom: 1rem;
right: 1rem;
}
.icon--navarrow-next {
transform: rotate(45deg);
}
.nav--prev {
top: 1rem;
left: 1rem;
}
.icon--navarrow-prev {
transform: rotate(-135deg);
}
.slideshow--previewopen .nav {
opacity: 0;
transition-duration: 0.4s;
}
.slideshow--previewopen .nav--next {
transform: translate3d(100%, 100%, 0);
}
.slideshow--previewopen .nav--prev {
transform: translate3d(-100%, -100%, 0);
}
.slide__img {
width: 100%;
height: 100%;
left: 0;
top: 0;
background-size: cover;
background-position: 50% 50%;
position: absolute;
pointer-events: none;
transform: scale3d(1.01,1.01,1);
}
.js .slide__img-wrap,
.js .slide__title-wrap,
.js .slide__side {
opacity: 0;
pointer-events: none;
}
.js .slide--current .slide__img-wrap {
opacity: 1;
pointer-events: auto;
}
.slide--visible .slide__img-wrap {
pointer-events: auto;
}
.slide__title-wrap {
justify-self: flex-end;
width: 100%;
position: relative;
z-index: 1000;
}
.slide__number {
display: block;
font-size: 2rem;
font-weight: bold;
}
.slide__number::before {
content: "\2014";
display: inline-block;
margin: 0 1rem 0 0;
}
.slide__title,
.slide__subtitle,
.slide__side {
display: none;
}
.content {
position: fixed;
top: 10rem;
left: 0;
width: 100%;
height: calc(100% - 10rem);
pointer-events: none;
z-index: 100;
}
.content__item {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
padding: 10vh 5vw;
overflow: auto;
}
.content__item--current,
.content__item--current ~ .content__close {
pointer-events: auto;
}
.content__close {
position: absolute;
top: 1rem;
left: 1rem;
background: none;
color: currentColor;
border: 0;
margin: 0;
padding: 0;
}
.icon--longarrow {
width: 2rem;
}
.content__close:focus {
outline: none;
}
.content__number {
font-weight: bold;
}
.content__number::before {
content: "\2014";
display: inline-block;
margin: 0 1rem 0 0;
}
.content__title {
margin: 0.5rem 0;
font-size: 2rem;
}
.content__subtitle {
margin: 0 0 0.5rem;
font-size: 1rem;
font-weight: normal;
}
.content__text {
font-size: 0.85rem;
}
.js .content__title,
.js .content__subtitle,
.js .content__number,
.js .content__text,
.js .content__close {
opacity: 0;
}
@media screen and (min-width: 53em) {
body {
padding: 0;
}
.frame {
top: 0;
display: grid;
align-items: start;
justify-items: end;
grid-template-columns: 50% 50%;
grid-template-rows: 100%;
grid-template-areas: '... header';
}
.codrops-header {
grid-area: header;
padding: 1rem 0.5rem;
display: block;
}
.codrops-header__title {
padding: 0 0.5rem;
}
.codrops-links {
margin: 0.25rem auto 0 0.25rem;
}
.slideshow {
height: 100vh;
grid-template-columns: 27% 27% 27%;
grid-column-gap: 9.5%;
}
.slide {
padding: 10vh 0 7vh;
flex-direction: column;
justify-content: space-between;
}
.slide__side {
margin: 0 0 0 -1.85rem;
}
.slide__title-wrap {
margin: 0 0 0 -1.85rem;
}
.slide__title,
.slide__subtitle,
.slide__side {
display: block;
}
.slide__title {
font-size: 3.25rem;
margin: 0 0 0.25rem;
}
.slide__subtitle {
font-weight: normal;
margin: 0;
min-height: 50px;
}
.slide__side {
color: var(--color-side);
position: relative;
-webkit-writing-mode: vertical-rl;
writing-mode: vertical-rl;
transform: rotate(180deg);
z-index: 1000;
}
.content {
top: 0;
height: 100%;
}
.content__item {
padding: calc(10vh + 5rem) 0 7vh;
width: 50.5%;
right: 7.5%;
overflow: visible;
}
.content__close {
left: 42%;
top: calc(10vh + 1rem);
}
.content__number {
position: absolute;
bottom: 7vh;
right: 0;
font-size: 2rem;
}
.content__title {
font-size: 7vw;
}
.content__subtitle {
font-size: 1.15rem;
margin-bottom: 7.5vh;
}
.content__text {
font-size: 0.95rem;
column-count: 2;
column-gap: 2rem;
max-width: 600px;
text-align: justify;
}
}
/**
* demo.js
* http://www.codrops.com
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2018, Codrops
* http://www.codrops.com
*/
{
// From http://www.quirksmode.org/js/events_properties.html#position
// Get the mouse position.
const getMousePos = (e) => {
let posx = 0;
let posy = 0;
if (!e) e = window.event;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { x : posx, y : posy }
};
// Gets a random integer.
const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
// Equation of a line (y = mx + b ).
const lineEq = (y2, y1, x2, x1, currentVal) => {
const m = (y2 - y1) / (x2 - x1);
const b = y1 - m * x1;
return m * currentVal + b;
};
// Some random chars.
const chars = ['$','%','#','&','=','*','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','.',':',',','^'];
const charsTotal = chars.length;
// Randomize letters function. Used when navigating the slideshow to switch the curretn slide´s texts.
const randomizeLetters = (letters) => {
return new Promise((resolve, reject) => {
const lettersTotal = letters.length;
let cnt = 0;
letters.forEach((letter, pos) => {
let loopTimeout;
const loop = () => {
letter.innerHTML = chars[getRandomInt(0,charsTotal-1)];
loopTimeout = setTimeout(loop, getRandomInt(50,500));
};
loop();
const timeout = setTimeout(() => {
clearTimeout(loopTimeout);
letter.style.opacity = 1;
letter.innerHTML = letter.dataset.initial;
++cnt;
if ( cnt === lettersTotal ) {
resolve();
}
}, pos*lineEq(40,0,8,200,lettersTotal));
});
});
};
// Hide each of the letters with random delays. Used when showing the current slide´s content.
const disassembleLetters = (letters) => {
return new Promise((resolve, reject) => {
const lettersTotal = letters.length;
let cnt = 0;
letters.forEach((letter, pos) => {
setTimeout(() => {
letter.style.opacity = 0;
++cnt;
if ( cnt === lettersTotal ) {
resolve();
}
}, pos*30);
});
});
}
// The Slide class.
class Slide {
constructor(el) {
this.DOM = {el: el};
// The image wrap element.
this.DOM.imgWrap = this.DOM.el.querySelector('.slide__img-wrap');
// The image element.
this.DOM.img = this.DOM.imgWrap.querySelector('.slide__img');
// The texts: the parent wrap, title, number and side text.
this.DOM.texts = {
wrap: this.DOM.el.querySelector('.slide__title-wrap'),
title: this.DOM.el.querySelector('.slide__title'),
number: this.DOM.el.querySelector('.slide__number'),
side: this.DOM.el.querySelector('.slide__side'),
};
// Split the title and side texts into spans, one per letter. Sort these so we can later animate then with the
// randomizeLetters or disassembleLetters functions when navigating and showing the content.
charming(this.DOM.texts.title);
charming(this.DOM.texts.side);
this.DOM.titleLetters = Array.from(this.DOM.texts.title.querySelectorAll('span')).sort(() => 0.5 - Math.random());
this.DOM.sideLetters = Array.from(this.DOM.texts.side.querySelectorAll('span')).sort(() => 0.5 - Math.random());
this.DOM.titleLetters.forEach(letter => letter.dataset.initial = letter.innerHTML);
this.DOM.sideLetters.forEach(letter => letter.dataset.initial = letter.innerHTML);
// Calculate the sizes of the image wrap.
this.calcSizes();
// And also the transforms needed per position.
// We have 5 different possible positions for a slide: center, bottom right, top left and outside the viewport (top left or bottom right).
this.calcTransforms();
// Init/Bind events.
this.initEvents();
}
// Gets the size of the image wrap.
calcSizes() {
this.width = this.DOM.imgWrap.offsetWidth;
this.height = this.DOM.imgWrap.offsetHeight;
}
// Gets the transforms per slide position.
calcTransforms() {
/*
Each position corresponds to the position of a given slide:
0: left top corner outside the viewport
1: left top corner (prev slide position)
2: center (current slide position)
3: right bottom corner (next slide position)
4: right bottom corner outside the viewport
5: left side, for when the content is shown
*/
this.transforms = [
{x: -1*(winsize.width/2+this.width), y: -1*(winsize.height/2+this.height), rotation: -30},
{x: -1*(winsize.width/2-this.width/3), y: -1*(winsize.height/2-this.height/3), rotation: 0},
{x: 0, y: 0, rotation: 0},
{x: winsize.width/2-this.width/3, y: winsize.height/2-this.height/3, rotation: 0},
{x: winsize.width/2+this.width, y: winsize.height/2+this.height, rotation: 30},
{x: -1*(winsize.width/2 - this.width/2 - winsize.width*0.075), y: 0, rotation: 0}
];
}
// Init events:
// Mouseevents for mousemove/tilt/scale on the current image, and window resize.
initEvents() {
this.mouseenterFn = () => {
if ( !this.isPositionedCenter() || !allowTilt ) return;
clearTimeout(this.mousetime);
this.mousetime = setTimeout(() => {
// Scale the image.
TweenMax.to(this.DOM.img, 0.8, {
ease: Power3.easeOut,
scale: 1.1
});
}, 40);
};
this.mousemoveFn = ev => requestAnimationFrame(() => {
// Tilt the current slide.
if ( !allowTilt || !this.isPositionedCenter() ) return;
this.tilt(ev);
});
this.mouseleaveFn = (ev) => requestAnimationFrame(() => {
if ( !allowTilt || !this.isPositionedCenter() ) return;
clearTimeout(this.mousetime);
// Reset tilt and image scale.
TweenMax.to([this.DOM.imgWrap,this.DOM.texts.wrap], 1.8, {
ease: 'Power4.easeOut',
x: 0,
y: 0,
rotationX: 0,
rotationY: 0
});
TweenMax.to(this.DOM.img, 1.8, {
ease: 'Power4.easeOut',
scale: 1
});
});
// When resizing the window recalculate size and transforms, since both will depend on the window size.
this.resizeFn = () => {
this.calcSizes();
this.calcTransforms();
};
this.DOM.imgWrap.addEventListener('mouseenter', this.mouseenterFn);
this.DOM.imgWrap.addEventListener('mousemove', this.mousemoveFn);
this.DOM.imgWrap.addEventListener('mouseleave', this.mouseleaveFn);
window.addEventListener('resize', this.resizeFn);
}
// Tilt the image wrap and texts when mouse moving the current slide.
tilt(ev) {
const mousepos = getMousePos(ev);
// Document scrolls.
const docScrolls = {
left : document.body.scrollLeft + document.documentElement.scrollLeft,
top : document.body.scrollTop + document.documentElement.scrollTop
};
const bounds = this.DOM.imgWrap.getBoundingClientRect();;
// Mouse position relative to the main element (this.DOM.el).
const relmousepos = {
x : mousepos.x - bounds.left - docScrolls.left,
y : mousepos.y - bounds.top - docScrolls.top
};
// Move the element from -20 to 20 pixels in both x and y axis.
// Rotate the element from -15 to 15 degrees in both x and y axis.
let t = {x:[-20,20],y:[-20,20]},
r = {x:[-15,15],y:[-15,15]};
const transforms = {
translation : {
x: (t.x[1]-t.x[0])/bounds.width*relmousepos.x + t.x[0],
y: (t.y[1]-t.y[0])/bounds.height*relmousepos.y + t.y[0]
},
rotation : {
x: (r.x[1]-r.x[0])/bounds.height*relmousepos.y + r.x[0],
y: (r.y[1]-r.y[0])/bounds.width*relmousepos.x + r.y[0]
}
};
// Move the image wrap.
TweenMax.to(this.DOM.imgWrap, 1.5, {
ease: 'Power1.easeOut',
x: transforms.translation.x,
y: transforms.translation.y,
rotationX: transforms.rotation.x,
rotationY: transforms.rotation.y
});
// Move the texts wrap.
TweenMax.to(this.DOM.texts.wrap, 1.5, {
ease: 'Power1.easeOut',
x: -1*transforms.translation.x,
y: -1*transforms.translation.y
});
}
// Positions one slide (left, right or current) in the viewport.
position(pos) {
TweenMax.set(this.DOM.imgWrap, {
x: this.transforms[pos].x,
y: this.transforms[pos].y,
rotationX: 0,
rotationY: 0,
opacity: 1,
rotationZ: this.transforms[pos].rotation
});
}
// Sets it as current.
setCurrent(isContentOpen) {
this.isCurrent = true;
this.DOM.el.classList.add('slide--current', 'slide--visible');
// Position it on the current´s position.
this.position(isContentOpen ? 5 : 2);
}
// Position the slide on the left side.
setLeft(isContentOpen) {
this.isRight = this.isCurrent = false;
this.isLeft = true;
this.DOM.el.classList.add('slide--visible');
// Position it on the left position.
this.position(isContentOpen ? 0 : 1);
}
// Position the slide on the right side.
setRight(isContentOpen) {
this.isLeft = this.isCurrent = false;
this.isRight = true;
this.DOM.el.classList.add('slide--visible');
// Position it on the right position.
this.position(isContentOpen ? 4 : 3);
}
// Check if the slide is positioned on the right side (if it´s the next slide in the slideshow).
isPositionedRight() {
return this.isRight;
}
// Check if the slide is positioned on the left side (if it´s the previous slide in the slideshow).
isPositionedLeft() {
return this.isLeft;
}
// Check if the slide is the current one.
isPositionedCenter() {
return this.isCurrent;
}
// Reset classes and state.
reset() {
this.isRight = this.isLeft = this.isCurrent = false;
this.DOM.el.classList = 'slide';
}
hide() {
TweenMax.set(this.DOM.imgWrap, {x:0, y:0, rotationX:0, rotationY:0, rotationZ:0, opacity:0});
}
// Moves a slide to a specific position defined in settings.position.
// Also, moves it from position settings.from and if we need to reset the image scale when moving the slide then settings.resetImageScale should be true.
moveToPosition(settings) {
return new Promise((resolve, reject) => {
/*
Moves the slide to a specific position:
-2: left top corner outside the viewport
-1: left top corner (prev slide position)
0: center (current slide position)
1: right bottom corner (next slide position)
2: right bottom corner outside the viewport
3: left side, for when the content is shown
*/
TweenMax.to(this.DOM.imgWrap, .8, {
ease: Power4.easeInOut,
delay: settings.delay || 0,
startAt: settings.from !== undefined ? {
x: this.transforms[settings.from+2].x,
y: this.transforms[settings.from+2].y,
rotationX: 0,
rotationY: 0,
rotationZ: this.transforms[settings.from+2].rotation
} : {},
x: this.transforms[settings.position+2].x,
y: this.transforms[settings.position+2].y,
rotationX: 0,
rotationY: 0,
rotationZ: this.transforms[settings.position+2].rotation,
onStart: settings.from !== undefined ? () => TweenMax.set(this.DOM.imgWrap, {opacity: 1}) : null,
onComplete: resolve
});
// Reset image scale when showing the content of the current slide.
if ( settings.resetImageScale ) {
TweenMax.to(this.DOM.img, .8, {
ease: Power4.easeInOut,
scale: 1
});
}
});
}
// Hides the current slide´s texts.
hideTexts(animation = false) {
if ( animation ) {
disassembleLetters(this.DOM.titleLetters).then(() => TweenMax.set(this.DOM.texts.wrap, {opacity: 0}));
disassembleLetters(this.DOM.sideLetters).then(() => TweenMax.set(this.DOM.texts.side, {opacity: 0}));
}
else {
TweenMax.set(this.DOM.texts.wrap, {opacity: 0});
TweenMax.set(this.DOM.texts.side, {opacity: 0});
}
}
// Shows the current slide´s texts.
showTexts(animation = true) {
TweenMax.set(this.DOM.texts.wrap, {opacity: 1});
TweenMax.set(this.DOM.texts.side, {opacity: 1});
if ( animation ) {
randomizeLetters(this.DOM.titleLetters);
randomizeLetters(this.DOM.sideLetters);
TweenMax.to(this.DOM.texts.number, 0.6, {
ease: Elastic.easeOut.config(1,0.5),
startAt: {x: '-10%', opacity: 0},
x: '0%',
opacity: 1
});
}
}
}
// The Content class. Represents one content item per slide.
class Content {
constructor(el) {
this.DOM = {el: el};
this.DOM.number = this.DOM.el.querySelector('.content__number');
this.DOM.title = this.DOM.el.querySelector('.content__title');
this.DOM.subtitle = this.DOM.el.querySelector('.content__subtitle');
this.DOM.text = this.DOM.el.querySelector('.content__text');
this.DOM.backCtrl = this.DOM.el.parentNode.querySelector('.content__close');
this.DOM.backCtrl.addEventListener('click', () => slideshow.hideContent());
}
show() {
this.DOM.el.classList.add('content__item--current');
TweenMax.staggerTo([this.DOM.backCtrl,this.DOM.number,this.DOM.title,this.DOM.subtitle,this.DOM.text], 0.8, {
ease: Power4.easeOut,
delay: 0.4,
opacity: 1,
startAt: {y: 40},
y: 0
}, 0.05);
}
hide() {
this.DOM.el.classList.remove('content__item--current');
TweenMax.staggerTo([this.DOM.backCtrl,this.DOM.number,this.DOM.title,this.DOM.subtitle,this.DOM.text].reverse(), 0.3, {
ease: Power3.easeIn,
opacity: 0,
y: 10
}, 0.01);
}
}
// The Slideshow class.
class Slideshow {
constructor(el) {
this.DOM = {el: el};
// The slides.
this.slides = [];
Array.from(this.DOM.el.querySelectorAll('.slide')).forEach(slideEl => this.slides.push(new Slide(slideEl)));
// The total number of slides.
this.slidesTotal = this.slides.length;
// At least 4 slides to continue...
if ( this.slidesTotal < 4 ) {
return false;
}
// Current slide position.
this.current = 0;
this.DOM.deco = this.DOM.el.querySelector('.slideshow__deco');
this.contents = [];
Array.from(document.querySelectorAll('.content > .content__item')).forEach(contentEl => this.contents.push(new Content(contentEl)));
// Set the current/next/previous slides.
this.render();
this.currentSlide.showTexts(false);
// Init/Bind events.
this.initEvents();
}
render() {
// The current, next, and previous slides.
this.currentSlide = this.slides[this.current];
this.nextSlide = this.slides[this.current+1 <= this.slidesTotal-1 ? this.current+1 : 0];
this.prevSlide = this.slides[this.current-1 >= 0 ? this.current-1 : this.slidesTotal-1];
this.currentSlide.setCurrent();
this.nextSlide.setRight();
this.prevSlide.setLeft();
}
initEvents() {
// Clicking the next and previous slide starts the navigation / clicking the current shows its content..
this.clickFn = (slide) => {
if ( slide.isPositionedRight() ) {
this.navigate('next');
}
else if ( slide.isPositionedLeft() ) {
this.navigate('prev');
}
else {
this.showContent();
}
};
for (let slide of this.slides) {
slide.DOM.imgWrap.addEventListener('click', () => this.clickFn(slide));
}
this.resizeFn = () => {
// Reposition the slides.
this.nextSlide.setRight(this.isContentOpen);
this.prevSlide.setLeft(this.isContentOpen);
this.currentSlide.setCurrent(this.isContentOpen);
if ( this.isContentOpen ) {
TweenMax.set(this.DOM.deco, {
scaleX: winsize.width/this.DOM.deco.offsetWidth,
scaleY: winsize.height/this.DOM.deco.offsetHeight,
x: -20,
y: 20
});
}
};
window.addEventListener('resize', this.resizeFn);
}
showContent() {
if ( this.isContentOpen || this.isAnimating ) return;
allowTilt = false;
this.isContentOpen = true;
this.DOM.el.classList.add('slideshow--previewopen');
TweenMax.to(this.DOM.deco, .8, {
ease: Power4.easeInOut,
scaleX: winsize.width/this.DOM.deco.offsetWidth,
scaleY: winsize.height/this.DOM.deco.offsetHeight,
x: -20,
y: 20
});
// Move away right/left slides.
this.prevSlide.moveToPosition({position: -2});
this.nextSlide.moveToPosition({position: 2});
// Position the current slide and reset its image scale value.
this.currentSlide.moveToPosition({position: 3, resetImageScale: true});
// Show content and back arrow (to close the content).
this.contents[this.current].show();
// Hide texts.
this.currentSlide.hideTexts(true);
}
hideContent() {
if ( !this.isContentOpen || this.isAnimating ) return;
this.DOM.el.classList.remove('slideshow--previewopen');
// Hide content.
this.contents[this.current].hide();
TweenMax.to(this.DOM.deco, .8, {
ease: Power4.easeInOut,
scaleX: 1,
scaleY: 1,
x: 0,
y: 0
});
// Move in right/left slides.
this.prevSlide.moveToPosition({position: -1});
this.nextSlide.moveToPosition({position: 1});
// Position the current slide.
this.currentSlide.moveToPosition({position: 0}).then(() => {
allowTilt = true;
this.isContentOpen = false;
});
// Show texts.
this.currentSlide.showTexts();
}
// Animates the element behind the current slide.
bounceDeco(direction, delay) {
TweenMax.to(this.DOM.deco, .4, {
ease: 'Power2.easeIn',
delay: delay+delay*0.2,
x: direction === 'next' ? -40 : 40,
y: direction === 'next' ? -40 : 40,
onComplete: () => {
TweenMax.to(this.DOM.deco, 0.6, {
//ease: Elastic.easeOut.config(1, 0.65),
ease: 'Power2.easeOut',
x: 0,
y: 0
});
}
});
}
// Navigate the slideshow.
navigate(direction) {
// If animating return.
if ( this.isAnimating ) return;
this.isAnimating = true;
allowTilt = false;
const upcomingPos = direction === 'next' ?
this.current < this.slidesTotal-2 ? this.current+2 : Math.abs(this.slidesTotal-2-this.current):
this.current >= 2 ? this.current-2 : Math.abs(this.slidesTotal-2+this.current);
this.upcomingSlide = this.slides[upcomingPos];
// Update current.
this.current = direction === 'next' ?
this.current < this.slidesTotal-1 ? this.current+1 : 0 :
this.current > 0 ? this.current-1 : this.slidesTotal-1;
// Move slides (the previous, current, next and upcoming slide).
this.prevSlide.moveToPosition({position: direction === 'next' ? -2 : 0, delay: direction === 'next' ? 0 : 0.14}).then(() => {
if ( direction === 'next' ) {
this.prevSlide.hide();
}
});
this.currentSlide.moveToPosition({position: direction === 'next' ? -1 : 1, delay: 0.07 });
this.currentSlide.hideTexts();
this.bounceDeco(direction, 0.07);
this.nextSlide.moveToPosition({position: direction === 'next' ? 0 : 2, delay: direction === 'next' ? 0.14 : 0 }).then(() => {
if ( direction === 'prev' ) {
this.nextSlide.hide();
}
});
if ( direction === 'next' ) {
this.nextSlide.showTexts();
}
else {
this.prevSlide.showTexts();
}
this.upcomingSlide.moveToPosition({position: direction === 'next' ? 1 : -1, from: direction === 'next' ? 2 : -2, delay: 0.21 }).then(() => {
// Reset classes.
[this.nextSlide,this.currentSlide,this.prevSlide].forEach(slide => slide.reset());
this.render();
allowTilt = true;
this.isAnimating = false;
});
}
}
// Window sizes.
let winsize;
const calcWinsize = () => winsize = {width: window.innerWidth, height: window.innerHeight};
calcWinsize();
window.addEventListener('resize', calcWinsize);
let allowTilt = true;
// Init slideshow.
const slideshow = new Slideshow(document.querySelector('.slideshow'));
// Preload all the images in the page..
const loader = document.querySelector('.loader');
imagesLoaded(document.querySelectorAll('.slide__img'), {background: true}, () => document.body.classList.remove('loading'));
}
Also see: Tab Triggers