HTML

<!-- best viewed in fullscreen/debug mode -->
<nav class="navbar">
  <div class="align-left top">
    <button class="menu-btn"></button>
    <div class="menu">
      <ul class="menu-links">
        <li><a href="#">About</a></li>
        <li><a href="#">Places</a></li>
        <li><a href="#">Media</a></li>
      </ul>
    </div>
  </div>
  <h1 class="page-title">FREE SOLO</h1>
  <div class="align-right top">
    <div class="search-container">
      <button class="search-btn"></button>
      <input class="search-input" type="text" placeholder="Search...">
    </div>
  </div>
</nav>
<main>
  <div class="slider">
    <div class="slider-inner">
      <div class="slide">
        <h1 data-text="EL CAPITAN" class="slide-title active">EL CAPITAN</h1>
      </div>
      <div class="slide">
        <h1 data-text="HALF DOME" class="slide-title">HALF DOME</h1>
      </div>
      <div class="slide">
        <h1 data-text="FAIR HEAD" class="slide-title">FAIR HEAD</h1>
      </div>
      <div class="slide">
        <h1 data-text="ANGEL'S LANDING" class="slide-title">ANGEL'S LANDING</h1>
      </div>
    </div>
    <img class="slide-img" src="https://kasperdebruyne.be/img/freesolo/1.jpg" />
    <img class="slide-img" src="https://kasperdebruyne.be/img/freesolo/2.jpg" />
    <img class="slide-img" src="https://kasperdebruyne.be/img/freesolo/3.jpg" />
    <img class="slide-img" src="https://kasperdebruyne.be/img/freesolo/4.jpg" />
  </div>
  <div class="slider-static">
    <ul class="slide-description-list">
      <li class="slide-description">
        Yosemite Valley, California
      </li>
      <li class="slide-description">
        Yosemite Valley, California
      </li>
      <li class="slide-description">
        County Antrim, Northern Ireland
      </li>
      <li class="slide-description">
        Zion Canyon, Utah
      </li>
    </ul>
  </div>
  <div class="bottom-bar">
    <div class="align-left bot">
      <div class="slide-indicator-container">
        <ul class="slide-indicator">
          <li>01</li>
          <li>02</li>
          <li>03</li>
          <li>04</li>
        </ul>
      </div>
      <span class="slide-progress-bar"></span>
      <span class="slide-indicator">04</span>
    </div>
    <div class="slider-controls">
      <button class="slider-prev-btn"></button>
      <button class="slider-next-btn"></button>
    </div>
    <div class="align-right bot">
      <div class="btn-container">
        <h2 class="route-text">EXPLORE THE ROUTE</h2>
        <button class="route-btn"></button>
      </div>
    </div>
  </div>
</main>
<div class="preloader">
  <svg version="1.1" id="preloader-logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 120 65" xml:space="preserve">
        <line id="preloader-bot" fill="none" stroke="#FFF" stroke-width="4" stroke-linecap="round" stroke-miterlimit="10" x1="113.776" y1="62.781" x2="10.185" y2="62.781"/>
        <path id="preloader-outline" fill="none" stroke="#FFF" stroke-width="4" stroke-linecap="round" stroke-miterlimit="10" d="M10.185,62.781
            L38.467,14.04c0.833-1.228,2.421-2.259,3.905-2.259h0c1.622,0,3.269,1.148,4.059,2.565l9.475,16.977
            c0.64,0.996,1.874,1.038,2.559,0.086l13.48-25.624c0.93-1.767,2.493-3.165,4.412-3.718c0.41-0.118,0.897-0.07,1.384,0.06
            c1.389,0.37,2.541,1.342,3.267,2.583l32.768,58.071"/>
        <g id="snowGroup">
        <path fill="none" stroke="#FFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
        M33.071,26.571c0,0,2.929,2.21,4.824,3.354l3.732-3.768c0.207-0.209,0.542-0.218,0.758-0.018L46,29.781l6-4"/>
        <path fill="none" stroke="#FFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
            M65,20.781l5,5l6.308-3.42c0.405-0.322,0.988-0.281,1.345,0.093L84,25.781l5.091-6.704"/>
        </g>    
        </svg>
</div>

CSS (SCSS)

    left:-20px;
    width:2px;
    height:2px;
    border:2px solid white;
    border-radius:50%;
}
.slide-description:before {
    left: 13px;
}
.slide-description:after {
    left: 18px;
}

li.slide-description:nth-child(3):before {
    left: 0px;
}
li.slide-description:nth-child(3):after {
    left: 5px;
}
li.slide-description:nth-child(4):before {
    left: 42px;
}
li.slide-description:nth-child(4):after {
    left: 47px;
}
.slider {
    height: 100vh;
    text-align: center;
    position: relative;
    z-index: -1;
    will-change:transform;
}
.slider-inner {
    height: 100%;
    width: 100%;
    position: relative;
}
.slider img {
    position:absolute;
    top:0;
    left:0;
    z-index:-10;
    opacity:0;
}
/* MEDIA QUERIES */
@media screen and (max-width:1600px) {
    .slide-title {
        font-size:120px;
    }
    .slide-description-list {
        margin: 0;
    }
}
@media screen and (max-width:1190px) {
    .route-text {
        margin-right:15px;
    }
    .route-btn:before {
        display:none;
    }
    .slide-title {
        font-size:100px;
    }
    .slide-description-list {
        top:58%;
    }
}
@media screen and (max-width:990px) {
    .slide-title {
        font-size:80px;
    }
    .slide-description-list {
        top:57%;
    }
}
@media screen and (max-width:800px) {
    .nav-links,.social-links {
        display:none;
    }
    .slide-title {
        font-size:60px;
    }
    .route-text {
        font-size: 20px;
    }
}
@media screen and (max-width:768px) {
    .route-text {
        font-size: 18px;
    }
    .slider-prev-btn {
        margin-right:15px;
    }
    .slider-next-btn {
        margin-left:15px;
    }
    .slide-description-list {
        top:56%;
    }
}
@media screen and (max-width:560px) {
    .btn-container {
        padding: 5px 20px;
    }
    .route-text {
        font-size: 16px;
    }
    .slide-progress-bar {
        width: 50px;
    }
    .slide-title {
        font-size:40px;
        letter-spacing:0px;
        -webkit-text-stroke-width:1px;
    }
}
@media screen and (max-width:420px) {
    .slide-progress-bar {
        display:none;
    }
    .slide-indicator {
        display:none;
    }
    .route-text {
        display:none;
    }
    .slide-description-list {
        top:55%;
    }
    .slide-description {
        font-size:14px;
    }
    .search-container {
        max-width:none;
    }
    li.slide-description:nth-child(4):before {
        left: 37px;
    }
    li.slide-description:nth-child(4):after {
        left: 42px;
    }
}
@media screen and (max-width:360px) {
    .slide-description-list {
        top:55%;
    }
    .slide-description {
        display:none;
    }
}

/* media query for cp iframe preview */
@media screen and (max-height:456px) {
  .slide-description-list {
    top:62%
  }
}

const sliderContainer = document.querySelector('.slider');
var vertex = `
    varying vec2 vUv;
    void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
`;

var fragment = `
    varying vec2 vUv;

    uniform sampler2D currentImage;
    uniform sampler2D nextImage;
    uniform sampler2D disp;

    uniform float dispFactor;
    float intensity = 0.25;
    void main() {

        vec2 uv = vUv;

        vec4 disp = texture2D(disp, uv);

        vec2 distortedPosition = vec2(uv.x + dispFactor * (disp.r*intensity), uv.y);
        vec2 distortedPosition2 = vec2(uv.x - (1.0 - dispFactor) * (disp.r*intensity), uv.y);

        vec4 _currentImage = texture2D(currentImage, distortedPosition);
        vec4 _nextImage = texture2D(nextImage, distortedPosition2);

        vec4 finalTexture = mix(_currentImage, _nextImage, dispFactor);

        gl_FragColor = finalTexture;
    }
`;

// Scene
let scene = new THREE.Scene();
const imgs = Array.from(document.querySelectorAll('.slide-img'));
const sliderImages=[]
let renderWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
let renderHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);

const camera = new THREE.PerspectiveCamera(
    60,
    renderWidth / renderHeight,
    1,
    100
)
camera.position.z = 1

// Renderer
let renderer = new THREE.WebGLRenderer({
    antialias: true,
});
// Renderer opts
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setClearColor( 0x23272A, 1.0 );
renderer.setSize( renderWidth, renderHeight );


// add the renderer to the dom
sliderContainer.appendChild( renderer.domElement );


let textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = "anonymous";

imgs.forEach( ( img ) => {
    let image = textureLoader.load( img.getAttribute( 'src' ));
    image.magFilter = image.minFilter = THREE.LinearFilter;
    image.anisotropy = renderer.capabilities.getMaxAnisotropy();
    sliderImages.push( image );
});

let dispImg = textureLoader.load('https://kasperdebruyne.be/img/freesolo/18.jpg');

// let dispImg = textureLoader.load('https://kasperdebruyne.be/img/freesolo/23.jpg');
// let dispImg = textureLoader.load('https://kasperdebruyne.be/img/freesolo/32.jpg');


dispImg.wrapS = dispImg.wrapT = THREE.RepeatWrapping;

let mat = new THREE.ShaderMaterial({
    uniforms: {
        dispFactor: { type: "f", value: 0.0 },
        currentImage: { type: "t", value: sliderImages[0] },
        nextImage: { type: "t", value: sliderImages[1] },
        disp: { type: "t", value: dispImg },
    },
    vertexShader: vertex,
    fragmentShader: fragment,
    transparent: true,
    opacity:1.0
});

let geometry = new THREE.PlaneBufferGeometry(
    2.4,1.16
);

let object = new THREE.Mesh(geometry, mat);
object.position.set(0, 0, 0);
scene.add(object);

window.addEventListener('resize',() => {
    renderer.setSize(window.innerWidth,window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
})

let animate = () => {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
};

animate();

let index = 0;
const slider = document.querySelector('.slider-inner');
const slide = document.querySelectorAll('.slide')
const slideImg = document.querySelector('.slide-background-img')
const next = document.querySelector(".slider-next-btn");
const prev = document.querySelector(".slider-prev-btn");
const slideNum = document.querySelector('.slide-indicator');
const description = document.querySelectorAll('.slide-description');
const title = document.querySelectorAll('.slide-title');
const descriptionArray = Array.from(description);

// Position all description elements 
TweenMax.set(descriptionArray[2],{y:"-100%"})
TweenMax.set(descriptionArray[3],{y:"-200%"})
// Slide to the right 
let autoPlay = true;

let isSliding = false;

let slideTl = (transformVal,transformVal2,elIndex) => {
    const tl = new TimelineMax({
        onStart:()=> {
            isSliding = true
        },
        onComplete:() => {
            isSliding = false
        }
    });
    tl.to(slider,0.85,{x:transformVal,ease:Sine.easeInOut},'slide')
    .to(slideNum,0.85,{y:transformVal2,ease:Sine.easeInOut},'slide')
    .fromTo(descriptionArray[elIndex],0.25,{y:0},{y:"+=100%",ease:Circ.easeIn},'slide')
    .fromTo(descriptionArray[index],0.3,{y:0},{y:"-=100%"},'slide+=0.75')
    .set(title[elIndex],{className:"-=active"},'slide')
    .set(title[index],{className:"+=active"},'slide')    
    return tl;
}   
const animateDisplace = () => {
    mat.uniforms.nextImage.value = sliderImages[index];
    mat.uniforms.nextImage.needsUpdate = true;

    TweenLite.to( mat.uniforms.dispFactor, 1, {
        value: 1,
        ease: 'Sine.easeInOut',
        onComplete: () => {
            mat.uniforms.currentImage.value = sliderImages[index];
            mat.uniforms.currentImage.needsUpdate = true;
            mat.uniforms.dispFactor.value = 0.0;   
        }
    });
}
const nextSlide = () => {  
    if (isSliding == false) {
        index ++
        slideTl("-=100%","-=25%",index - 1);
        animateDisplace();
    }    
}

const prevSlide = () => {
    if (isSliding == false) {
        index --
        slideTl("+=100%","+=25%",index + 1);
        animateDisplace();
    } 
}

// ANIMATE SLIDE INDICATOR
const progressEase = "M0,0 C0.092,0.01 0.162,0.108 0.212,0.21 0.328,0.448 0.458,1 1,1";
const progressBar = CSSRulePlugin.getRule('.slide-progress-bar:after');
const progressTl = (delay) => {
    const tl = new TimelineMax({
        repeat:-1,
        repeatDelay:1.5,
        paused:true,
        delay:delay
    });
    tl.fromTo(progressBar,2,{cssRule:{x:"-100%"}},{cssRule:{x:"0%"},ease:CustomEase.create("custom",progressEase)})
        .add(() => {
            if (index <= 2 && autoPlay === true) {
                nextSlide();
                TweenMax.to(progressBar,1,{cssRule:{x:"100%"},ease:CustomEase.create("custom",progressEase)})
            }
            if (index === 3 && autoPlay === true) {
                tl.stop();
            }
        })
    return tl;
}
const animateProgress = progressTl(0.4);

// Reset progress bar on hover
const resetProgress = () => {
    if (index < 3 && autoPlay === true) {
        TweenMax.to(animateProgress,
            1 * animateProgress.progress(),{
                progress:0,
                onComplete:()=> {
                    animateProgress.progress(0)
                    animateProgress.stop();
                }
            }
        )
    }
}
// Change Timeline state 
const changeState = (timeline) => {
    if (timeline.progress() == 1) {
        timeline.reverse()
    }
    else {
        timeline.play()
    } 
}

// ANIMATE SEARCH ICON
const searchBtn = document.querySelector('.search-btn')
const searchInput = document.querySelector('.search-input')
const searchTl = () => {
    const tl = new TimelineLite({paused:true});
    tl.to(searchInput,0.15,{width:"225px",fontSize:"16px"},'in')

    return tl;
}
const animateSearch = searchTl()

// ANIMATE MENU ICON
const menuBtn = document.querySelector('.menu-btn')
const menu = document.querySelector('.menu');
const menuTl = () => {
    const tl = new TimelineLite({paused:true})
    tl.to(menu,0.2,{x:0,ease:Sine.easeInOut},'in')
    return tl;
}
const animateMenu = menuTl()

// Click Event listener slider buttons
next.addEventListener('click',() => {
    if (index < 3) {
        nextSlide();
    } 
})
prev.addEventListener('click',() => {
    if (index > 0) {
        prevSlide();
    }
})
// Hover Event listeners for buttons
// reset or restart progress bar animation on enter/leave
next.addEventListener('mouseenter',() => {
    resetProgress();   
})
prev.addEventListener('mouseenter',() => {
    resetProgress();  
})

next.addEventListener('mouseleave',() => {
    animateProgress.progress(0)
    animateProgress.restart()
})
prev.addEventListener('mouseleave',() => {
    animateProgress.progress(0)
    animateProgress.restart()
})

// Search event listener
// toggle search
searchBtn.addEventListener('click',() =>{
    changeState(animateSearch)
})
// Menu event Listener
// toggle menu
menuBtn.addEventListener('click',() => {
    changeState(animateMenu)
})

// IMAGESLOADED
const loader = document.querySelector('.preloader'),
loaderOutline = document.querySelector('#preloader-outline'),
loaderBot = document.querySelector('#preloader-bot'),
snow = document.querySelectorAll('#snowGroup path');

TweenMax.set('svg',{visibility:"visible"})
TweenMax.set(loaderOutline,{drawSVG:"0% 0%"})
TweenMax.set(snow,{drawSVG:"0% 0%"})
TweenMax.set(loaderBot,{drawSVG:"50% 50%"})

imgToLoad = document.querySelectorAll('img , canvas');
imgLoad = imagesLoaded(imgToLoad)

let loadedCount = 0
let loadingProgress = 0

const imgLoadTl = new TimelineMax({
    onComplete:() => {
        TweenLite.to(loader,0.4,{opacity:0})
        TweenLite.set(loader,{visibility:"hidden",delay:0.4})
        // Start sliding -- animate the progress bar 
        animateProgress.play();
    }
});
// Animate the logo 
imgLoadTl
    .to(loaderOutline,1,{drawSVG:"0% 100%"},'in')
    .to(loaderBot,1.5,{drawSVG:"0% 100%"},'in')
    .staggerTo(snow,0.4,{drawSVG:"0% 100%"},0.35,'in+=0.45')

imgLoad.on('progress',() => {
    loadedCount++
    let loadingProgress = loadedCount / imgToLoad.length;
    // Animate the progress of the logo animation
    TweenLite.to(imgLoadTl,0.5,{progress:loadingProgress,ease:Linear.easeNone})
});

Дополнительные скрипты:

https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js

https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js

https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/CustomEase.min.js

https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/plugins/CSSRulePlugin.min.js

https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/DrawSVGPlugin.min.js

https://cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/4.1.4/imagesloaded.pkgd.min.js

Комментарии ()

    Написать комментарий