/** * AKSRT Enhancements Part 2 — 高级交互组件 * * 包含:3D 悬停倾斜、滚动触发动画、封面轮播、打赏按钮 * * @package AKSRT */ (function () { 'use strict'; var animateObserver = null; var rippleBound = false; var stickyScrollBound = false; var parallaxScrollBound = false; var modalBound = false; var stickyElements = []; var parallaxElements = []; var parallaxTicking = false; function scopeFor(root) { return root && root.querySelectorAll ? root : document; } function eachMatch(root, selector, callback) { var scope = scopeFor(root); if (scope.nodeType === 1 && scope.matches(selector)) callback(scope); scope.querySelectorAll(selector).forEach(callback); } function initTilt(root) { eachMatch(root, '[data-aksrt-tilt]', function (el) { if (el.dataset.tiltInit) return; el.dataset.tiltInit = '1'; var maxTilt = parseInt(el.dataset.aksrtTiltMax || '15', 10); var perspective = parseInt(el.dataset.aksrtTiltPerspective || '1000', 10); var scale = parseFloat(el.dataset.aksrtTiltScale || '1.03'); var speed = parseInt(el.dataset.aksrtTiltSpeed || '400', 10); var glare = el.dataset.aksrtTiltGlare === '1'; el.style.transformStyle = 'preserve-3d'; el.style.transition = 'transform ' + speed + 'ms ease'; var glareEl = null; if (glare) { glareEl = document.createElement('div'); glareEl.style.cssText = 'position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;transition:opacity ' + speed + 'ms;'; el.style.position = el.style.position || 'relative'; el.style.overflow = 'hidden'; el.appendChild(glareEl); } el.addEventListener('mousemove', function (e) { var rect = el.getBoundingClientRect(); var x = (e.clientX - rect.left) / rect.width; var y = (e.clientY - rect.top) / rect.height; var tiltX = (0.5 - y) * maxTilt; var tiltY = (x - 0.5) * maxTilt; el.style.transform = 'perspective(' + perspective + 'px) rotateX(' + tiltX + 'deg) rotateY(' + tiltY + 'deg) scale3d(' + scale + ',' + scale + ',' + scale + ')'; if (glareEl) { glareEl.style.opacity = '1'; glareEl.style.background = 'linear-gradient(135deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0) 100%)'; glareEl.style.transform = 'translate(' + ((x - 0.5) * 100) + '%, ' + ((y - 0.5) * 100) + '%)'; } }); el.addEventListener('mouseleave', function () { el.style.transform = 'perspective(' + perspective + 'px) rotateX(0deg) rotateY(0deg) scale3d(1,1,1)'; if (glareEl) glareEl.style.opacity = '0'; }); }); } function prepareAnimation(el) { if (el.dataset.aksrtAnimateBound === '1') return; el.dataset.aksrtAnimateBound = '1'; var animation = el.dataset.aksrtAnimate || 'fadeInUp'; el.style.opacity = '0'; switch (animation) { case 'fadeIn': el.style.transform = 'none'; break; case 'fadeInUp': el.style.transform = 'translateY(30px)'; break; case 'fadeInDown': el.style.transform = 'translateY(-30px)'; break; case 'fadeInLeft': el.style.transform = 'translateX(-30px)'; break; case 'fadeInRight': el.style.transform = 'translateX(30px)'; break; case 'zoomIn': el.style.transform = 'scale(0.9)'; break; default: el.style.transform = 'translateY(30px)'; } animateObserver.observe(el); } function initScrollAnimations(root) { if (!('IntersectionObserver' in window)) { eachMatch(root, '[data-aksrt-animate]', function (el) { el.style.opacity = '1'; el.style.transform = 'none'; }); return; } if (!animateObserver) { animateObserver = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; var el = entry.target; var delay = parseInt(el.dataset.aksrtAnimateDelay || '0', 10); var duration = parseInt(el.dataset.aksrtAnimateDuration || '600', 10); animateObserver.unobserve(el); window.setTimeout(function () { el.style.transition = 'opacity ' + duration + 'ms ease, transform ' + duration + 'ms ease'; el.style.opacity = '1'; el.style.transform = 'translateY(0) translateX(0) scale(1)'; el.classList.add('aksrt-animated'); }, delay); }); }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); } eachMatch(root, '[data-aksrt-animate]', prepareAnimation); } function updateSticky() { stickyElements = stickyElements.filter(function (el) { return document.documentElement.contains(el); }); stickyElements.forEach(function (el) { var top = parseInt(el.dataset.aksrtStickyTop || '80', 10); var parent = el.parentElement; if (!parent) return; var parentRect = parent.getBoundingClientRect(); if (parentRect.top < top && parentRect.bottom > el.offsetHeight + top) { el.style.position = 'sticky'; el.style.top = top + 'px'; } }); } function initSticky(root) { eachMatch(root, '[data-aksrt-sticky]', function (el) { if (el.dataset.aksrtStickyBound === '1') return; el.dataset.aksrtStickyBound = '1'; stickyElements.push(el); }); if (!stickyScrollBound) { stickyScrollBound = true; window.addEventListener('scroll', updateSticky, { passive: true }); } updateSticky(); } function updateParallax() { parallaxElements = parallaxElements.filter(function (el) { return document.documentElement.contains(el); }); parallaxElements.forEach(function (el) { var speed = parseFloat(el.dataset.aksrtParallax || '0.3'); var rect = el.getBoundingClientRect(); var center = rect.top + rect.height / 2; var offset = (center - window.innerHeight / 2) * speed; el.style.transform = 'translateY(' + offset + 'px)'; }); parallaxTicking = false; } function initParallax(root) { eachMatch(root, '[data-aksrt-parallax]', function (el) { if (el.dataset.aksrtParallaxBound === '1') return; el.dataset.aksrtParallaxBound = '1'; parallaxElements.push(el); }); if (!parallaxScrollBound) { parallaxScrollBound = true; window.addEventListener('scroll', function () { if (parallaxTicking) return; parallaxTicking = true; requestAnimationFrame(updateParallax); }, { passive: true }); } updateParallax(); } function initRipple(root, reason) { if (rippleBound) return; rippleBound = true; document.addEventListener('click', function (e) { var el = e.target.closest('.aksrt-ripple'); if (!el) return; var rect = el.getBoundingClientRect(); var size = Math.max(rect.width, rect.height) * 2; var wave = document.createElement('span'); wave.className = 'aksrt-ripple__wave'; wave.style.width = wave.style.height = size + 'px'; wave.style.left = (e.clientX - rect.left - size / 2) + 'px'; wave.style.top = (e.clientY - rect.top - size / 2) + 'px'; el.appendChild(wave); wave.addEventListener('animationend', function () { wave.remove(); }); }); } function initFloatLabels(root) { eachMatch(root, '.aksrt-float-label__input', function (input) { if (input.value) input.setAttribute('data-has-value', '1'); if (input.dataset.aksrtFloatLabelBound === '1') return; input.dataset.aksrtFloatLabelBound = '1'; input.addEventListener('input', function () { if (this.value) this.setAttribute('data-has-value', '1'); else this.removeAttribute('data-has-value'); }); }); } function initSigninCalendar(root) { eachMatch(root, '[data-aksrt-signin-calendar]', function (calendar) { if (calendar.dataset.aksrtSigninCalendarBound === '1') return; calendar.dataset.aksrtSigninCalendarBound = '1'; var data = []; try { data = JSON.parse(calendar.dataset.aksrtSigninCalendar || '[]'); } catch (e) {} var today = new Date(); var html = '