SelectionIndicatorViewModel.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import Cartesian2 from "../../Core/Cartesian2.js";
  2. import defaultValue from "../../Core/defaultValue.js";
  3. import defined from "../../Core/defined.js";
  4. import DeveloperError from "../../Core/DeveloperError.js";
  5. import EasingFunction from "../../Core/EasingFunction.js";
  6. import SceneTransforms from "../../Scene/SceneTransforms.js";
  7. import knockout from "../../ThirdParty/knockout.js";
  8. const screenSpacePos = new Cartesian2();
  9. const offScreen = "-1000px";
  10. /**
  11. * The view model for {@link SelectionIndicator}.
  12. * @alias SelectionIndicatorViewModel
  13. * @constructor
  14. *
  15. * @param {Scene} scene The scene instance to use for screen-space coordinate conversion.
  16. * @param {Element} selectionIndicatorElement The element containing all elements that make up the selection indicator.
  17. * @param {Element} container The DOM element that contains the widget.
  18. */
  19. function SelectionIndicatorViewModel(
  20. scene,
  21. selectionIndicatorElement,
  22. container
  23. ) {
  24. //>>includeStart('debug', pragmas.debug);
  25. if (!defined(scene)) {
  26. throw new DeveloperError("scene is required.");
  27. }
  28. if (!defined(selectionIndicatorElement)) {
  29. throw new DeveloperError("selectionIndicatorElement is required.");
  30. }
  31. if (!defined(container)) {
  32. throw new DeveloperError("container is required.");
  33. }
  34. //>>includeEnd('debug')
  35. this._scene = scene;
  36. this._screenPositionX = offScreen;
  37. this._screenPositionY = offScreen;
  38. this._tweens = scene.tweens;
  39. this._container = defaultValue(container, document.body);
  40. this._selectionIndicatorElement = selectionIndicatorElement;
  41. this._scale = 1;
  42. /**
  43. * Gets or sets the world position of the object for which to display the selection indicator.
  44. * @type {Cartesian3}
  45. */
  46. this.position = undefined;
  47. /**
  48. * Gets or sets the visibility of the selection indicator.
  49. * @type {Boolean}
  50. */
  51. this.showSelection = false;
  52. knockout.track(this, [
  53. "position",
  54. "_screenPositionX",
  55. "_screenPositionY",
  56. "_scale",
  57. "showSelection",
  58. ]);
  59. /**
  60. * Gets the visibility of the position indicator. This can be false even if an
  61. * object is selected, when the selected object has no position.
  62. * @type {Boolean}
  63. */
  64. this.isVisible = undefined;
  65. knockout.defineProperty(this, "isVisible", {
  66. get: function () {
  67. return this.showSelection && defined(this.position);
  68. },
  69. });
  70. knockout.defineProperty(this, "_transform", {
  71. get: function () {
  72. return `scale(${this._scale})`;
  73. },
  74. });
  75. /**
  76. * Gets or sets the function for converting the world position of the object to the screen space position.
  77. *
  78. * @member
  79. * @type {SelectionIndicatorViewModel.ComputeScreenSpacePosition}
  80. * @default SceneTransforms.wgs84ToWindowCoordinates
  81. *
  82. * @example
  83. * selectionIndicatorViewModel.computeScreenSpacePosition = function(position, result) {
  84. * return Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position, result);
  85. * };
  86. */
  87. this.computeScreenSpacePosition = function (position, result) {
  88. return SceneTransforms.wgs84ToWindowCoordinates(scene, position, result);
  89. };
  90. }
  91. /**
  92. * Updates the view of the selection indicator to match the position and content properties of the view model.
  93. * This function should be called as part of the render loop.
  94. */
  95. SelectionIndicatorViewModel.prototype.update = function () {
  96. if (this.showSelection && defined(this.position)) {
  97. const screenPosition = this.computeScreenSpacePosition(
  98. this.position,
  99. screenSpacePos
  100. );
  101. if (!defined(screenPosition)) {
  102. this._screenPositionX = offScreen;
  103. this._screenPositionY = offScreen;
  104. } else {
  105. const container = this._container;
  106. const containerWidth = container.parentNode.clientWidth;
  107. const containerHeight = container.parentNode.clientHeight;
  108. const indicatorSize = this._selectionIndicatorElement.clientWidth;
  109. const halfSize = indicatorSize * 0.5;
  110. screenPosition.x =
  111. Math.min(
  112. Math.max(screenPosition.x, -indicatorSize),
  113. containerWidth + indicatorSize
  114. ) - halfSize;
  115. screenPosition.y =
  116. Math.min(
  117. Math.max(screenPosition.y, -indicatorSize),
  118. containerHeight + indicatorSize
  119. ) - halfSize;
  120. this._screenPositionX = `${Math.floor(screenPosition.x + 0.25)}px`;
  121. this._screenPositionY = `${Math.floor(screenPosition.y + 0.25)}px`;
  122. }
  123. }
  124. };
  125. /**
  126. * Animate the indicator to draw attention to the selection.
  127. */
  128. SelectionIndicatorViewModel.prototype.animateAppear = function () {
  129. this._tweens.addProperty({
  130. object: this,
  131. property: "_scale",
  132. startValue: 2,
  133. stopValue: 1,
  134. duration: 0.8,
  135. easingFunction: EasingFunction.EXPONENTIAL_OUT,
  136. });
  137. };
  138. /**
  139. * Animate the indicator to release the selection.
  140. */
  141. SelectionIndicatorViewModel.prototype.animateDepart = function () {
  142. this._tweens.addProperty({
  143. object: this,
  144. property: "_scale",
  145. startValue: this._scale,
  146. stopValue: 1.5,
  147. duration: 0.8,
  148. easingFunction: EasingFunction.EXPONENTIAL_OUT,
  149. });
  150. };
  151. Object.defineProperties(SelectionIndicatorViewModel.prototype, {
  152. /**
  153. * Gets the HTML element containing the selection indicator.
  154. * @memberof SelectionIndicatorViewModel.prototype
  155. *
  156. * @type {Element}
  157. */
  158. container: {
  159. get: function () {
  160. return this._container;
  161. },
  162. },
  163. /**
  164. * Gets the HTML element that holds the selection indicator.
  165. * @memberof SelectionIndicatorViewModel.prototype
  166. *
  167. * @type {Element}
  168. */
  169. selectionIndicatorElement: {
  170. get: function () {
  171. return this._selectionIndicatorElement;
  172. },
  173. },
  174. /**
  175. * Gets the scene being used.
  176. * @memberof SelectionIndicatorViewModel.prototype
  177. *
  178. * @type {Scene}
  179. */
  180. scene: {
  181. get: function () {
  182. return this._scene;
  183. },
  184. },
  185. });
  186. /**
  187. * A function that converts the world position of an object to a screen space position.
  188. * @callback SelectionIndicatorViewModel.ComputeScreenSpacePosition
  189. * @param {Cartesian3} position The position in WGS84 (world) coordinates.
  190. * @param {Cartesian2} result An object to return the input position transformed to window coordinates.
  191. * @returns {Cartesian2} The modified result parameter.
  192. */
  193. export default SelectionIndicatorViewModel;