Performance issue when zooming (Leaflet 0.7.3) - performance

I'm facing a performance problem with Leaflet (version 0.7.3). I'm working with an OSM map that I use to display a bunch of CircleMarkers linked by decorated Polylines (with arrow pattern every 25px). Loading take a little time but the main problem is that when I zoom the map I start facing severe lag (from the zoom level 16) and, beyond a certain limit (say 18 most of the time), browser just freeze and eventually crash (tested with chrome and firefox). I tried with a bunch of 1,000 linked markers, then I dropped to a set of around 100, but still the same concern... Of course, with 10 markers or less I don't have any problem.
Did you already face a similar trouble? How can I optimize Leaflet performances so that I can use an accurate zoom (beyond level 16) with more than 100 linked CircleMarkers ? I also wonder why performances are dropping so badly when zooming, while marker amount stay the same...
Thank you in advance for your answers,
Lenalys.
Cannot get the PolylineDecorator plugin to work on jsfiddle.
But here is the code that generate markers :
Map initialization :
var map;
function initializeMap(){
"use strict";
var layer;
var layer2;
function layerUrl(key, layer) {
return "http://wxs.ign.fr/" + key
+ "/geoportail/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&"
+ "LAYER=" + layer + "&STYLE=normal&TILEMATRIXSET=PM&"
+ "TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image%2Fjpeg";
}
layer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',
{
attribution: '© OpenStreetMap contributors',
maxZoom: 18
});
layer2 = L.tileLayer(
layerUrl(IGN_AMBIENTE_KEY, "GEOGRAPHICALGRIDSYSTEMS.MAPS"),
{attribution: '© IGN'}
);
var baseMaps = {
"Terrestre": layer,
"Bathymetrique": layer2
};
map = L.map('map', {
layers: [layer],
zoom: 8,
center: [42.152796, 9.139150],
zoomControl: false
});
L.control.layers(baseMaps).addTo(map);
//add zoom control with your options
L.control.zoom({
position:'topright' //topleft
}).addTo(map);
L.control.scale({
position:'bottomleft',
imperial : false
}).addTo(map);
}
Data Sample :
var jsonData ={"12":[{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657283333333","longitude":"9.42362","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 15:37:12"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657381666667","longitude":"9.42365","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 16:42:16"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657381666667","longitude":"9.4236933333333","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 17:47:21"},
{"id_stm_device":"7","individual_name":"cerf3","latitude":"42.657283333333","longitude":"9.4237383333333","temperature":null,"pulse":null,"battery":"20","date_time":"2015-03-17 19:57:23"}],
"13":[{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.61683","longitude":"9.4804633333333","temperature":"17.45","pulse":null,"battery":"80","date_time":"2015-04-08 07:45:20"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.538858333333","longitude":"9.48169","temperature":"14.37","pulse":null,"battery":"80","date_time":"2015-04-08 08:00:29"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.458748333333","longitude":"9.500225","temperature":"14.46","pulse":null,"battery":"80","date_time":"2015-04-08 08:15:49"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.3302","longitude":"9.5374583333333","temperature":"15.19","pulse":null,"battery":"80","date_time":"2015-04-08 08:31:05"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.170133333333","longitude":"9.5272116666667","temperature":"15.48","pulse":null,"battery":"80","date_time":"2015-04-08 08:46:20"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07959","longitude":"9.47688","temperature":"15.97","pulse":null,"battery":"80","date_time":"2015-04-08 09:01:31"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.076163333333","longitude":"9.4828633333333","temperature":"20.42","pulse":null,"battery":"80","date_time":"2015-04-08 09:16:59"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07194","longitude":"9.4908866666667","temperature":"17.36","pulse":null,"battery":"80","date_time":"2015-04-08 09:32:17"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.072583333333","longitude":"9.4901516666667","temperature":"17.36","pulse":null,"battery":"80","date_time":"2015-04-08 09:47:32"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.07238","longitude":"9.4904266666667","temperature":"19.38","pulse":null,"battery":"80","date_time":"2015-04-08 10:02:42"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.072298333333","longitude":"9.4904983333333","temperature":"17.46","pulse":null,"battery":"80","date_time":"2015-04-08 10:17:55"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.095093333333","longitude":"9.5148383333333","temperature":"17.47","pulse":null,"battery":"80","date_time":"2015-04-08 10:33:12"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.112881666667","longitude":"9.5133133333333","temperature":"19.3","pulse":null,"battery":"80","date_time":"2015-04-08 10:48:23"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.112875","longitude":"9.513285","temperature":"22.71","pulse":null,"battery":"80","date_time":"2015-04-08 11:03:57"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.141096666667","longitude":"9.5078216666667","temperature":"23.73","pulse":null,"battery":"80","date_time":"2015-04-08 11:19:12"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.282186666667","longitude":"9.5505183333333","temperature":"18.97","pulse":null,"battery":"80","date_time":"2015-04-08 11:34:28"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.405126666667","longitude":"9.531145","temperature":"20.71","pulse":null,"battery":"80","date_time":"2015-04-08 11:49:42"},
{"id_stm_device":"8","individual_name":"cerf5","latitude":"42.482063333333","longitude":"9.480665","temperature":"21.7","pulse":null,"battery":"80","date_time":"2015-04-08 12:05:07"}]}
var oJSON = JSON.parse(jsonData);
var colors = [
"#400080",
"#008000",
"#EC7600",
"#E40341",
"#0D5E5E",
"#919191",
"#FF3C9D",
"#A70A0E",
"#00BFBF",
"#7171FF"
];
var classes = [
"color1",
"color2",
"color3",
"color4",
"color5",
"color6",
"color7",
"color8",
"color9",
"color10"
];
var lastMarkers = [];
var layers = new Array();
var polyline;
var decorator;
window.graphicsDevices = [];
var offsetLatitude = 0.003333;
var offsetLongitude = 0.011666;
Marker instanciation :
function printGPSOnMap(oJSON){
var nbKeys = 0;
for (var key in oJSON) {
nbKeys++;
var classe = classes[(key-1)%classes.length];
var color = colors[(key-1)%colors.length];
var positionInfo = [];
if (oJSON.hasOwnProperty(key)) {
var aInfo = oJSON[key];
var marker;
var latlngs = Array();
var startMarker = lastMarkers[key];
if(startMarker !== undefined && startMarker != null) {
var myIcon = L.divIcon({className: "myCircle "+classe, iconSize : [ 20, 20 ] });
startMarker.setIcon(myIcon);
latlngs.push(startMarker.getLatLng());
}
for(var i = 0; i < aInfo.length; i++) {
var oInfos = aInfo[i];
var sIdIndividual = oInfos["id_individual"];
var sLongitude = oInfos["longitude"];
var sLatitude = oInfos["latitude"];
var sTemperature = oInfos["temperature"];
var sPulse = oInfos["pulse"];
var sBattery = oInfos["battery"];
var sDatetime = oInfos["date_time"];
var sIndividualName = oInfos["individual_name"];
var id_device = oInfos["id_stm_device"];
var popupMsg = "...";
latlngs.push(L.marker([sLatitude,sLongitude]).getLatLng());
marker = new MyCustomMarker([sLatitude,sLongitude], {
icon : L.divIcon({
className : "myCircle "+classe + ((i == aInfo.length-1) ? ' myCircleEnd' : ''),
iconSize : [ 20, 20 ]
})
});
marker.bindPopup(popupMsg, {
showOnMouseOver: true
});
marker.bindLabel(key, {
noHide: true,
direction: 'middle',
offset: [offset[0], offset[1]]
});
positionInfo.push(marker);
}
lastMarkers[key] = marker;
}
if(latlngs.length > 1)
{
polyline = L.polyline(latlngs, {className: classe, weight: 2,opacity: 0.4}).addTo(map);
decorator = L.polylineDecorator(polyline, {
patterns: [
// define a pattern of 10px-wide arrows, repeated every 20px on the line
{offset: 0, repeat: '25px', symbol: new L.Symbol.arrowHead({pixelSize: 10, pathOptions: {fillOpacity:
0.76, color: color, weight: 1}})}
]}).addTo(map);
}
if(!window.graphicsDevices.hasOwnProperty(key))
window.graphicsDevices[key] = [];
for(var i = 0; i < positionInfo.length; i++) {
window.graphicsDevices[key].push(positionInfo[i]);
positionInfo[i].addTo(map);
if(latlngs.length > 1){
window.graphicsDevices[key].push(polyline);
polyline.addTo(map);
window.graphicsDevices[key].push(decorator);
decorator.addTo(map);
}
}
}//foreach key
}
Code for the custom marker :
var MyCustomMarker = L.Marker.extend({
bindPopup: function(htmlContent, options) {
if (options && options.showOnMouseOver) {
// call the super method
L.Marker.prototype.bindPopup.apply(this, [htmlContent, options]);
// unbind the click event
this.off("click", this.openPopup, this);
// bind to mouse over
this.on("mouseover", function(e) {
// get the element that the mouse hovered onto
var target = e.originalEvent.fromElement || e.originalEvent.relatedTarget;
var parent = this._getParent(target, "leaflet-popup");
// check to see if the element is a popup, and if it is this marker's popup
if (parent == this._popup._container)
return true;
// show the popup
this.openPopup();
}, this);
// and mouse out
this.on("mouseout", function(e) {
// get the element that the mouse hovered onto
var target = e.originalEvent.toElement || e.originalEvent.relatedTarget;
// check to see if the element is a popup
if (this._getParent(target, "leaflet-popup")) {
L.DomEvent.on(this._popup._container, "mouseout", this._popupMouseOut, this);
return true;
}
// hide the popup
this.closePopup();
}, this);
}
},
_popupMouseOut: function(e) {
// detach the event
L.DomEvent.off(this._popup, "mouseout", this._popupMouseOut, this);
// get the element that the mouse hovered onto
var target = e.toElement || e.relatedTarget;
// check to see if the element is a popup
if (this._getParent(target, "leaflet-popup"))
return true;
// check to see if the marker was hovered back onto
if (target == this._icon)
return true;
// hide the popup
this.closePopup();
},
_getParent: function(element, className) {
var parent = null;
if(element != null) parent = element.parentNode;
while (parent != null) {
if (parent.className && L.DomUtil.hasClass(parent, className))
return parent;
parent = parent.parentNode;
}
return false;
}
});

Have you gauged performance in canvas mode?
Use L_PREFER_CANVAS = true before initializing your leaflet map container.Might be able to help you possibly.

Related

Chartjs animate x-axis displaying data incorrectly in mac device

I am working with chartjs, I am trying to animate chart from right to left or left to right on load.
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.setTransform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
Example 1
The above code works fine on windows, but I'm facing issue on mac devices.While animating from left to right the data displays incorrectly means that the data moves to upward from x axis.How to fix this issue?
I am attaching screenshot.
Screenshot
Please change setTransform to transform.
Try the following code
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.transform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});

Aframe: size of model

When using a model as a source to an entity, say gltf, is there a way we know the original size? Since the scale attribute works on relative size, it seems to be a trial an error to fit the model to our desired size. I tried using the geometry.getComputingBox() of the mesh of the model but it returns null. Wondering if there is a component that is available that lets us specify the scale in absolute terms.
Ah, figured it out.
var model = this.el.object3D;
var box = new THREE.Box3().setFromObject( model );
var size = box.getSize();
gives you the size. then using the above any desired size can be set.
Created a simple component that can be conveniently used
AFRAME.registerComponent('resize', {
schema: {
axis: {
type: 'string',
default: 'x'
},
value: {
type: 'number',
default: 1
}
},
init: function() {
var el = this.el;
var data = this.data;
var model = el.object3D;
el.addEventListener('model-loaded', function(e) {
var box = new THREE.Box3().setFromObject( model );
var size = box.getSize();
var x = size.x;
var y = size.y;
var z = size.z;
if(data.axis === 'x') {
var scale = data.value / x;
}
else if(data.axis === 'y') {
var scale = data.value / y;
}
else {
var scale = data.value / z;
}
el.setAttribute('scale', scale + ' ' + scale + ' ' + scale);
});
}
});
And it can be used as to proportionately resize the model with x axis length as 0.5
<a-entity resize='axis:x; value:0.5' gltf-model='#model`></a-entity>
(This would have come as a comment but as I don't have enough rep points this is coming as an answer.)
I found that the model doesn't have a size directly after the model-loaded event listener so I trigger the rescale from the update method. Funnily enough though if you don't have the model-loaded event listener then the size of the model will be 0 even after the first update is fired.
This is my variant of the above code with the difference being that the dimension is set in meters:
/**
* Scales the object proportionally to a set value given in meters.
*/
AFRAME.registerComponent("natural-size", {
schema: {
width: {
type: "number",
default: undefined, // meters
},
height: {
type: "number",
default: undefined, // meters
},
depth: {
type: "number",
default: undefined, // meters
},
},
init() {
this.el.addEventListener("model-loaded", this.rescale.bind(this));
},
update() {
this.rescale();
},
rescale() {
const el = this.el;
const data = this.data;
const model = el.object3D;
const box = new THREE.Box3().setFromObject(model);
const size = box.getSize();
if (!size.x && !size.y && !size.z) {
return;
}
let scale = 1;
if (data.width) {
scale = data.width / size.x;
} else if (data.height) {
scale = data.height(size.y);
} else if (data.depth) {
scale = data.depth / size.y;
}
el.setAttribute("scale", `${scale} ${scale} ${scale}`);
},
remove() {
this.el.removeEventListener("model-loaded", this.rescale);
},
});
Then:
<a-entity natural-size='width:0.72' gltf-model='#model`></a-entity>
box.getSize has changed, I combined what I found here with what I found in another answer and noticed in the console to produce a more minimalist answer just to determine the size itself of a model:
getDimensions(object3d) {
// e.g., object3d = document.querySelector('#goban').object3D
var box = new THREE.Box3().setFromObject( object3d );
var x = box.max.x - box.min.x
var y = box.max.y - box.min.y
var z = box.max.z - box.min.z
return {x,y,z}
}

Hide actionbar on scroll listview

I have an app that has an action bar and a Tabview. Inside the tabview there is a listview. What I want is the actionbar to hide when the user is scrolling down the list and pop up when the user is scrolling up and do it nicely. As an example youtube app for android is doing this.
I have tried this code https://gist.github.com/vakrilov/6edc783b49df1f5ffda5 but as I hide the bar a white space appears on the bottom of the screen so not really useful in this case.
I tried and fail to modify it and increase the height as I hide the bar using:
var params = userList.android.getLayoutParams();
params.height = 500;
userList.android.setLayoutParams(params);
userList.android.requestLayout();
Also this
var LayoutParams= android.view.ViewGroup.LayoutParams;
var params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
Finally I came out with a kind of working thing but it is too sudden no animation on the hiding/appearing
var isChangingBar = false;
var isBarHidden = false;
userList.on("pan", function(args) {
var delta = args.deltaY;
console.log("deltaY: " + delta);
if (!isChangingBar) {
if (delta > 0 && isBarHidden === true) {
isChangingBar = true;
isBarHidden = false;
page.actionBarHidden = false;
isBarHidden = false;
setTimeout(function() {
isChangingBar = false;
}, 250);
}
else if (delta < 0 && isBarHidden === false) {
isChangingBar = true;
page.actionBarHidden = true;
isBarHidden = true;
setTimeout(function() {
isChangingBar = false;
}, 250);
}
}
}
Some idea if there is a better way?
You can add actionbar animation on hide/show:
declare const java: any;
declare const android: any;
export enum LayoutTransitionTypes {
CHANGE_APPEARING = 0,
CHANGE_DISAPPEARING,
APPEARING,
DISAPPEARING,
CHANGING
}
export function initPageActionBarVisibilityAnimations(page) {
if (!page.actionBar) {
return;
}
const actionBarH = page.actionBar.getMeasuredHeight();
if (actionBarH < 1) {
return;
}
const lt = new android.animation.LayoutTransition();
lt.setAnimator(LayoutTransitionTypes.APPEARING, (function () {
const a = new android.animation.ObjectAnimator();
a.setPropertyName('translationY');
a.setFloatValues([0.0]);
a.setDuration(lt.getDuration(2));
return a;
})());
lt.setAnimator(LayoutTransitionTypes.DISAPPEARING, (function () {
const a = new android.animation.ObjectAnimator();
a.setPropertyName('translationY');
a.setFloatValues([-actionBarH]);
a.setDuration(lt.getDuration(3));
return a;
})());
lt.setStartDelay(LayoutTransitionTypes.CHANGE_APPEARING, 0);
lt.setStartDelay(LayoutTransitionTypes.CHANGE_DISAPPEARING, 0);
lt.setStartDelay(LayoutTransitionTypes.APPEARING, 0);
lt.setStartDelay(LayoutTransitionTypes.DISAPPEARING, 0);
lt.setStartDelay(LayoutTransitionTypes.CHANGING, 0);
page.nativeView.setLayoutTransition(lt);
}
Now we may use page pan event to automatically hide/show action bar on scroll pan up/down events. Every change of page.actionBarHidden will start smooth actionbar hide/show transition.
export function onScrollPan(ev: PanGestureEventData) {
const actionBar = page.actionBar;
const scrollView: ScrollView = <ScrollView>page.getViewById('mainScrollView');
const voffset = scrollView.verticalOffset;
const dh = 50;
if (page.actionBarHidden && ev.deltaY > dh * 5) {
initPageActionBarVisibilityAnimations(page);
page.actionBarHidden = false;
} else if (!page.actionBarHidden
&& ev.deltaY < -dh
&& voffset > 0 && voffset > 2 * actionBar.getMeasuredHeight()) {
initPageActionBarVisibilityAnimations(page);
page.actionBarHidden = true;
}
}

animating multiple markers in OpenLayers 3

I am creating animations of migrating sea animals across the Pacific using OpenLayers. I would like each "animal" to trace a track as it goes over time. At the head of the track will be an icon/marker/overlay representing that animal. I have gotten this to work for one track, but although I am able to grow the track of each animal as a linestring constructed segment by segment, I am unable to specifically assign an icon/marker/overlay to each track. Instead, I am only able to animate one icon on one track. The rest of the linestring tracks proceed, but they do not have an icon at the head of the track as it is traced out. Here is my code. Any help appreciated.
// draw tracks
function makeLineString(id, species, multipointCoords, tracksTime) {
// layer structure and style assignment
var trackSource = new ol.source.Vector();
var trackLayer = new ol.layer.Vector({
source: trackSource,
style: BWtrackStyle
});
map.addLayer(trackLayer);
var lineString = new ol.geom.LineString([
ol.proj.fromLonLat(multipointCoords[0][0])
]);
var trackFeature = new ol.Feature({
geometry: lineString
});
if (species === "Blue Whale") {
trackFeature.setStyle([BWtrackStyle, shadowStyle]);
};
trackSource.addFeature(trackFeature);
// icon-marker-overlay styling
var BW2205005icon = document.getElementById('BW2205005icon');
var BW2205005marker = new ol.Overlay({
positioning: 'center-center',
offset: [0, 0],
element: BW2205005icon,
stopEvent: false
});
map.addOverlay(BW2205005marker);
var BW2205012icon = document.getElementById('BW2205012icon');
var BW2205012marker = new ol.Overlay({
positioning: 'center-center',
offset: [0, 0],
element: BW2205012icon,
stopEvent: false
});
map.addOverlay(BW2205012marker);
var coordinate, i = 1,
length = multipointCoords[0].length;
var currentTime = tracksTime[0][0];
var nextTime = tracksTime[0][1];
speedOption = 100; // the highter this value, the faster the tracks, see next line
var transitionTime = (nextTime - currentTime) / speedOption;
console.log(transitionTime);
var timer;
timer = setInterval(function() {
segmentConstruction(id, multipointCoords, tracksTime);
}, transitionTime);
function segmentConstruction(id, multipointCoords, tracksTime) {
coordinate = ol.proj.fromLonLat(multipointCoords[0][i]);
lineString.appendCoordinate(coordinate);
console.log(id);
if (id === "BW2205005") {
BW2205005marker.setPosition(coordinate);
} else {
BW2205012marker.setPosition(coordinate);
};
if (i >= length - 1) {
clearInterval(timer);
} else {
i++;
clearInterval(timer);
currentTime = tracksTime[0][i];
nextTime = tracksTime[0][i + 1];
transitionTime = (nextTime - currentTime) / speedOption;
timer = setInterval(function() {
segmentConstruction(id, multipointCoords, tracksTime);
}, transitionTime);
};
};
};
I would suggest this for animation of multiple markers with smooth movement, but it requires a delay for other styles:
var vehicles= [{ "curlon":77.654397, "curlat":12.959898, "prevlon":77.651951, "prevlat":12.951074 },{ "curlon":77.672936, "curlat":12.958100, "prevlon":77.649290, "prevlat":12.960024 }];
function push_data_for_multimarker(map,gps_obj)
{
destruct=false;
var getz = gps_obj;
var data_arry = [];
for (var i = 0, length = getz.length; i < length; i++)
{
gps_smooth_coordinates_generator(parseFloat(getz[i].prevlon),parseFloat(getz[i].prevlat),parseFloat(getz[i].curlon),parseFloat(getz[i].curlat));
}
}
function gps_smooth_coordinates_generator(map,sourcelon,sourcelat,destinationlon,destinationlat)
{
var vehicle_data=[];
var beginz=ol.proj.transform([sourcelon,sourcelat], 'EPSG:4326', 'EPSG:3857');
var endz=ol.proj.transform([destinationlon,destinationlat], 'EPSG:4326', 'EPSG:3857');
vehicle_data.push(beginz);
vehicle_data.push(endz);
var path= vehicle_data;
var genrated_positions = [];
for(var k = 1;k<path.length;k++)
{
var pointsNo = 1000;
var startPos = {};
startPos.lat = path[k-1][1];
startPos.lng = path[k-1][0];
var endPos = {};
endPos.lat = path[k][1];
endPos.lng = path[k][0];
var latDelta = (endPos.lat - startPos.lat) / pointsNo;
var lngDelta = (endPos.lng - startPos.lng) / pointsNo;
for (var i = 0; i < pointsNo; i++)
{
var curLat = startPos.lat + i * latDelta;
var curLng = startPos.lng + i * lngDelta;
var arr = [];
arr.push(curLng);
arr.push(curLat);
genrated_positions.push(arr);
}
}
animate_multiple_marker(genrated_positions);
}
function animate_multiple_marker(map,veh_croods)
{
var routeCoords = veh_croods;
var routeLength = routeCoords.length;
console.log("routeCoords"+routeCoords);
console.log("routeLength"+routeLength);
var geoMarker = new ol.Feature({
type: 'geoMarker',
geometry: new ol.geom.Point(routeCoords[0])
});
var off_style =new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
snapToPixel: false,
fill: new ol.style.Fill({color: 'black'}),
stroke: new ol.style.Stroke({
color: 'white', width: 2
})
})
});
var now;
var animating = false;
var speed=20;
var moveFeature = function(event) {
var vectorContext = event.vectorContext;
var frameState = event.frameState;
if (animating) {
var elapsedTime = frameState.time - now;
var index = Math.round(speed * elapsedTime / 1000);
if (index >= routeLength) {
console.log("index>>"+index);
stopAnimation();
animating = false;
console.log("animation ends");
}
if(animating)
{
var currentPoint = new ol.geom.Point(routeCoords[index]);
var feature = new ol.Feature(currentPoint);
vectorContext.drawFeature(feature,off_style);
}
else
{}
if(destruct)
{
console.log("termination initiated");
stopAnimation();
//destruct=false;
}
else
{ }
}
else
{
console.log("Not amnimating!!");
}
// tell OL3 to continue the postcompose animation
map.render();
};
triggerz_animation();
function stopAnimation() {
animating = false;
caller=false;
//remove listener
map.un('postcompose', moveFeature);
}
function start_vehicles()
{
animating = true;
now = new Date().getTime();
map.on('postcompose', moveFeature);
map.render();
}
if(caller)
{
start_vehicles();
}
else
{
}
}
var caller=false;
var drive_vehicle;
function triggerz_animation()
{
caller=true;
}
var destruct=false;
function collapse_animation()
{
destruct=true;
}
with reference to
http://openlayers.org/en/latest/examples/feature-move-animation.html?q=animation
Let this be helpfull..Thank You
pass data as json to the function:
push_data_for_multimarker(map,vehicles);
I think your overlay is being reused for each line. In any case, it would probably be simpler and more performant to use a point feature instead of the overlay, as follows:
add a new style to your layer for points (circle, image, etc.)
add a point feature representing the head of each line
update the coordinates of the point feature when necessary
In other words, each animal would have a linestring and a point feature. The style on the layer would include the stroke style for the line and the icon style for the point.
You could also style the points independently by giving each point feature it's own style, or using a style function on the layer.

Open curtain animation on a famo.us Scrollview

I'm trying to create an open curtain animation on a Scrollview, so when an item in a Scrollview is clicked, it and item to its left move left, and all items after it move to the right. when the item is clicked again, the curtain closes. Thinking of doing it by moving items out from the Scrollview to 2 SequentialLayouts and once the curtain closes, move them back into the Scollview. Can this be done? Can you move nodes / views around in the render tree from one node to another?
Any other design approaches I should consider?
Here is my version of the curtains you described. It was hard to know exactly what you wanted, but I took a stab at it.
define('main', function(require, exports, module) {
var Engine = require("famous/core/Engine");
var Surface = require("famous/core/Surface");
var RenderNode = require("famous/core/RenderNode");
var Modifier = require("famous/core/Modifier");
var Utility = require("famous/utilities/Utility");
var Scrollview = require("famous/views/Scrollview");
var Transitionable = require("famous/transitions/Transitionable");
var SnapTransition = require("famous/transitions/SnapTransition");
Transitionable.registerMethod('snap', SnapTransition);
var snap = {
method: 'snap',
period: 600,
dampingRatio: 0.6
};
var mainContext = Engine.createContext();
var scrollview = new Scrollview({
direction: Utility.Direction.X
});
var views = [];
scrollview.sequenceFrom(views);
function _resize(index, views, event) {
console.log(index, event);
var itsMe = (index === event.index);
if (itsMe) {
this.trans.halt();
if (this.open)
this.trans.set(100, snap);
else
this.trans.set(400, snap);
this.open = !this.open;
scrollview.goToPage(index);
} else {
if (event.isOpen) {
this.trans.halt();
this.trans.set(100, snap);
} else {
this.trans.halt();
this.trans.set(20, snap);
}
this.open = false;
}
}
function _resizeChosen(index, views, event) {
scrollview._eventOutput.emit('itemChosen', {
index: index,
isOpen: views[index].surface.open
});
}
function _surfaceSize() {
return [this.trans.get(), undefined];
}
for (var i = 0; i < 20; i++) {
var node = new RenderNode();
node.surface = new Surface({
content: (i + 1),
size: [undefined, undefined],
properties: {
backgroundColor: "hsl(" + (i * 360 / 20) + ", 90%, 50%)",
lineHeight: "50px",
textAlign: "center"
}
});
node.surface._index = i;
node.surface.open = false;
node.surface.state = new Modifier();
node.surface.trans = new Transitionable(50);
node.surface.state.sizeFrom(_surfaceSize.bind(node.surface));
node.add(node.surface.state).add(node.surface);
node.surface.pipe(scrollview);
node.surface._eventOutput.subscribe(scrollview._eventOutput);
node.surface.on('click', _resizeChosen.bind(node.surface, i, views));
node.surface.on('itemChosen', _resize.bind(node.surface, i, views));
views.push(node);
}
mainContext.add(scrollview);
});
require(['main']);
<script src="http://requirejs.org/docs/release/2.1.16/minified/require.js"></script>
<script src="http://code.famo.us/lib/requestAnimationFrame.js"></script>
<script src="http://code.famo.us/lib/classList.js"></script>
<script src="http://code.famo.us/lib/functionPrototypeBind.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.famo.us/famous/0.3.5/famous.css" />
<script src="http://code.famo.us/famous/0.3.5/famous.min.js"></script>

Resources