fix(runtime): 按参考主题硬切换为 loader/main/auto_fun 单一 DOM 生命周期
- 删除 AksrtFrontend registry/rehydrate 双路架构及 build/public.js 拼包入口,
前台唯一初始路径为 PHP 本地栈(jQuery 1.9.1 → Bootstrap 3.4.1)
→ loader_js(_win/aksrtData 唯一注入)→ tbquire(['main']) → runtime/main.min.js
- window.auto_fun = debounce(z_auto_fun, 100) 为唯一重扫入口;所有模块改为
执行一次 + off/on('auto_fun.aksrt-*') 命名空间重扫,data/class 标记幂等
- 局部更新协议统一:HTML 注入 → post_ajax.ed → auto_fun();
远程 modal → loaded.bs.modal → auto_fun();JSON 成功 → zib_ajax.success
- 灯箱恢复 Miniimgbox/imgbox 唯一 owner(imgbox-container、a[data-imgbox]、
imgbox_data),删除 AksrtLightbox/data-aksrt-lightbox 第二实现及 CSS/overlay,
_win.imgbox* 重新接上 post-display 设置
- 死代码清理:runtime 下 11 组被自有实现替代的旧模块副本、无消费 vendored 库、
顶层 grade.min.js、revenue.js、无调用方的 aksrt_get_win_runtime_config(),
以及 loader 中 16 条死 mapping
- PHP 入队改为本地栈 + loader_js,routed/widget/submit 依赖统一换绑;
用户卡片发布按钮恢复 nav_user_newposts_btn 设置门控
- 测试对齐:新增 frontend-lifecycle-contract,重写 navigation/lazyload/toast/
auth-source/comment/page-display/post-visual 等断言为目标架构契约
验证:node --test widget-system+navigation 223 通过 0 失败;
构建脚本重建 main/imgbox min 与 CSS bundles;改动 PHP php -l、JS node --check
全部通过;git diff --check 干净;credits 重入 harness 通过。
This commit is contained in:
1 parent
1cd81fb61a
commit
5fd7c212ed
124 files changed
+2128
-17328
No files matched your search
+205
-154
@@ -9,27 +9,11 @@
|
||||
(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) {
|
||||
/* ============================================================
|
||||
* 1. 3D 悬停倾斜效果 (vanilla-tilt 轻量版)
|
||||
* ============================================================ */
|
||||
function initTilt() {
|
||||
document.querySelectorAll('[data-aksrt-tilt]').forEach(function (el) {
|
||||
if (el.dataset.tiltInit) return;
|
||||
el.dataset.tiltInit = '1';
|
||||
|
||||
@@ -74,184 +58,251 @@
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
/* ============================================================
|
||||
* 2. 滚动触发动画 (IntersectionObserver)
|
||||
* ============================================================ */
|
||||
function initScrollAnimations() {
|
||||
if (!('IntersectionObserver' in window)) {
|
||||
eachMatch(root, '[data-aksrt-animate]', function (el) {
|
||||
// 不支持时直接显示所有元素
|
||||
document.querySelectorAll('[data-aksrt-animate]').forEach(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' });
|
||||
}
|
||||
var observer = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) return;
|
||||
|
||||
eachMatch(root, '[data-aksrt-animate]', prepareAnimation);
|
||||
}
|
||||
var el = entry.target;
|
||||
var animation = el.dataset.aksrtAnimate || 'fadeInUp';
|
||||
var delay = parseInt(el.dataset.aksrtAnimateDelay || '0', 10);
|
||||
var duration = parseInt(el.dataset.aksrtAnimateDuration || '600', 10);
|
||||
|
||||
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';
|
||||
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);
|
||||
|
||||
observer.unobserve(el);
|
||||
});
|
||||
}, {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -50px 0px',
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-aksrt-animate]').forEach(function (el) {
|
||||
if (el.dataset.aksrtAnimateInit) return;
|
||||
el.dataset.aksrtAnimateInit = '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)';
|
||||
}
|
||||
|
||||
observer.observe(el);
|
||||
});
|
||||
}
|
||||
|
||||
function initSticky(root) {
|
||||
eachMatch(root, '[data-aksrt-sticky]', function (el) {
|
||||
if (el.dataset.aksrtStickyBound === '1') return;
|
||||
el.dataset.aksrtStickyBound = '1';
|
||||
stickyElements.push(el);
|
||||
/* ============================================================
|
||||
* 5. 粘性元素 (Sticky)
|
||||
* ============================================================ */
|
||||
function initSticky() {
|
||||
document.querySelectorAll('[data-aksrt-sticky]').forEach(function (el) {
|
||||
if (el.dataset.aksrtStickyInit) return;
|
||||
el.dataset.aksrtStickyInit = '1';
|
||||
|
||||
var top = parseInt(el.dataset.aksrtStickyTop || '80', 10);
|
||||
var offset = parseInt(el.dataset.aksrtStickyOffset || '0', 10);
|
||||
|
||||
function update() {
|
||||
var rect = el.getBoundingClientRect();
|
||||
var parentRect = el.parentElement.getBoundingClientRect();
|
||||
|
||||
if (parentRect.top < top && parentRect.bottom > el.offsetHeight + top) {
|
||||
el.style.position = 'sticky';
|
||||
el.style.top = top + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', update, { passive: true });
|
||||
});
|
||||
if (!stickyScrollBound) {
|
||||
stickyScrollBound = true;
|
||||
window.addEventListener('scroll', updateSticky, { passive: true });
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 6. 视差滚动
|
||||
* ============================================================ */
|
||||
var parallaxScrollBound = false;
|
||||
|
||||
function initParallax() {
|
||||
if (!document.querySelector('[data-aksrt-parallax]')) return;
|
||||
if (parallaxScrollBound) return;
|
||||
parallaxScrollBound = true;
|
||||
|
||||
var ticking = false;
|
||||
|
||||
function update() {
|
||||
document.querySelectorAll('[data-aksrt-parallax]').forEach(function (el) {
|
||||
var speed = parseFloat(el.dataset.aksrtParallax || '0.3');
|
||||
var rect = el.getBoundingClientRect();
|
||||
var center = rect.top + rect.height / 2;
|
||||
var windowCenter = window.innerHeight / 2;
|
||||
var offset = (center - windowCenter) * speed;
|
||||
|
||||
el.style.transform = 'translateY(' + offset + 'px)';
|
||||
});
|
||||
ticking = false;
|
||||
}
|
||||
updateSticky();
|
||||
|
||||
window.addEventListener('scroll', function () {
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(update);
|
||||
ticking = true;
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/* ============================================================
|
||||
* 7. 涟漪点击效果 — document 委托只绑定一次
|
||||
* ============================================================ */
|
||||
var rippleBound = 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) {
|
||||
function initRipple() {
|
||||
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 x = e.clientX - rect.left;
|
||||
var y = e.clientY - rect.top;
|
||||
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(); });
|
||||
});
|
||||
}
|
||||
wave.style.left = (x - size / 2) + 'px';
|
||||
wave.style.top = (y - size / 2) + 'px';
|
||||
|
||||
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');
|
||||
el.appendChild(wave);
|
||||
|
||||
wave.addEventListener('animationend', function () {
|
||||
wave.remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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 = '<div class="aksrt-signin-calendar__grid">';
|
||||
for (var i = 29; i >= 0; i--) {
|
||||
var d = new Date(today);
|
||||
d.setDate(d.getDate() - i);
|
||||
var dateStr = d.toISOString().split('T')[0];
|
||||
var isSigned = data.indexOf(dateStr) !== -1;
|
||||
html += '<div class="aksrt-signin-calendar__day' + (isSigned ? ' is-signed' : '') + (i === 0 ? ' is-today' : '') +
|
||||
'" title="' + dateStr + (isSigned ? ' ✓' : '') + '"><span>' + d.getDate() + '</span></div>';
|
||||
/* ============================================================
|
||||
* 8. 表单浮动标签自动初始化
|
||||
* ============================================================ */
|
||||
function initFloatLabels() {
|
||||
document.querySelectorAll('.aksrt-float-label__input').forEach(function (input) {
|
||||
if (input.dataset.aksrtFloatInit) return;
|
||||
input.dataset.aksrtFloatInit = '1';
|
||||
|
||||
// 检查初始值
|
||||
if (input.value) {
|
||||
input.setAttribute('data-has-value', '1');
|
||||
}
|
||||
calendar.innerHTML = html + '</div>';
|
||||
input.addEventListener('input', function () {
|
||||
if (this.value) {
|
||||
this.setAttribute('data-has-value', '1');
|
||||
} else {
|
||||
this.removeAttribute('data-has-value');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function bindModalLifecycle() {
|
||||
if (modalBound) return;
|
||||
modalBound = true;
|
||||
document.addEventListener('aksrt:modal:open', function (event) {
|
||||
var root = event.target && event.target.querySelectorAll ? event.target : document;
|
||||
window.setTimeout(function () { initSigninCalendar(root); }, 30);
|
||||
});
|
||||
/* ============================================================
|
||||
* 8. 签到日历可视化
|
||||
* ============================================================ */
|
||||
function initSigninCalendar() {
|
||||
var calendar = document.querySelector('[data-aksrt-signin-calendar]');
|
||||
if (!calendar) return;
|
||||
|
||||
var data = [];
|
||||
try {
|
||||
data = JSON.parse(calendar.dataset.aksrtSigninCalendar || '[]');
|
||||
} catch (e) {}
|
||||
|
||||
// 生成最近 30 天的格子
|
||||
var today = new Date();
|
||||
var html = '<div class="aksrt-signin-calendar__grid">';
|
||||
|
||||
for (var i = 29; i >= 0; i--) {
|
||||
var d = new Date(today);
|
||||
d.setDate(d.getDate() - i);
|
||||
var dateStr = d.toISOString().split('T')[0];
|
||||
var isSigned = data.indexOf(dateStr) !== -1;
|
||||
var isToday = i === 0;
|
||||
|
||||
html += '<div class="aksrt-signin-calendar__day' +
|
||||
(isSigned ? ' is-signed' : '') +
|
||||
(isToday ? ' is-today' : '') +
|
||||
'" title="' + dateStr + (isSigned ? ' ✓' : '') + '">' +
|
||||
'<span>' + d.getDate() + '</span>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
calendar.innerHTML = html;
|
||||
}
|
||||
|
||||
function init(root, reason) {
|
||||
initTilt(root);
|
||||
initScrollAnimations(root);
|
||||
initRipple(root, reason);
|
||||
initSticky(root);
|
||||
initParallax(root);
|
||||
initFloatLabels(root);
|
||||
initSigninCalendar(root);
|
||||
bindModalLifecycle();
|
||||
/* ============================================================
|
||||
* 初始化入口 — 先执行一次,随后监听 auto_fun 重扫(各 init 幂等)
|
||||
* ============================================================ */
|
||||
function init() {
|
||||
initTilt();
|
||||
initScrollAnimations();
|
||||
initRipple();
|
||||
initSticky();
|
||||
initParallax();
|
||||
initFloatLabels();
|
||||
initSigninCalendar();
|
||||
}
|
||||
|
||||
window.AksrtFrontend.register('aksrt-enhancements-advanced', init);
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
if (window.jQuery) {
|
||||
window.jQuery(document).on('auto_fun', init);
|
||||
}
|
||||
|
||||
// 暴露到全局供动态内容使用
|
||||
window.AksrtEnhancements = {
|
||||
initTilt: initTilt,
|
||||
initScrollAnimations: initScrollAnimations,
|
||||
initRipple: initRipple,
|
||||
};
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user