1( function( $ ) { 2 'use strict'; 3 4 /*! ------------------------------------------------ 5 * Project Name: Rayo - Digital Agency & Personal Portfolio HTML Template 6 * Project Description: Elevate your digital presence with Rayo - dynamic and stylish HTML template designed for creative agencies and personal brands. With modern layouts, smooth interactions and a polished aesthetic, Rayo template helps showcase projects, services and expertise with clarity and impact. 7 * Tags: mix_design, resume, portfolio, personal page, cv, template, one page, responsive, html5, css3, creative, clean, agency, studio 8 * Version: 1.0.0 9 * Build Date: July 2025 10 * Last Update: July 2025 11 * This product is available exclusively on Themeforest 12 * Author: mix_design 13 * Author URI: https://themeforest.net/user/mix_design 14 * File name: app.js 15 * ------------------------------------------------ 16 17 * ------------------------------------------------ 18 * Table of Contents 19 * ------------------------------------------------ 20 * 21 * 01. Loader & Loading Animation 22 * 02. Lenis Scroll Plugin 23 * 03. Typed.js Plugin 24 * 04. Header Scroll Behavior 25 * 05. Hero #02 Scroll Out Animation 26 * 06. Hero #07 Scroll Out Animation 27 * 07. Hero #08 Scroll Out Animation 28 * 08. SVG Fallback 29 * 09. Chrome Smooth Scroll 30 * 10. Images Moving Ban 31 * 11. Detecting Mobile/Desktop 32 * 12. Smooth Scrolling 33 * 13. Menu & Hamburger 34 * 14. Menu Accordion 35 * 15. Layout Masonry 36 * 16. Accordion 37 * 17. Magnific Popup Video 38 * 18. Mailchimp Subscribe Form 39 * 19. Contact Form 40 * 20. Parallax - Ukiyo Images & Video 41 * 21. Pinned Images 42 * 22. Stacking Cards 43 * 23. Animation - Buttons Common 44 * 24. Animation - Text Reveal 45 * 25. Animation - Scroll Universal 46 * 26. Animation - Images Reveal on Hover 47 * 27. Swiper Slider - Testimonials #01 48 * 28. Swiper Slider - Testimonials #02 49 * 29. Swiper Slider - Inner Pages Demo 50 * 30. CountUp - All Counters Options 51 * 31. Marquee - Two Lines 52 * 32. Marquee - One Line To Right 53 * 33. Marquee - One Line To Left 54 * 34. SVG DOM Injection 55 * 35. Color Switch 56 * 36. Scroll to Top Button 57 * 37. Parallax Universal 58 * 59 * ------------------------------------------------ 60 * Table of Contents End 61 * ------------------------------------------------ */ 62 63gsap.registerPlugin(ScrollTrigger); 64gsap.registerPlugin(Flip); 65 66// --------------------------------------------- // 67// Lenis Scroll Plugin Start 68// --------------------------------------------- // 69const lenis = new Lenis(); 70lenis.on('scroll', ScrollTrigger.update); 71gsap.ticker.add((time) => { 72 lenis.raf(time * 1000) 73}); 74gsap.ticker.lagSmoothing(0); 75// --------------------------------------------- // 76// Lenis Scroll Plugin End 77// --------------------------------------------- // 78 79$(window).on("scroll", function() { 80 81 // --------------------------------------------- // 82 // Header Scroll Behavior Start 83 // --------------------------------------------- // 84 if($(window).scrollTop() > 10) { 85 $(".mxd-header").addClass("is-hidden"); 86 } else { 87 $(".mxd-header").removeClass("is-hidden"); 88 } 89 // --------------------------------------------- // 90 // Header Scroll Behavior End 91 // --------------------------------------------- // 92 93}); 94 95// --------------------------------------------- // 96// Hero #02 Scroll Out Animation Start 97// --------------------------------------------- // 98// Hero #02 scroll animated elements 99const createScrollEffect1 = (selector, trigger, start, end, from, to, ease = "sine") => { 100 document.querySelectorAll(selector).forEach(el => { 101 gsap.timeline({ 102 scrollTrigger: { trigger, start, end, scrub: true, ease } 103 }).fromTo(el, from, to); 104 }); 105}; 106 107// Hero #02 main elements 108createScrollEffect1(".hero-02-static-anim-el", ".hero-02-static__tl-trigger", 109 "top 14%", "top 0.2%", 110 { y: 0, scaleY: 1, opacity: 1 }, 111 { y: -80, scaleY: 1.3, opacity: 0 } 112); 113 114// Hero #02 pinned elements 115createScrollEffect1(".hero-02-fade-out-scroll", ".mxd-pinned-fullscreen__tl-trigger", 116 "top 80%", "top 10%", 117 { opacity: 1 }, 118 { opacity: 0 } 119); 120 121// --------------------------------------------- // 122// Hero #02 Scroll Out Animation End 123// --------------------------------------------- // 124 125// --------------------------------------------- // 126// Hero #07 Scroll Out Animation Start 127// --------------------------------------------- // 128const createScrollEffect = (selector, trigger, start, end, from, to, ease = "power4.out") => { 129 document.querySelectorAll(selector).forEach(el => { 130 gsap.timeline({ 131 scrollTrigger: { trigger, start, end, scrub: true, ease } 132 }).fromTo(el, from, to); 133 });
134}; 135 136// Slide-out mạnh 137createScrollEffect(".hero-07-slide-out-scroll", ".mxd-hero-07__tl-trigger", 138 "top 86%", "top 10%", 139 { y: 0, scaleY: 1 }, 140 { y: -416, scaleY: 0.8 } 141); 142 143// Fade-out nhẹ 144createScrollEffect(".hero-07-fade-out-scroll", ".mxd-hero-07__tl-trigger", 145 "top 70%", "top 40%", 146 { y: 0, opacity: 1 }, 147 { y: -160, opacity: 0 }, 148 "elastic.out(1,0.3)" 149); 150// --------------------------------------------- // 151// Hero #07 Scroll Out Animation End 152// --------------------------------------------- // 153 154// --------------------------------------------- // 155// Hero #08 Scroll Out Animation Start 156// --------------------------------------------- // 157const createScrollOut = (selector, trigger, start, end, from, to) => { 158 document.querySelectorAll(selector).forEach(el => { 159 gsap.timeline({ 160 scrollTrigger: { trigger, start, end, scrub: true, ease: "power4.inOut" } 161 }).fromTo(el, from, to); 162 }); 163}; 164 165// Fade out 166createScrollOut(".hero-08-slide-out-scroll", ".mxd-hero-08__tl-trigger", "top 80%", "top 40%", 167 { y: 0, opacity: 1 }, 168 { y: -80, opacity: 0 } 169); 170 171// Scale out 172createScrollOut(".hero-08-scale-out-scroll", ".mxd-hero-08__tl-trigger", "top 40%", "top 10%", 173 { y: 0, scaleY: 1, opacity: 1 }, 174 { y: -80, scaleY: 1.2, opacity: 0 } 175); 176// --------------------------------------------- // 177// Hero #08 Scroll Out Animation End 178// --------------------------------------------- // 179 180 181$(function() { 182 183 // --------------------------------------------- // 184 // Chrome Smooth Scroll Start 185 // --------------------------------------------- // 186 try { 187 $.browserSelector(); 188 if($("html").hasClass("chrome")) { 189 $.smoothScroll(); 190 } 191 } catch(err) { 192}; 193// --------------------------------------------- // 194// Chrome Smooth Scroll End 195// --------------------------------------------- // 196 197// --------------------------------------------- // 198// Images Moving Ban Start 199// --------------------------------------------- // 200$("img, a").on("dragstart", function(event) { event.preventDefault(); }); 201// --------------------------------------------- // 202// Images Moving Ban End 203// --------------------------------------------- // 204 205// --------------------------------------------- // 206// Detecting Mobile/Desktop Start 207// --------------------------------------------- // 208var isMobile = false; 209if( /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { 210 $('html').addClass('touch'); 211 isMobile = true; 212} 213else { 214 $('html').addClass('no-touch'); 215 isMobile = false; 216} 217//IE, Edge 218var isIE = /MSIE 9/i.test(navigator.userAgent) || /rv:11.0/i.test(navigator.userAgent) || /MSIE 10/i.test(navigator.userAgent) || /Edge\/\d+/.test(navigator.userAgent); 219// --------------------------------------------- // 220// Detecting Mobile/Desktop End 221// --------------------------------------------- // 222 223// --------------------------------------------- // 224// Smooth Scrolling Start 225// --------------------------------------------- // 226$('a[href*="#"]').not('[href="#"]').not('[href="#0"]').click(function(event) { 227 if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) { 228 var target = $(this.hash); 229 target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 230 if (target.length) { 231 event.preventDefault(); 232 $('html, body').animate({ 233 scrollTop: target.offset().top 234 }, 1000, function() { 235 var $target = $(target); 236 $target.focus(); 237 if ($target.is(":focus")) { 238 return false; 239 } else { 240 $target.attr('tabindex','-1'); 241 $target.focus(); 242 }; 243 }); 244 } 245 } 246}); 247// --------------------------------------------- // 248// Smooth Scrolling End 249// --------------------------------------------- // 250 251// --------------------------------------------- // 252// Menu & Hamburger Start 253// --------------------------------------------- // 254$(".mxd-nav__wrap").each(function() { 255 let navWrap = $(this); 256 let hamburgerEl = $(this).find(".mxd-nav__hamburger"); 257 let navLineEl = $(this).find(".hamburger__line"); 258 let menuContainEl = $(this).find(".mxd-menu__contain"); 259 let flipItemEl = $(this).find(".hamburger__base"); 260 let menuWrapEl = $(this).find(".mxd-menu__wrapper"); 261 let menuBaseEl = $(this).find(".mxd-menu__base"); 262 let menuLinkEl = $(this).find(".mxd-menu__link"); 263 let menuItem = $(this).find(".main-menu__item"); 264 let videoEl = $(this).find(".menu-promo__video video"); 265 let fadeInEl = $(this).find(".menu-fade-in"); 266 let flipDuration = 0.6; 267 268 function flip(forwards) { 269 let state = Flip.getState(flipItemEl); 270 if (forwards) { 271 flipItemEl.appendTo(menuContainEl); 272 } else { 273 flipItemEl.appendTo(hamburgerEl); 274 } 275 Flip.from(state, { ease: "power4.inOut", duration: 0.8 });
276 } 277 278 let tl = gsap.timeline({ paused: true }); 279 tl.set(menuWrapEl, { display: "flex" }); 280 tl.from(menuBaseEl, { 281 opacity: 0, 282 duration: flipDuration, 283 ease: "none", 284 onStart: () => { 285 flip(true); 286 } 287 }); 288 tl.to(navLineEl.eq(0), { y: 5, duration: 0.16 }, "<") 289 tl.to(navLineEl.eq(1), { y: -5, duration: 0.16 }, "<") 290 tl.to(navLineEl.eq(0), { rotate: 45, duration: 0.16 }, 0.2) 291 tl.to(navLineEl.eq(1), { rotate: -45, duration: 0.16 }, 0.2) 292 tl.add("fade-in-up") 293 .from(menuItem, { 294 opacity: 0, 295 yPercent: 50, 296 duration: 0.2, 297 stagger: {amount: 0.2}, 298 onReverseComplete: () => { 299 flip(false); 300 } 301 }, "fade-in-up") 302 .from(menuLinkEl, { 303 opacity: 0, 304 yPercent: 20, 305 duration: 0.2, 306 }, "fade-in-up") 307 .from(videoEl, { 308 opacity: 0, 309 yPercent: 20, 310 duration: 0.2, 311 }, "fade-in-up"); 312 tl.from(fadeInEl, { opacity: 0, duration: 0.3, }); 313 314 function openMenu(open) { 315 if (!tl.isActive()) { 316 if (open) { 317 tl.play(); 318 navWrap.addClass("wrap-open"); 319 hamburgerEl.addClass("nav-open"); 320 } else { 321 tl.reverse(); 322 navWrap.removeClass("wrap-open"); 323 hamburgerEl.removeClass("nav-open"); 324 } 325 } 326 } 327 328 hamburgerEl.on("click", function() { 329 event.preventDefault(); 330 if ($(this).hasClass("nav-open")) { 331 openMenu(false); 332 } else { 333 openMenu(true); 334 } 335 }); 336 menuBaseEl.on("click", function () { 337 openMenu(false); 338 }); 339 $(document).on("keydown", function (e) { 340 if (e.key === "Escape") { 341 openMenu(false); 342 } 343 }); 344 345 window.addEventListener("beforeunload", (event) => { 346 openMenu(false); 347 }); 348 349}); 350 // --------------------------------------------- // 351 // Menu & Hamburger End 352 // --------------------------------------------- // 353 354 // --------------------------------------------- // 355 // Header/Menu Z-index Change Start 356 // --------------------------------------------- // 357 $(".mxd-nav__hamburger").on("click", function() { 358 if ($(".mxd-nav__hamburger").hasClass("nav-open")) { 359 $(".mxd-header").addClass("menu-is-visible"); 360 } else { 361 setTimeout(function() { 362 $(".mxd-header").removeClass("menu-is-visible"); 363 }, 1100); 364 } 365 }); 366 // --------------------------------------------- // 367 // Header/Menu Z-index Change End 368 // --------------------------------------------- // 369 370 // --------------------------------------------- // 371 // Menu Accordion Start 372 // --------------------------------------------- // 373 var Accordion = function(el, multiple) { 374 this.el = el || {}; 375 this.multiple = multiple || false; 376 var links = this.el.find('.main-menu__toggle'); 377 links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown) 378 } 379 Accordion.prototype.dropdown = function(e) { 380 var $el = e.data.el, 381 $this = $(this), 382 $next = $this.next(); 383 $next.slideToggle(); 384 $this.parent().toggleClass('open'); 385 if (!e.data.multiple) { 386 $el.find('.submenu').not($next).slideUp().parent().removeClass('open'); 387 }; 388 } 389 var accordion = new Accordion($('#main-menu'), false); 390 // --------------------------------------------- // 391 // Menu Accordion End 392 // --------------------------------------------- // 393 394 // --------------------------------------------- // 395 // Layout Masonry After Each Image Loads Start 396 // --------------------------------------------- // 397 $('.mxd-projects-masonry__gallery').imagesLoaded().progress( function() { 398 $('.mxd-projects-masonry__gallery').masonry('layout'); 399 ScrollTrigger.refresh(); 400 }); 401 // --------------------------------------------- // 402 // Layout Masonry After Each Image Loads End 403 // --------------------------------------------- // 404 405}); 406 407// --------------------------------------------- // 408// Parallax - Ukiyo Images & Video Start 409// --------------------------------------------- // 410const images = document.querySelectorAll(".parallax-img"); 411const imagesSmall = document.querySelectorAll(".parallax-img-small"); 412const video = document.querySelectorAll(".parallax-video"); 413new Ukiyo(images,{ 414 scale: 1.5, 415 speed: 1.5, 416 externalRAF: false, 417}); 418imagesSmall.forEach(img => { 419 new Ukiyo(img, { 420 scale: 1.2, 421 speed: 1.5, 422 externalRAF: false 423 });
424 img.dataset.ukiyoInitialized = 'true'; 425}); 426new Ukiyo(video,{ 427 scale: 1.5, 428 speed: 1.5, 429 externalRAF: false 430}); 431// --------------------------------------------- // 432// Parallax - Ukiyo Images & Video End 433// --------------------------------------------- // 434 435// --------------------------------------------- // 436// Animation - Buttons Common Start 437// --------------------------------------------- // 438let elements = document.querySelectorAll(".btn-anim .btn-caption, .rayo-btn-anim .btn-caption"); 439 440elements.forEach((element) => { 441 let innerText = element.innerText; 442 element.innerHTML = ""; 443 let textContainer = document.createElement("div"); 444 textContainer.classList.add("btn-anim__block"); 445 for (let letter of innerText) { 446 let span = document.createElement("span"); 447 span.innerText = letter.trim() === "" ? "\xa0" : letter; 448 span.classList.add("btn-anim__letter"); 449 textContainer.appendChild(span); 450 } 451 element.appendChild(textContainer); 452 element.appendChild(textContainer.cloneNode(true)); 453}); 454 455elements.forEach((element) => { 456 element.addEventListener("mouseover", () => { 457 element.classList.remove("play"); 458 }) 459}); 460// --------------------------------------------- // 461// Animation - Buttons Common End 462// --------------------------------------------- // 463 464// --------------------------------------------- // 465// Scroll to Top Button Start 466// --------------------------------------------- // 467const toTop = document.querySelector(".btn-to-top"); 468 469$(".btn-to-top").each(function() { 470 toTop.addEventListener("click", function(event){ 471 event.preventDefault() 472 }); 473 474 toTop.addEventListener("click", () => gsap.to(window, { 475 scrollTo: 0, 476 ease: 'power4.inOut', 477 duration: 1.3, 478 })); 479 480 gsap.set(toTop, { opacity: 0 }); 481 482 gsap.to(toTop, { 483 opacity: 1, 484 autoAlpha: 1, 485 scrollTrigger: { 486 trigger: "body", 487 start: "top -20%", 488 end: "top -20%", 489 toggleActions: "play none reverse none" 490 } 491 }); 492}); 493// --------------------------------------------- // 494// Scroll to Top Button End 495// --------------------------------------------- // 496 497// ------------------------------------------------------------------------------ // 498// Parallax Universal (apply parallax effect to any element with a data-speed attribute) Start 499// ------------------------------------------------------------------------------ // 500const parallaxEls = document.querySelectorAll("[data-speed]"); 501if (parallaxEls.length > 0) { 502 gsap.to(parallaxEls, { 503 y: (i, el) => { 504 const speedAttr = el.getAttribute("data-speed"); 505 const speed = speedAttr ? parseFloat(speedAttr) : 1; 506 return (1 - speed) * ScrollTrigger.maxScroll(window); 507 }, 508 ease: "none", 509 scrollTrigger: { 510 start: 0, 511 end: "max", 512 scrub: 0, 513 invalidateOnRefresh: true, 514 } 515 }); 516} 517// --------------------------------------------- // 518// Parallax Universal End 519// --------------------------------------------- // 520 521} )( jQuery );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.