You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过IntersectionObserver控制AMCharts地球动画启停

问题根源

你之前的代码不生效,核心是三个问题:

  • 自定义的cancelAnimationFrame拿不到AMCharts内部维护的渲染帧ID,相当于对着空气喊停,完全碰不到实际运行的渲染逻辑
  • 配置项am4core.options.onlyShowOnViewport = true仅控制图表首次懒加载时机,图表加载完成后滚出视口,不会自动停止已经启动的渲染循环
  • 单独停止经度旋转的补间动画没用,AMCharts内部的全局渲染循环还在持续运行,依然会占用大量系统资源
实现方案

AMCharts v4 原生提供了渲染启停、动画暂停的内置方法,不需要自己手动拦截帧循环:

  • 用图表实例自带的chart.sleep()方法终止内部所有渲染循环,完全释放帧资源占用;回到视口时调用chart.wake()恢复渲染
  • 旋转动画用内置的pause()/resume()方法控制暂停和续播,保留动画进度,不会出现回到视口动画重置跳变的问题
  • 加状态标记避免滚动过程中重复触发启停逻辑,给IntersectionObserver加阈值减少边缘抖动
修正后完整代码

JS部分(替换原globe.js内容即可)

/**
 * ---------------------------------------
 * This demo was created using amCharts 4.
 * ---------------------------------------
 */

// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
am4core.options.queue = true;
am4core.options.onlyShowOnViewport = true;
var chart = am4core.create("chartdiv", am4maps.MapChart);

// Set map definition
chart.geodata = am4geodata_worldLow;

// Set projection
chart.projection = new am4maps.projections.Orthographic();
chart.panBehavior = "none";
chart.deltaLatitude = -20;
chart.padding(20,20,20,20);

// Create map polygon series
var polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
polygonSeries.useGeodata = true;

// Configure series
var polygonTemplate = polygonSeries.mapPolygons.template;
polygonTemplate.fill = am4core.color("#d5ebfe");
polygonTemplate.stroke = am4core.color("#fff");
polygonTemplate.strokeWidth = 0.0;

var graticuleSeries = chart.series.push(new am4maps.GraticuleSeries());
graticuleSeries.mapLines.template.line.stroke = am4core.color("#00000");
graticuleSeries.mapLines.template.line.strokeOpacity = 0.00;
graticuleSeries.fitExtent = false;

chart.maxZoomLevel = 1;
chart.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 0.0;
chart.backgroundSeries.mapPolygons.template.polygon.fill = am4core.color("#ffffff");

// 存储旋转动画实例、运行状态标记
let rotationAnimation;
let isChartRunning = false;

// 初始化旋转动画
setTimeout(function(){
  if(!rotationAnimation){
    rotationAnimation = chart.animate({property:"deltaLongitude", to:100000}, 20000000);
    // 初始化完成先暂停,等进入视口再启动
    rotationAnimation.pause();
  }
}, 3000)

chart.seriesContainer.events.on("down", function(){
  if(rotationAnimation) rotationAnimation.stop();
})

// 启动渲染
function startChart() {
  if(isChartRunning) return;
  chart.wake();
  if(rotationAnimation) rotationAnimation.resume();
  isChartRunning = true;
  console.log('render has been started');
}

// 停止渲染
function stopChart() {
  if(!isChartRunning) return;
  if(rotationAnimation) rotationAnimation.pause();
  chart.sleep();
  isChartRunning = false;
  console.log('render has been halted');
}

const statusElem = document.querySelector('.status');
const onScreen = new Set();
const intersectionObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      onScreen.add(entry.target);
      startChart();
    } else {
      onScreen.delete(entry.target);
      stopChart();
    }     
  });
  statusElem.textContent = onScreen.size
    ? `on screen: ${[...onScreen].map(e => e.id).join(', ')}`
    : 'none';
}, {
  // 露出10%时触发启动,避免边缘反复抖动
  threshold: 0.1
});

document.querySelectorAll('#chartdiv').forEach(elem => intersectionObserver.observe(elem));

HTML部分(修复重复status元素的问题)

<body>
  <script src="https://www.amcharts.com/lib/4/core.js"></script>
  <script src="https://www.amcharts.com/lib/4/maps.js"></script>
  <script src="https://www.amcharts.com/lib/4/geodata/worldLow.js"></script>
  <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
  
  <div class="status"></div>
  <header id="header"></header>

  <div class="section" id="section0">
    <div class="intro">
      <br><br><br><br><br><br><br><br><br><br><br><br>
      <div id="chartdiv"></div>
      <script src="js/globe.js"></script>
      <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
    </div>
  </div>
</body>

原CSS代码不需要修改,可以直接使用。

效果说明
  • 图表滚出视口后,内部渲染循环会被完全终止,CPU/GPU占用直接降到接近0,不会再拖慢页面其他动画
  • 旋转动画进度会被保留,滚回视口时从暂停位置继续旋转,不会出现角度跳变
  • 加了重复执行拦截,滚动过程中不会反复触发启停逻辑,性能损耗可以忽略

内容的提问来源于stack exchange,提问作者David Morton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 03:27:52