Select Page

Here is the JS code for the starter JS for the landing page, it includes a header stick on scroll, replace image with SVG, fade-in animation, number animation, parallax animation functions, and much more.

Vendors: libraries required

<!-- Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css" integrity="sha512-tS3S5qG0BlhnQROyJXvNjeEM4UpMXHrQfTGmbQ1gKmelCxlSEBUaxhRBj/EFTzpbP4RVSrpEikbmdJobCvhE3g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  <!-- Custom CSS  -->
  <link rel="stylesheet" href="./assets/styles/landing.css" />

  <!-- jQuery cdn -->
  <script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
  <!-- GSAP  -->
  <script src="./assets/scripts/GSAP/gsap.min.js"></script>
  <script src="./assets/scripts/GSAP/ScrollToPlugin.min.js"></script>
  <script src="./assets/scripts/GSAP/ScrollTrigger.min.js"></script>
  <script src="./assets/scripts/GSAP/ScrollSmoother.min.js"></script>
  <script src="./assets/scripts/GSAP/TextPlugin.min.js"></script>
  <script src="./assets/scripts/GSAP/SplitText.min.js"></script>
  <!-- Bootstrap JS  -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  <!-- OWL Carousel -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js" integrity="sha512-bPs7Ae6pVvhOSiIcyUClR7/q2OAsRiovw4vAkX+zJbw3ShAeeqezq50RIIcIURq7Oa20rW2n2q+fyXBNcU9lrw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/atom-one-dark.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>

  <!-- Custom JS  -->
  <script src="./assets/scripts/landing.js"></script>

Landing.js

let vw = Math.max(document.documentElement.clientWidth);
let vh = Math.max(document.documentElement.clientHeight);
console.log( 'Viewport Dimension: ' + vw + ' X ' + vh);

// Register GSAP plugins
const initGSAP = (normalise = true) => {
  gsap.registerPlugin(ScrollTrigger, ScrollToPlugin, ScrollSmoother, SplitText, TextPlugin);
  ScrollTrigger.normalizeScroll(normalise);
}

// Page smooth scrolling
const enableScrollSmoother = () => {
  ScrollSmoother.create({
    smooth: 1,
    effects: true,
    smoothTouch: 0.1,
  });
}

// Remove uwanted elements in mobile and desktop versions
const removeUnwantedElements = () => {
  if(vw >= 992){
    $('.mob-only').remove();
  }else{
    $('.desk-only').remove();
  }
}

// Reload page if resized
const reloadPageIfAbove1200 = () => {
  window.addEventListener("resize", function(){
    if (window.innerWidth > 1200) {
      location.reload();
    }
  });
}

// Fade animation - use selector: .fade-in
const initFadeInAnimation = () => {
  const fadeElements = document.querySelectorAll(".fade-in");
  
  fadeElements.forEach((element) => {
    let classesArray = Array.from(element.classList);
    let matchingClass = classesArray.find(className => className.startsWith("fade-in-delay-"));
    let delayValue = 0;
    if (matchingClass) {
      delayValue = matchingClass.slice("fade-in-delay-".length);
    }
    
    gsap.fromTo(element, {
        opacity: 0, 
        y: 100,
      }, {
        opacity: 1,
        y: 0,
        duration: 1,
        delay: delayValue,

        scrollTrigger: {
          trigger: element,
          start: "top bottom",
          end: "bottom 0%",
          toggleActions: "play none none reverse",
        },
    });
  });
}

/* parallex effect */
const parallexTopAnim = () => {
  var initialVal = 400;
  var finalVal = -200;
  if( vw < 768 ){
    var initialVal = 200;
    var finalVal = -100;
  }

  const parallexElements = document.querySelectorAll(".parallex-top");
  
  parallexElements.forEach((element) => {
    
    gsap.fromTo(element, {
        y: initialVal,
      }, {
        y: finalVal,

        scrollTrigger: {
          trigger: element,
          start: "top bottom",
          end: "bottom top",
          toggleActions: "play none none reverse",
          scrub: 1,
        },
    });
  });
}

const parallexBottomAnim = () => {
  var initialVal = 0;
  var finalVal = 200;
  if( vw < 1400 ){
     initialVal = 0;
     finalVal = 125;
  }
  if( vw < 768 ){
     initialVal = 0;
     finalVal = 100;
  }

  const parallexElements = document.querySelectorAll(".parallex-bottom");
  
  parallexElements.forEach((element) => {
    
    gsap.fromTo(element, {
        y: initialVal,
      }, {
        y: finalVal,

        scrollTrigger: {
          trigger: element,
          start: "top bottom",
          end: "bottom top",
          toggleActions: "play none none reverse",
          scrub: 1,
        },
    });
  });
}



// Check section visible on viewport
const checkSectionActive = () => {
  const sectionElements = document.querySelectorAll(".section");
  
  sectionElements.forEach((element) => {
    
    gsap.fromTo(element, {

    }, 
    {
        scrollTrigger: {
          trigger: element,
          start: "top bottom",
          end: "bottom top",
          scrub: true,
          toggleActions: "restart complete none none",

          onEnter: function(){
            $(element).addClass('section--active');
          },

          onEnterBack: function(){
            $(element).addClass('section--active');
          },

          onLeave: function(){
            $(element).removeClass('section--active');
          },

          onLeaveBack: function(){
            $(element).removeClass('section--active');
          },
        },

    });
  });
}

// Check section visible on viewport
const checkViewportActive = () => {
  const sectionElements = document.querySelectorAll(".check-viewport");
  
  sectionElements.forEach((element) => {
    
    gsap.fromTo(element, {

    }, 
    {
        scrollTrigger: {
          trigger: element,
          start: "top bottom",
          end: "bottom top",
          scrub: true,
          toggleActions: "restart complete none none",

          onEnter: function(){
            $(element).addClass('viewport-active');
          },

          onEnterBack: function(){
            $(element).addClass('viewport-active');
          },

          onLeave: function(){
            $(element).removeClass('viewport-active');
          },

          onLeaveBack: function(){
            $(element).removeClass('viewport-active');
          },
        },

    });
  });
}


/* Check if all SVGs got loaded */
function executeAfterSVGsLoaded(className, callback) {
  const svgs = document.querySelectorAll(`.${className}`);
  let unloadedSVGs = svgs.length;
  
  function handleSVGLoad() {
    unloadedSVGs--;
    if (unloadedSVGs === 0) {
      // console.log('All SVGs with class "' + className + '" loaded successfully..');
      callback();
    }
  }

  for (const svg of svgs) {
    if (svg.complete) {
      handleSVGLoad();
    } else {
      svg.addEventListener("load", handleSVGLoad());
    }
  }
}

// equalizeHeights
const equalizeHeights = () => {

  const parents = document.querySelectorAll('.equalize-columns');

  parents.forEach(parent => {
    const elements = parent.querySelectorAll('.equalize');
    let maxHeight = 0;

    // Reset heights to 'auto' before recalculating
    elements.forEach(element => {
      element.style.height = 'auto';
      element.style.minHeight = 'auto';
    });

    // Find the maximum height for this parent
    elements.forEach(element => {
      maxHeight = Math.max(maxHeight, element.offsetHeight);
    });

    // Set all elements in this parent to the maximum height
    elements.forEach(element => {
      element.style.minHeight = maxHeight + 'px';
    });
  });
  
  window.addEventListener('resize', function() {
    equalizeHeights();
  });
}



// Replace image tag with svg
const replaceWithSVG = () => {
  let imgSelector = '.replaceWithSVG';

  $(imgSelector).each(function(index, element) {
    const totalElements = $(imgSelector).length;
    let classes = $(element).attr('class');
    if( $(element).attr('src') || $(element).attr('data-src') ){
      let imgsrc = '';
      if( $(element).attr('src') ){
        imgsrc = $(element).attr('src');
      }
      if( $(element).attr('data-src') ){
        imgsrc = $(element).attr('data-src');
      }

      fetch( imgsrc )
      .then((response) => response.text())
      .then((svgCode) => {
        let tempElement = $(svgCode);
        tempElement.addClass( classes );
        $(element).after(tempElement);
        $(element).remove();
        if (index === totalElements - 1) {
          setTimeout(function(){
            executeAfterSVGsLoaded("replaceWithSVG", function () {
              setTimeout(function(){
                console.log( $(imgSelector).length + ' image/s replaced with SVG' );
                runAfterSvgLoad();
              }, 1000);
            });
          }, 1000);
        }
      })
      .catch((error) => {
        console.error("Error fetching SVG image:", error);
      });
    }else{
      console.log( $(imgSelector).length + ' image/s already replaced with SVG' );
    }
  });
  
  if($(imgSelector).length == 0){
    setTimeout(function(){
      console.log( $(imgSelector).length + ' image/s replaced with SVG' );
      runAfterSvgLoad();
    }, 1000);
  }
}



// Smooth scroll to section on anchor click use id of section as src value
const smoothScrollToSection = () => {
  // Smooth scroll to section
  let headerHeight = $('.headerSec').height() - 50;
  let targetPosition = 0;

  if( window.location.hash && $(window.location.hash).length > 0) { // Checks # on URL
    targetPosition = $(window.location.hash).offset().top - headerHeight;
    gsap.to(window, {duration: 2, scrollTo:  targetPosition });
  }

  $("a").click(function( event ) {
    let href = $(this).attr('href');
    let hash = href.substring(href.indexOf('#') + 1);
    if(hash){
      if ($('#' + hash).length) {
        event.preventDefault();
        $(this).closest('ul').find('a').removeClass('active');
        $(this).closest('a').addClass('active');
        targetPosition = $('#' + hash).offset().top - headerHeight;
        gsap.to(window, {duration: 2, scrollTo:  targetPosition });
      }
    }
  });
}

// Let us know weather scroll is up or down
const handleScrollDirection = () => {
  let lastScrollTop = 0;

  // Function to handle the mouse scroll event and toggle the class
  function handleMouseScroll() {
    const elements = document.querySelectorAll('body'); // Replace 'your-target-class' with the class name of the elements you want to apply the class toggle to

    const st = window.pageYOffset || document.documentElement.scrollTop;

    elements.forEach(element => {
      const scrollDirection = st > lastScrollTop ? 1 : -1;

      // Toggle the class based on the scroll direction
      if (scrollDirection === 1) {
        element.classList.remove('upwards'); // Replace 'your-class' with the class name you want to add when scrolling downwards
        element.classList.add('downwards');
      } else if (scrollDirection === -1) {
        element.classList.remove('downwards');
        element.classList.add('upwards'); // Replace 'your-class' with the class name you want to remove when scrolling upwards
      }
    });

    lastScrollTop = st <= 0 ? 0 : st; // Save the current scroll position for the next scroll event
  }

  // Add an event listener to the window to detect mouse scroll
  window.addEventListener('scroll', handleMouseScroll);
}

// Menu script
const menuSecScripts = () => {
  /* menu-btn */
  $('.menu-btn, .menuSec__links a').click(function(){
    $('.menuSec').addClass('animating');
    $('body').toggleClass('no-scroll');
    $('.menuSec').toggleClass('active');
    setTimeout(function(){
      $('.menuSec').removeClass('animating');
    }, 1000)
  })
}



// Toggle active header on scroll
const headerSection = () => {
  // Toggle class on header on scroll
  if ($(this).scrollTop() > 50 ) {
    $('.headerSec').addClass('scrolling');
  }else{
    $('.headerSec').removeClass('scrolling');
  }
  $(window).scroll(function() {
    if ($(this).scrollTop() > 50 ) {
      $('.headerSec').addClass('scrolling');
    }else{
      $('.headerSec').removeClass('scrolling');
    }
  });

  // Fixed Navbar
  $(document).ready(function () { 
    let height = $('.top-header').height();
    $('.headerSec').css('top', height + 'px');
  });
  $(window).on('scroll', function () { 
    let height = $('.top-header').height();

    if ($(this).scrollTop() > height) {
        $('.sticky-top').css('top', '0px');
        $('.sticky-top nav').addClass('shadow-sm');
    } else {
        $('.sticky-top').css('top', height + 'px');
        $('.sticky-top nav').removeClass('shadow-sm')
    }
  });
}


// Animate numbers
const animateNumbers = () => {
  const numberElements = document.querySelectorAll(".animate-number");

  numberElements.forEach((element) => {
    gsap.fromTo(
      element,
      {},
      {
        scrollTrigger: {
          trigger: element,
          start: "top bottom",
          end: "bottom top",
          toggleActions: "play none none reverse",
          onEnter: function () {
            $(element)
              .prop("Counter", 0)
              .animate(
                {
                  Counter: $(element).data("number"),
                },
                {
                  duration: 1500,
                  easing: "swing",
                  step: function (now) {
                    $(element).text(Math.ceil(now));
                  },
                }
              );
          },
        },
      }
    );
  });
};



// Initialise all functions
const initAllFunctions = () =>  {
  initGSAP(false);
  //removeUnwantedElements();
  //reloadPageIfAbove1200();
  replaceWithSVG();
  menuSecScripts();
  headerSection();

  if  (vw >= 1200){
    //enableScrollSmoother();
  }
}

// Run animations after SVG load
const runAfterSvgLoad = () => {
  smoothScrollToSection();
  initFadeInAnimation();
  checkSectionActive();
  checkViewportActive();
  parallexTopAnim();
  parallexBottomAnim();
  equalizeHeights();

  // handleScrollDirection();
  
  if(vw >= 992){
    // Desktop specific animations
  }else{
    // Mobile specific animations
  }
}

// Initialise Fuctions on Document Ready
$(document).ready(function() {
  initAllFunctions(); 
});



/* Range Slider */
const rangeSlider = (slider_id) => {
  var slider = document.getElementById(slider_id);
  var sliderIndicator = slider.parentElement.querySelector('.slider__indicator');
  sliderIndicator.style.width = slider.value + "%";
  
  slider.addEventListener("input", function() {
    var percent = (slider.value - slider.min) / (slider.max - slider.min) * 100;
    sliderIndicator.style.width = percent + "%";
  });
}


// Sort buttons
const sortButton = () => {
  var sortButtonsAll = document.querySelector('.sortButtons__all');
    
    sortButtonsAll.addEventListener('click', function(event) {
      var target = event.target;

      // Toggle classes based on the current state
      if (!target.classList.contains('active-up') && !target.classList.contains('active-down')) {
        // If the element has no class, remove all "active-up" and "active-down" classes from other divs
        var allSortButtons = sortButtonsAll.children;
        for (var i = 0; i < allSortButtons.length; i++) {
          allSortButtons[i].classList.remove('active-up', 'active-down');
        }
        target.classList.add('active-up');
      } else if (target.classList.contains('active-up')) {
        // If the element has "active-up" class, change it to "active-down" class
        target.classList.remove('active-up');
        target.classList.add('active-down');
      } else if (target.classList.contains('active-down')) {
        // If the element has "active-down" class, change it to "" class (empty)
        target.classList.remove('active-down');
      }
    });
}

// Datatable
const invokeDatatable1 = (id, searchid) => {
  let datatable = new DataTable( id , {
    responsive: true,
  });

  $(searchid).on('keyup', function() {
    datatable.search(this.value).draw();
  });
}