not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

312 lines
8.4 KiB

  1. /**
  2. Slideable is a control that can be dragged either horizontally or vertically
  3. between a minimum and a maximum value. When released from dragging, a
  4. Slideable will animate to its minimum or maximum position, depending on the
  5. direction of the drag.
  6. The *min* value specifies a position left of or above the initial position,
  7. to which the Slideable may be dragged.
  8. The *max* value specifies a position right of or below the initial position,
  9. to which the Slideable may be dragged.
  10. The *value* property specifies the current position of the Slideable,
  11. between the minimum and maximum positions.
  12. *min*, *max*, and *value* may be specified in units of "px" or "%".
  13. The *axis* property controls whether the Slideable slides left-right (h) or
  14. up-down (v).
  15. The following control is placed 90% off the screen to the right, and slides
  16. to its natural position.
  17. {kind: "enyo.Slideable", value: -90, min: -90, unit: "%",
  18. classes: "enyo-fit", style: "width: 300px;",
  19. components: [
  20. {content: "stuff"}
  21. ]
  22. }
  23. */
  24. enyo.kind({
  25. name: "enyo.Slideable",
  26. kind: "Control",
  27. published: {
  28. //* Direction of sliding; can be "h" or "v"
  29. axis: "h",
  30. //* A value between min and max to position the Slideable
  31. value: 0,
  32. //* Unit for min, max, and value; can be "px" or "%"
  33. unit: "px",
  34. //* A minimum value to slide to
  35. min: 0,
  36. //* A maximum value to slide to
  37. max: 0,
  38. accelerated: "auto",
  39. //* Set to false to prevent the Slideable from dragging with elasticity past its min/max value.
  40. overMoving: true,
  41. //* Set to false to disable dragging.
  42. draggable: true
  43. },
  44. events: {
  45. //* Fires when the Slideable finishes animating.
  46. onAnimateFinish: "",
  47. onChange: ""
  48. },
  49. // Set to true to prevent a drag from bubbling beyond the Slideable.
  50. preventDragPropagation: false,
  51. //* @protected
  52. tools: [
  53. {kind: "Animator", onStep: "animatorStep", onEnd: "animatorComplete"}
  54. ],
  55. handlers: {
  56. ondragstart: "dragstart",
  57. ondrag: "drag",
  58. ondragfinish: "dragfinish"
  59. },
  60. kDragScalar: 1,
  61. dragEventProp: "dx",
  62. unitModifier: false,
  63. canTransform: false,
  64. //* @protected
  65. create: function() {
  66. this.inherited(arguments);
  67. this.acceleratedChanged();
  68. this.transformChanged();
  69. this.axisChanged();
  70. this.valueChanged();
  71. this.addClass("enyo-slideable");
  72. },
  73. initComponents: function() {
  74. this.createComponents(this.tools);
  75. this.inherited(arguments);
  76. },
  77. rendered: function() {
  78. this.inherited(arguments);
  79. this.canModifyUnit();
  80. this.updateDragScalar();
  81. },
  82. resizeHandler: function() {
  83. this.inherited(arguments);
  84. this.updateDragScalar();
  85. },
  86. canModifyUnit: function() {
  87. if (!this.canTransform) {
  88. var b = this.getInitialStyleValue(this.hasNode(), this.boundary);
  89. // If inline style of "px" exists, while unit is "%"
  90. if (b.match(/px/i) && (this.unit === "%")) {
  91. // Set unitModifier - used to over-ride "%"
  92. this.unitModifier = this.getBounds()[this.dimension];
  93. }
  94. }
  95. },
  96. getInitialStyleValue: function(inNode, inBoundary) {
  97. var s = enyo.dom.getComputedStyle(inNode);
  98. if (s) {
  99. return s.getPropertyValue(inBoundary);
  100. } else if (inNode && inNode.currentStyle) {
  101. return inNode.currentStyle[inBoundary];
  102. }
  103. return "0";
  104. },
  105. updateBounds: function(inValue, inDimensions) {
  106. var inBounds = {};
  107. inBounds[this.boundary] = inValue;
  108. this.setBounds(inBounds, this.unit);
  109. this.setInlineStyles(inValue, inDimensions);
  110. },
  111. updateDragScalar: function() {
  112. if (this.unit == "%") {
  113. var d = this.getBounds()[this.dimension];
  114. this.kDragScalar = d ? 100 / d : 1;
  115. if (!this.canTransform) {
  116. this.updateBounds(this.value, 100);
  117. }
  118. }
  119. },
  120. transformChanged: function() {
  121. this.canTransform = enyo.dom.canTransform();
  122. },
  123. acceleratedChanged: function() {
  124. if (!(enyo.platform.android > 2)) {
  125. enyo.dom.accelerate(this, this.accelerated);
  126. }
  127. },
  128. axisChanged: function() {
  129. var h = this.axis == "h";
  130. this.dragMoveProp = h ? "dx" : "dy";
  131. this.shouldDragProp = h ? "horizontal" : "vertical";
  132. this.transform = h ? "translateX" : "translateY";
  133. this.dimension = h ? "width" : "height";
  134. this.boundary = h ? "left" : "top";
  135. },
  136. setInlineStyles: function(inValue, inDimensions) {
  137. var inBounds = {};
  138. if (this.unitModifier) {
  139. inBounds[this.boundary] = this.percentToPixels(inValue, this.unitModifier);
  140. inBounds[this.dimension] = this.unitModifier;
  141. this.setBounds(inBounds);
  142. } else {
  143. if (inDimensions) {
  144. inBounds[this.dimension] = inDimensions;
  145. } else {
  146. inBounds[this.boundary] = inValue;
  147. }
  148. this.setBounds(inBounds, this.unit);
  149. }
  150. },
  151. valueChanged: function(inLast) {
  152. var v = this.value;
  153. if (this.isOob(v) && !this.isAnimating()) {
  154. this.value = this.overMoving ? this.dampValue(v) : this.clampValue(v);
  155. }
  156. // FIXME: android cannot handle nested compositing well so apply acceleration only if needed
  157. // desktop chrome doesn't like this code path so avoid...
  158. if (enyo.platform.android > 2) {
  159. if (this.value) {
  160. if (inLast === 0 || inLast === undefined) {
  161. enyo.dom.accelerate(this, this.accelerated);
  162. }
  163. } else {
  164. enyo.dom.accelerate(this, false);
  165. }
  166. }
  167. // If platform supports transforms
  168. if (this.canTransform) {
  169. enyo.dom.transformValue(this, this.transform, this.value + this.unit);
  170. // else update inline styles
  171. } else {
  172. this.setInlineStyles(this.value, false);
  173. }
  174. this.doChange();
  175. },
  176. getAnimator: function() {
  177. return this.$.animator;
  178. },
  179. isAtMin: function() {
  180. return this.value <= this.calcMin();
  181. },
  182. isAtMax: function() {
  183. return this.value >= this.calcMax();
  184. },
  185. calcMin: function() {
  186. return this.min;
  187. },
  188. calcMax: function() {
  189. return this.max;
  190. },
  191. clampValue: function(inValue) {
  192. var min = this.calcMin();
  193. var max = this.calcMax();
  194. return Math.max(min, Math.min(inValue, max));
  195. },
  196. dampValue: function(inValue) {
  197. return this.dampBound(this.dampBound(inValue, this.min, 1), this.max, -1);
  198. },
  199. dampBound: function(inValue, inBoundary, inSign) {
  200. var v = inValue;
  201. if (v * inSign < inBoundary * inSign) {
  202. v = inBoundary + (v - inBoundary) / 4;
  203. }
  204. return v;
  205. },
  206. percentToPixels: function(value, dimension) {
  207. return Math.floor((dimension / 100) * value);
  208. },
  209. pixelsToPercent: function(value) {
  210. var boundary = this.unitModifier ? this.getBounds()[this.dimension] : this.container.getBounds()[this.dimension];
  211. return (value / boundary) * 100;
  212. },
  213. // dragging
  214. shouldDrag: function(inEvent) {
  215. return this.draggable && inEvent[this.shouldDragProp];
  216. },
  217. isOob: function(inValue) {
  218. return inValue > this.calcMax() || inValue < this.calcMin();
  219. },
  220. dragstart: function(inSender, inEvent) {
  221. if (this.shouldDrag(inEvent)) {
  222. inEvent.preventDefault();
  223. this.$.animator.stop();
  224. inEvent.dragInfo = {};
  225. this.dragging = true;
  226. this.drag0 = this.value;
  227. this.dragd0 = 0;
  228. return this.preventDragPropagation;
  229. }
  230. },
  231. drag: function(inSender, inEvent) {
  232. if (this.dragging) {
  233. inEvent.preventDefault();
  234. var d = this.canTransform ? inEvent[this.dragMoveProp] * this.kDragScalar : this.pixelsToPercent(inEvent[this.dragMoveProp]);
  235. var v = this.drag0 + d;
  236. var dd = d - this.dragd0;
  237. this.dragd0 = d;
  238. if (dd) {
  239. inEvent.dragInfo.minimizing = dd < 0;
  240. }
  241. this.setValue(v);
  242. return this.preventDragPropagation;
  243. }
  244. },
  245. dragfinish: function(inSender, inEvent) {
  246. if (this.dragging) {
  247. this.dragging = false;
  248. this.completeDrag(inEvent);
  249. inEvent.preventTap();
  250. return this.preventDragPropagation;
  251. }
  252. },
  253. completeDrag: function(inEvent) {
  254. if (this.value !== this.calcMax() && this.value != this.calcMin()) {
  255. this.animateToMinMax(inEvent.dragInfo.minimizing);
  256. }
  257. },
  258. // animation
  259. isAnimating: function() {
  260. return this.$.animator.isAnimating();
  261. },
  262. play: function(inStart, inEnd) {
  263. this.$.animator.play({
  264. startValue: inStart,
  265. endValue: inEnd,
  266. node: this.hasNode()
  267. });
  268. },
  269. //* @public
  270. //* Animates to the given value.
  271. animateTo: function(inValue) {
  272. this.play(this.value, inValue);
  273. },
  274. //* Animates to the minimum value.
  275. animateToMin: function() {
  276. this.animateTo(this.calcMin());
  277. },
  278. //* Animates to the maximum value.
  279. animateToMax: function() {
  280. this.animateTo(this.calcMax());
  281. },
  282. //* @protected
  283. animateToMinMax: function(inMin) {
  284. if (inMin) {
  285. this.animateToMin();
  286. } else {
  287. this.animateToMax();
  288. }
  289. },
  290. animatorStep: function(inSender) {
  291. this.setValue(inSender.value);
  292. return true;
  293. },
  294. animatorComplete: function(inSender) {
  295. this.doAnimateFinish(inSender);
  296. return true;
  297. },
  298. //* @public
  299. //* Toggles between min and max with animation.
  300. toggleMinMax: function() {
  301. this.animateToMinMax(!this.isAtMin());
  302. }
  303. });