UNPKG

bytefun

Version:

一个打通了原型设计、UI设计与代码转换、跨平台原生代码开发等的平台

295 lines (215 loc) 9.73 kB
/** * HTML零宽高修复器 * 参考vsFixZeroWH.ts算法,通过子节点位置计算父节点的合适宽高 */ export class HtmlZeroWHFixer { /** * 修复HTML中的零宽高节点 * @param htmlContent HTML内容字符串 * @returns 修复后的HTML内容 */ public static fixZeroWidthAndHeight(htmlContent: string): string { try { let modifiedHtml = htmlContent; let hasChanges = false; // 先修正零宽度节点 const widthChanges = this.fixZeroWidthNodes(modifiedHtml); if (widthChanges.hasChanges) { modifiedHtml = widthChanges.modifiedHtml; hasChanges = true; } // 再修正零高度节点 const heightChanges = this.fixZeroHeightNodes(modifiedHtml); if (heightChanges.hasChanges) { modifiedHtml = heightChanges.modifiedHtml; hasChanges = true; } if (hasChanges) { } else { } return modifiedHtml; } catch (error) { console.error('❌ [HtmlZeroWHFixer] 修正零宽高节点失败:', error); return htmlContent; // 出错时返回原始内容 } } /** * 修正零宽度节点 * @param htmlContent HTML内容 * @returns 修正结果 */ private static fixZeroWidthNodes(htmlContent: string): { hasChanges: boolean, modifiedHtml: string } { try { let modifiedHtml = htmlContent; let hasChanges = false; // 查找所有w="0"的节点 const zeroWidthRegex = /<([^>]+)\sw="0"([^>]*)>/g; let match; const zeroWidthNodes: Array<{ fullTag: string, tagName: string, attributes: string }> = []; while ((match = zeroWidthRegex.exec(htmlContent)) !== null) { const fullTag = match[0]; const beforeW = match[1]; const afterW = match[2]; // 提取标签名 const tagNameMatch = beforeW.match(/^\s*(\w+)/); const tagName = tagNameMatch ? tagNameMatch[1] : 'div'; zeroWidthNodes.push({ fullTag: fullTag, tagName: tagName, attributes: beforeW + afterW }); } for (let i = 0; i < zeroWidthNodes.length; i++) { const node = zeroWidthNodes[i]; // 查找该节点的子节点 const nodePattern = this.escapeRegExp(node.fullTag); const nodeRegex = new RegExp(nodePattern + '([\\s\\S]*?)<\\/' + node.tagName + '>', 'i'); const nodeMatch = modifiedHtml.match(nodeRegex); if (!nodeMatch) { continue; } const nodeContent = nodeMatch[1]; // 提取子节点的位置和宽度信息 const childNodes = this.extractChildNodesInfo(nodeContent); if (childNodes.length === 0) { continue; } // 过滤出有效宽度的子节点 const validChildren = childNodes.filter(child => child.w > 0); if (validChildren.length === 0) { continue; } // 计算子节点的最左边和最右边位置 let minLeft = Number.MAX_SAFE_INTEGER; let maxRight = Number.MIN_SAFE_INTEGER; validChildren.forEach(child => { const childRight = child.x + child.w; minLeft = Math.min(minLeft, child.x); maxRight = Math.max(maxRight, childRight); }); // 提取父节点的x坐标 const parentXMatch = node.fullTag.match(/\sx="(\d+)"/); const parentX = parentXMatch ? parseInt(parentXMatch[1]) : 0; // 计算父节点应该的宽度 const newW = maxRight - Math.min(parentX, minLeft); // 替换w="0"为新计算的宽度 const newTag = node.fullTag.replace(/\sw="0"/, ` w="${newW}"`); // 如果需要调整x坐标 const adjustedX = Math.min(parentX, minLeft); let finalTag = newTag; if (adjustedX !== parentX) { finalTag = newTag.replace(/\sx="(\d+)"/, ` x="${adjustedX}"`); } modifiedHtml = modifiedHtml.replace(node.fullTag, finalTag); hasChanges = true; } return { hasChanges, modifiedHtml }; } catch (error) { console.error('❌ [HtmlZeroWHFixer] 修正零宽度节点失败:', error); return { hasChanges: false, modifiedHtml: htmlContent }; } } /** * 修正零高度节点 * @param htmlContent HTML内容 * @returns 修正结果 */ private static fixZeroHeightNodes(htmlContent: string): { hasChanges: boolean, modifiedHtml: string } { try { let modifiedHtml = htmlContent; let hasChanges = false; // 查找所有h="0"的节点 const zeroHeightRegex = /<([^>]+)\sh="0"([^>]*)>/g; let match; const zeroHeightNodes: Array<{ fullTag: string, tagName: string, attributes: string }> = []; while ((match = zeroHeightRegex.exec(htmlContent)) !== null) { const fullTag = match[0]; const beforeH = match[1]; const afterH = match[2]; // 提取标签名 const tagNameMatch = beforeH.match(/^\s*(\w+)/); const tagName = tagNameMatch ? tagNameMatch[1] : 'div'; zeroHeightNodes.push({ fullTag: fullTag, tagName: tagName, attributes: beforeH + afterH }); } for (let i = 0; i < zeroHeightNodes.length; i++) { const node = zeroHeightNodes[i]; // 查找该节点的子节点 const nodePattern = this.escapeRegExp(node.fullTag); const nodeRegex = new RegExp(nodePattern + '([\\s\\S]*?)<\\/' + node.tagName + '>', 'i'); const nodeMatch = modifiedHtml.match(nodeRegex); if (!nodeMatch) { continue; } const nodeContent = nodeMatch[1]; // 提取子节点的位置和高度信息 const childNodes = this.extractChildNodesInfo(nodeContent); if (childNodes.length === 0) { continue; } // 过滤出有效高度的子节点 const validChildren = childNodes.filter(child => child.h > 0); if (validChildren.length === 0) { continue; } // 计算子节点的最上边和最下边位置 let minTop = Number.MAX_SAFE_INTEGER; let maxBottom = Number.MIN_SAFE_INTEGER; validChildren.forEach(child => { const childBottom = child.y + child.h; minTop = Math.min(minTop, child.y); maxBottom = Math.max(maxBottom, childBottom); }); // 提取父节点的y坐标 const parentYMatch = node.fullTag.match(/\sy="(\d+)"/); const parentY = parentYMatch ? parseInt(parentYMatch[1]) : 0; // 计算父节点应该的高度 const newH = maxBottom - Math.min(parentY, minTop); // 替换h="0"为新计算的高度 const newTag = node.fullTag.replace(/\sh="0"/, ` h="${newH}"`); // 如果需要调整y坐标 const adjustedY = Math.min(parentY, minTop); let finalTag = newTag; if (adjustedY !== parentY) { finalTag = newTag.replace(/\sy="(\d+)"/, ` y="${adjustedY}"`); } modifiedHtml = modifiedHtml.replace(node.fullTag, finalTag); hasChanges = true; } return { hasChanges, modifiedHtml }; } catch (error) { console.error('❌ [HtmlZeroWHFixer] 修正零高度节点失败:', error); return { hasChanges: false, modifiedHtml: htmlContent }; } } /** * 提取子节点的位置和尺寸信息 * @param content 节点内容 * @returns 子节点信息数组 */ private static extractChildNodesInfo(content: string): Array<{ x: number, y: number, w: number, h: number }> { const childNodes: Array<{ x: number, y: number, w: number, h: number }> = []; // 查找所有带有x,y,w,h属性的标签 const nodeRegex = /<[^>]+\sx="(\d+)"[^>]+\sy="(\d+)"[^>]+\sw="(\d+)"[^>]+\sh="(\d+)"[^>]*>/g; let match; while ((match = nodeRegex.exec(content)) !== null) { const x = parseInt(match[1]); const y = parseInt(match[2]); const w = parseInt(match[3]); const h = parseInt(match[4]); childNodes.push({ x, y, w, h }); } return childNodes; } /** * 转义正则表达式特殊字符 * @param string 要转义的字符串 * @returns 转义后的字符串 */ private static escapeRegExp(string: string): string { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } }