leafletjs: polyline list into geojson - javascript

i have list of polylines. i want to make it more organized. how can i make my polyline list into the GeoJSON?
var point1 = new coordsToLatLngArray(0, 100);
var point2 = new coordsToLatLngArray(200, 150);
var point3 = new coordsToLatLngArray(400, 70);
var point4 = new coordsToLatLngArray(10, 70);
var point5 = new coordsToLatLngArray(90, 50);
var pointList = [point1, point2, point3, point4, point5];
var firstpolyline = new L.polyline(pointList, {
color: 'red',
weight: 3,
opacity: 0.5,
smoothFactor: 1
});
firstpolyline.addTo(map);
i have trying to work with: http://leafletjs.com/examples/geojson.html
but it didn't work. my line were outside the map:
var geojsonFeature = [{
"type": "LineString",
"coordinates":
[
[0,0],
[-100,100]
]
}];
var myStyle = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
var myLayer = L.geoJson(coordsToLatLngArray(), {style: myStyle }).addTo(l);
myLayer.addData(geojsonFeature);
Demo of the problem:
http://jsfiddle.net/n4k1psuh/

There will only be passed a single argument to coordsToLatLngArray, an array:
[longitude,latitude]
The function must look like this:
var coordsToLatLngArray = function(c) {
var e = fixLat(c[1]),
t = c[0],
n = e / latScale + latScaleVal / 2 / latScale,
r = t / longScale + longScaleVal / 2 / longScale;
return [-n, r]
};
Note: in geoJSON a coordinate has a reversed order, [longitude,latitude], so the coordinates must be:
[
[100, 0],
[150, 200],
[70,400],
[70,10],
[50,90]
]
to draw the feature call:
var myLayer = L.geoJson(geojsonFeature, {
style: myStyle,
coordsToLatLng:coordsToLatLngArray
}).addTo(l);
Demo: http://jsfiddle.net/Ltaw5teo/

Related

Leaflet - Custom Control Layer Button

I am kinda stumped on this. The code below works correctly to create a set of gridlines. I would like to set it up so that I can turn them on and off under the overlay/control layers box. How would I go about turning this code on and off using a check box in the control box?
Thank you!
//Grid lines
var hold = 0
for (let i = 0; i <17; i++) {
var pointA = map.unproject([hold, 0], map.getMaxZoom());
var pointB = map.unproject([hold, 8192], map.getMaxZoom());
var pointList = [pointA, pointB];
var firstpolyline = new L.Polyline(pointList, {
color: 'grey',
weight: 4,
opacity: 0.8,
smoothFactor: 1
});
firstpolyline.addTo(map);
var pointA = map.unproject([0, hold], map.getMaxZoom());
var pointB = map.unproject([8192, hold], map.getMaxZoom());
var pointList = [pointA, pointB];
var firstpolyline = new L.Polyline(pointList, {
color: 'grey',
weight: 4,
opacity: 0.8,
smoothFactor: 1
});
firstpolyline.addTo(map);
hold = hold + 512
}```
You can create a FeatureGroup and add to them the layers and then you can add the FeatureGroup to the LayersControl.
var fg = L.featureGroup().addTo(map); // If you want to hide it initial remove .addTo(map)
yourLayerControlVariable.addOverlay(fg);
...
firstpolyline.addTo(fg);

How to read slider values dynamically into a function in JSXGraph?

UPDATE from 22.5.2019
I did a simpler example of the "not working" code and also imitated the "working code" by defining K1 and KK locally when drawing the points, but doing this inside a method to have them defined only once and have the same definition for all points. Since I want the points to be drawn on a parabola, I now create points that have a fixed radius from the axis of revolution and a sign, so that I can create two points 180 degrees apart by just switching the sign from +1 to -1 when drawing the parametrized points in the xz plane. Still, nothing gets drawn. Here is a link to the thing I want to see (but the code is ugly).
Below the newest try (with less points being drawn, just to see if it works at all).
const board = JXG.JSXGraph.initBoard('jxgbox', {
boundingbox: [-10, 10, 10, -10],
axis: true,
showCopyright: true,
showNavigation: true,
pan: false,
grid: false,
zoom: {
factorX: 1.25,
factorY: 1.25,
wheel: false
}
});
//create z axis
var zAxis = board.create('axis', [
[0, 0],
[-1, -1]
], {
ticks: {
majorHeight: 10,
drawLabels: false
}
});
//create direction of view for projections
var cam = [4, 4, 30]; // [x,y,z]
var r = 6.0;
var origin = [0, 0, 0];
// Function for parallel projection
var project = function(crd, cam) {
var d = -crd[2] / cam[2];
return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};
//create slider for rotating the parabola
var sRadius = board.create('slider', [
[1, -8.5],
[6, -8.5],
[-10, 0, 10]
], {
name: 'angle',
needsRegularUpdate: true
//snapWidth: 1
});
//create slider for adjusting the angular speed
var sOmega = board.create('slider', [
[1, -7.5],
[6, -7.5],
[0, 2, 10]
], {
name: 'Omega',
needsRegularUpdate: true
//snapWidth: 1,
});
//fix parameters
const g = 9.81 //gravitational acceleration
const h0 = 5 //initial height of the water surface
//define radius from the y-axis for I3 and I4
const R34 = Math.sqrt(2);
// Function for parallel projection
var project = function(crd, cam) {
var d = -crd[2] / cam[2];
return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};
//function creates points for drawing conic sections
function PPoint2(radius,sign,namep,fixval) {
this.R=radius;
this.S=sign;
this.Namep=namep;
this.Fixval=fixval
}
//method for drawing each Point
PPoint2.prototype.draw = function(pp) {
board.create('point', [function() {
var K1 = sOmega.Value()*sOmega.Value()/g,
KK = 1/4*sOmega.Value()*sOmega.Value()/g,
v = sRadius.Value() * Math.PI * 0.5 / 10.0,
c = [pp.sign*pp.R*Math.sin(v),K1/2*pp.R*pp.R-KK+h0,pp.sign*pp.R*Math.cos(v)];
//debugger
return project(c, cam);
}
], {
fixed: this.Fixval,
name: this.Namep,
visible: true
})
}
//create and draw points
var p3 = new PPoint2(0,-1,'p_3','false');
var I_1 = new PPoint2(r,1,'I_1','false');
//debugger
p3.draw(p3)
I_1.draw(I_1)
Original question below:
I am doing an illustration of the "bucket argument" (how water takes the shape of a paraboloid in a spinning bucket) using JSXGraph. I would like to
A) Have the shape of the parabola be dependent on the angular velocity "Omega" of the bucket.
B) Have the parabola be projected from 3D into a 2D image and the user being able to turn the parabola using a slider.
For A) my code uses the slider "Omega" and for B) the slider "angle".
The slider values are read into global variables K1 (coeffiecient of the second order term of the parabola) and KK (constant term of the parabola). Then five points (p3 and I_1-I_4) are drawn and the parabola should be drawn through these points. The points are drawn with the initial slider values, but updating (i.e. sliding) the sliders doesn't make the points move. Also, the parabola is not drawn at all.
How to make the points adjust their positions according to the current slider values?
The functionality I want is implemented in this fiddle https://jsfiddle.net/ync3pkx5/1/ (but the code is ugly and KK and K1 are defined locally for each point, but I want them to be global).
HTML
<div id="jxgbox" class="jxgbox" style="width:500px; height:500px">
</div>
JS
//create drawing board
const board = JXG.JSXGraph.initBoard('jxgbox', {
boundingbox: [-10, 10, 10, -10],
axis: true,
showCopyright: true,
showNavigation: true,
pan: false,
grid: false,
zoom: {
factorX: 1.25,
factorY: 1.25,
wheel: false
}
});
//create z axis
var zAxis = board.create('axis', [
[0, 0],
[-1, -1]
], {
ticks: {
majorHeight: 10,
drawLabels: false
}
});
//create direction of view for projections
var cam = [4, 4, 30]; // [x,y,z]
var r = 6.0;
var origin = [0, 0, 0];
// Function for parallel projection
var project = function(crd, cam) {
var d = -crd[2] / cam[2];
return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};
//create slider for rotating the parabola
var sRadius = board.create('slider', [
[1, -8.5],
[6, -8.5],
[-10, 0, 10]
], {
name: 'angle',
//snapWidth: 1
});
//create slider for adjusting the angular speed (inactive)
var sOmega = board.create('slider', [
[1, -7.5],
[6, -7.5],
[0, 0, 10]
], {
name: 'Omega',
//snapWidth: 1,
});
//fix parameters
var g = 9.81 //gravitational acceleration
var h0 = 5 //initial height of the water surface
//peak coordinates of the fixed parabola
var KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g; //constant term in the equation of the parabola
var peak = [0, -KK+h0];
//point for mirroring
var pmirr = board.create('point', [0, h0/2], {
visible: false
});
//define radius from the y-axis for I3 and I4
var R34 = Math.sqrt(2);
//function for projecting poomntson the parabola
var PProject = function(xx,yy,zz) {
var K1 = sOmega.Value() * sOmega.Value() / g,
v = sRadius.Value() * Math.PI * 0.5 / 10.0,
KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
return project([xx * Math.sin(v), K1/2 * yy * yy-KK+h0, zz * Math.cos(v)], cam);
}
//p1-p3 are used for drawing the elliptical curves circ1 and prbl2
var p1 = board.create('point', [r, 0], {
fixed: true,
name: 'p_1',
visible: false
});
var p2 = board.create('point', [-r, 0], {
fixed: true,
name: 'p_2',
visible: false
});
var p3 = board.create('point', [
function() {
var KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g,
c =[0,-KK+h0,0];
//alert(KK);
//alert(h0);
return project(c, cam);
}
], {
visible: true,
name: 'p3'
});
//divisor when drawing points A-C for ellipses and points A2-C2
var div = Math.sqrt(2)
//point variables for drawing circles
var A = board.create('point', [
function() {
var c = [r / div, 0, r / div];
return project(c, cam);
}
], {
name: 'A',
visible: false
});
var B = board.create('point', [
function() {
var c = [-r / div, 0, r / div];
return project(c, cam);
}
], {
name: 'B',
visible: false
});
var C = board.create('point', [
function() {
var c = [r / div, 0, -r / div];
return project(c, cam);
}
], {
name: 'C',
visible: false
});
//I-I4 are points for drawing the rotating parabola
var I = board.create('point', [
function() {
var K1 = sOmega.Value() * sOmega.Value() / g,
v = sRadius.Value() * Math.PI * 0.5 / 10.0,
KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
return project([r * Math.sin(v), K1/2 * r * r-KK+h0, r * Math.cos(v)], cam);
}
], {
visible: true,
name: 'I'
});
var I2 = board.create('point', [
function() {
var K1 = sOmega.Value() * sOmega.Value() / g,
v = sRadius.Value() * Math.PI * 0.5 / 10.0,
KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
return project([-r * Math.sin(v), K1/2 * r * r-KK+h0, -r * Math.cos(v)], cam);
}
], {
visible: true,
name: 'I_2'
});
var I3 = board.create('point', [
function() {
var K1 = sOmega.Value() * sOmega.Value() / g,
v = sRadius.Value() * Math.PI * 0.5 / 10.0,
KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
return project([R34 * Math.sin(v), K1/2 * R34 * R34-KK+h0, R34 * Math.cos(v)], cam);
}
], {
visible: true,
name: 'I_3'
});
var I4 = board.create('point', [
function() {
var K1 = sOmega.Value() * sOmega.Value() / g,
v = sRadius.Value() * Math.PI * 0.5 / 10.0,
KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g;
return project([-R34 * Math.sin(v), K1/2 * R34 * R34-KK+h0, -R34 * Math.cos(v)], cam);
}
], {
visible: true,
name: 'I_4'
});
//draw circle on surface y=0
var circ1 = board.create('conic', [A, B, C, p2, p1]);
//draw a mirror circle of circ1 w.r.t. to pmirr and a small circle that delimits the parabolas
var circ2 = board.create('mirrorelement', [circ1, pmirr]);
//draw the rotating parabola
var prbl2 = board.create('conic', [I, I2, I3, I4, p3], {
strokeColor: '#CA7291',
strokeWidth: 2,
//trace :true
});
debugger;
//add textbox
var txt1 = board.create('text', [3, 7, 'The blue lines delimit the volume of water when Omega = 0 and the red parabola delimits the volume without water as the bucket is rotating (surface h(r)). The water volume is constant, independent of Omega']);
Here is the fiddle I am working on and would want to get to work
https://jsfiddle.net/c8tr4dh3/2/
HTML
<div id="jxgbox" class="jxgbox" style="width:500px; height:500px">
</div>
JS
const board = JXG.JSXGraph.initBoard('jxgbox', {
boundingbox: [-10, 10, 10, -10],
axis: true,
showCopyright: true,
showNavigation: true,
pan: false,
grid: false,
zoom: {
factorX: 1.25,
factorY: 1.25,
wheel: false
}
});
//create z axis
var zAxis = board.create('axis', [
[0, 0],
[-1, -1]
], {
ticks: {
majorHeight: 10,
drawLabels: false
}
});
//create direction of view for projections
var cam = [4, 4, 30]; // [x,y,z]
var r = 6.0;
var origin = [0, 0, 0];
// Function for parallel projection
var project = function(crd, cam) {
var d = -crd[2] / cam[2];
return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};
//create slider for rotating the parabola
var sRadius = board.create('slider', [
[1, -8.5],
[6, -8.5],
[-10, 0, 10]
], {
name: 'angle',
needsRegularUpdate: true
//snapWidth: 1
});
//create slider for adjusting the angular speed (inactive)
var sOmega = board.create('slider', [
[1, -7.5],
[6, -7.5],
[0, 0, 10]
], {
name: 'Omega',
needsRegularUpdate: true
//snapWidth: 1,
});
//fix parameters
var g = 9.81 //gravitational acceleration
var h0 = 5 //initial height of the water surface
var K1 = sOmega.Value() * sOmega.Value() / g; //coeffficient of the quadratic term of the parabola
var KK = 1/4*sOmega.Value()*sOmega.Value()*r*r/g; //constant term in the equation of the parabola
//peak coordinates of the fixed parabola
var peak = [0, -KK+h0];
//slider auxiliary variable
var v = sRadius.Value() * Math.PI * 0.5 / 10.0;
//define radius from the y-axis for I3 and I4
var R34 = Math.sqrt(2);
// Function for parallel projection
var project = function(crd, cam) {
var d = -crd[2] / cam[2];
return [1, crd[0] + d * cam[0], crd[1] + d * cam[1]];
};
//function creates points for drawing conic sections
function PPoint(xx, yy,zz,namep,fixval) {
this.XX=xx;
this.YY=yy;
this.ZZ=zz;
this.Namep=namep;
this.Fixval=fixval
}
//method for drawing each Point
PPoint.prototype.draw = function(pp) {
board.create('point', [function() {
var c = [pp.XX,pp.YY,pp.ZZ];
//debugger
return project(c, cam);
}
], {
fixed: this.Fixval,
name: this.Namep,
visible: true
})
}
var div=Math.sqrt(2);
//create and draw points
var p3 = new PPoint(0,peak[1],0,'p_3','false');
//debugger
var I_1 = new PPoint(r*Math.sin(v),K1/2*r*r-KK+h0,r*Math.cos(v),'I_1','false');
var I_2 = new PPoint(-r*Math.sin(v),K1/2*r*r-KK+h0,-r*Math.cos(v),'I_2','false');
var I_3 = new PPoint(R34*Math.sin(v),K1/2*R34*R34-KK+h0,R34*Math.cos(v),'I_3','false');
var I_4 = new PPoint(-R34*Math.sin(v),K1/2*R34*R34-KK+h0,-R34*Math.cos(v),'I_4','false');
p3.draw(p3)
I_1.draw(I_1)
I_2.draw(I_2)
I_3.draw(I_3)
//debugger;
I_4.draw(I_4)
//draw the rotating parabola
var prbl = board.create('conic', [[I_1.XX,I_1.YY,I_1.ZZ], [I_2.XX,I_2.YY,I_2.ZZ], [I_3.XX,I_3.YY,I_3.ZZ], [I_4.XX,I_4.YY,I_4.ZZ],[p3.XX,p3.YY,p3.ZZ]], {
strokeColor: '#CA7291',
strokeWidth: 2,
//trace :true
});
//debugger;
//add textbox
var txt1 = board.create('text', [3, 7, 'The blue lines delimit the volume of water when Omega = 0 and the red parabola delimits the volume without water as the bucket is rotating (surface h(r)). The water volume is constant, independent of Omega']);
The blue circles in the first fiddle are not critical, they can be added to the other one later.
Having done some debugging, the parents of the parabola all have "isReal: true" in both fiddles, but in the fiddle that isn't working the parabola itself has "isReal: false" while the fiddle that's working has "isReal: true" for the parabola. Not sure whether that's relevant, though.
In the non-working fiddle, I also tried enclosing the whole code into "board.on('mouse,function(){here all code from line 59 onwards{) to get the points move, but that didn't help; the points aren't drawn at all, not even the initial positions.
It seems that in your updated code posted above there is a very simple error: The value of sign is stored in the property pp.S, but you try to access it as pp.sign. My suggestion is to use the following code:
function PPoint2(radius,sign,namep,fixval) {
this.R = radius;
this.S = sign;
this.Namep = namep;
this.Fixval = fixval;
}
//method for drawing each Point
PPoint2.prototype.draw = function() {
var pp = this;
this.point = board.create('point', [function() {
var K1 = sOmega.Value()*sOmega.Value()/g,
KK = 1/4*sOmega.Value()*sOmega.Value()/g,
v = sRadius.Value() * Math.PI * 0.5 / 10.0,
c = [pp.S*pp.R*Math.sin(v),
K1/2*pp.R*pp.R-KK+h0,
pp.S*pp.R*Math.cos(v)];
return project(c, cam);
}], {
fixed: this.Fixval,
name: this.Namep,
visible: true
});
};
//create and draw points
var p3 = new PPoint2(0,-1,'p_3','false');
var I_1 = new PPoint2(r,1,'I_1','false');
p3.draw();
I_1.draw();

Openlayers trackline length (include altitude)

I using OpenLayers 4, and I want to get length at the LineString.
My code snippet:
var trackline = new ol.geom.LineString(points); //points an array with coordinates
var featurething = new ol.Feature({geometry: trackline});
var linestyle = new ol.style.Style({
stroke : new ol.style.Stroke({width: 4, color: 'rgba(0, 180, 220, 1)'})
});
featurething.setStyle(linestyle);
vectorSource.addFeature( featurething );
How get the trackline length, include altitude?
Thanks!

TextGeometry performance in Three.js

I need to have more than ~400 text objects on the scene. Each text object is one sentence of a decimal number with two digit. Echa text is reading from a JSON file. Each text object have a position (x,y,z) in the scene.
Using a basic scene i'm having, it takes a lot of times to load each text.
The code is here:
function setText(text, textColor, textSize, positionX, positionY, positionZ) {
var textGeo = new THREE.TextGeometry(text, {
height: 0,
curveSegments: 4,
font: "helvetiker",
//font: "optimer",
weight: "normal",
style: "normal",
size: textSize,
//
//bevelThickness: bevelThickness,
//bevelSize: bevelSize,
//bevelEnabled: bevelEnabled,
material: 0,
extrudeMaterial: 1
});
textGeo.computeBoundingBox();
textGeo.computeVertexNormals();
var textMaterial = new THREE.MeshBasicMaterial({
shading: THREE.FlatShading,
transparent: true,
depthWrite: false,
transparent: true,
needsUpdate: true,
color: textColor,
side:THREE.DoubleSide
});
var text = new THREE.Mesh(textGeo, textMaterial);
text.position.x = positionX;
text.position.y = positionY;
text.position.z = positionZ;
return text;
}
Examples of texts:
"model", "metamodel", "syntax", "sentence" ...
Examples of numbers:
7.32,
7.81,
8.30,
8.78,
Could you please help me? Thanks!
EDIT: The new code is here :
function createTextGeo(textSize, text) {
var tGeo = new THREE.TextGeometry(text, {
height: 0,
curveSegments: 4,
font: "helvetiker",
weight: "normal",
style: "normal",
size: textSize,
material: 0,
extrudeMaterial: 1
});
tGeo.computeBoundingBox();
tGeo.computeVertexNormals();
return tGeo;
}
function createGeoMaterial(textColor) {
var tMat = new THREE.MeshBasicMaterial({
color: textColor,
});
return tMat;
}
function setText(text, textColor, tMat, tGeo, X, Y, Z) {
tMat.color = textColor;
tGeo.text = text;
var text = new THREE.Mesh(tGeo, tMat);
text.position.set( X, Y, Z );
return text;
}
function createTexts(value) {
var text;
var mesh;
var x = 5;
var y = 8;
var z = 10;
var keyColor = new THREE.Color('#0B0B61');
var tMat = createGeoMaterial(keyColor);
var tGeo = createTextGeo(5, "");
for (var i = 0; i < value; i++) {
mesh = setText(i, keyColor, tMat, tGeo, x * i, y * i, z * i);
scene.add(mesh);
}
}

Drawing animated openlayers linestring path

I have seen an impressive mapping example on http://jerusalem.com/map#!/tour/the_way_of_the_cross/location/abu_jaafar, Does anybody how a similar animation on the drawn path of the points can done using openlayers?
The following fiddle shows the linestrings http://jsfiddle.net/pwuVz/58/ but I need is to be able to animate the line string itself so that the string is not directly drawn.
var map = new OpenLayers.Map( 'map', {theme:null,
controls:[new OpenLayers.Control.Navigation()]} );
layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0",
{layers: 'basic'} );
map.addLayer(layer);
map.setCenter([3, 49], 5);
var startPt=new OpenLayers.Geometry.Point( 2, 45);
var endPt=new OpenLayers.Geometry.Point(7,55);
//make the line:
var line=new OpenLayers.Geometry.LineString([startPt, endPt]);
//style
var style={strokeColor:"#0500bd", strokeWidth:15, strokeOpacity: 0.5, strokeColor: '#0000ff'};
//make vector
var fea=new OpenLayers.Feature.Vector(line, {}, style);
//make vectorLayer
var vec= new OpenLayers.Layer.Vector();
//add the feature
vec.addFeatures([fea]);
//add to map
map.addLayer(vec);
setTimeout(function() {
var startPt=new OpenLayers.Geometry.Point( 7, 55);
var endPt=new OpenLayers.Geometry.Point(13,52);
//make the line:
var line=new OpenLayers.Geometry.LineString([startPt, endPt]);
//style
var style={strokeColor:"#0500bd", strokeWidth:15, strokeOpacity: 0.5, strokeColor: '#0000ff'};
//make vector
var fea=new OpenLayers.Feature.Vector(line, {}, style);
//make vectorLayer
var vec= new OpenLayers.Layer.Vector();
//add the feature
vec.addFeatures([fea]);
//add to map
map.addLayer(vec);
}, 2000);
You can animate it by drawing only one part of the line at a time. Here is one way you could do it:
function drawAnimatedLine(startPt, endPt, style, steps, time, fn) {
var directionX = (endPt.x - startPt.x) / steps;
var directionY = (endPt.y - startPt.y) / steps;
var i = 0;
var prevLayer;
var ivlDraw = setInterval(function () {
if (i > steps) {
clearInterval(ivlDraw);
if (fn) fn();
return;
}
var newEndPt = new OpenLayers.Geometry.Point(startPt.x + i * directionX, startPt.y + i * directionY);
var line = new OpenLayers.Geometry.LineString([startPt, newEndPt]);
var fea = new OpenLayers.Feature.Vector(line, {}, style);
var vec = new OpenLayers.Layer.Vector();
vec.addFeatures([fea]);
map.addLayer(vec);
if(prevLayer) map.removeLayer(prevLayer);
prevLayer = vec;
i++;
}, time / steps);
}
The time argument specifies how long you want the animation to last (in milliseconds), and the steps specifies how many steps you want to divide the animation into. fn is a callback that will be executed when the animation is complete.
Here is a jsFiddle demo that demonstrates this.

Categories

Resources