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

如何用JavaScript将带路线的Google Map转为图片?解决Canvas污染问题

嘿,这个问题我之前帮不少开发者踩过坑!你遇到的Tainted canvases may not be exported错误,本质是浏览器同源策略的限制:当你用Google Maps Directions Service加载路线相关资源时,这些资源来自Google的跨域服务器,一旦Canvas加载了这类未正确配置CORS的跨域资源,就会被标记为“污染”,浏览器会直接阻止你调用toDataURL导出它。普通地图没问题,是因为基础瓦片的CORS配置在静态渲染时没触发限制,但路线服务的动态资源刚好踩中了这个规则。

下面给你一套可行的解决方案,一步步来:

1. 先升级html2canvas版本

你现在用的是0.4.1这个超老版本,它对跨域资源的处理有很多bug。直接换成最新的稳定版(比如1.4.1),新版本对CORS的支持完善太多了。

2. 调整html2canvas的配置

要同时开启useCORS和allowTaint(别担心,allowTaint只是允许合法的跨域资源加载,配合useCORS不会有安全问题),再加个scale参数提升导出图片的清晰度:

$("#divclick").click(function() {
  html2canvas(document.querySelector("#totalimage"), {
    useCORS: true,
    allowTaint: true,
    scale: 2 // 导出2倍图,更清晰
  }).then(function(canvas) {
    try {
      const dataURL = canvas.toDataURL("image/png");
      alert(dataURL);
      $('#img_val').val(dataURL);
      $("#show_img").append(canvas);
    } catch (e) {
      console.error("导出失败:", e);
    }
  }).catch(err => console.error("渲染失败:", err));
});

3. 优化Google Maps的初始化配置

在创建地图的时候,加上imageExportEnabled: true这个关键配置(它专门用来允许地图资源被导出),同时禁用默认UI减少不必要的跨域资源加载:

window.onload = function () {
  var mapOptions = {
    center: new google.maps.LatLng(markers[0].lat, markers[0].lng),
    zoom: 10,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    imageExportEnabled: true, // 允许地图资源导出
    disableDefaultUI: true // 禁用默认UI,减少跨域资源
  };
  var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
  
  // 后面的标记点、路线代码保持不变就行
  // ...
};

4. 合并重复的点击事件

你的代码里重复绑定了#divclick的点击事件(一次在开头,一次在$(function(){})里),会导致触发两次导出,直接合并成一次就好。

修正后的完整代码

我把所有调整整合好了,直接用就行:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://maps.google.com/maps/api/js?&libraries=places&key=AIzaSyAHRgfjMojo9Q6LtvpmB40V39FZtYZTPJ0"></script>
<script src="https://cdn.jsdelivr.net/npm/canvas2image@1.0.5/canvas2image.min.js"></script>

<div id="totalimage">
  <div id="dvMap" style="width: 100%; height: 500px"></div>
</div>
<div id="show_img"></div>
<button id="divclick">print</button>

<script>
var markers = [
  {
    "title": 'Alibaug',
    "lat": '18.641400',
    "lng": '72.872200',
    "description": 'Alibaug is a coastal town and a municipal council in Raigad District in the Konkan region of Maharashtra, India.'
  },
  {
    "title": 'Mumbai',
    "lat": '18.964700',
    "lng": '72.825800',
    "description": 'Mumbai formerly Bombay, is the capital city of the Indian state of Maharashtra.'
  },
  {
    "title": 'Pune',
    "lat": '18.523600',
    "lng": '73.847800',
    "description": 'Pune is the seventh largest metropolis in India, the second largest in the state of Maharashtra after Mumbai.'
  }
];

window.onload = function () {
  var mapOptions = {
    center: new google.maps.LatLng(markers[0].lat, markers[0].lng),
    zoom: 10,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    imageExportEnabled: true,
    disableDefaultUI: true
  };
  var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
  var infoWindow = new google.maps.InfoWindow();
  var lat_lng = new Array();
  var latlngbounds = new google.maps.LatLngBounds();
  
  for (i = 0; i < markers.length; i++) {
    var data = markers[i];
    var myLatlng = new google.maps.LatLng(data.lat, data.lng);
    lat_lng.push(myLatlng);
    var marker = new google.maps.Marker({
      position: myLatlng,
      map: map,
      title: data.title
    });
    latlngbounds.extend(marker.position);
    
    (function (marker, data) {
      google.maps.event.addListener(marker, "click", function (e) {
        infoWindow.setContent(data.description);
        infoWindow.open(map, marker);
      });
    })(marker, data);
  }
  
  map.setCenter(latlngbounds.getCenter());
  map.fitBounds(latlngbounds);

  // 路线绘制逻辑
  var path = new google.maps.MVCArray();
  var service = new google.maps.DirectionsService();
  var poly = new google.maps.Polyline({
    map: map,
    strokeColor: '#4986E7'
  });

  for (var i = 0; i < lat_lng.length; i++) {
    if ((i + 1) < lat_lng.length) {
      var src = lat_lng[i];
      var des = lat_lng[i + 1];
      path.push(src);
      poly.setPath(path);
      
      service.route({
        origin: src,
        destination: des,
        travelMode: google.maps.DirectionsTravelMode.DRIVING
      }, function (result, status) {
        if (status == google.maps.DirectionsStatus.OK) {
          for (var j = 0, len = result.routes[0].overview_path.length; j < len; j++) {
            path.push(result.routes[0].overview_path[j]);
          }
        }
      });
    }
  }
};

$(function() {
  $("#divclick").click(function() {
    html2canvas(document.querySelector("#totalimage"), {
      useCORS: true,
      allowTaint: true,
      scale: 2
    }).then(function(canvas) {
      try {
        const dataURL = canvas.toDataURL("image/png");
        alert(dataURL);
        $('#img_val').val(dataURL);
        $("#show_img").append(canvas);
      } catch (e) {
        console.error("导出Canvas失败:", e);
      }
    }).catch(function(err) {
      console.error("html2canvas渲染失败:", err);
    });
  });
});
</script>

备选方案

如果上面的方法还是不行,你可以试试Google Maps静态地图API——直接生成带路线的图片URL,不存在跨域污染问题,缺点是没法交互,但对于导出图片的需求来说,这也是个非常可靠的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:59:52