layout.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. // Layout helpers for each component positioning
  41. import * as zrUtil from 'zrender/lib/core/util.js';
  42. import BoundingRect from 'zrender/lib/core/BoundingRect.js';
  43. import { parsePercent } from './number.js';
  44. import * as formatUtil from './format.js';
  45. var each = zrUtil.each;
  46. /**
  47. * @public
  48. */
  49. export var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];
  50. /**
  51. * @public
  52. */
  53. export var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];
  54. function boxLayout(orient, group, gap, maxWidth, maxHeight) {
  55. var x = 0;
  56. var y = 0;
  57. if (maxWidth == null) {
  58. maxWidth = Infinity;
  59. }
  60. if (maxHeight == null) {
  61. maxHeight = Infinity;
  62. }
  63. var currentLineMaxSize = 0;
  64. group.eachChild(function (child, idx) {
  65. var rect = child.getBoundingRect();
  66. var nextChild = group.childAt(idx + 1);
  67. var nextChildRect = nextChild && nextChild.getBoundingRect();
  68. var nextX;
  69. var nextY;
  70. if (orient === 'horizontal') {
  71. var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);
  72. nextX = x + moveX; // Wrap when width exceeds maxWidth or meet a `newline` group
  73. // FIXME compare before adding gap?
  74. if (nextX > maxWidth || child.newline) {
  75. x = 0;
  76. nextX = moveX;
  77. y += currentLineMaxSize + gap;
  78. currentLineMaxSize = rect.height;
  79. } else {
  80. // FIXME: consider rect.y is not `0`?
  81. currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
  82. }
  83. } else {
  84. var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);
  85. nextY = y + moveY; // Wrap when width exceeds maxHeight or meet a `newline` group
  86. if (nextY > maxHeight || child.newline) {
  87. x += currentLineMaxSize + gap;
  88. y = 0;
  89. nextY = moveY;
  90. currentLineMaxSize = rect.width;
  91. } else {
  92. currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
  93. }
  94. }
  95. if (child.newline) {
  96. return;
  97. }
  98. child.x = x;
  99. child.y = y;
  100. child.markRedraw();
  101. orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;
  102. });
  103. }
  104. /**
  105. * VBox or HBox layouting
  106. * @param {string} orient
  107. * @param {module:zrender/graphic/Group} group
  108. * @param {number} gap
  109. * @param {number} [width=Infinity]
  110. * @param {number} [height=Infinity]
  111. */
  112. export var box = boxLayout;
  113. /**
  114. * VBox layouting
  115. * @param {module:zrender/graphic/Group} group
  116. * @param {number} gap
  117. * @param {number} [width=Infinity]
  118. * @param {number} [height=Infinity]
  119. */
  120. export var vbox = zrUtil.curry(boxLayout, 'vertical');
  121. /**
  122. * HBox layouting
  123. * @param {module:zrender/graphic/Group} group
  124. * @param {number} gap
  125. * @param {number} [width=Infinity]
  126. * @param {number} [height=Infinity]
  127. */
  128. export var hbox = zrUtil.curry(boxLayout, 'horizontal');
  129. /**
  130. * If x or x2 is not specified or 'center' 'left' 'right',
  131. * the width would be as long as possible.
  132. * If y or y2 is not specified or 'middle' 'top' 'bottom',
  133. * the height would be as long as possible.
  134. */
  135. export function getAvailableSize(positionInfo, containerRect, margin) {
  136. var containerWidth = containerRect.width;
  137. var containerHeight = containerRect.height;
  138. var x = parsePercent(positionInfo.left, containerWidth);
  139. var y = parsePercent(positionInfo.top, containerHeight);
  140. var x2 = parsePercent(positionInfo.right, containerWidth);
  141. var y2 = parsePercent(positionInfo.bottom, containerHeight);
  142. (isNaN(x) || isNaN(parseFloat(positionInfo.left))) && (x = 0);
  143. (isNaN(x2) || isNaN(parseFloat(positionInfo.right))) && (x2 = containerWidth);
  144. (isNaN(y) || isNaN(parseFloat(positionInfo.top))) && (y = 0);
  145. (isNaN(y2) || isNaN(parseFloat(positionInfo.bottom))) && (y2 = containerHeight);
  146. margin = formatUtil.normalizeCssArray(margin || 0);
  147. return {
  148. width: Math.max(x2 - x - margin[1] - margin[3], 0),
  149. height: Math.max(y2 - y - margin[0] - margin[2], 0)
  150. };
  151. }
  152. /**
  153. * Parse position info.
  154. */
  155. export function getLayoutRect(positionInfo, containerRect, margin) {
  156. margin = formatUtil.normalizeCssArray(margin || 0);
  157. var containerWidth = containerRect.width;
  158. var containerHeight = containerRect.height;
  159. var left = parsePercent(positionInfo.left, containerWidth);
  160. var top = parsePercent(positionInfo.top, containerHeight);
  161. var right = parsePercent(positionInfo.right, containerWidth);
  162. var bottom = parsePercent(positionInfo.bottom, containerHeight);
  163. var width = parsePercent(positionInfo.width, containerWidth);
  164. var height = parsePercent(positionInfo.height, containerHeight);
  165. var verticalMargin = margin[2] + margin[0];
  166. var horizontalMargin = margin[1] + margin[3];
  167. var aspect = positionInfo.aspect; // If width is not specified, calculate width from left and right
  168. if (isNaN(width)) {
  169. width = containerWidth - right - horizontalMargin - left;
  170. }
  171. if (isNaN(height)) {
  172. height = containerHeight - bottom - verticalMargin - top;
  173. }
  174. if (aspect != null) {
  175. // If width and height are not given
  176. // 1. Graph should not exceeds the container
  177. // 2. Aspect must be keeped
  178. // 3. Graph should take the space as more as possible
  179. // FIXME
  180. // Margin is not considered, because there is no case that both
  181. // using margin and aspect so far.
  182. if (isNaN(width) && isNaN(height)) {
  183. if (aspect > containerWidth / containerHeight) {
  184. width = containerWidth * 0.8;
  185. } else {
  186. height = containerHeight * 0.8;
  187. }
  188. } // Calculate width or height with given aspect
  189. if (isNaN(width)) {
  190. width = aspect * height;
  191. }
  192. if (isNaN(height)) {
  193. height = width / aspect;
  194. }
  195. } // If left is not specified, calculate left from right and width
  196. if (isNaN(left)) {
  197. left = containerWidth - right - width - horizontalMargin;
  198. }
  199. if (isNaN(top)) {
  200. top = containerHeight - bottom - height - verticalMargin;
  201. } // Align left and top
  202. switch (positionInfo.left || positionInfo.right) {
  203. case 'center':
  204. left = containerWidth / 2 - width / 2 - margin[3];
  205. break;
  206. case 'right':
  207. left = containerWidth - width - horizontalMargin;
  208. break;
  209. }
  210. switch (positionInfo.top || positionInfo.bottom) {
  211. case 'middle':
  212. case 'center':
  213. top = containerHeight / 2 - height / 2 - margin[0];
  214. break;
  215. case 'bottom':
  216. top = containerHeight - height - verticalMargin;
  217. break;
  218. } // If something is wrong and left, top, width, height are calculated as NaN
  219. left = left || 0;
  220. top = top || 0;
  221. if (isNaN(width)) {
  222. // Width may be NaN if only one value is given except width
  223. width = containerWidth - horizontalMargin - left - (right || 0);
  224. }
  225. if (isNaN(height)) {
  226. // Height may be NaN if only one value is given except height
  227. height = containerHeight - verticalMargin - top - (bottom || 0);
  228. }
  229. var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);
  230. rect.margin = margin;
  231. return rect;
  232. }
  233. /**
  234. * Position a zr element in viewport
  235. * Group position is specified by either
  236. * {left, top}, {right, bottom}
  237. * If all properties exists, right and bottom will be igonred.
  238. *
  239. * Logic:
  240. * 1. Scale (against origin point in parent coord)
  241. * 2. Rotate (against origin point in parent coord)
  242. * 3. Translate (with el.position by this method)
  243. * So this method only fixes the last step 'Translate', which does not affect
  244. * scaling and rotating.
  245. *
  246. * If be called repeatedly with the same input el, the same result will be gotten.
  247. *
  248. * Return true if the layout happened.
  249. *
  250. * @param el Should have `getBoundingRect` method.
  251. * @param positionInfo
  252. * @param positionInfo.left
  253. * @param positionInfo.top
  254. * @param positionInfo.right
  255. * @param positionInfo.bottom
  256. * @param positionInfo.width Only for opt.boundingModel: 'raw'
  257. * @param positionInfo.height Only for opt.boundingModel: 'raw'
  258. * @param containerRect
  259. * @param margin
  260. * @param opt
  261. * @param opt.hv Only horizontal or only vertical. Default to be [1, 1]
  262. * @param opt.boundingMode
  263. * Specify how to calculate boundingRect when locating.
  264. * 'all': Position the boundingRect that is transformed and uioned
  265. * both itself and its descendants.
  266. * This mode simplies confine the elements in the bounding
  267. * of their container (e.g., using 'right: 0').
  268. * 'raw': Position the boundingRect that is not transformed and only itself.
  269. * This mode is useful when you want a element can overflow its
  270. * container. (Consider a rotated circle needs to be located in a corner.)
  271. * In this mode positionInfo.width/height can only be number.
  272. */
  273. export function positionElement(el, positionInfo, containerRect, margin, opt, out) {
  274. var h = !opt || !opt.hv || opt.hv[0];
  275. var v = !opt || !opt.hv || opt.hv[1];
  276. var boundingMode = opt && opt.boundingMode || 'all';
  277. out = out || el;
  278. out.x = el.x;
  279. out.y = el.y;
  280. if (!h && !v) {
  281. return false;
  282. }
  283. var rect;
  284. if (boundingMode === 'raw') {
  285. rect = el.type === 'group' ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) : el.getBoundingRect();
  286. } else {
  287. rect = el.getBoundingRect();
  288. if (el.needLocalTransform()) {
  289. var transform = el.getLocalTransform(); // Notice: raw rect may be inner object of el,
  290. // which should not be modified.
  291. rect = rect.clone();
  292. rect.applyTransform(transform);
  293. }
  294. } // The real width and height can not be specified but calculated by the given el.
  295. var layoutRect = getLayoutRect(zrUtil.defaults({
  296. width: rect.width,
  297. height: rect.height
  298. }, positionInfo), containerRect, margin); // Because 'tranlate' is the last step in transform
  299. // (see zrender/core/Transformable#getLocalTransform),
  300. // we can just only modify el.position to get final result.
  301. var dx = h ? layoutRect.x - rect.x : 0;
  302. var dy = v ? layoutRect.y - rect.y : 0;
  303. if (boundingMode === 'raw') {
  304. out.x = dx;
  305. out.y = dy;
  306. } else {
  307. out.x += dx;
  308. out.y += dy;
  309. }
  310. if (out === el) {
  311. el.markRedraw();
  312. }
  313. return true;
  314. }
  315. /**
  316. * @param option Contains some of the properties in HV_NAMES.
  317. * @param hvIdx 0: horizontal; 1: vertical.
  318. */
  319. export function sizeCalculable(option, hvIdx) {
  320. return option[HV_NAMES[hvIdx][0]] != null || option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null;
  321. }
  322. export function fetchLayoutMode(ins) {
  323. var layoutMode = ins.layoutMode || ins.constructor.layoutMode;
  324. return zrUtil.isObject(layoutMode) ? layoutMode : layoutMode ? {
  325. type: layoutMode
  326. } : null;
  327. }
  328. /**
  329. * Consider Case:
  330. * When default option has {left: 0, width: 100}, and we set {right: 0}
  331. * through setOption or media query, using normal zrUtil.merge will cause
  332. * {right: 0} does not take effect.
  333. *
  334. * @example
  335. * ComponentModel.extend({
  336. * init: function () {
  337. * ...
  338. * let inputPositionParams = layout.getLayoutParams(option);
  339. * this.mergeOption(inputPositionParams);
  340. * },
  341. * mergeOption: function (newOption) {
  342. * newOption && zrUtil.merge(thisOption, newOption, true);
  343. * layout.mergeLayoutParam(thisOption, newOption);
  344. * }
  345. * });
  346. *
  347. * @param targetOption
  348. * @param newOption
  349. * @param opt
  350. */
  351. export function mergeLayoutParam(targetOption, newOption, opt) {
  352. var ignoreSize = opt && opt.ignoreSize;
  353. !zrUtil.isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
  354. var hResult = merge(HV_NAMES[0], 0);
  355. var vResult = merge(HV_NAMES[1], 1);
  356. copy(HV_NAMES[0], targetOption, hResult);
  357. copy(HV_NAMES[1], targetOption, vResult);
  358. function merge(names, hvIdx) {
  359. var newParams = {};
  360. var newValueCount = 0;
  361. var merged = {};
  362. var mergedValueCount = 0;
  363. var enoughParamNumber = 2;
  364. each(names, function (name) {
  365. merged[name] = targetOption[name];
  366. });
  367. each(names, function (name) {
  368. // Consider case: newOption.width is null, which is
  369. // set by user for removing width setting.
  370. hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
  371. hasValue(newParams, name) && newValueCount++;
  372. hasValue(merged, name) && mergedValueCount++;
  373. });
  374. if (ignoreSize[hvIdx]) {
  375. // Only one of left/right is premitted to exist.
  376. if (hasValue(newOption, names[1])) {
  377. merged[names[2]] = null;
  378. } else if (hasValue(newOption, names[2])) {
  379. merged[names[1]] = null;
  380. }
  381. return merged;
  382. } // Case: newOption: {width: ..., right: ...},
  383. // or targetOption: {right: ...} and newOption: {width: ...},
  384. // There is no conflict when merged only has params count
  385. // little than enoughParamNumber.
  386. if (mergedValueCount === enoughParamNumber || !newValueCount) {
  387. return merged;
  388. } // Case: newOption: {width: ..., right: ...},
  389. // Than we can make sure user only want those two, and ignore
  390. // all origin params in targetOption.
  391. else if (newValueCount >= enoughParamNumber) {
  392. return newParams;
  393. } else {
  394. // Chose another param from targetOption by priority.
  395. for (var i = 0; i < names.length; i++) {
  396. var name_1 = names[i];
  397. if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) {
  398. newParams[name_1] = targetOption[name_1];
  399. break;
  400. }
  401. }
  402. return newParams;
  403. }
  404. }
  405. function hasProp(obj, name) {
  406. return obj.hasOwnProperty(name);
  407. }
  408. function hasValue(obj, name) {
  409. return obj[name] != null && obj[name] !== 'auto';
  410. }
  411. function copy(names, target, source) {
  412. each(names, function (name) {
  413. target[name] = source[name];
  414. });
  415. }
  416. }
  417. /**
  418. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  419. */
  420. export function getLayoutParams(source) {
  421. return copyLayoutParams({}, source);
  422. }
  423. /**
  424. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  425. * @param {Object} source
  426. * @return {Object} Result contains those props.
  427. */
  428. export function copyLayoutParams(target, source) {
  429. source && target && each(LOCATION_PARAMS, function (name) {
  430. source.hasOwnProperty(name) && (target[name] = source[name]);
  431. });
  432. return target;
  433. }