宣传单:有效淡入/淡出图层组

时间:2016-07-14 13:53:40

标签: javascript jquery svg leaflet

我有一张Leaflet地图,其中包含一个图块层,然后是LayerGroupdensityLayer),其中包含许多(通常为几百个)Rectangle个图层这是一个半透明的填充叠加层,其fillColor基于特定年份的人口密度。

根据用户操作,densityLayer的内容会发生变化。只需运行densityLayer.clearLayers(),然后为每个图层生成所有新的Rectangle图层和densityLayer.addLayer(aRectangle),这非常简单。

想要做什么要做的是从旧数据到新数据的淡入淡出动画:即,生成所有新的Rectangle图层并将它们放入新的{ {1}} LayerGroup),同时淡出原始newDensityLayer并淡入oldDensityLayer,当淡入淡出完成后,清除并删除newDensityLayer并将其替换为oldDensityLayer

我目前的解决方案非常低效:

newDensityLayer

这基本上可行,但除非是非常快的机器,否则会变得非常不稳定和缓慢。

是否有某种方法可以淡出/淡出整个var oldDensityLayer = densityLayer var newDensityLayer = {...create new density layer here, add polygons, etc...} oldDensityLayer.eachLayer(function(l) { $(l._path).fadeOut(1000) // 1000ms animation time }) setTimeout(function() { oldDensityLayer.clearLayers() myLeafletMap.removeLayer(oldDensityLayer) oldDensityLayer = null }, 1000) myLeafletMap.addLayer(newDensityLayer) // now fade in all the new polygons newDensityLayer.eachLayer(function(l) { $(l._path).hide() // so they start out invisible $(l._path).fadeIn(1000) }) densityLayer = newDensityLayer ,或者某些选项我还没有考虑过......?

这是关键功能,所以如果添加另一个js库会有所帮助,那很好。此外,特定于SVG的答案很好,因为我已经将Leaflet用于其绘图功能,并且跨浏览器兼容性并不是此应用程序中的一个问题。

2 个答案:

答案 0 :(得分:3)

  

有没有办法淡出/淡出整个LayerGroup,或者某些我没有考虑的选项......?

事实上,您有一个未考虑的选项:操纵实际将几何绘制为HTML元素的L.Renderer。这意味着操纵<canvas>的实际L.Canvas<svg>的实际L.SVG

请记住L.PathPolygon s,Polyline s等)的任何子类都可以its own renderer。默认情况下,Leaflet只创建L.Renderer的一个实例,并在所有L.Path中重用它,除非另有说明 - 这意味着更少的HTML元素和(在99%的用例中)更好的性能。

所以看起来应该是这样的:

var rendererA = L.canvas();
var rendererB = L.canvas();

var groupA = L.layerGroup().addTo(map);
var layerA1 = L.polygon(…, {renderer: rendererA}).addTo(groupA);
var layerA2 = L.polygon(…, {renderer: rendererA}).addTo(groupA);

var groupB = L.layerGroup().addTo(map);
var layerB1 = L.polygon(…, {renderer: rendererB}).addTo(groupB);
var layerB2 = L.polygon(…, {renderer: rendererB}).addTo(groupB);

// Here comes the magic - using the *undocumented*, *private* _container
// property of L.Canvas to access the <canvas> HTML element itself
rendererA._container.style.opacity = 0.5;

代码显然是不完整的,但它应该正确地说明这个想法。

这将在浏览器中创建两个不同的<canvas>,并且更改HTML元素本身的不透明度将绕过重新呈现功能。应该有一个使用L.SVG的类似解决方案,但我不确定浏览器如何合成SVG容器的不透明度。

这种方法有明显的缺点 - 比如丢失任何z排序(bringToFront等),如果来自两个组的几何都应该交织在一起。

此外,请记住:建议不要使用未记录的传单对象的私有属性,除非您真的真的知道自己在做什么并且愿意看到您的代码在API更改中发生中断或罕见情况。

答案 1 :(得分:2)

您可以包含填充了形状的SVG图层。下面在淡化500 svg形状的示例中,然后构建一组新的SVG形状并将它们淡入。(编辑为在其位置缩放形状)

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title>Fade Out/In SVG Elements in Leaflet World Map</title>
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src='https://api.tiles.mapbox.com/mapbox.js/v2.1.5/mapbox.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox.js/v2.1.5/mapbox.css' rel='stylesheet' />

</head>

<body style='font-family:arial'>
<center><h4>Fade Out/In SVG Elements in Leaflet World Map</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
This adds 500 svg elements(circles, ellipses, rects, polygons) to the SVG layer in the world map. The map's mouse wheel zoom remains smooth in IE/CH/FF. Each element has a lat/lng value, converted to the needed x,y values to translate each symbol to the desired point.
During map zoom level changes, the symbols are automatically scaled and translated, maintaining their original size and position.

</div>
<br />
<table border=1>
<tr>
<td>
<b>Scenerio:</b><br />
1). The map is placed into its DIV (width:800px, height:400px).<br />
2). The map is centered at Lat/Lng (0,0) at zoom level 1.<br />
3.) The SVG element is added via <b>initPathRoot.</b><br />
4.) 500 SVG elements are added, randomly place about the world.<br />
5.) The svg <b>viewBox</b> is computed, used to create x,y values for the symbols.<br />
6.)Each element is translated/scaled when the map is zoomed, using the <b>viewreset</b> event.<br /> This calls the map method <b>latLngToLayerPoint(MyLatLng)</b> to accomplish this.
 <br>7.) Select <button>fade out/in</button> to fade out the current elements, build a new group, then fade In new group
</td>
</tr>
</table>

<div  style='width:800px;height:400px' id='MyMap'></div>
  <button onClick=fadeOutIn()>fade out/in</button>

  <br />Javascript:<br />
<textarea spellcheck=false id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>

<script id=myScript>
L.mapbox.accessToken = 'pk.eyJ1IjoiZmhlbXNoZXIiLCJhIjoiODQ5MW9WayJ9.px2P6wVMFucfXHE1zmDA1A';
MyMap = L.mapbox.map('MyMap', 'mapbox.streets', { zoomControl:false,center: new L.latLng(0,0),zoom:1,minZoom:1});
//---zooming the map---
MyMap.on("viewreset", adjustSVGSymbols);

var MySVG
var SymbolG //---<g> element containing all symbols---
var VBw
var VBh
var NS="http://www.w3.org/2000/svg"

//---body onload---
function initSVG()
{
	MyMap._initPathRoot() //---creates an svg layer---
	MySVG=document.querySelector("svg") //---access svg element---
	//---place symbols in here---
	SymbolG=document.createElementNS(NS,"g")
    SymbolG.setAttribute("id","symbolG")
	MySVG.appendChild(SymbolG)
	//---create random svg elements, place in SymbolG--
	getViewBox()//---used to place svg random elements
	//---create 500 symbols at size 10 pixels--
	svgGLOB(500,10)
}

//--- on map zoom - fired via map event: viewreset---
function adjustSVGSymbols()
{

	var symbols=SymbolG.childNodes
	for(var k=0;k<symbols.length;k++)
	{
		var symbol=symbols.item(k)
		//---initial lat/lng for symbol---
		var lat=parseFloat(symbol.getAttribute("lat"))
		var lng=parseFloat(symbol.getAttribute("lng"))
		var latLng= new  L.latLng(lat, lng)
		var transX=MyMap.latLngToLayerPoint(latLng).x
		var transY=MyMap.latLngToLayerPoint(latLng).y
           //---scale---
                var initZoom=parseFloat(symbol.getAttribute("initZoom"))
                var scale = (Math.pow(2, MyMap.getZoom())/2)/(Math.pow(2, initZoom)/2);
 		//---trash previous transform---
		symbol.setAttribute("transform","")
		symbol.removeAttribute("transform")

		var transformRequestObj=MySVG.createSVGTransform()
		var animTransformList=symbol.transform
		//---get baseVal to access/place object transforms
		var transformList=animTransformList.baseVal
		//---translate----
		transformRequestObj.setTranslate(transX,transY)
		transformList.appendItem(transformRequestObj)
		transformList.consolidate()
		//---scale---
		transformRequestObj.setScale(scale,scale)
		transformList.appendItem(transformRequestObj)
		transformList.consolidate()
	}


}
//---needed for random symbol placement: create x,y values---
function getViewBox()
{
	vb=MySVG.viewBox.baseVal
	VBw=vb.width
	VBh=vb.height
}
//---compute svg elems: circles, rects, ellipses, polygons---
function svgGLOB(elems,elemSize)
{
	//---note: each browser creates a different sized svg layer---
	var svgWidth=VBw
	var svgHeight=VBh
	//---obtain a random whole number from a thru b---
	function rdm(a,b)
	{
		return a + Math.floor(Math.random()*(b-a+1));
	}

	function randomPoints(elems,svgWidth,svgHeight,elemSize)
	{
		//--return format:[ [x,y],[x,y],,, ]
		//---Generate  random points---
		function times(n, fn)
		{
			var a = [], i;
			for (i = 0; i < n; i++)
			{
				a.push(fn(i));
			}
			return a;
		}
		var width=svgWidth-2*elemSize //---offset from edge---
		var height=svgHeight-2*elemSize //---offset from edge---

		return 	RandomPnts = times(elems, function() { return [Math.floor(width * Math.random()) + elemSize, Math.floor(height * Math.random()) + elemSize] });
	}
	//---random color---
	function rcolor()
	{
		var letters = '0123456789ABCDEF'.split('');
		var color = '#';
		for (var i = 0; i < 6; i++ )
		{
			color += letters[Math.round(Math.random() * 15)];
		}
		return color;
	}
	function polygon(vCnt,radius,centerX,centerY)
	{
		var myPoints=[]
		var polyXPts      = Array(vCnt);
		var polyYPts      = Array(vCnt);
		var vertexAngle   = 360/vCnt;
		//---init polygon points processor---
		for(var v=0; v<vCnt; v++)
		{
			theAngle = (v*vertexAngle)*Math.PI/180;
			polyXPts[v] = radius*Math.cos(theAngle);
			polyYPts[v] = -radius*Math.sin(theAngle);
		}
		//--note points are CCW---
		for(var v=0;v<vCnt; v++)
		{
			var point=[centerX+polyXPts[v],centerY+polyYPts[v]]
			myPoints.push(point)
		}
		return myPoints
	}

	var Points=randomPoints(elems,svgWidth,svgHeight,elemSize)

	var n=Points.length
	var circleCnt=0
	var ellipseCnt=0
	var rectCnt=0
	var polygonCnt=0

	var RandomElems=[]
	RandomElems[0]="circle"
	RandomElems[1]="rect"
	RandomElems[2]="ellipse"
	RandomElems[3]="polygon_3"
	RandomElems[4]="polygon_4"
	RandomElems[5]="polygon_5"
	RandomElems[6]="polygon_6"
	RandomElems[7]="polygon_7"
	RandomElems[8]="polygon_8"
	RandomElems[9]="polygon_9"
	RandomElems[10]="polygon_10"
	RandomElems[11]="polygon_11"
	RandomElems[12]="polygon_12"

	//---create all at center(0,0), then translate---

	for(var k=0;k<n;k++)
	{
		var rand=rdm(0,12)
		var elemStr=RandomElems[rand]

		if(!elemStr.indexOf("_"))
		var elemSt=elemStr
		else
		var elemSt=elemStr.split("_")[0]

		//var elem=document.createElementNS(NS,elemSt)
		var x=Points[k][0]
		var y=Points[k][1]

		var lng=((x * 360 / VBw) - 180)
		var lat= (90 - (y * 180 / VBh))

		var id="symbol"+k

		var fill=rcolor()
		var elem=document.createElementNS(NS,elemSt)
		elem.setAttribute("id",id)
		elem.setAttribute("cursor","default")
		elem.setAttribute("fill",fill)
		elem.setAttribute("lat",lat)
		elem.setAttribute("lng",lng)

		if(elemSt=="circle")
		{
			var r=elemSize
			elem.setAttribute("r",r)
		}
		else if(elemSt=="ellipse")
		{
			var rx=elemSize
			var ry=elemSize/2
			elem.setAttribute("rx",rx)
			elem.setAttribute("ry",ry)
		}
		else if(elemSt=="rect")
		{
			var width=elemSize
			var height=elemSize
			//---center at 0,0---
			var x=-elemSize/2
			var y=-elemSize/2

			elem.setAttribute("width",width)
			elem.setAttribute("height",height)
			elem.setAttribute("x",x)
			elem.setAttribute("y",y)
		}
		else if(elemSt=="polygon")
		{
			var pgonSides=parseInt(elemStr.split("_")[1])
			var pgonPnts=polygon(pgonSides,elemSize,0,0)

			var points=pgonPnts.join()
			elem.setAttribute("points",points)
		}

        elem.setAttribute("initZoom",1)

		SymbolG.appendChild(elem)
	}
	//---initialize locations---
	adjustSVGSymbols()
 }
//---fade out/In button----
function fadeOutIn()
{
    $("#symbolG").fadeOut(1000, function(){
            createAnotherGlob()
        });
}
function createAnotherGlob()
{
    var symbols=SymbolG.childNodes
    for(var k=symbols.length-1;k>=0;k--)
    SymbolG.removeChild(symbols.item(k))

    svgGLOB(500,10,true)

     $("#symbolG").fadeIn(1500)

}

</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
	jsValue.value=myScript.text
	initSVG()
}
</script>

</body>

</html>