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
相关产品推荐
相关产品推荐

