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
Vendored
+116
-77
@@ -10,64 +10,68 @@
|
||||
'use strict';
|
||||
|
||||
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
var countUpObserver = null;
|
||||
var countdownElements = [];
|
||||
var countdownTimer = null;
|
||||
var fpsDetectionStarted = false;
|
||||
|
||||
function scopeFor(root) {
|
||||
return root && root.querySelectorAll ? root : document;
|
||||
}
|
||||
/* ============================================================
|
||||
* 1. 数字递增 (count-up) — IntersectionObserver 触发
|
||||
* 用法:<span data-aksrt-countup data-target="1234" data-duration="2000">0</span>
|
||||
* ============================================================ */
|
||||
function initCountUp() {
|
||||
if (reducedMotion) return;
|
||||
if (!('IntersectionObserver' in window)) return;
|
||||
|
||||
function eachMatch(root, selector, callback) {
|
||||
var scope = scopeFor(root);
|
||||
if (scope.nodeType === 1 && scope.matches(selector)) callback(scope);
|
||||
scope.querySelectorAll(selector).forEach(callback);
|
||||
}
|
||||
var els = document.querySelectorAll('[data-aksrt-countup]:not([data-aksrt-countup-init])');
|
||||
if (!els.length) return;
|
||||
|
||||
function animateCountUp(el) {
|
||||
if (el.dataset.aksrtCountupBound === '1') return;
|
||||
el.dataset.aksrtCountupBound = '1';
|
||||
countUpObserver.observe(el);
|
||||
}
|
||||
var observer = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) return;
|
||||
var el = entry.target;
|
||||
observer.unobserve(el);
|
||||
|
||||
function initCountUp(root) {
|
||||
if (reducedMotion || !('IntersectionObserver' in window)) return;
|
||||
if (!countUpObserver) {
|
||||
countUpObserver = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) return;
|
||||
var el = entry.target;
|
||||
countUpObserver.unobserve(el);
|
||||
var target = parseFloat(el.dataset.target || '0');
|
||||
var duration = parseInt(el.dataset.duration || '2000', 10);
|
||||
var decimals = (el.dataset.decimals || '0|').split('|');
|
||||
var prefix = decimals[1] || '';
|
||||
var dec = parseInt(decimals[0], 10) || 0;
|
||||
var startTime = null;
|
||||
var target = parseFloat(el.dataset.target || '0');
|
||||
var duration = parseInt(el.dataset.duration || '2000', 10);
|
||||
var decimals = (el.dataset.decimals || '0|').split('|');
|
||||
var prefix = decimals[1] || '';
|
||||
var dec = parseInt(decimals[0], 10) || 0;
|
||||
var start = 0;
|
||||
var startTime = null;
|
||||
|
||||
function step(timestamp) {
|
||||
if (!startTime) startTime = timestamp;
|
||||
var progress = Math.min((timestamp - startTime) / duration, 1);
|
||||
var current = target * (1 - Math.pow(1 - progress, 3));
|
||||
el.textContent = prefix + current.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
if (progress < 1) requestAnimationFrame(step);
|
||||
else el.textContent = prefix + target.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
function step(timestamp) {
|
||||
if (!startTime) startTime = timestamp;
|
||||
var progress = Math.min((timestamp - startTime) / duration, 1);
|
||||
// ease-out cubic
|
||||
var eased = 1 - Math.pow(1 - progress, 3);
|
||||
var current = start + (target - start) * eased;
|
||||
el.textContent = prefix + current.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(step);
|
||||
} else {
|
||||
el.textContent = prefix + target.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(step);
|
||||
});
|
||||
}, { threshold: 0.3 });
|
||||
}
|
||||
eachMatch(root, '[data-aksrt-countup]', animateCountUp);
|
||||
requestAnimationFrame(step);
|
||||
});
|
||||
}, { threshold: 0.3 });
|
||||
|
||||
els.forEach(function (el) {
|
||||
el.dataset.aksrtCountupInit = '1';
|
||||
observer.observe(el);
|
||||
});
|
||||
}
|
||||
|
||||
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
||||
/* ============================================================
|
||||
* 2. 倒计时组件
|
||||
* 用法:<span data-aksrt-countdown data-end="2026-12-31T23:59:59">--:--:--</span>
|
||||
* ============================================================ */
|
||||
function initCountdown() {
|
||||
var els = document.querySelectorAll('[data-aksrt-countdown]:not([data-aksrt-countdown-init])');
|
||||
if (!els.length) return;
|
||||
|
||||
function updateCountdowns() {
|
||||
var now = Date.now();
|
||||
countdownElements = countdownElements.filter(function (el) { return document.documentElement.contains(el); });
|
||||
countdownElements.forEach(function (el) {
|
||||
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
||||
|
||||
function update(el) {
|
||||
var now = Date.now();
|
||||
var end = new Date(el.dataset.end).getTime();
|
||||
var diff = end - now;
|
||||
if (diff <= 0) {
|
||||
@@ -76,35 +80,42 @@
|
||||
}
|
||||
var d = Math.floor(diff / 86400000);
|
||||
var h = Math.floor((diff % 86400000) / 3600000);
|
||||
var m = Math.floor((diff % 3600000) / 60000);
|
||||
var m = Math.floor((diff % 86400000) / 60000);
|
||||
var s = Math.floor((diff % 60000) / 1000);
|
||||
var format = el.dataset.format || 'dhms';
|
||||
if (format === 'dhms') el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + ':' + pad(m) + ':' + pad(s);
|
||||
else if (format === 'hms') el.textContent = pad(h + d * 24) + ':' + pad(m) + ':' + pad(s);
|
||||
else el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + '时' + pad(m) + '分' + pad(s) + '秒';
|
||||
if (format === 'dhms') {
|
||||
el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + ':' + pad(m) + ':' + pad(s);
|
||||
} else if (format === 'hms') {
|
||||
el.textContent = pad(h + d * 24) + ':' + pad(m) + ':' + pad(s);
|
||||
} else {
|
||||
el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + '时' + pad(m) + '分' + pad(s) + '秒';
|
||||
}
|
||||
}
|
||||
|
||||
els.forEach(function (el) {
|
||||
el.dataset.aksrtCountdownInit = '1';
|
||||
update(el);
|
||||
setInterval(function () { update(el); }, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function initCountdown(root) {
|
||||
eachMatch(root, '[data-aksrt-countdown]', function (el) {
|
||||
if (el.dataset.aksrtCountdownBound === '1') return;
|
||||
el.dataset.aksrtCountdownBound = '1';
|
||||
countdownElements.push(el);
|
||||
});
|
||||
updateCountdowns();
|
||||
if (!countdownTimer && countdownElements.length) countdownTimer = window.setInterval(updateCountdowns, 1000);
|
||||
}
|
||||
|
||||
function initTilt(root) {
|
||||
/* ============================================================
|
||||
* 4. 3D 悬停倾斜 (vanilla tilt)
|
||||
* 用法:<div data-aksrt-tilt data-tilt-max="15">...</div>
|
||||
* ============================================================ */
|
||||
function initTilt() {
|
||||
if (reducedMotion) return;
|
||||
eachMatch(root, '[data-aksrt-tilt]', function (el) {
|
||||
|
||||
document.querySelectorAll('[data-aksrt-tilt]').forEach(function (el) {
|
||||
if (el.dataset.tiltBound) return;
|
||||
el.dataset.tiltBound = '1';
|
||||
|
||||
var maxDeg = parseFloat(el.dataset.tiltMax || '10');
|
||||
var perspective = parseFloat(el.dataset.tiltPerspective || '800');
|
||||
var scale = parseFloat(el.dataset.tiltScale || '1.03');
|
||||
var glare = el.dataset.tiltGlare === '1';
|
||||
var glareEl = null;
|
||||
|
||||
if (glare) {
|
||||
glareEl = document.createElement('div');
|
||||
glareEl.style.cssText = 'position:absolute;inset:0;border-radius:inherit;pointer-events:none;background:linear-gradient(135deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0.25) 100%);opacity:0;transition:opacity 0.2s;';
|
||||
@@ -112,13 +123,17 @@
|
||||
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;
|
||||
el.style.transform = 'perspective(' + perspective + 'px) rotateX(' + ((0.5 - y) * maxDeg) + 'deg) rotateY(' + ((x - 0.5) * maxDeg) + 'deg) scale3d(' + scale + ',' + scale + ',' + scale + ')';
|
||||
var rotateX = (0.5 - y) * maxDeg;
|
||||
var rotateY = (x - 0.5) * maxDeg;
|
||||
el.style.transform = 'perspective(' + perspective + 'px) rotateX(' + rotateX + 'deg) rotateY(' + rotateY + 'deg) scale3d(' + scale + ',' + scale + ',' + scale + ')';
|
||||
if (glareEl) glareEl.style.opacity = '1';
|
||||
});
|
||||
|
||||
el.addEventListener('mouseleave', function () {
|
||||
el.style.transform = 'perspective(' + perspective + 'px) rotateX(0deg) rotateY(0deg) scale3d(1,1,1)';
|
||||
if (glareEl) glareEl.style.opacity = '0';
|
||||
@@ -126,30 +141,54 @@
|
||||
});
|
||||
}
|
||||
|
||||
function initFpsDetection(root, reason) {
|
||||
if (fpsDetectionStarted || !('requestAnimationFrame' in window)) return;
|
||||
fpsDetectionStarted = true;
|
||||
/* ============================================================
|
||||
* 5. FPS 检测 + 低端设备降级 — 只检测一次
|
||||
* ============================================================ */
|
||||
var fpsDetected = false;
|
||||
|
||||
function initFpsDetection() {
|
||||
if (fpsDetected || !('requestAnimationFrame' in window)) return;
|
||||
fpsDetected = true;
|
||||
|
||||
var frames = 0;
|
||||
var startTime = performance.now();
|
||||
|
||||
function countFrames() {
|
||||
frames++;
|
||||
var elapsed = performance.now() - startTime;
|
||||
if (elapsed >= 1000) {
|
||||
if (Math.round(frames * 1000 / elapsed) < 24 && document.body) document.body.classList.add('fps-accelerat');
|
||||
return;
|
||||
var fps = Math.round(frames * 1000 / elapsed);
|
||||
if (fps < 24) {
|
||||
document.body.classList.add('fps-accelerat');
|
||||
}
|
||||
return; // 只检测一次
|
||||
}
|
||||
requestAnimationFrame(countFrames);
|
||||
}
|
||||
|
||||
requestAnimationFrame(countFrames);
|
||||
}
|
||||
|
||||
function init(root, reason) {
|
||||
initCountUp(root);
|
||||
initCountdown(root);
|
||||
if (typeof initMarquee === 'function') initMarquee(root, reason);
|
||||
initTilt(root);
|
||||
initFpsDetection(root, reason);
|
||||
/* ============================================================
|
||||
* 初始化入口 — 先执行一次,随后监听 auto_fun 重扫(各 init 幂等)
|
||||
* ============================================================ */
|
||||
function init() {
|
||||
initCountUp();
|
||||
initCountdown();
|
||||
if (typeof initMarquee === 'function') {
|
||||
initMarquee();
|
||||
}
|
||||
initTilt();
|
||||
initFpsDetection();
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
if (window.jQuery) {
|
||||
window.jQuery(document).on('auto_fun', init);
|
||||
}
|
||||
|
||||
window.AksrtFrontend.register('aksrt-effects', init);
|
||||
})();
|
||||
Reference in new issue
Block a user