You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何让动态变化的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;
}

关键优化点解释

  1. 格式锁死:用whiteSpace: "pre"配合等宽字体,把NWS那种用空格堆出来的对齐排版死死固定住,原始换行、空格一个都不会丢
  2. SVG智能缩放preserveAspectRatio: "xMidYMin meet"加上width:100%,让SVG自动把整个天气预报内容等比缩放到容器宽度,不管内容原来有多宽,都会刚好适配移动屏幕,不会横向溢出
  3. 精准尺寸计算:用scrollWidthscrollHeight获取渲染后的真实内容尺寸,确保SVG的viewBox完全包裹内容,不会出现裁切或留白
  4. 滚动优化:去掉横向滚动,只保留垂直滚动,让用户专注于上下浏览长内容,体验更顺畅

这样调整后,不管NWS的预报内容是长是短、是宽是窄,都会完美保留原始格式,自动缩放到你的移动屏幕宽度,字体大小跟着SVG缩放比例自动适配,绝对不会乱换行~

内容来源于stack exchange

火山引擎 最新活动