feat: z-index排序 + 重叠去重 + overflow裁剪 + 图片支持
- z-index 排序:兄弟节点按 zIndex 排序,高层在上 - 重叠去重:同坐标完全相同的节点只保留后者 - overflow:hidden:用 clipRect 裁剪,完全在区域外的节点跳过 - 图片支持:RECTANGLE 有 backgroundImages 的也作为图片渲染 - 三个 PPT 都验证通过
This commit is contained in:
+53
-4
@@ -571,13 +571,62 @@ function convert(input) {
|
|||||||
// 移除已合并的原始节点
|
// 移除已合并的原始节点
|
||||||
sg.children = sg.children.filter(n => !merged.has(n.id));
|
sg.children = sg.children.filter(n => !merged.has(n.id));
|
||||||
|
|
||||||
|
// ===== z-index 排序:同父容器的兄弟节点按 zIndex 排序 =====
|
||||||
|
sg.children.sort(function(a, b) {
|
||||||
|
const za = parseInt(a.styles?.zIndex) || 0;
|
||||||
|
const zb = parseInt(b.styles?.zIndex) || 0;
|
||||||
|
return za - zb;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== 同坐标完全重叠去重:保留后者(高层) =====
|
||||||
|
var seen = {};
|
||||||
|
var overlapRemove = new Set();
|
||||||
|
for (var i = sg.children.length - 1; i >= 0; i--) {
|
||||||
|
var ci = sg.children[i];
|
||||||
|
if (!ci.rect) continue;
|
||||||
|
var key = Math.round(ci.rect.x) + ',' + Math.round(ci.rect.y) + ',' + Math.round(ci.rect.w) + ',' + Math.round(ci.rect.h);
|
||||||
|
if (seen[key]) {
|
||||||
|
overlapRemove.add(ci.id); // 后出现的(更高层)保留,前面的移除
|
||||||
|
} else {
|
||||||
|
seen[key] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (overlapRemove.size > 0) {
|
||||||
|
sg.children = sg.children.filter(n => !overlapRemove.has(n.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== overflow:hidden 裁剪:子元素超出父容器 clipped 区域的跳过 =====
|
||||||
|
// 建立父容器 clip 区域映射
|
||||||
|
var clipMap = {}; // id → {x, y, w, h}
|
||||||
|
function buildClipMap(node, parentClip) {
|
||||||
|
if (!node) return;
|
||||||
|
var myClip = parentClip;
|
||||||
|
if (node.clipped && node.clipRect) {
|
||||||
|
myClip = { x: node.clipRect.x, y: node.clipRect.y, w: node.clipRect.width, h: node.clipRect.height };
|
||||||
|
}
|
||||||
|
clipMap[node.id] = myClip;
|
||||||
|
if (node.children) for (var c of node.children) buildClipMap(c, myClip);
|
||||||
|
}
|
||||||
|
// 只对 slide 容器的子树建 clip 映射
|
||||||
|
var slideNode = slideContainers.find(s => s.name === sg.name);
|
||||||
|
if (slideNode) buildClipMap(slideNode, null);
|
||||||
|
|
||||||
for (const n of sg.children) {
|
for (const n of sg.children) {
|
||||||
// 跳过 raster 底图(保留 IMAGE 类型)
|
// 跳过 raster 底图(保留 IMAGE 类型和有背景图的 RECTANGLE)
|
||||||
if (n.type === 'RECTANGLE') { continue; }
|
if (n.type === 'RECTANGLE' && !n.backgroundImages?.length) { continue; }
|
||||||
if (n.tag === 'BODY' || n.tag === 'HTML') { continue; }
|
if (n.tag === 'BODY' || n.tag === 'HTML') { continue; }
|
||||||
|
|
||||||
const r = n.rect;
|
const r = n.rect;
|
||||||
if (r == null || r.w == null || r.h == null) { continue; }
|
if (r == null || r.w == null || r.h == null) { continue; }
|
||||||
|
|
||||||
|
// overflow:hidden 裁剪:节点完全在 clip 区域外时跳过
|
||||||
|
var clip = clipMap[n.id];
|
||||||
|
if (clip) {
|
||||||
|
var nx = r.x, ny = r.y, nr2 = r.x + r.w, nb = r.y + r.h;
|
||||||
|
if (nx > clip.x + clip.w || nr2 < clip.x || ny > clip.y + clip.h || nb < clip.y) {
|
||||||
|
continue; // 完全在 clip 区域外
|
||||||
|
}
|
||||||
|
}
|
||||||
const S = 1/72;
|
const S = 1/72;
|
||||||
const opts = {
|
const opts = {
|
||||||
x: Math.round(r.x * S * 1000) / 1000,
|
x: Math.round(r.x * S * 1000) / 1000,
|
||||||
@@ -647,8 +696,8 @@ function convert(input) {
|
|||||||
continue; // TEXT 节点不参与后续的填充色/边框逻辑
|
continue; // TEXT 节点不参与后续的填充色/边框逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 图片节点 =====
|
// ===== 图片节点(IMAGE 类型或有背景图的 RECTANGLE) =====
|
||||||
if (n.type === 'IMAGE' && n.backgroundImages && n.backgroundImages.length > 0) {
|
if ((n.type === 'IMAGE' || n.type === 'RECTANGLE') && n.backgroundImages && n.backgroundImages.length > 0) {
|
||||||
var imgKey = n.backgroundImages[0];
|
var imgKey = n.backgroundImages[0];
|
||||||
var asset = ctx.assets[imgKey];
|
var asset = ctx.assets[imgKey];
|
||||||
if (asset && asset.data) {
|
if (asset && asset.data) {
|
||||||
|
|||||||
Reference in New Issue
Block a user