fix(static): 特效节点全屏 iframe 改挂 body,修复被后续卡片盖住
全屏特效 iframe 原挂载在卡片容器内,卡片入场动画的 opacity<1 会在 卡片上产生层叠上下文,iframe 的 z-index:9999 被困在卡片内部生效, DOM 序靠后的兄弟卡片把整张卡片(含特效)压在下面。改为全屏特效 直接挂载 document.body,z-index 在根层叠上下文竞争;in-node 特效 保持挂在卡片内。清理路径(clearAllEffects 全文档按 class 查找) 不受影响。
This commit is contained in:
1 parent
74aaa668ce
commit
f3ba13a6b9
2 files changed
+19
-12
No files matched your search
@@ -32,9 +32,8 @@ body::after {
|
||||
}
|
||||
|
||||
/* ===== 节点卡片入场:逐张淡入(JS 动态插入时播放) =====
|
||||
* 注意:此处只能动画 opacity,不能带 transform——特效节点的全屏 iframe 是
|
||||
* position:fixed,祖先 transform 会成为 fixed 后代的包含块,把全屏特效困在卡片内
|
||||
*(in-node 特效是 relative 定位不受影响,故障表现为"部分特效不自动启动") */
|
||||
* 防御性约定:只动画 opacity、不带 transform——祖先 transform 会成为 fixed 后代的包含块。
|
||||
* 全屏特效 iframe 现已直接挂载 body(见 dnfs.js renderSpJs),不再受卡片动画影响 */
|
||||
@keyframes card-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
|
||||
@@ -496,8 +496,8 @@ function renderSpLink(container, sp, appendNote) {
|
||||
* 采用 iframe 沙箱而非注入父页面全局作用域,原因:
|
||||
* - 特效脚本运行在独立 iframe 内,无法访问父页面 DOM/JS,任何报错、死循环、变量覆盖
|
||||
* 都不会污染父页面,从而保证"运行特效后卡片点击/节点导航依然正常";
|
||||
* - iframe 元素挂载在节点容器内部(DOM 归属该节点),但 keep 全屏 fixed 定位,视觉特效悬浮覆盖整个页面;
|
||||
* pointer-events:none 不拦截父页面点击,不影响卡片点击/节点导航;
|
||||
* - 全屏特效 iframe 挂载到 document.body(z-index:9999 在根层叠上下文生效,
|
||||
* 不被后续卡片盖住),pointer-events:none 不拦截父页面点击,不影响卡片点击/节点导航;
|
||||
* - 不使用 new Function/eval(CSP 未开 unsafe-eval),srcdoc 内联脚本由 CSP 'unsafe-inline' 放行。
|
||||
* 仅管理员可发布(后端强制)。 */
|
||||
/* 无特效模式:true 为关闭自动执行(用户面板开关控制,localStorage 持久化) */
|
||||
@@ -534,9 +534,14 @@ function renderSpJs(container, sp, appendNote) {
|
||||
/* 启动特效:创建隔离 iframe 沙箱,完整前端代码(含 <style>/<script>/<body>)由 srcdoc 承载,
|
||||
* 脚本在独立上下文执行,无法影响父页面导航。
|
||||
* 两种模式:
|
||||
* - 代码含 "<!-- in-node -->" 标记 → 渲染在节点内部(relative 定位、可交互、随节点滚动);
|
||||
* - 否则 → 全屏 fixed 定位,视觉特效悬浮覆盖整个页面(pointer-events:none 不拦截父页面点击)。
|
||||
* iframe 元素始终挂载在节点容器内部(DOM 归属该节点)。 */
|
||||
* - 代码含 "<!-- in-node -->" 标记 → 渲染在节点内部(relative 定位、可交互、随节点滚动),
|
||||
* iframe 挂载在节点容器内(随卡片隐藏/移除);
|
||||
* - 否则 → 全屏 fixed 定位,视觉特效悬浮覆盖整个页面(pointer-events:none 不拦截父页面点击),
|
||||
* iframe 直接挂载到 document.body:若挂在卡片内部,卡片入场动画等 opacity<1 的属性
|
||||
* 会在卡片上产生层叠上下文,iframe 的 z-index:9999 就被困在该卡片内部生效,
|
||||
* DOM 序靠后的兄弟卡片会把整张卡片(含特效)压在下面;挂到 body 后 z-index
|
||||
* 直接在根层叠上下文竞争,恒位于所有卡片之上。
|
||||
* 清理不受影响:clearAllEffects 按 class 全文档查找,两种挂载位置都能正确回收。 */
|
||||
function startEffect() {
|
||||
if (frame) return;
|
||||
frame = document.createElement('iframe');
|
||||
@@ -544,10 +549,13 @@ function renderSpJs(container, sp, appendNote) {
|
||||
frame.setAttribute('sandbox', 'allow-scripts');
|
||||
frame.srcdoc = sp.code;
|
||||
var inNode = /<!--\s*in-node\s*-->/.test(sp.code || '');
|
||||
frame.style.cssText = inNode
|
||||
? 'position:relative;display:block;width:100%;height:420px;border:1px solid rgba(255,255,255,.08);border-radius:12px;overflow:hidden;background:transparent;'
|
||||
: 'position:fixed;top:0;left:0;width:100%;height:100%;border:0;pointer-events:none;z-index:9999;';
|
||||
container.appendChild(frame);
|
||||
if (inNode) {
|
||||
frame.style.cssText = 'position:relative;display:block;width:100%;height:420px;border:1px solid rgba(255,255,255,.08);border-radius:12px;overflow:hidden;background:transparent;';
|
||||
container.appendChild(frame);
|
||||
} else {
|
||||
frame.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;border:0;pointer-events:none;z-index:9999;';
|
||||
document.body.appendChild(frame);
|
||||
}
|
||||
toggle_btn.textContent = '停止特效';
|
||||
out.style.display = 'block';
|
||||
out.textContent = inNode ? '已载入(运行于隔离沙箱)' : '特效已启动(运行于隔离沙箱,不影响页面导航)';
|
||||
|
||||
Reference in new issue
Block a user