move.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. $axure.internal(function($ax) {
  2. var _move = {};
  3. $ax.move = _move;
  4. var widgetMoveInfo = {};
  5. var _getMoveInfo = $ax.move.RegisterMoveInfo = function (id, x, y, to, options, jobj) {
  6. var fixedInfo = jobj ? {} : $ax.dynamicPanelManager.getFixedInfo(id);
  7. var widget = $jobj(id);
  8. var query = $ax('#' + id);
  9. var isLayer = $ax.getTypeFromElementId(id) == $ax.constants.LAYER_TYPE;
  10. var rootLayer = _move.getRootLayer(id);
  11. if(rootLayer) {
  12. $ax.visibility.pushContainer(rootLayer, false);
  13. if(isLayer) widget = $ax.visibility.applyWidgetContainer(id, true);
  14. }
  15. if (!jobj) jobj = widget;
  16. var horzProp = 'left';
  17. var vertProp = 'top';
  18. var horzX = to ? x - query.locRelativeIgnoreLayer(false) : x;
  19. var vertY = to ? y - query.locRelativeIgnoreLayer(true) : y;
  20. if (fixedInfo.horizontal == 'right') {
  21. horzProp = 'right';
  22. horzX = to ? $(window).width() - x - Number(jobj.css('right').replace('px', '')) - query.width() : -x;
  23. } else if(fixedInfo.horizontal == 'center') {
  24. horzProp = 'margin-left';
  25. if (to) horzX = x - $(window).width() / 2;
  26. }
  27. if (fixedInfo.vertical == 'bottom') {
  28. vertProp = 'bottom';
  29. vertY = to ? $(window).height() - y - Number(jobj.css('bottom').replace('px', '')) - query.height() : -y;
  30. } else if (fixedInfo.vertical == 'middle') {
  31. vertProp = 'margin-top';
  32. if (to) vertY = y - $(window).height() / 2;
  33. }
  34. //todo currently this always save the info, which is not needed for compound vector children and maybe some other cases
  35. //let's optimize it later, only register if registerid is valid..
  36. widgetMoveInfo[id] = {
  37. x: horzX,
  38. y: vertY,
  39. options: options
  40. };
  41. return {
  42. horzX: horzX,
  43. vertY: vertY,
  44. horzProp: horzProp,
  45. vertProp: vertProp,
  46. rootLayer: rootLayer,
  47. jobj: jobj
  48. };
  49. };
  50. $ax.move.GetWidgetMoveInfo = function() {
  51. return $.extend({}, widgetMoveInfo);
  52. };
  53. _move.getRootLayer = function (id) {
  54. var isLayer = $ax.getTypeFromElementId(id) == $ax.constants.LAYER_TYPE;
  55. var rootLayer = isLayer ? id : '';
  56. var parentIds = $ax('#' + id).getParents(true, '*')[0];
  57. for(var i = 0; i < parentIds.length; i++) {
  58. var parentId = parentIds[i];
  59. // Keep climbing up layers until you hit a non-layer. At that point you have your root layer
  60. if($ax.public.fn.IsLayer($ax.getTypeFromElementId(parentId))) rootLayer = parentId;
  61. else break;
  62. }
  63. return rootLayer;
  64. };
  65. $ax.move.MoveWidget = function (id, x, y, options, to, animationCompleteCallback, shouldFire, jobj, moveInfo) {
  66. $ax.drag.LogMovedWidgetForDrag(id, options.dragInfo);
  67. if(!moveInfo) moveInfo = _getMoveInfo(id, x, y, to, options, jobj);
  68. jobj = moveInfo.jobj;
  69. _moveElement(id, options, animationCompleteCallback, shouldFire, jobj, moveInfo);
  70. $ax.event.raiseSyntheticEvent(id, "onMove");
  71. var object = $obj(id);
  72. if(object && $ax.public.fn.IsLayer(object.type)) {
  73. var childrenIds = $ax.public.fn.getLayerChildrenDeep(id, true);
  74. for(var i = 0; i < childrenIds.length; i++) $ax.event.raiseSyntheticEvent(childrenIds[i], 'onMove');
  75. }
  76. };
  77. var _moveElement = function (id, options, animationCompleteCallback, shouldFire, jobj, moveInfo){
  78. var cssStyles = {};
  79. if(!$ax.dynamicPanelManager.isPercentWidthPanel($obj(id))) cssStyles[moveInfo.horzProp] = '+=' + moveInfo.horzX;
  80. cssStyles[moveInfo.vertProp] = '+=' + moveInfo.vertY;
  81. // I don't think root layer is necessary anymore after changes to layer container structure.
  82. // Wait to try removing it until more stable.
  83. var rootLayer = moveInfo.rootLayer;
  84. var query = $addAll(jobj, id);
  85. if(options.easing == 'none') {
  86. query.animate(cssStyles, { duration: 0, queue: false });
  87. if(animationCompleteCallback) animationCompleteCallback();
  88. if(rootLayer) $ax.visibility.popContainer(rootLayer, false);
  89. //if this widget is inside a layer, we should just remove the layer from the queue
  90. if(shouldFire) $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.move);
  91. } else {
  92. var completeCount = query.length;
  93. query.animate(cssStyles, {
  94. duration: options.duration, easing: options.easing, queue: false, complete: function () {
  95. if (animationCompleteCallback) animationCompleteCallback();
  96. completeCount--;
  97. if(completeCount == 0 && rootLayer) $ax.visibility.popContainer(rootLayer, false);
  98. if(shouldFire) $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.move);
  99. }});
  100. }
  101. // //moveinfo is used for moving 'with this'
  102. // var moveInfo = new Object();
  103. // moveInfo.x = horzX;
  104. // moveInfo.y = vertY;
  105. // moveInfo.options = options;
  106. // widgetMoveInfo[id] = moveInfo;
  107. };
  108. _move.nopMove = function(id, options) {
  109. var moveInfo = new Object();
  110. moveInfo.x = 0;
  111. moveInfo.y = 0;
  112. moveInfo.options = {};
  113. moveInfo.options.easing = 'none';
  114. moveInfo.options.duration = 0;
  115. widgetMoveInfo[id] = moveInfo;
  116. // Layer move using container now.
  117. var obj = $obj(id);
  118. if($ax.public.fn.IsLayer(obj.type)) if(options.onComplete) options.onComplete();
  119. $ax.event.raiseSyntheticEvent(id, "onMove");
  120. };
  121. //rotationDegree: total degree to rotate
  122. //centerPoint: the center of the circular path
  123. var _noRotateOnlyMove = function (id, moveDelta, rotatableMove, fireAnimationQueue, easing, duration, completionCallback) {
  124. moveDelta.x += rotatableMove.x;
  125. moveDelta.y += rotatableMove.y;
  126. if (moveDelta.x == 0 && moveDelta.y == 0) {
  127. if(fireAnimationQueue) {
  128. $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.rotate);
  129. $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.move);
  130. }
  131. } else {
  132. $jobj(id).animate({ top: '+=' + moveDelta.y, left: '+=' + moveDelta.x }, {
  133. duration: duration,
  134. easing: easing,
  135. queue: false,
  136. complete: function () {
  137. if(fireAnimationQueue) {
  138. $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.move);
  139. $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.rotate);
  140. }
  141. if (completionCallback) completionCallback();
  142. }
  143. });
  144. }
  145. }
  146. _move.circularMove = function (id, degreeDelta, centerPoint, moveDelta, rotatableMove, resizeOffset, options, fireAnimationQueue, completionCallback) {
  147. var elem = $jobj(id);
  148. var moveInfo = $ax.move.RegisterMoveInfo(id, moveDelta.x, moveDelta.y, false, options);
  149. // If not rotating, still need to check moveDelta and may need to handle that.
  150. if (degreeDelta === 0) {
  151. _noRotateOnlyMove(id, moveDelta, rotatableMove, fireAnimationQueue, options.easing, options.duration, completionCallback);
  152. return;
  153. }
  154. var stepFunc = function(newDegree) {
  155. var deg = newDegree - rotation.degree;
  156. var widgetCenter = $ax.public.fn.getWidgetBoundingRect(id).centerPoint;
  157. //console.log("widget center of " + id + " x " + widgetCenter.x + " y " + widgetCenter.y);
  158. var widgetNewCenter = $axure.fn.getPointAfterRotate(deg, widgetCenter, centerPoint);
  159. // Start by getting the move not related to rotation, and make sure to update center point to move with it.
  160. var ratio = deg / degreeDelta;
  161. var xdelta = (moveDelta.x + rotatableMove.x) * ratio;
  162. var ydelta = (moveDelta.y + rotatableMove.y) * ratio;
  163. if(resizeOffset) {
  164. var resizeShift = {};
  165. resizeShift.x = resizeOffset.x * ratio;
  166. resizeShift.y = resizeOffset.y * ratio;
  167. $axure.fn.getPointAfterRotate(rotation.degree, resizeShift, { x: 0, y: 0 });
  168. xdelta += resizeShift.x;
  169. ydelta += resizeShift.y;
  170. }
  171. centerPoint.x += xdelta;
  172. centerPoint.y += ydelta;
  173. // Now for the move that is rotatable, it must be rotated
  174. rotatableMove = $axure.fn.getPointAfterRotate(deg, rotatableMove, { x: 0, y: 0 });
  175. // Now add in circular move to the mix.
  176. xdelta += widgetNewCenter.x - widgetCenter.x;
  177. ydelta += widgetNewCenter.y - widgetCenter.y;
  178. if(xdelta < 0) elem.css('left', '-=' + -xdelta);
  179. else if(xdelta > 0) elem.css('left', '+=' + xdelta);
  180. if(ydelta < 0) elem.css('top', '-=' + -ydelta);
  181. else if(ydelta > 0) elem.css('top', '+=' + ydelta);
  182. };
  183. var onComplete = function() {
  184. if(fireAnimationQueue) $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.move);
  185. if(completionCallback) completionCallback();
  186. if(moveInfo.rootLayer) $ax.visibility.popContainer(moveInfo.rootLayer, false);
  187. var isPercentWidthPanel = $ax.dynamicPanelManager.isPercentWidthPanel($obj(id));
  188. if(isPercentWidthPanel) {
  189. $ax.dynamicPanelManager.updatePanelPercentWidth(id);
  190. $ax.dynamicPanelManager.updatePanelContentPercentWidth(id);
  191. }
  192. if(elem.css('position') == 'fixed') {
  193. if(!isPercentWidthPanel) elem.css('left', '');
  194. elem.css('top', '');
  195. }
  196. };
  197. var rotation = { degree: 0 };
  198. if(!options.easing || options.easing === 'none' || options.duration <= 0) {
  199. stepFunc(degreeDelta);
  200. onComplete();
  201. } else {
  202. $(rotation).animate({ degree: degreeDelta }, {
  203. duration: options.duration,
  204. easing: options.easing,
  205. queue: false,
  206. step: stepFunc,
  207. complete: onComplete
  208. });
  209. }
  210. };
  211. //rotate a widget by degree, center is 50% 50%
  212. _move.rotate = function (id, degree, easing, duration, to, shouldFire, completionCallback) {
  213. var currentDegree = _getRotationDegree(id);
  214. if(to) degree = degree - currentDegree;
  215. if(degree === 0) {
  216. if (shouldFire) $ax.action.fireAnimationFromQueue(id, $ax.action.queueTypes.rotate);
  217. return;
  218. }
  219. var query = $jobj(id).add($jobj(id + '_ann')).add($jobj(id + '_ref'));
  220. var stepFunc = function(now) {
  221. var degreeDelta = now - rotation.degree;
  222. var newDegree = currentDegree + degreeDelta;
  223. query.css($ax.public.fn.setTransformHowever("rotate(" + newDegree + "deg)"));
  224. currentDegree = newDegree;
  225. };
  226. var onComplete = function() {
  227. if(shouldFire) {
  228. $ax.action.fireAnimationFromQueue($ax.public.fn.compoundIdFromComponent(id), $ax.action.queueTypes.rotate);
  229. }
  230. if(completionCallback) completionCallback();
  231. };
  232. var rotation = { degree: 0 };
  233. //if no animation, setting duration to 1, to prevent RangeError in rotation loops without animation
  234. if(!easing || easing === 'none' || duration <= 0) {
  235. stepFunc(degree);
  236. onComplete();
  237. } else {
  238. $(rotation).animate({ degree: degree }, {
  239. duration: duration,
  240. easing: easing,
  241. queue: false,
  242. step: stepFunc,
  243. complete: onComplete
  244. });
  245. }
  246. };
  247. _move.compoundRotateAround = function (id, degreeDelta, centerPoint, moveDelta, rotatableMove, resizeOffset, easing, duration, fireAnimationQueue, completionCallback) {
  248. if (degreeDelta === 0) {
  249. _noRotateOnlyMove($ax.public.fn.compoundIdFromComponent(id), moveDelta, rotatableMove, fireAnimationQueue, easing, duration, completionCallback, $ax.action.queueTypes.rotate);
  250. return;
  251. }
  252. var elem = $jobj(id);
  253. var rotation = { degree: 0 };
  254. if (!easing || easing === 'none' || duration <= 0) {
  255. duration = 1;
  256. easing = 'linear'; //it doesn't matter anymore here...
  257. }
  258. var originalWidth = Number(elem.css('width').replace('px', ''));
  259. var originalHeight = Number(elem.css('height').replace('px', ''));
  260. var originalLeft = Number(elem.css('left').replace('px', ''));
  261. var originalTop = Number(elem.css('top').replace('px', ''));
  262. $(rotation).animate({ degree: degreeDelta }, {
  263. duration: duration,
  264. easing: easing,
  265. queue: false,
  266. step: function (newDegree) {
  267. var transform = $ax.public.fn.transformFromElement(elem[0]);
  268. var originalCenter = { x: originalLeft + 0.5 * originalWidth, y: originalTop + 0.5 * originalHeight};
  269. var componentCenter = { x: originalCenter.x + transform[4], y: originalCenter.y + transform[5] };
  270. var deg = newDegree - rotation.degree;
  271. var ratio = deg / degreeDelta;
  272. var xdelta = (moveDelta.x + rotatableMove.x) * ratio;
  273. var ydelta = (moveDelta.y + rotatableMove.y) * ratio;
  274. if (resizeOffset) {
  275. var resizeShift = {};
  276. resizeShift.x = resizeOffset.x * ratio;
  277. resizeShift.y = resizeOffset.y * ratio;
  278. $axure.fn.getPointAfterRotate(rotation.degree, resizeShift, { x: 0, y: 0 });
  279. xdelta += resizeShift.x;
  280. ydelta += resizeShift.y;
  281. }
  282. var rotationMatrix = $ax.public.fn.rotationMatrix(deg);
  283. var compositionTransform = $ax.public.fn.matrixMultiplyMatrix(rotationMatrix,
  284. { m11: transform[0], m21: transform[1], m12: transform[2], m22: transform[3] });
  285. //console.log("widget center of " + id + " x " + widgetCenter.x + " y " + widgetCenter.y);
  286. var widgetNewCenter = $axure.fn.getPointAfterRotate(deg, componentCenter, centerPoint);
  287. var newMatrix = $ax.public.fn.matrixString(compositionTransform.m11, compositionTransform.m21, compositionTransform.m12, compositionTransform.m22,
  288. widgetNewCenter.x - originalCenter.x + xdelta, widgetNewCenter.y - originalCenter.y + ydelta);
  289. elem.css($ax.public.fn.setTransformHowever(newMatrix));
  290. },
  291. complete: function () {
  292. if (fireAnimationQueue) {
  293. $ax.action.fireAnimationFromQueue(elem.parent()[0].id, $ax.action.queueTypes.rotate);
  294. }
  295. if(completionCallback) completionCallback();
  296. }
  297. });
  298. };
  299. var _getRotationDegree = _move.getRotationDegree = function(elementId) {
  300. if($ax.public.fn.IsLayer($obj(elementId).type)) {
  301. return $jobj(elementId).data('layerDegree');
  302. }
  303. var element = document.getElementById(elementId);
  304. if(element == null) return NaN;
  305. //var transformString = element.style.transform ||
  306. // element.style.OTransform ||
  307. // element.style.msTransform ||
  308. // element.style.MozTransform ||
  309. // element.style.webkitTransform;
  310. var transformString = element.style['transform'] ||
  311. element.style['-o-transform'] ||
  312. element.style['-ms-transform'] ||
  313. element.style['-moz-transform'] ||
  314. element.style['-webkit-transform'];
  315. if(transformString) {
  316. var rotateRegex = /rotate\(([-?0-9]+)deg\)/;
  317. var degreeMatch = rotateRegex.exec(transformString);
  318. if(degreeMatch && degreeMatch[1]) return parseFloat(degreeMatch[1]);
  319. }
  320. if(window.getComputedStyle) {
  321. var st = window.getComputedStyle(element, null);
  322. } else {
  323. console.log('rotation is not supported for ie 8 and below in this version of axure rp');
  324. return 0;
  325. }
  326. var tr = st.getPropertyValue("transform") ||
  327. st.getPropertyValue("-o-transform") ||
  328. st.getPropertyValue("-ms-transform") ||
  329. st.getPropertyValue("-moz-transform") ||
  330. st.getPropertyValue("-webkit-transform");
  331. if(!tr || tr === 'none') return 0;
  332. var values = tr.split('(')[1];
  333. values = values.split(')')[0],
  334. values = values.split(',');
  335. var a = values[0];
  336. var b = values[1];
  337. var radians = Math.atan2(b, a);
  338. if(radians < 0) {
  339. radians += (2 * Math.PI);
  340. }
  341. var angle = Math.round(radians * (180 / Math.PI));
  342. return angle;
  343. };
  344. // var generateFilter = function(deg) {
  345. // var rot, cos, sin, matrix;
  346. //
  347. // rot=deg>=0 ? Math.PI*deg/180 : Math.PI*(360+deg)/180;
  348. // cos=Math.cos(rot);
  349. // sin=Math.sin(rot);
  350. // matrix='M11='+cos+',M12='+(-sin)+',M21='+sin+',M22='+cos+',SizingMethod="auto expand"';
  351. // return 'progid:DXImageTransform.Microsoft.Matrix('+matrix+')';
  352. // }
  353. });