2016-07-14 2 views
1

У меня листовка карту с одного слоя тайлов, а затем LayerGroup (densityLayer), состоящий из многих (как правило, несколько сотен) Rectangle слоев, каждый из которых представляет собой полупрозрачный заполненными накладывать с помощью fillColor на основе плотности населения за конкретный год.листовка: Затухание в группах/из слоя эффективно

Исходя из пользовательского действия, содержимое densityLayer изменить. Довольно тривиально просто запустить densityLayer.clearLayers(), а затем сгенерировать все новые слои Rectangle и densityLayer.addLayer(aRectangle) для каждого из них.

Что я хочу сделать, хотя, чтобы оживить замирание от старого к новому данных: то есть, генерировать все новые Rectangle слои и поместить их в новый 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 

В основном это работает, но получает довольно изменчивы и медленно на что-нибудь, но очень быструю машину.

Есть ли способ увядать в/из целого LayerGroup, или, возможно, какой-то вариант, который я не рассматривал ...?

Это критическая функциональность, поэтому, если добавить другую библиотеку js, это поможет. Кроме того, ответы, специфичные для SVG, прекрасны, так как это то, что я использовал в Leaflet для его функций рисования, а совместимость с несколькими браузерами не является проблемой в этом приложении.

ответ

3

Есть ли способ увядать в/из целого LayerGroup, или, возможно, какой-то вариант, который я не рассматривал ...?

Существует, по сути, опция, которую вы не учли: манипулировать L.Renderer, которая фактически рисует геометрии как элемент HTML. Это означает управление фактическим <canvas>L.Canvas или фактическим <svg>L.SVG.

Помните, что каждый подкласс L.Path (Polygon с, Polyline с и такие) могут иметь its own renderer. Листовка, по умолчанию, создает только один экземпляр 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-изменения или редкие обстоятельства.

+0

Это выглядит очень многообещающим. Собираемся проверить это сегодня. – DanM

+0

Ah - это функция Leaflet 1.0. Учитывая, что он может решить мою проблему, я попытаюсь выполнить обновление до 1.0RC1. – DanM

+0

Это работало неплохо; Я создал три визуализатора («a», «b» и «c») и прокручивал их, затухал один, а один исчезал, а третий был «черствым» слоем, поэтому я не столкнулся с какими-либо условиями гонки , – DanM

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() 
 
{ 
 
\t MyMap._initPathRoot() //---creates an svg layer--- 
 
\t MySVG=document.querySelector("svg") //---access svg element--- 
 
\t //---place symbols in here--- 
 
\t SymbolG=document.createElementNS(NS,"g") 
 
    SymbolG.setAttribute("id","symbolG") 
 
\t MySVG.appendChild(SymbolG) 
 
\t //---create random svg elements, place in SymbolG-- 
 
\t getViewBox()//---used to place svg random elements 
 
\t //---create 500 symbols at size 10 pixels-- 
 
\t svgGLOB(500,10) 
 
} 
 

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

 
\t var symbols=SymbolG.childNodes 
 
\t for(var k=0;k<symbols.length;k++) 
 
\t { 
 
\t \t var symbol=symbols.item(k) 
 
\t \t //---initial lat/lng for symbol--- 
 
\t \t var lat=parseFloat(symbol.getAttribute("lat")) 
 
\t \t var lng=parseFloat(symbol.getAttribute("lng")) 
 
\t \t var latLng= new L.latLng(lat, lng) 
 
\t \t var transX=MyMap.latLngToLayerPoint(latLng).x 
 
\t \t 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); 
 
    \t \t //---trash previous transform--- 
 
\t \t symbol.setAttribute("transform","") 
 
\t \t symbol.removeAttribute("transform") 
 

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

 

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

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

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

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

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

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

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

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

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

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

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

 
\t \t var id="symbol"+k 
 

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

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

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

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

 
     elem.setAttribute("initZoom",1) 
 

 
\t \t SymbolG.appendChild(elem) 
 
\t } 
 
\t //---initialize locations--- 
 
\t 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() 
 
{ 
 
\t jsValue.value=myScript.text 
 
\t initSVG() 
 
} 
 
</script> 
 

 
</body> 
 

 
</html>

Смежные вопросы