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

Datamap如何实现点击指定国家缩放与鼠标滚轮缩放功能共存

问题根因

d3的zoom行为会独立维护内部的位移、缩放状态,你手动修改DOM的transform属性不会同步更新zoom实例的内部状态,因此滚轮触发zoom事件时,d3会基于初始默认值(位移[0,0]、缩放比例1)计算变换参数,导致地图跳回初始状态。

修复步骤

1. 保存zoom实例

不要在done回调中匿名创建zoom行为,将实例挂载到可全局访问的位置(比如datamap实例本身):

done: function (datamap) {
  /* 鼠标滚轮缩放 */
  // 保存zoom实例,后续同步状态需要调用
  const zoomInstance = d3.behavior.zoom().on("zoom", redraw);
  datamap.zoomInstance = zoomInstance;
  datamap.svg.call(zoomInstance);
  function redraw() {
    datamap.svg
      .selectAll("g")
      .attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
  }
}

2. 修正变换顺序,同步状态到zoom实例

你原有点击逻辑里的transform顺序是先缩放后位移,和zoom事件的变换顺序不一致,会导致坐标偏移,需要调整顺序,同时在完成跳转动画后将你计算出的位移、缩放参数同步到zoom实例:

const canvas = document.getElementById("choropleth_map");
const { width, height } = canvas.getBoundingClientRect();
const canvasFactor = width / height;
selectedCountry = isoCodeConverter[Country[0].code];
const countryFeature = map.current.svg.selectAll(`.datamaps-subunit.${selectedCountry}`)["0"]["0"].__data__;
if (countryFeature !== undefined) {
  const bounds = path.bounds(countryFeature); // 获取选中国家的边界
  bounds.s = bounds[0][1];
  bounds.n = bounds[1][1];
  bounds.w = bounds[0][0];
  bounds.e = bounds[1][0];
  bounds.height = Math.abs(bounds.n - bounds.s);
  bounds.width = Math.abs(bounds.e - bounds.w);
  newScale = 0.95 / Math.max(bounds.width / width, bounds.height / height);
  const factor = bounds.width / bounds.height;
  /* 保持国家相对于画布的宽高比例,使其居中显示 */
  if (factor < 1.8) {
    bounds.w = bounds.w - Math.abs(bounds.height * canvasFactor - bounds.width) / 2;
  }
  if (factor > 1.8) {
    bounds.s = bounds.s - Math.abs(bounds.width / canvasFactor - bounds.height) / 2;
  }
  /* 更新当前地图:调整transform顺序为先位移后缩放,和zoom事件逻辑一致 */
  map.current.svg
    .selectAll("g")
    .transition()
    .duration(750)
    .attr("transform", `translate(${-bounds.w},${-bounds.s})scale(${newScale})`)
    .on('end', function() {
      // 动画结束后同步状态到zoom实例
      map.current.zoomInstance
        .scale(newScale)
        .translate([-bounds.w, -bounds.s])
        .event(map.current.svg);
    });
}

补充说明

点击国家的回调中d3.event为null是正常现象:d3.event仅在d3自身触发的事件回调生命周期内有值,自定义的点击事件中不会生成该对象,无需尝试从该属性获取参数,直接使用你自行计算的newScale和位移值即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 05:48:03