LayaAir Widget教程:UI对齐与多分辨率适配原理及实战
Widget 是 LayaAir 里做 UI 对齐和适配的重要组件这玩意儿几乎每个带界面的项目都会碰到。很多开发者一开始容易把它当成“设置坐标的辅助工具”用完发现位置不对、不刷新、跟手动改坐标冲突各种问题都来了。这篇文章结合我做 UI 适配踩过的坑把 Widget 的原理、属性、常见场景和排查方法一次讲清楚希望能帮你在做自适应界面时少走弯路。1. Widget 到底解决什么问题1.1 为多分辨率而生的“锚点盒子”做游戏或者做工具类应用最头疼的就是不同屏幕尺寸下 UI 不乱。iPhone 的小屏、平板的大屏、甚至浏览器窗口还能随便拖拽缩放如果每个控件都用固定坐标去摆放那换一个环境就全乱了。Widget 的作用简单说就是让 UI 元素相对父容器去对齐父容器变大变小它跟着变。打个比方手工时代你要把一张照片钉在墙上得量好墙的尺寸再算钉子位置。墙换了从头再量。Widget 相当于在墙上画好了参考线照片永远贴左、贴右、贴顶、居中或者拉伸填满墙变大变小都不用管具体像素值。这个组件在 LayaAir 里属于 UI 组件体系的一部分运行时会自动监听父容器的尺寸变化一旦父容器宽高改变它就重新算坐标保证控件的相对位置不变。正因如此Widget 也是多分辨率适配方案里最底层的那个环节你用得越熟练适配代码就越少。1.2 手工对齐 vs Widget 对齐差在哪手工对齐的典型做法是在界面初始化或者窗口尺寸变化时手动计算坐标// 伪代码示意手动居中 uiPanel.x (stageWidth - uiPanel.width) / 2; uiPanel.y (stageHeight - uiPanel.height) / 2;这种写法有两个问题。一是每次父容器尺寸变化都要手动重新计算漏一处就错一处。二是多处代码耦合在一起改一个尺寸相关的逻辑可能牵扯十几个地方排查起来非常费劲。Widget 的解决思路是“声明式对齐”你只需要告诉它“我要贴顶部”“我要水平居中”“我要右边距 20 像素”剩下的计算和更新它自己做。开发态少写代码运行态少出 bug。尤其在做弹窗系统、飘字提示、Tab 切换这类大量需要定位的 UI 时用 Widget 能让代码量降一个量级。2. Widget 核心属性与计算规则2.1 六个对齐属性怎么配Widget 的属性设计其实不复杂核心就是水平方向的 left、centerX、right和垂直方向的 top、centerY、bottom。每个方向有三种对齐基准你选一个方向再配一个偏移值就完成了定位。属性作用典型值left距离父容器左边界的距离10表示左对齐且往右偏移 10pxcenterX相对父容器水平中心线的偏移0表示水平居中正数右移负数左移right距离父容器右边界的距离20表示右对齐且往左偏移 20pxtop距离父容器上边界的距离0表示顶部对齐centerY相对父容器垂直中心线的偏移-50表示居中后向上偏移 50pxbottom距离父容器下边界的距离30表示底部对齐且往上偏移 30px这里要注意水平方向的三个属性不能同时生效。引擎内部有优先级判断left、centerX、right同时设置时会取其中一个作为最终水平定位依据。虽然在编辑器里你都能填但实际运算是按优先顺序来的我习惯的做法是同一方向只设置一个对齐属性避免自己把自己绕晕。垂直方向同理。top、centerY、bottom三选一再加一个偏移量就够了。另外有个容易忽略的组合用法水平方向同时设置left和right或者垂直方向同时设置top和bottom这时候控件会进入“拉伸填充”模式。比如按钮需要跟随父容器宽度自适应设置left20, right20父容器多宽它就多宽两边永远留 20px 边距。2.2 内部计算逻辑理解后不再“凭感觉”很多人用 Widget 靠试试出来对就完事了一旦出现偏差就不知道哪里出了问题。其实 Widget 的坐标计算逻辑非常固定搞清楚之后基本可以心算结果。水平方向计算规则left模式target.x left。控件的左边界放到父容器左边界往右偏移 left 像素的位置。centerX模式target.x (parent.width - target.width) / 2 centerX。先算父容器水平居中位置再叠加上你给的偏移。right模式target.x parent.width - target.width - right。控件的右边界贴着父容器的右边界再往左偏移 right 像素。垂直方向计算规则一一对应top模式target.y top。centerY模式target.y (parent.height - target.height) / 2 centerY。bottom模式target.y parent.height - target.height - bottom。拉伸填充模式的计算同时设置left和right时控件的宽度会被动态计算为parent.width - left - right而不是你手动设置的宽度。同时设置top和bottom时控件的高度同理。理解这套规则的关键点在于Widget 算的是控件的 x、y和拉伸模式下的宽高它依赖父容器的尺寸和控件自身的尺寸。如果你发现 Widget 的位置和预期不一样第一反应应该去检查父容器尺寸和控件尺寸是否和你想的一致而不是盲目调偏移。另外Widget 的坐标计算发生在组件激活、父容器尺寸变化这两个时机。平时一切正常如果代码里手动改了控件尺寸但没触发 Widget 刷新那位置就可能停留在旧状态这在后面排查章节我会详细说。3. 典型场景实操从导航栏到全屏拉伸3.1 顶部导航栏与底部操作栏移动端界面最常见的布局就是顶部标题栏、底部操作栏、中间内容区。用 Widget 做这种结构非常直接。顶部标题栏给标题栏节点挂上 Widget设置top0, centerX0。这时无论屏幕多宽标题栏都水平居中、紧贴顶部如果同时想要它撑满宽度可以再设置left0, right0。底部操作栏挂 Widget 后设置bottom0, left0, right0。这样操作栏会始终贴在父容器底部并且宽度自动跟随父容器两边不留空隙。中间内容区通常是一个滚动列表或内容面板设置top标题栏高度, bottom操作栏高度, left0, right0。这样内容区就被上下两个固定栏“夹”在中间屏幕变高它变高屏幕变矮它变矮永远不会被遮挡。这里有个实操细节如果顶部栏和底部栏都有固定高度内容区的top和bottom要分别等于两个栏的高度值而不是 0。比如标题栏高 88px操作栏高 120px内容区就设置top88, bottom120。3.2 居中弹窗与四角按钮弹窗居中是最常用的需求。给弹窗节点挂 Widget设置centerX0, centerY0不管弹窗尺寸多大它都会出现在父容器的正中心。如果弹窗需要从中心往上挪一点比如要避开底部关闭按钮就把centerY设为负值比如centerY-30表示居中后再往上偏移 30px。四角按钮这种需求也适合用 Widget。左上角返回按钮设置left15, top15右上角设置按钮设置right15, top15左下角帮助按钮设置left15, bottom15右下角音效开关设置right15, bottom15。这样不管屏幕是什么尺寸四个角的内容永远待在墙角且边距保持一致。可能有人会问这跟我写死坐标有什么区别区别在于父容器尺寸变化后的自动跟随。一个固定 375x812 的设计稿里右上角按钮写死x340, y20没问题但换到 768x1024 的平板上这个按钮就飘到中间去了。而用right15, top15在 768 宽的容器里它会自动跑到 768-15 这个位置完全不用你管。3.3 全屏背景图与左右自适应容器全屏背景图通常的做法是让图片节点覆盖整个父容器。给背景节点挂 Widget设置left0, right0, top0, bottom0这样背景会跟随父容器尺寸拉伸始终填满整个区域。要注意的是这种拉伸模式只改变节点宽高不会改变图片本身的九宫格或填充方式。如果背景图片带圆角或边框拉伸后可能变形这时需要用九宫格方式处理图片资源或者用sizeGrid属性来保护边角。左右自适应容器适合做分栏布局。比如一个设置界面左侧是菜单栏、右侧是内容区左侧固定 200px 宽右侧应该占满剩余空间。给右侧容器挂 Widget设置left200, right0, top0, bottom0这样无论父容器多宽右侧都是“总宽度减 200”自动完成分栏。这类布局在 PC 端或可拖拽窗口的工具类应用里特别实用。窗口尺寸变化时左侧固定右侧自适应整个界面不会有任何错位。4. 动态创建与代码调用4.1 UI 编辑器配置转代码如果你的项目使用 LayaAir IDE 的 UI 编辑器配置 Widget 基本是可视化操作选中节点在属性面板添加 Widget 组件然后填对齐方式和偏移值即可。编辑器生成的 UI 代码里会自动包含 Widget 的相关配置。这里分享一个经验UI 编辑器里配 Widget尽量把对齐需求在编辑器里完成不要在加载完 UI 之后再用代码手动改坐标。因为 Widget 会在运行时的某些时机重新计算坐标你手动设置的 x、y 可能会被覆盖出现“明明改了为什么又弹回去”的怪现象。如果确实需要在运行时调整建议通过修改 Widget 的属性来改而不是直接改节点坐标。比如想让居中弹窗向上偏移应该改centerY而不是改y。4.2 运行时动态添加 Widget动态创建 UI 的时候也可以直接在代码里挂 Widget。以 LayaAir 3.x 的组件化写法为例大致是这样的流程// 创建一个按钮并添加到某个容器 const btn new Button(); btn.skin resources/btn_normal.png; container.addChild(btn); // 给按钮动态添加 Widget 组件并设置对齐 const widget btn.addComponent(Widget); widget.alignH center; widget.alignV middle; widget.centerX 0; widget.centerY -40;在 LayaAir 2.x 里习惯上也可以直接在显示对象上挂 widget 属性思路是一样的。不管哪个版本关键都是把对齐声明交给 Widget而不是自己手算坐标。动态添加 Widget 最常见的坑是时机问题如果节点已经添加到显示列表并且父容器已完成布局你再去添加 Widget组件内部会立刻执行一次对齐计算通常没问题。但如果你在父容器尺寸变化之前就添加了 Widget之后父容器变化时Widget 会正常监听并更新可如果你的父容器没有正确派发尺寸变化事件位置就可能不准。所以动态创建控件时我习惯在onEnable或onAwake之后设置 Widget避免过早挂载导致初始化顺序问题。代码写法的细节各版本略有差异我一般会在项目里封装一个工具方法// 设置水平居中垂直方向指定偏移 function alignCenter(node: Laya.Sprite, offsetY: number 0): void { const widget node.getComponent(Widget) || node.addComponent(Widget); widget.alignH center; widget.alignV middle; widget.centerX 0; widget.centerY offsetY; }这样调用方只关心“我要什么效果”不关心 Widget 的具体配置代码会更整洁。5. 常见问题与排查心得5.1 Widget 不生效的几种原因我见过最多的“Widget 没反应”问题根源基本是下面几种。第一种父容器不是实际布局容器。Widget 对齐依赖父容器的宽高如果你的控件直接挂在舞台根节点下而舞台上还有其他容器、适配模式又设置了缩放那 Widget 对齐的“父容器”可能和你想的不是同一个。排查时先确认节点的 parent 是谁父容器的宽高是不是符合预期。第二种控件自身尺寸为 0 或没初始化完成。居中计算依赖控件自身的宽高如果控件还没加载完资源、宽度高度还是 0centerX算出来的位置自然不对。这种情况尤其容易出现在动态创建图片节点时皮肤还没加载就加了 Widget。解决办法是先设置好资源或尺寸再让 Widget 重新对齐。第三种Widget 添加时机不对。前面提到 Widget 在特定时机计算坐标如果你在节点加入显示列表之前就配置好 Widget、之后才加入某些版本下对齐计算可能没有触发。我处理这种情况一般是强制刷新一下或者延迟一帧再设置 Widget 属性。第四种手动改了 x、y 之后以为 Widget 会“记住”。Widget 不等于一次性设置坐标改动 x、y 后只要父容器尺寸变化或 Widget 重新计算坐标会被重新覆盖。这个不算 bug但要理解 Widget 的定位逻辑优先级更高。5.2 Widget 与 anchor、手动坐标的冲突anchor轴心点是很多人忽略的一个坑。Widget 计算出来的 x、y 是节点坐标而节点实际渲染位置还受轴心点影响。默认情况下 LayaAir 大部分 UI 组件的轴心点在左上角也就是anchorX0, anchorY0这种情况下 Widget 的计算结果和视觉位置一致。一旦手动设置了轴心点比如让按钮绕着中心旋转把anchorX0.5, anchorY0.5设置了再挂上 Widget 做居中视觉上就会出现偏差Widget 把节点坐标放在了父容器中心但由于轴心点在节点中心节点看起来还是居中的听起来没问题可如果轴心点设置不对称或者轴心点在高和宽的比例不同就会偏。所以我的建议是如果某个节点既要旋转缩放又要做 Widget 对齐优先用子节点的方式分离逻辑。父节点挂 Widget 负责定位子节点负责旋转缩放避免轴心点互相干扰。手动坐标的冲突前面提过这里再补充一个场景拖动交互。如果你做了一个可拖拽的弹窗拖拽过程中在改节点的 x、y而弹窗本身挂着 Widget那么一旦有父容器尺寸变化弹窗就被 Widget“拉回”到原来的对齐位置。遇到这种需求要么拖拽结束后清掉 Widget 并手动记录位置要么让拖拽逻辑同时更新 Widget 的偏移属性否则就会出现“拖过去又弹回来”的诡异表现。5.3 性能与批量适配经验Widget 本身性能开销不大但如果你有大量节点都在做对齐比如一个界面几十个控件都挂了 Widget父容器尺寸变化时会触发一大批坐标计算和属性刷新。大部分项目不会因为这个卡但在低端机上如果同时伴随大量图片重绘可能会有可感知的卡顿。我处理批量适配的顺序是这样先区分哪些控件真的需要动态跟随哪些只需要在初始化时对齐一次。对于只需要初始化时对齐一次的控件我不会挂 Widget而是初始化时手动计算坐标或者挂上 Widget 之后在合适时机移除组件。这样可以减少运行期的监听开销。批量适配另一个高效做法是分层处理。让最外层容器做 Widget 定位内部子控件用相对坐标布局而不是每个小控件都挂 Widget。比如一个弹窗内部有标题、文本、按钮只需要弹窗整体挂 Widget 居中内部控件全部都相对弹窗本体摆放不需要各自再做对齐。这样 Widget 数量少布局思路也更清晰排查问题也更直观。关于不同适配模式下的 Widget 表现我补充一个经验LayaAir 的全局缩放适配会影响舞台内容的视觉尺寸但 Widget 本身是相对父容器计算的所以适配模式变了并不会让 Widget 失效只是父容器尺寸在不同模式下会不同。你在设计时要明确父容器到底是逻辑尺寸还是物理尺寸不然会算出不预期的位置。6. 个人经验总结Widget 用久了回头再看它其实就是一个“声明式布局”工具核心价值是把 UI 定位从“每次都要算”变成“告诉它怎么摆”。用得好多分辨率适配工作量能减少一大半用不好反而会出现各种隐性问题。我个人在实际项目里的体会是能用 Widget 的地方尽量用但不要滥用。像弹窗居中、底部操作栏、顶栏标题、全屏背景这类“跟随容器变化”的场景Widget 是最优解。而像列表项内部、循环复用的子控件就不太适合全场挂 Widget最好由父容器统一管理布局。最后分享一个小技巧当你调试某个 Widget 定位问题时别急着改属性先在运行时把父容器的尺寸、控件自身尺寸打出来看看。八成问题都出在“我以为父容器是这个尺寸”和“实际父容器尺寸不一样”的偏差上。数据清楚了解决方案通常会自己浮现。Widget 这个东西原理不复杂但结合具体项目场景细节特别多。这篇把常见场景和坑都过了一遍希望能给你一些实在的参考。接下来做新界面的时候可以先想清楚“哪个节点该挂 Widget、挂哪种对齐”布局写起来会顺手很多。

相关新闻

分布式驱动汽车直接横摆力矩控制的分层架构设计实践

分布式驱动汽车直接横摆力矩控制的分层架构设计实践

做分布式驱动汽车稳定性控制,最直观的感受是:车变“活”了,但问题也变多了。四个轮子各自带一个驱动电机,每个轮子的扭矩都能独立控制,这对整车动力学控制来说是一个全新维度。传统燃油车时代,稳定性控制依…

2026/10/12 5:24:09 阅读更多 →
从AnyPS5看PS5模拟器开发:硬件逆向与系统层挑战

从AnyPS5看PS5模拟器开发:硬件逆向与系统层挑战

把 "AnyPS5" 这个项目文件夹建起来的时候,其实是2020年底,PS5刚发了没几个月。当时圈子里讨论最多的是"这东西什么时候能被摸透",大家猜得五花八门,但真正动手的人没几个。我属于那种闲不住的人,索…

2026/10/12 5:24:09 阅读更多 →
自建PS5外设桥接层AnyPS5:让第三方手柄摇杆统统兼容

自建PS5外设桥接层AnyPS5:让第三方手柄摇杆统统兼容

客厅里那台PS5静静躺了两个月,媳妇想用某品牌手柄玩双人游戏,孩子想用廉价飞行摇杆体验模拟飞行,我本想拿键鼠打射击游戏省钱,结果全部被系统挡在门外。外设的兼容问题比想象中更折腾:不是插上就能用的USB设备&#xf…

2026/10/12 5:24:09 阅读更多 →

最新新闻

有害气体控制洁净工程的底层逻辑:从过滤到吸附,从压差到监测

有害气体控制洁净工程的底层逻辑:从过滤到吸附,从压差到监测

空气里最危险的不是脏,而是失控:有害气体控制洁净工程的底层逻辑干了这么多年洁净工程,我越来越觉得“洁净”这个词会误导人。很多人一听到洁净室,想到的就是无尘、高等级过滤、白大褂和干干净净的地板,下意识把“颗粒…

2026/10/12 6:03:33 阅读更多 →
Pygame乒乓球游戏开发实战:从游戏循环到碰撞检测的完整指南

Pygame乒乓球游戏开发实战:从游戏循环到碰撞检测的完整指南

简介:游戏循环是几乎所有实时游戏的心跳,它决定了每一帧里输入、更新与渲染的执行顺序。碰撞检测则负责回答“物体是否重叠”这个基本问题,而引擎中那些微妙的物理手感,往往源于对碰撞响应和状态管理的精细控制。乒乓球游戏恰好是…

2026/10/12 6:03:33 阅读更多 →
栈的压入、弹出序列判定算法详解:辅助栈模拟与 Java 实现(YCBlogs 剑指 Offer 系列)

栈的压入、弹出序列判定算法详解:辅助栈模拟与 Java 实现(YCBlogs 剑指 Offer 系列)

教程技术博客文档 【免费下载链接】YCBlogs 技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分fl…

2026/10/12 6:03:33 阅读更多 →
C# TCP服务器与客户端双向通信:骨架搭建与避坑指南

C# TCP服务器与客户端双向通信:骨架搭建与避坑指南

简介:这是一份面向C#网络编程初学者的TCP通信示例工程,目标是用一个程序实现TCP客户端与服务器之间的互发消息,并支持在客户端界面点击按钮弹出服务器界面。资源围绕System.Net命名空间下的TcpListener与TcpClient展开,覆盖端口绑…

2026/10/12 6:03:32 阅读更多 →
CodeIgniter 4.7.4 安全与稳定性更新详解:四个安全公告与十余项缺陷修复

CodeIgniter 4.7.4 安全与稳定性更新详解:四个安全公告与十余项缺陷修复

后端Web框架 【免费下载链接】CodeIgniter4 Open Source PHP Framework (originally from EllisLab) 项目地址: https://gitcode.com/gh_mirrors/co/CodeIgniter4 点击查看 免费下载 CodeIgniter 4.7.4(2026 年 7 月 7 日发布)是一次以安全加…

2026/10/12 6:03:32 阅读更多 →
Tortoise-ORM 与 Sanic 集成实战:register_tortoise 生命周期管理全解析

Tortoise-ORM 与 Sanic 集成实战:register_tortoise 生命周期管理全解析

数据库后端 【免费下载链接】tortoise-orm Familiar asyncio ORM for python, built with relations in mind 项目地址: https://gitcode.com/gh_mirrors/to/tortoise-orm 点击查看 免费下载 本文以 Tortoise-ORM 仓库中 Sanic 集成示例 为主线,系统讲解…

2026/10/12 6:02:32 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →