D3 Choropleth Map + всплывающая подсказка + проблема с размещением в браузере - PullRequest
0 голосов
/ 01 октября 2018

спасибо заранее за проверку этого вопроса.Я создал карту хороплета D3, и у меня возникают проблемы при попытке выяснить, почему всплывающая подсказка имеет некоторые проблемы в отношении того, где карта находится в браузере (например, прокрутка вверх и вниз по странице и перемещение карты вверх / вниз),Для начала вот код, с которым я работаю, "choropleth.tag":

<%@ include file="/WEB-INF/jsp/site/taglib.jsp"%>
<%@ attribute name="baseDataUrl" required="true" %>
<%@ attribute name="domain" required="true" %>
<%@ attribute name="id" required="true" %>
<%@ attribute name="dataInputLabel" required="false" %>

<div class="container">
	<div class="row">
		<div class="chropleth-map">
			<svg id="${id}-d3-choroplethmap" viewBox="-120 -150 1200 700" class="d3-map-animation" preserveAspectRatio="xMidYMid meet"></svg>
			<!--<svg id="${id}-svgkey" viewBox="650 -25 150 50" class="fade-in-slow-2 svgkey"></svg>-->
		</div>
		<div class="col-xs-6 tooltip" style="opacity: 0;"></div>
	</div>
</div>

<script src="<c:url value="/js/libs/d3v4/d3.v4.min.js"/>"></script>
<script src="<c:url value="/js/libs/d3v4/d3-scale-chromatic.v1.min.js"/>"></script>
<script src="<c:url value="/js/libs/d3v4/topojson.v2.min.js"/>"></script>
<script src="https://d3js.org/d3-axis.v1.min.js"></script>

<script type="text/javascript">

    var panZoomMaps;
    var drawMapFunctions;
    
	panZoomMaps = ( typeof panZoomMaps !== 'undefined' && panZoomMaps instanceof Array ) ? panZoomMaps : [];
	drawMapFunctions = ( typeof drawMapFunctions !== 'undefined' && drawMapFunctions instanceof Array ) ? drawMapFunctions : [];
   		
	$.EventBus("currentOrganizationChange").subscribe(function(){
		/*panZoomMaps["${id}"].destroy();*/
		$("#${id}-d3-choroplethmap").empty();
        drawMapFunctions["${id}"]();
    });

    drawMapFunctions["${id}"] = function (){
        var svgMap = d3.select("#${id}-d3-choroplethmap");
			width = +svgMap
				.attr("width");
			height = +svgMap
				.attr("height");
		var cbsaMap = d3.map();
		var color = d3.scaleLinear().domain(${domain}).range(d3.schemeBlues[${domain}.length])
		var orgId = sessionStorage.getItem("currentOrganizationId");

		if (!orgId){
				orgId = -1;
			}
			d3.queue()
			.defer(d3.json, "${contextRoot}/2013_us.topojson.json")
			.defer(d3.json, "${baseDataUrl}/"+orgId)
			.await(ready);

		function ready(error, cbsa, d) {

			var width = 960,
		    		height = 500,
		    		centered;			
			var projection = d3.geoAlbersUsa()
			    .scale(1070)
			    .translate([width / 2, height / 2]);			
			var path = d3.geoPath().projection(projection);
			var svg = d3.select("#${id}-d3-choroplethmap");
						width = +svg
						.attr("width");
						height = +svg
						.attr("height");		
			svg.append("rect")
			    .attr("class", "d3MapBackground")
			    .attr("width", width)
			    .attr("height", height)
			    .on("click", clicked);	
			
			var g = svg.append("g");			
			
			d3.json("${contextRoot}/2013_us.topojson.json", function(error, us) {
				  if (error) throw error;

			});		

			function clicked(d) {
				  var x, y, k;

				  if (d && centered !== d) {
				    var centroid = path.centroid(d);
				    x = centroid[0];
				    y = centroid[1];
				    k = 4;
				    centered = d;
				    $('#btn').click(function(){
				        var btn = $(this);
				        $.post(/*...*/).complete(function(){
				            btn.prop('disabled', false);
				        });
				        btn.prop('disabled', true);

				    });
				    $("#dialogBox, #mobileDialogBox").css("display", "block").css("visibility", "visible").css("height", "104");
				    
				  } else {
				    x = width / 2;
				    y = height / 2;
				    k = 1;
				    centered = null;
				    $("#dialogBoxCloseButton, #mobileDialogBoxCloseButton").click();
				    $("#dialogBox, #mobileDialogBox").css("display", "table-column").css("visibility", "hidden").css("height", "0");
				  }
				d3.select("#${id}-d3MapCbsa").selectAll("path")
					.classed("active", centered && function(d) { return d === centered; });
					  
				d3.select("#${id}-d3MapState").transition()
					.duration(750)
					.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
					.attr("transform-origin", "-160px -60px 10px")
					.style("stroke-width", 1.5 / k + "px");
							
				d3.select("#${id}-d3MapCbsa").transition()
					.duration(750)
					.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
					.attr("transform-origin", "-160px -60px 10px")
					.style("stroke-width", 1.5 / k + "px");
							
				d3.select("#${id}-d3MapStateBorder").transition()
					.duration(750)
					.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
					.attr("transform-origin", "-160px -60px 10px")
					.style("stroke-width", 1.5 / k + "px");
			}	
					
		    var tooltip = d3.select("body").append("div") 
	        		.attr("class", "tooltip")       
	        		.style("opacity", 0);
			var us = cbsa;
			var svgMap = d3.select("#${id}-d3-choroplethmap");
			var mouseDownTime=new Date().getTime();
			var prevMouseUp=new Date().getTime();
			var clicks=0;
			var timeout;
			var path = d3.geoPath().projection(projection);
			var stateAndMicroColor= "#cccccc";

			if (error)
				throw error;

			for (var i=0; i<d.length; i++){
			    cbsaMap.set(d[i].cbsaId, +d[i].num);
			}				
			
			svgMap.append("g")
				.attr("class", "state")
				.attr("id", "${id}-d3MapState")
				.selectAll("path").data(topojson.feature(us, us.objects.states).features)
				.enter()
				.append("path")
				.attr("fill", function(){
					return stateAndMicroColor;
				})
				.attr("d", path)
				.attr("pointer-events", "none")
				.on("click", clicked);

			svgMap.append("g")
				.attr("class", "cbsa")
				.attr("id", "${id}-d3MapCbsa")
				.selectAll("path")
				.data(topojson.feature(us, us.objects.cbsas).features)
				.enter().append("path")
				.attr("fill", function(d){
						if(cbsaMap.has(d.properties.id)){
							return color(cbsaMap.get(d.properties.id));
						}
						return stateAndMicroColor;
					})
				.attr("stroke", function(){
					return "#ffffff";
				})
				.attr("stroke-width", 0.5)
				.attr("d", path)
				.on("click", clicked)
				.on("mouseover", function(d) {    
					var data = cbsaMap.get(d.properties.id)
		            tooltip.transition()    
		            .duration(200)    
		            .style("opacity", .9);
					tooltip.html("${dataInputLabel}: "+ data)
		            .style("left", (d3.event.pageX + 20) + "px")
		            .style("top", (d3.event.pageY - 45) + "px");
		          })          
				.on("mouseout", function(d) {   
		            tooltip.transition()    
		            .duration(500)    
		            .style("opacity", 0); 
				})
				.on("mousedown", function(){
					mouseDownTime=new Date().getTime();
					clearTimeout(timeout);
				})
				.on("mouseup", function(d) {
					clicks++;
					
						if (clicks === 1){
							var x = d3.event.pageX - document.getElementById("${id}-d3-choroplethmap").getBoundingClientRect().x + 700;
							var y = d3.event.pageY - document.getElementById("${id}-d3-choroplethmap").getBoundingClientRect().y + 380;
							timeout = setTimeout(function () {
	
								
	 							if(new Date().getTime() - mouseDownTime < 250 && cbsaMap.has(d.properties.id)){
									$('#dialogText, #mobileDialogText').text("Header");
									$('#dialogText, #mobileDialogText').html("The ID of this Core Based Statistical Area (CBSA) is <b>" + d.properties.id + "</b>.<br/> There are a total of <b>" + cbsaMap.get(d.properties.id) + "</b> ${dataInputLabel}. ");
	
									/* NOTE: The following function works on smaller devices and viewports with the help of the "#dialogBox" media query.  The dialog box is stuck to the bottom of the map and is updated on CBSA clicking.  Space is made or reduced to fit the dialog box. */
									/* $('#dialogBox').show().draggable(); */
									
									/* NOTE: The following jQuery funtion works on desktop screensizes and not on mobile or tablet.  How can the .offset({}) be adjusted for different screensizes like CSS media queries? */
									$('#dialogBox, #mobileDialogBox').show().offset({top: y,left: x}).draggable();							
									
									$('#dialogBoxCloseButton, #mobileDialogBoxCloseButton').click(function() {
										$('#dialogBox, #mobileDialogBox').hide();
									});
									
								}						
								clicks=0;
							}, 100);
						} else if (clicks === 2) {
							clearTimeout(timeout);
							$("#dialogBoxCloseButton, #mobileDialogBoxCloseButton").click();
							clicks = 0;
						}
					prevMouseUp=new Date().getTime();
				})
			;

			svgMap.append("g")
				.attr("class", "stateBorder")
				.attr("id", "${id}-d3MapStateBorder")
				.selectAll("path").data(topojson.feature(us, us.objects.states).features)			
				.enter().append("path")
				.attr("fill", function(){
					return "transparent";
			})
				.attr("stroke", "#fff")
				.attr("stroke-width", 0.8)
				.attr("d", path)
				.attr("pointer-events", "none")
				.on("click", clicked);			
		}
	}
    drawMapFunctions["${id}"]();

	/* Closing of the D3 map dialog box upon clicking each of the four CTA's. */
		$('#cardholdersCTA, #transactionsCTA, #ordersCTA, #pointsCTA' ).click(function(){
		   	$("#dialogBoxCloseButton, #mobileDialogBoxCloseButton").click();
		});  

</script>

Итак, для начала при начальной загрузке страницы создается карта, и если вы наводите указатель мыши на любую CBSA / географическую область в Соединенных Штатах, подсказка DIV будет правильно сгенерирована и расположенарядом с курсором мыши.Проблема начинается, когда пользователь прокручивает страницу вниз.В этот момент, если пользователь наводит курсор на одну и ту же область карты.всплывающая подсказка находится в той же области в окне обозревателя, а на карте нет регистратора.Это похоже на то, что всплывающая подсказка размещается не на основе курсора мыши, а на основе размеров окна баузера.Я считаю, что проблема заключается в коде:

.style("left", (d3.event.pageX + 20) + "px")
.style("top", (d3.event.pageY - 45) + "px");

Вся карта полностью отзывчива, за исключением этой проблемы.Вот несколько скриншотов курсора, наведенного на область CBSA около Денвера, штат Колорадо, и прокручивающего страницу вниз:

Начальная загрузка страницы и представление карты;парящий над Денвером, штат Колорадо:

Номер зависания и прокрутки1

Я прокручиваю страницу вниз и снова зависаю над Денвером, штат Колорадо;всплывающая подсказка находится в том же месте в браузере, но должна располагаться рядом с тем местом, где находится мышь, Денвер, Колорадо:

Наведение и прокрутка №.2

Я прокручиваю дальше и снова зависаю над Денвером, штат Колорадо:

Наведение и прокрутка №.3

... и еще один скриншот прокрутки вниз страницы, а затем повторного наведения над Денвером.

Наведение и прокрутка №.4

Таким образом, похоже, что всплывающая подсказка размещается на основе размеров браузера, а не карты.Чего мне не хватает?Как я могу это исправить?Какие-либо предложения?Большое спасибо за вашу помощь и понимание.Я очень ценю это.

ОБНОВЛЕНИЕ: Вот что я изменил для функции наведения мыши:

var scrollNumber = window.pageYOffset;
var data = cbsaMap.get(d.properties.id)
  tooltip.transition()    
    .duration(200)    
    .style("opacity", .9);
  tooltip.html("${dataInputLabel}: "+ data)
    .style("left", (d3.event.pageX + 20) + "px")
    .style("top", (d3.event.pageY - scrollNumber - 45) + "px");	
...