leaflet.motion.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /**
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Igor Vladyka <igor.vladyka@gmail.com> (https://github.com/Igor-Vladyka/leaflet.motion)
  4. **/
  5. L.Motion = L.Motion || {
  6. Event: {
  7. Started:"motion-started",
  8. Paused: "motion-paused",
  9. Resumed: "motion-resumed",
  10. Section: "motion-section",
  11. Ended: "motion-ended"
  12. }
  13. };
  14. L.motion = L.motion || {};
  15. L.Motion.Animate = {
  16. options: {
  17. pane: "polymotionPane",
  18. attribution: "Leaflet.Motion © " + (new Date()).getFullYear() + " Igor Vladyka"
  19. },
  20. motionOptions: {
  21. auto: false,
  22. easing: function(x){ return x; }, // linear
  23. speed: 0, // KM/H
  24. duration: 0 // ms
  25. },
  26. markerOptions: undefined,
  27. initialize: function (latlngs, options, motionOptions, markerOptions) {
  28. L.Util.setOptions(this, options);
  29. if (motionOptions) {
  30. this.motionOptions = L.Util.extend({}, this.motionOptions, motionOptions);
  31. }
  32. if (markerOptions) {
  33. this.markerOptions = L.Util.extend({}, markerOptions);
  34. }
  35. this._bounds = L.latLngBounds();
  36. this._linePoints = this._convertLatLngs(latlngs);
  37. if (!L.Motion.Utils.isFlat(this._linePoints)) {
  38. this._linePoints = this._linePoints[0];
  39. }
  40. this._initializeMarker();
  41. this._latlngs = [];
  42. L.Util.stamp(this); // Enforce proper animation order;
  43. },
  44. addLatLng: function(latLng, ring) {
  45. latLng = L.Motion.Utils.toLatLng(latLng);
  46. this._linePoints.push(latLng);
  47. if (this._latlngs.length) {
  48. this._latlngs.push(latLng);
  49. }
  50. return this;
  51. },
  52. /**
  53. @param {Map} map the Leaflet Map
  54. */
  55. beforeAdd: function (map) {
  56. if (!map.getPane(this.options.pane)) {
  57. map.createPane(this.options.pane).style.zIndex = 599;
  58. }
  59. this._renderer = map.getRenderer(this);
  60. },
  61. /**
  62. @param {Map} map the Leaflet Map
  63. @return {MotionObject} this
  64. */
  65. onAdd: function (map) {
  66. this._renderer._initPath(this);
  67. this._reset();
  68. this._renderer._addPath(this);
  69. if (this.__marker && this.markerOptions.showMarker) {
  70. this.__marker.addTo(map);
  71. if(this.__marker._icon && this.__marker._icon.children.length){
  72. Array.from(this.__marker._icon.children).forEach(function(icon) {
  73. var baseRotationAngle = icon.getAttribute("motion-base");
  74. if (baseRotationAngle) {
  75. icon.style.transform = "rotate(" + baseRotationAngle + "deg)";
  76. }
  77. });
  78. }
  79. }
  80. if (this.motionOptions.auto) {
  81. this.motionStart();
  82. }
  83. return this;
  84. },
  85. /**
  86. @param {Map} map the Leaflet Map
  87. */
  88. onRemove: function (map) {
  89. this.motionStop();
  90. if (this.__marker) {
  91. map.removeLayer(this.__marker);
  92. }
  93. this._renderer._removePath(this);
  94. },
  95. /**
  96. @param {DateTime} startTime time from start animation
  97. */
  98. _motion: function (startTime) {
  99. var ellapsedTime = (new Date()).getTime() - startTime;
  100. var durationRatio = 1; // 0 - 1
  101. if (this.motionOptions.duration) {
  102. durationRatio = ellapsedTime / this.motionOptions.duration;
  103. }
  104. if (durationRatio < 1) {
  105. durationRatio = this.motionOptions.easing(durationRatio, ellapsedTime, 0, 1, this.motionOptions.duration);
  106. var interpolatedLine = L.Motion.Utils.interpolateOnLine(this._map, this._linePoints, durationRatio);
  107. this.setLatLngs(interpolatedLine.traveledPath);
  108. this._drawMarker(interpolatedLine.latLng);
  109. this.__ellapsedTime = ellapsedTime;
  110. this.animation = L.Util.requestAnimFrame(function(){
  111. this._motion(startTime);
  112. }, this);
  113. } else {
  114. this.motionStop(true);
  115. }
  116. },
  117. /**
  118. Draws marker according to line position
  119. @param {LatLng} nextPoint next animation point
  120. */
  121. _drawMarker: function (nextPoint) {
  122. var marker = this.getMarker();
  123. if (marker) {
  124. var prevPoint = marker.getLatLng();
  125. // [0, 0] Means that marker is not added yet to the map
  126. var initialPoints = this._linePoints[0];
  127. if (prevPoint.lat === initialPoints.lat && prevPoint.lng === initialPoints.lng) {
  128. marker.addTo(this._map);
  129. marker.addEventParent(this);
  130. } else {
  131. if (marker._icon && marker._icon.children.length) {
  132. Array.from(marker._icon.children).forEach(function(icon) {
  133. var needToRotateMarker = icon.getAttribute("motion-base");
  134. if (needToRotateMarker) {
  135. var motionMarkerOnLine = 0;
  136. if (needToRotateMarker && !isNaN(+needToRotateMarker)) {
  137. motionMarkerOnLine = +needToRotateMarker;
  138. }
  139. icon.style.transform = "rotate(-" + Math.round(L.Motion.Utils.getAngle(prevPoint, nextPoint) + motionMarkerOnLine) +"deg)";
  140. }
  141. });
  142. }
  143. }
  144. marker.setLatLng(nextPoint);
  145. }
  146. },
  147. /**
  148. Removes marker from the map
  149. */
  150. _removeMarker: function (animEnded) {
  151. if (this.markerOptions && this.__marker) {
  152. if (!animEnded || this.markerOptions.removeOnEnd) {
  153. this._map.removeLayer(this.__marker);
  154. }
  155. }
  156. },
  157. /**
  158. Initialize marker from marker options and add it to the map if needed
  159. */
  160. _initializeMarker: function () {
  161. if (this.markerOptions) {
  162. this.__marker = L.marker(this._linePoints[0], this.markerOptions);
  163. }
  164. },
  165. /**
  166. Starts animation of current object
  167. */
  168. motionStart: function () {
  169. if (this._map && !this.animation) {
  170. if (!this.motionOptions.duration) {
  171. if (this.motionOptions.speed) {
  172. this.motionOptions.duration = L.Motion.Utils.getDuration(this._map, this._linePoints, this.motionOptions.speed);
  173. } else {
  174. this.motionOptions.duration = 0;
  175. }
  176. }
  177. this.setLatLngs([]);
  178. this._motion((new Date).getTime());
  179. this.fire(L.Motion.Event.Started, {layer: this}, false);
  180. }
  181. return this;
  182. },
  183. /**
  184. Stops animation of current object
  185. @param {LatLng[]} points full object points collection or empty collection for cleanup
  186. */
  187. motionStop: function (animEnded) {
  188. this.motionPause();
  189. this.setLatLngs(this._linePoints);
  190. this.__ellapsedTime = null;
  191. this._removeMarker(animEnded);
  192. this.fire(L.Motion.Event.Ended, {layer: this}, false);
  193. return this;
  194. },
  195. /**
  196. Pauses animation of current object
  197. */
  198. motionPause: function () {
  199. if (this.animation) {
  200. L.Util.cancelAnimFrame(this.animation);
  201. this.animation = null;
  202. this.fire(L.Motion.Event.Paused, {layer: this}, false);
  203. }
  204. return this;
  205. },
  206. /**
  207. Resume animation of current object
  208. */
  209. motionResume: function () {
  210. if (!this.animation && this.__ellapsedTime) {
  211. if (!this.motionOptions.duration) {
  212. if (this.motionOptions.speed) {
  213. this.motionOptions.duration = L.Motion.Utils.getDuration(this._map, this._linePoints, this.motionOptions.speed);
  214. } else {
  215. this.motionOptions.duration = 0;
  216. }
  217. }
  218. this._motion((new Date).getTime() - (this.__ellapsedTime));
  219. this.fire(L.Motion.Event.Resumed, {layer: this}, false);
  220. }
  221. return this;
  222. },
  223. /**
  224. Toggles animation of current object; Start/Pause/Resume;
  225. */
  226. motionToggle: function () {
  227. if (this.animation) {
  228. if (this.__ellapsedTime) {
  229. this.motionPause();
  230. }
  231. } else {
  232. if (this.__ellapsedTime) {
  233. this.motionResume();
  234. } else {
  235. this.motionStart();
  236. }
  237. }
  238. return this;
  239. },
  240. /**
  241. Setup motion duration at any time
  242. */
  243. motionDuration: function (duration) {
  244. var prevDuration = this.motionSpeed.duration;
  245. this.motionOptions.duration = duration || 0;
  246. if (this.animation && prevDuration) {
  247. this.motionPause();
  248. this.__ellapsedTime = this.__ellapsedTime * (prevDuration / duration);
  249. this.motionOptions.duration = duration;
  250. this.motionResume();
  251. }
  252. return this;
  253. },
  254. /**
  255. Setup motion speed at any time
  256. */
  257. motionSpeed: function (speed) {
  258. var prevSpeed = this.motionOptions.speed;
  259. this.motionOptions.speed = speed || 0;
  260. if (this.animation && prevSpeed) {
  261. this.motionPause();
  262. this.__ellapsedTime = this.__ellapsedTime * (prevSpeed / speed);
  263. this.motionOptions.duration = L.Motion.Utils.getDuration(this._map, this._linePoints, this.motionOptions.speed);
  264. this.motionResume();
  265. }
  266. return this;
  267. },
  268. /**
  269. Returns current constructed marker
  270. */
  271. getMarker: function () {
  272. return this.__marker;
  273. },
  274. /**
  275. Returns markers array from all inner layers without flattering.
  276. */
  277. getMarkers: function () {
  278. return [this.getMarker()];
  279. }
  280. }