如何让动态变化的HTML天气预报文本适配移动屏幕并保留原始格式
如何让动态变化的HTML天气预报文本适配移动屏幕并保留原始格式
嘿,我完全懂你的痛点——NWS的天气预报格式特别“固执”,既要完完整整保留它的换行、分段甚至那种固定宽度的排版,又要让它在各种移动屏幕上都能完整显示,不能随便换行挤乱格式,字体小点儿没关系但结构绝对不能变。你用SVG的思路方向是对的,只是细节上可以调整优化,咱们一步步来:
现有方案的小卡点
你目前的SVG框架已经搭得很到位,但有两个细节拖了后腿:
- 内部XHTML内容的格式锁定还不够严谨,容易因为缩放出现意外的排版偏移
- 没有让SVG自动根据屏幕宽度计算最优的缩放比例,导致内容可能出现横向溢出或者留白
改进后的完整方案
我把你的代码做了针对性调整,核心就是死死锁死原始格式+让SVG自动等比适配屏幕,咱们直接看代码和解释:
1. 优化SVG生成逻辑
function getFireWeatherForecast() { /* Sample API response */ return "<h1>Fire Weather Discussion</h1><p>FNUS56 KMFR 252130<br>FWFMFR<p>Fire Weather Forecast for southern Oregon and northern California<br>National Weather Service Medford, OR<br>230 PM PDT Fri Jul 25 2025<p>...RED FLAG WARNING REMAINS IN EFFECT UNTIL 11 PM PDT THIS<br>EVENING FOR ABUNDANT LIGHTNING ON DRY FUELS FOR FIRE WEATHER<br>ZONES 280, 281, 282, 284, 285, 624, AND 625...<p>.DISCUSSION...Another episode of scattered to numerous<br>thunderstorms is expected this evening, focused upon northern <br>California with activity extending into south central Oregon. <br>Storms will be slow-moving and produce rain, but the amount of <br>lightning is expected to exceed critical thresholds. Isolated <br>late day storms are possible for portions of the area Saturday <br>through Monday. The next trough may bring an enhanced risk of<br>thunderstorms during mid-week.</p><h1>Fire Weather Forecast for CAZ282</h1><p>CAZ282-CAZ282-261215-<br>Shasta-Trinity National Forest in Siskiyou County-<br>230 PM PDT Fri Jul 25 2025<p>...RED FLAG WARNING IN EFFECT UNTIL 11 PM PDT THIS EVENING...<p>.Tonight...<br>* Sky/weather...........Mostly cloudy with scattered showers and <br> thunderstorms until midnight, then partly cloudy. Haze after <br> midnight. <br>* Min temperature.......45-55. <br>* Max humidity..........85-100 percent valleys and 65-80 percent <br> ridges. <br>* 20-foot winds......... <br>* Valleys/lwr slopes...Southeast winds 5 to 10 mph. Gusts up to 25 <br> mph in the evening. <br>* Ridges/upr slopes....Southeast winds 6 to 10 mph with gusts to <br> around 25 mph shifting to the southwest 5 to 6 mph after <br> midnight. <br>* Mixing Height.....................4000-7200 ft AGL until 2100, <br> then 100-1600 ft AGL. <br>* Chance of lightning...............44 percent. <br>* Chance of wetting rain (.10 in)...30 percent. <br>* Chance of wetting rain (.25 in)...17 percent. <p>&&&<br> TEMP / HUM / POP <br>Mount Shasta 51 92 40<br>.EXTENDED...<br>.SUNDAY NIGHT...Mostly clear. Lows 45 to 55. West winds 5 to<br>8 mph. <br>.MONDAY...Mostly clear. Highs 75 to 85. Light winds becoming<br>southwest 5 to 6 mph in the afternoon and evening.<p>"; } function createForecastSVG(forecast) { const SVG_NS = "http://www.w3.org/2000/svg"; const XHTML_NS = "http://www.w3.org/1999/xhtml"; // 创建SVG容器,关键调整缩放规则 const svg = document.createElementNS(SVG_NS, "svg"); svg.setAttribute("preserveAspectRatio", "xMidYMin meet"); svg.style.width = "100%"; svg.style.height = "auto"; // 创建承载XHTML的foreignObject const foreignObject = document.createElementNS(SVG_NS, "foreignObject"); svg.appendChild(foreignObject); // 内部内容容器:强制锁定原始格式 const div = document.createElementNS(XHTML_NS, "div"); div.style.whiteSpace = "pre"; // 严格保留所有原始换行、空格 div.style.fontFamily = "monospace"; // 等宽字体确保NWS的对齐排版不乱 div.style.color = "#e1e1e1"; div.style.margin = "0"; div.style.padding = "0"; div.innerHTML = forecast; foreignObject.appendChild(div); // 等待内容渲染完成后,精准设置尺寸 requestAnimationFrame(() => { const forecastWidth = div.scrollWidth; const forecastHeight = div.scrollHeight; // 让foreignObject完全包裹内容 foreignObject.setAttribute("width", forecastWidth); foreignObject.setAttribute("height", forecastHeight); // 设置SVG的viewBox,让SVG能基于内容原始尺寸等比缩放 svg.setAttribute("viewBox", `0 0 ${forecastWidth} ${forecastHeight}`); }); return svg; } // 表单提交逻辑微调 document.getElementById("forecastForm").addEventListener("submit", async (formSubmitEvent) => { formSubmitEvent.preventDefault(); const fireWeatherForecast = getFireWeatherForecast(); let forecastWrapper = document.getElementById("forecast-card"); forecastWrapper.innerHTML = ""; const svg = createForecastSVG(fireWeatherForecast); forecastWrapper.appendChild(svg); forecastWrapper.style.visibility = "visible"; });
2. 适配移动屏幕的CSS调整
#forecast-card { visibility: hidden; display: block; overflow-y: auto; /* 只保留垂直滚动看长内容 */ overflow-x: hidden; /* 横向靠SVG缩放,不需要滚动 */ margin: 2rem auto 0 auto; background: rgba(30, 32, 35, 0.8); padding: 1.5rem; max-width: 100vw; /* 确保容器不超出屏幕宽度 */ box-sizing: border-box; } /* 让SVG自动占满容器宽度,等比缩放内部内容 */ #forecast-card svg { display: block; width: 100%; height: auto; }
关键优化点解释
- 格式锁死:用
whiteSpace: "pre"配合等宽字体,把NWS那种用空格堆出来的对齐排版死死固定住,原始换行、空格一个都不会丢 - SVG智能缩放:
preserveAspectRatio: "xMidYMin meet"加上width:100%,让SVG自动把整个天气预报内容等比缩放到容器宽度,不管内容原来有多宽,都会刚好适配移动屏幕,不会横向溢出 - 精准尺寸计算:用
scrollWidth和scrollHeight获取渲染后的真实内容尺寸,确保SVG的viewBox完全包裹内容,不会出现裁切或留白 - 滚动优化:去掉横向滚动,只保留垂直滚动,让用户专注于上下浏览长内容,体验更顺畅
这样调整后,不管NWS的预报内容是长是短、是宽是窄,都会完美保留原始格式,自动缩放到你的移动屏幕宽度,字体大小跟着SVG缩放比例自动适配,绝对不会乱换行~
内容来源于stack exchange




