- Разработка
- Бесплатный слайдер на главную Solo WebGL
Бесплатный слайдер на главную Solo WebGL
Опубликовано: 24.09.2020
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="post/besplatnyij-slajder-na-glavnuyu-solo-webgl#">About</a></li>
<li><a href="post/besplatnyij-slajder-na-glavnuyu-solo-webgl#">Places</a></li>
<li><a href="post/besplatnyij-slajder-na-glavnuyu-solo-webgl#">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
Комментарии ()
Написать комментарий