document.addEventListener("DOMContentLoaded", function () { const video = document.querySelector("video"); if (!video) return; /* ========================================= VIDEO SETUP ========================================= */ video.pause(); video.autoplay = false; video.removeAttribute("autoplay"); video.muted = true; video.loop = false; /* ========================================= STATE ========================================= */ let progress = 0; let targetTime = 0; let currentTime = 0; let animating = false; let videoComplete = false; let controllingVideo = false; let animationFrame = null; const SPEED = 0.0003; const SMOOTH = 0.1; const TOP_THRESHOLD = 10; const END_SNAP = 0.04; /* ========================================= TOP SCROLLBAR CONTROL ========================================= */ const scrollbarStyle = document.createElement("style"); scrollbarStyle.id = "top-scrollbar-hide"; scrollbarStyle.textContent = ` html.hide-top-scrollbar { scrollbar-width: none !important; -ms-overflow-style: none !important; } html.hide-top-scrollbar::-webkit-scrollbar { display: none !important; } `; document.head.appendChild( scrollbarStyle ); function updateScrollbar() { if (window.scrollY <= TOP_THRESHOLD) { document.documentElement.classList.add( "hide-top-scrollbar" ); } else { document.documentElement.classList.remove( "hide-top-scrollbar" ); } } /* * Check immediately. */ updateScrollbar(); /* * Update whenever the user scrolls. */ window.addEventListener( "scroll", updateScrollbar, { passive: true } ); /* ========================================= SCROLL LOCK ========================================= */ let scrollLocked = false; function lockPageScroll() { if (scrollLocked) return; scrollLocked = true; window.scrollTo(0, 0); document.documentElement.style.overflow = "hidden"; document.body.style.overflow = "hidden"; document.documentElement.style.touchAction = "none"; document.body.style.touchAction = "none"; updateScrollbar(); } function unlockPageScroll() { if (!scrollLocked) return; scrollLocked = false; document.documentElement.style.overflow = ""; document.body.style.overflow = ""; document.documentElement.style.touchAction = ""; document.body.style.touchAction = ""; updateScrollbar(); } /* ========================================= EXTRA SCROLL LOCK PROTECTION ========================================= */ function enforceScrollLock() { if ( scrollLocked && window.scrollY !== 0 ) { window.scrollTo(0, 0); } } window.addEventListener( "scroll", enforceScrollLock, { passive: true } ); /* ========================================= TOP CHECK ========================================= */ function isAtTop() { return window.scrollY <= TOP_THRESHOLD; } /* ========================================= STOP ANIMATION ========================================= */ function stopAnimation() { animating = false; if (animationFrame) { cancelAnimationFrame( animationFrame ); animationFrame = null; } } /* ========================================= FREEZE LAST FRAME ========================================= */ function freezeLastFrame() { if (!video.duration) return; stopAnimation(); progress = 1; videoComplete = true; controllingVideo = false; const finalFrame = Math.max( 0, video.duration - END_SNAP ); currentTime = finalFrame; targetTime = finalFrame; video.pause(); if (video.readyState >= 2) { video.currentTime = finalFrame; } } /* ========================================= VIDEO ANIMATION ========================================= */ function animate() { if (!animating) return; if ( targetTime >= video.duration - END_SNAP ) { freezeLastFrame(); unlockPageScroll(); return; } currentTime += (targetTime - currentTime) * SMOOTH; if (video.readyState >= 2) { video.currentTime = currentTime; } if ( Math.abs( targetTime - currentTime ) < 0.01 ) { currentTime = targetTime; video.currentTime = targetTime; stopAnimation(); if (progress >= 1) { freezeLastFrame(); unlockPageScroll(); } return; } animationFrame = requestAnimationFrame( animate ); } function startAnimation() { if (animating) return; animating = true; animationFrame = requestAnimationFrame( animate ); } /* ========================================= SET VIDEO PROGRESS ========================================= */ function setProgress(value) { progress = Math.max( 0, Math.min(1, value) ); if (video.duration) { targetTime = progress * video.duration; if ( progress >= 1 || targetTime >= video.duration - END_SNAP ) { progress = 1; targetTime = Math.max( 0, video.duration - END_SNAP ); } } startAnimation(); } /* ========================================= START VIDEO CONTROL ========================================= */ function startVideoControl() { controllingVideo = true; videoComplete = false; lockPageScroll(); window.scrollTo(0, 0); updateScrollbar(); } /* ========================================= WHEEL HANDLER ========================================= */ function wheelHandler(e) { /* ===================================== VIDEO FINISHED ===================================== */ if (videoComplete) { if ( e.deltaY < 0 && isAtTop() ) { e.preventDefault(); e.stopPropagation(); startVideoControl(); videoComplete = false; setProgress( progress + e.deltaY * SPEED ); } return; } /* ===================================== VIDEO AT BEGINNING ===================================== */ if ( progress <= 0 && !controllingVideo ) { if ( e.deltaY > 0 && isAtTop() ) { e.preventDefault(); e.stopPropagation(); startVideoControl(); setProgress( progress + e.deltaY * SPEED ); } return; } /* ===================================== VIDEO CURRENTLY BEING SCRUBBED ===================================== */ if (controllingVideo) { e.preventDefault(); e.stopPropagation(); if (window.scrollY !== 0) { window.scrollTo(0, 0); } setProgress( progress + e.deltaY * SPEED ); /* ================================= VIDEO REACHED BEGINNING ================================= */ if (progress <= 0) { progress = 0; targetTime = 0; currentTime = 0; stopAnimation(); video.currentTime = 0; controllingVideo = false; videoComplete = false; window.scrollTo(0, 0); unlockPageScroll(); return; } /* ================================= VIDEO REACHED END ================================= */ if (progress >= 1) { progress = 1; targetTime = Math.max( 0, video.duration - END_SNAP ); startAnimation(); return; } } } /* ========================================= PREVENT AUTOPLAY ========================================= */ video.addEventListener( "play", function () { video.pause(); if (videoComplete) { freezeLastFrame(); } } ); /* ========================================= VIDEO ENDED ========================================= */ video.addEventListener( "ended", function () { progress = 1; freezeLastFrame(); unlockPageScroll(); } ); /* ========================================= SETUP ========================================= */ video.addEventListener( "loadedmetadata", function () { video.pause(); video.currentTime = 0; progress = 0; targetTime = 0; currentTime = 0; updateScrollbar(); window.addEventListener( "wheel", wheelHandler, { passive: false, capture: true } ); } ); /* ========================================= KEYBOARD SCROLL PROTECTION ========================================= */ window.addEventListener( "keydown", function (e) { if (!controllingVideo) return; const scrollKeys = [ "ArrowDown", "ArrowUp", "PageDown", "PageUp", "Home", "End", " ", "Spacebar" ]; if ( scrollKeys.includes(e.key) ) { e.preventDefault(); e.stopPropagation(); } }, { passive: false, capture: true } ); /* ========================================= TOUCH PROTECTION ========================================= */ window.addEventListener( "touchmove", function (e) { if (!controllingVideo) return; e.preventDefault(); }, { passive: false, capture: true } ); }); ```
window.addEventListener("load", function () { const title = document.getElementById("headertext1"); if (!title) return; const TEXT = "jayc"; /* ========================================= SETTINGS ========================================= */ const TRIGGER = 1000; const COLUMN_DURATION = 900; const EXIT_DURATION = 500; /* MATCHES LOGO: MOVE_UP = 80 SHOW_DURATION = 800 */ const RETURN_DISTANCE = 80; const RETURN_DURATION = 800; const SPACING = 45; const COLUMN_SHIFT_X = -80; const COLUMN_MIN_LEFT = 20; const BAR_WIDTH = 110; const BAR_START_OFFSET = 180; const BAR_HORIZONTAL_OFFSET = -20; const EXIT_DISTANCE = 220; /* ========================================= STATE ========================================= */ let progress = 0; let animationFrame = null; let letters = null; let hasTriggered = false; let exiting = false; /* ========================================= FOREGROUND LAYER ========================================= */ const layer = document.createElement("div"); layer.style.cssText = ` position:fixed!important; top:0!important; left:0!important; width:100vw!important; height:100vh!important; pointer-events:none!important; z-index:2147483647!important; overflow:visible!important; `; document.body.appendChild(layer); /* ========================================= BLACK BAR ========================================= */ const blackBar = document.createElement("div"); blackBar.style.cssText = ` position:fixed!important; top:0!important; bottom:0!important; width:${BAR_WIDTH}px!important; background:#000000!important; opacity:0!important; pointer-events:none!important; z-index:0!important; transform:translateX(-50%)!important; will-change:left,opacity!important; `; layer.appendChild(blackBar); /* ========================================= FIND TEXT NODE ========================================= */ function getTextNode() { const walker = document.createTreeWalker( title, NodeFilter.SHOW_TEXT ); let node; while ( node = walker.nextNode() ) { if ( node.textContent.trim() ) { return node; } } return null; } /* ========================================= CREATE LETTERS ========================================= */ function createLetters() { const node = getTextNode(); if (!node) return null; const positions = []; for ( let i = 0; i < TEXT.length; i++ ) { const range = document.createRange(); range.setStart( node, i ); range.setEnd( node, i + 1 ); const rect = range.getBoundingClientRect(); positions.push({ x: rect.left + rect.width / 2, y: rect.top + rect.height / 2, left: rect.left, top: rect.top, width: rect.width, height: rect.height }); } const anchorX = positions[0].x; const anchorY = positions[0].y; const style = window.getComputedStyle( title ); const result = []; for ( let i = 0; i < TEXT.length; i++ ) { const letter = document.createElement( "span" ); letter.textContent = TEXT[i]; letter.style.cssText = ` position:fixed!important; left: ${positions[i].left}px!important; top: ${positions[i].top}px!important; margin:0!important; padding:0!important; font-family: ${style.fontFamily}!important; font-size: ${style.fontSize}!important; font-weight: ${style.fontWeight}!important; font-style: ${style.fontStyle}!important; line-height: ${style.lineHeight}!important; color: ${style.color}!important; white-space: nowrap!important; pointer-events: none!important; visibility: visible!important; opacity: 1!important; display: block!important; will-change: transform, opacity!important; z-index: 1!important; `; layer.appendChild( letter ); const desiredColumnX = anchorX + COLUMN_SHIFT_X; const safeColumnX = Math.max( COLUMN_MIN_LEFT + positions[i].width / 2, desiredColumnX ); result.push({ element: letter, startX: positions[i].x, startY: positions[i].y, columnX: safeColumnX, columnY: anchorY + i * SPACING, columnOffsetX: 0, columnOffsetY: 0 }); } return result; } /* ========================================= RENDER COLUMN ========================================= */ function renderColumn( value ) { if (!letters) return; letters.forEach( function (item) { const x = ( item.columnX - item.startX ) * value; const y = ( item.columnY - item.startY ) * value; item.element.style.transform = `translate3d(${x}px,${y}px,0)`; if ( value === 1 ) { item.columnOffsetX = x; item.columnOffsetY = y; } } ); } /* ========================================= BAR POSITION ========================================= */ function getFinalBarX() { if ( !letters || !letters.length ) { return 0; } const first = letters[0]; return ( first.startX + first.columnOffsetX + BAR_HORIZONTAL_OFFSET ); } /* ========================================= SCROLL DOWN ========================================= */ function animateDown() { if (exiting) return; if (!letters) { letters = createLetters(); } if (!letters) return; blackBar.style.opacity = "0"; renderColumn(0); renderColumn(1); const finalBarX = getFinalBarX(); renderColumn(0); const startBarX = finalBarX - BAR_START_OFFSET; blackBar.style.left = `${startBarX}px`; blackBar.style.opacity = "0"; title.style.transition = "none"; title.style.transform = "translateY(0px)"; title.style.visibility = "hidden"; const start = progress; const startTime = performance.now(); function frame(time) { const raw = Math.min( ( time - startTime ) / COLUMN_DURATION, 1 ); const eased = 1 - Math.pow( 1 - raw, 3 ); progress = start + ( 1 - start ) * eased; renderColumn( progress ); const barX = startBarX + ( finalBarX - startBarX ) * eased; blackBar.style.left = `${barX}px`; blackBar.style.opacity = String( eased ); if ( raw < 1 ) { animationFrame = requestAnimationFrame( frame ); } else { progress = 1; animationFrame = null; renderColumn(1); blackBar.style.left = `${finalBarX}px`; blackBar.style.opacity = "1"; } } animationFrame = requestAnimationFrame( frame ); } /* ========================================= SCROLL BACK UP ========================================= */ function animateUp() { if ( !letters || exiting ) { return; } exiting = true; if ( animationFrame ) { cancelAnimationFrame( animationFrame ); animationFrame = null; } renderColumn(1); const startingBarX = getFinalBarX(); blackBar.style.left = `${startingBarX}px`; blackBar.style.opacity = "1"; /* ========================================= START JAYC RETURN IMMEDIATELY This now starts at the exact same trigger point as jclogo. ========================================= */ title.style.transition = "none"; title.style.transform = `translateY(-${RETURN_DISTANCE}px)`; title.style.visibility = "visible"; title.style.opacity = "1"; /* Force browser to register the upper position. */ void title.offsetHeight; /* MATCH LOGO SLIDE DOWN */ title.style.transition = `transform ${RETURN_DURATION}ms cubic-bezier(.4,0,.2,1)`; title.style.transform = "translateY(0px)"; /* ========================================= COLUMN + BAR EXIT ========================================= */ const startTime = performance.now(); function frame(time) { const raw = Math.min( ( time - startTime ) / EXIT_DURATION, 1 ); const eased = 1 - Math.pow( 1 - raw, 3 ); const moveLeft = EXIT_DISTANCE * eased; letters.forEach( function (item) { const x = item.columnOffsetX - moveLeft; const y = item.columnOffsetY; item.element.style.transform = `translate3d(${x}px,${y}px,0)`; item.element.style.opacity = String( 1 - eased ); } ); blackBar.style.left = `${startingBarX - moveLeft}px`; blackBar.style.opacity = String( 1 - eased ); if ( raw < 1 ) { requestAnimationFrame( frame ); } else { letters.forEach( function (item) { item.element.remove(); } ); letters = null; progress = 0; blackBar.style.opacity = "0"; exiting = false; } } requestAnimationFrame( frame ); } /* ========================================= SCROLL DETECTION ========================================= */ window.addEventListener( "scroll", function () { const scroll = window.scrollY || window.pageYOffset; /* DOWN */ if ( scroll >= TRIGGER && !hasTriggered ) { hasTriggered = true; animateDown(); } /* UP */ else if ( scroll < TRIGGER && hasTriggered ) { hasTriggered = false; animateUp(); } }, { passive:true } ); });
window.addEventListener("load", function () { const topbar = document.getElementById("topbar"); if (!topbar) return; /* ========================================= SETTINGS ========================================= */ const TRIGGER = 1000; const SHOW_DURATION = 400; const HIDE_DURATION = 500; const VISIBLE_TOP = -20; const HIDDEN_TOP = -120; /* ========================================= CREATE BLACK BOX ========================================= */ const oldBox = document.getElementById( "scroll-black-box" ); if (oldBox) { oldBox.remove(); } const blackBox = document.createElement("div"); blackBox.id = "scroll-black-box"; blackBox.style.cssText = ` position:fixed; top:${VISIBLE_TOP}px; left:0; width:100vw; height:100px; background:#000000; pointer-events:none; opacity:1; z-index:9999998; will-change:top; `; topbar.parentNode.insertBefore( blackBox, topbar ); topbar.style.zIndex = "9999999"; let hidden = false; let hiding = false; let showing = false; let animationFrame = null; /* ========================================= GET CURRENT POSITION ========================================= */ function getCurrentTop() { const value = parseFloat( blackBox.style.top ); return Number.isFinite(value) ? value : VISIBLE_TOP; } /* ========================================= SHOW ========================================= */ function showBox() { if ( showing && !hiding ) return; if (animationFrame) { cancelAnimationFrame( animationFrame ); animationFrame = null; } hiding = false; showing = true; blackBox.style.opacity = "1"; const startTop = getCurrentTop(); const startTime = performance.now(); function frame(time) { const progress = Math.min( (time - startTime) / SHOW_DURATION, 1 ); const eased = 1 - Math.pow( 1 - progress, 3 ); const top = startTop + ( VISIBLE_TOP - startTop ) * eased; blackBox.style.top = top + "px"; if (progress < 1) { animationFrame = requestAnimationFrame( frame ); } else { blackBox.style.top = VISIBLE_TOP + "px"; animationFrame = null; hidden = false; hiding = false; showing = false; } } animationFrame = requestAnimationFrame( frame ); } /* ========================================= HIDE ========================================= */ function hideBox() { if ( hiding && !showing ) return; if (animationFrame) { cancelAnimationFrame( animationFrame ); animationFrame = null; } showing = false; hiding = true; const startTop = getCurrentTop(); const startTime = performance.now(); function frame(time) { const progress = Math.min( (time - startTime) / HIDE_DURATION, 1 ); const eased = 1 - Math.pow( 1 - progress, 3 ); const top = startTop + ( HIDDEN_TOP - startTop ) * eased; blackBox.style.top = top + "px"; /* NO FADE */ blackBox.style.opacity = "1"; if (progress < 1) { animationFrame = requestAnimationFrame( frame ); } else { blackBox.style.top = HIDDEN_TOP + "px"; animationFrame = null; hidden = true; hiding = false; showing = false; } } animationFrame = requestAnimationFrame( frame ); } /* ========================================= SCROLL ========================================= */ function update() { const scroll = window.scrollY || window.pageYOffset; /* ABOVE 1000 MOVE UP IMMEDIATELY */ if ( scroll >= TRIGGER ) { hideBox(); } /* BELOW 1000 COME BACK IMMEDIATELY */ else { showBox(); } } window.addEventListener( "scroll", update, { passive:true } ); update(); });
window.addEventListener("load", function () { const logo = document.getElementById("jclogo"); if (!logo) return; const TRIGGER = 1000; const MOVE_UP = 80; const HIDE_DURATION = 300; const SHOW_DURATION = 800; let hideTimer = null; let state = null; /* ========================================= KILL CARRD FADE EFFECTS ========================================= */ function removeFade() { logo.style.setProperty( "opacity", "1", "important" ); logo.style.setProperty( "filter", "none", "important" ); logo.style.setProperty( "-webkit-filter", "none", "important" ); logo.style.setProperty( "animation", "none", "important" ); const children = logo.querySelectorAll("*"); children.forEach(function (el) { el.style.setProperty( "opacity", "1", "important" ); el.style.setProperty( "filter", "none", "important" ); el.style.setProperty( "-webkit-filter", "none", "important" ); el.style.setProperty( "transition", "none", "important" ); el.style.setProperty( "animation", "none", "important" ); }); } /* ========================================= GET CURRENT Y POSITION ========================================= */ function getCurrentY() { const transform = window.getComputedStyle( logo ).transform; if ( !transform || transform === "none" ) { return 0; } try { const matrix = new DOMMatrixReadOnly( transform ); return matrix.m42; } catch (e) { return 0; } } /* ========================================= FREEZE AT CURRENT POSITION ========================================= */ function freezePosition() { const currentY = getCurrentY(); logo.style.setProperty( "transition", "none", "important" ); logo.style.setProperty( "transform", `translateY(${currentY}px)`, "important" ); void logo.offsetHeight; return currentY; } /* ========================================= INITIAL STATE NO ANIMATION ========================================= */ removeFade(); const initialScroll = window.scrollY || window.pageYOffset; logo.style.setProperty( "transition", "none", "important" ); if ( initialScroll >= TRIGGER ) { state = "hidden"; logo.style.setProperty( "transform", `translateY(-${MOVE_UP}px)`, "important" ); logo.style.setProperty( "visibility", "hidden", "important" ); } else { state = "visible"; logo.style.setProperty( "transform", "translateY(0px)", "important" ); logo.style.setProperty( "visibility", "visible", "important" ); } /* ========================================= SLIDE UP ========================================= */ function hideLogo() { if ( state === "hidden" || state === "hiding" ) { return; } clearTimeout( hideTimer ); removeFade(); /* If we're currently sliding down, capture the exact current position instead of jumping. */ const currentY = freezePosition(); state = "hiding"; logo.style.setProperty( "visibility", "visible", "important" ); /* Calculate duration based on how much distance is actually left. */ const distanceLeft = Math.abs( currentY + MOVE_UP ); const duration = Math.max( 50, HIDE_DURATION * ( distanceLeft / MOVE_UP ) ); logo.style.setProperty( "transition", `transform ${duration}ms cubic-bezier(.4,0,.2,1)`, "important" ); requestAnimationFrame( function () { logo.style.setProperty( "transform", `translateY(-${MOVE_UP}px)`, "important" ); } ); hideTimer = setTimeout( function () { const scroll = window.scrollY || window.pageYOffset; if ( scroll >= TRIGGER ) { logo.style.setProperty( "visibility", "hidden", "important" ); state = "hidden"; } }, duration + 20 ); } /* ========================================= SLIDE DOWN ========================================= */ function showLogo() { if ( state === "visible" || state === "showing" ) { return; } clearTimeout( hideTimer ); removeFade(); /* If we're currently sliding upward, capture the exact current position. This is the part that prevents the logo from instantly appearing. */ let currentY = freezePosition(); /* If it was completely hidden, start at the normal upper position. */ if ( state === "hidden" ) { currentY = -MOVE_UP; logo.style.setProperty( "transform", `translateY(-${MOVE_UP}px)`, "important" ); } state = "showing"; logo.style.setProperty( "visibility", "visible", "important" ); void logo.offsetHeight; /* Calculate the return duration based on the remaining distance. */ const distanceLeft = Math.abs( currentY ); const duration = Math.max( 50, SHOW_DURATION * ( distanceLeft / MOVE_UP ) ); logo.style.setProperty( "transition", `transform ${duration}ms cubic-bezier(.4,0,.2,1)`, "important" ); requestAnimationFrame( function () { logo.style.setProperty( "transform", "translateY(0px)", "important" ); } ); /* Don't mark it visible until the animation has actually completed. */ setTimeout( function () { const scroll = window.scrollY || window.pageYOffset; if ( scroll < TRIGGER && state === "showing" ) { state = "visible"; logo.style.setProperty( "transition", "none", "important" ); logo.style.setProperty( "transform", "translateY(0px)", "important" ); } }, duration + 20 ); } /* ========================================= SCROLL ========================================= */ function update() { removeFade(); const scroll = window.scrollY || window.pageYOffset; if ( scroll >= TRIGGER ) { hideLogo(); } else { showLogo(); } } window.addEventListener( "scroll", update, { passive:true } ); });