labels.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { __assign, __read, __rest, __spreadArray } from "tslib";
  2. import { get, isFunction, memoize } from '@antv/util';
  3. import { fadeOut, onAnimateFinished, onAnimatesFinished, transition, transitionShape, } from '../../../animation';
  4. import { add, defined, ellipsisIt, getCallbackValue, hide, inRange, percentTransform, radToDeg, renderExtDo, scale, select, show, splitStyle, subStyleProps, wrapIt, } from '../../../util';
  5. import { CLASS_NAMES } from '../constant';
  6. import { processOverlap } from '../overlap';
  7. import { getFactor } from '../utils';
  8. import { getValuePos } from './line';
  9. import { filterExec, getCallbackStyle, getLabelVector, getLineTangentVector } from './utils';
  10. var angleNormalizer = function (angle) {
  11. var normalizedAngle = angle;
  12. while (normalizedAngle < 0)
  13. normalizedAngle += 360;
  14. return Math.round(normalizedAngle % 360);
  15. };
  16. var getAngle = memoize(function (v1, v2) {
  17. var _a = __read(v1, 2), x1 = _a[0], y1 = _a[1];
  18. var _b = __read(v2, 2), x2 = _b[0], y2 = _b[1];
  19. var _c = __read([x1 * x2 + y1 * y2, x1 * y2 - y1 * x2], 2), dot = _c[0], det = _c[1];
  20. return Math.atan2(det, dot);
  21. }, function (v1, v2) { return __spreadArray(__spreadArray([], __read(v1), false), __read(v2), false).join(); });
  22. /** to correct label rotation to avoid inverted character */
  23. function correctLabelRotation(_rotate) {
  24. var rotate = (_rotate + 360) % 180;
  25. if (!inRange(rotate, -90, 90))
  26. rotate += 180;
  27. return rotate;
  28. }
  29. /** get rotation from preset or layout */
  30. function getLabelRotation(datum, label, attr) {
  31. var _a;
  32. var labelAlign = attr.labelAlign;
  33. // if label rotate is set, use it
  34. var customRotate = (_a = label.style.transform) === null || _a === void 0 ? void 0 : _a.includes('rotate');
  35. if (customRotate)
  36. return label.getLocalEulerAngles();
  37. var rotate = 0;
  38. var labelVector = getLabelVector(datum.value, attr);
  39. var tangentVector = getLineTangentVector(datum.value, attr);
  40. if (labelAlign === 'horizontal')
  41. return 0;
  42. if (labelAlign === 'perpendicular')
  43. rotate = getAngle([1, 0], labelVector);
  44. else
  45. rotate = getAngle([tangentVector[0] < 0 ? -1 : 1, 0], tangentVector);
  46. return correctLabelRotation(radToDeg(rotate));
  47. }
  48. /** get the label align according to its tick and label angle */
  49. function getLabelStyle(value, rotate, attr) {
  50. var type = attr.type, labelAlign = attr.labelAlign;
  51. var labelVector = getLabelVector(value, attr);
  52. var labelAngle = angleNormalizer(rotate);
  53. var tickAngle = angleNormalizer(radToDeg(getAngle([1, 0], labelVector)));
  54. var textAlign = 'center';
  55. var textBaseline = 'middle';
  56. if (type === 'linear') {
  57. // tick 和 label 均为水平或垂直时,做快速判断
  58. if ([90, 270].includes(tickAngle) && labelAngle === 0) {
  59. textAlign = 'center';
  60. textBaseline = labelVector[1] === 1 ? 'top' : 'bottom';
  61. }
  62. else if (!(tickAngle % 180) && [90, 270].includes(labelAngle)) {
  63. textAlign = 'center';
  64. }
  65. // 根据 tick 和 label 的角度,判断 label 的对齐方式
  66. else if (tickAngle === 0) {
  67. if (inRange(labelAngle, 0, 90, false, true)) {
  68. textAlign = 'start';
  69. }
  70. else if (inRange(labelAngle, 0, 90) || inRange(labelAngle, 270, 360)) {
  71. textAlign = 'start';
  72. }
  73. }
  74. else if (tickAngle === 90) {
  75. if (inRange(labelAngle, 0, 90, false, true)) {
  76. textAlign = 'start';
  77. }
  78. else if (inRange(labelAngle, 90, 180) || inRange(labelAngle, 270, 360)) {
  79. textAlign = 'end';
  80. }
  81. }
  82. else if (tickAngle === 270) {
  83. if (inRange(labelAngle, 0, 90, false, true)) {
  84. textAlign = 'end';
  85. }
  86. else if (inRange(labelAngle, 90, 180) || inRange(labelAngle, 270, 360)) {
  87. textAlign = 'start';
  88. }
  89. }
  90. else if (tickAngle === 180) {
  91. if (labelAngle === 90) {
  92. textAlign = 'start';
  93. }
  94. else if (inRange(labelAngle, 0, 90) || inRange(labelAngle, 270, 360)) {
  95. textAlign = 'end';
  96. }
  97. }
  98. /**
  99. * todo tick 倾斜时的判断逻辑,该情况下坐标轴非垂直或水平
  100. */
  101. }
  102. else {
  103. // 弧线坐标轴 label 的对齐方式判断逻辑
  104. if (labelAlign === 'parallel') {
  105. if (inRange(tickAngle, 0, 180, true)) {
  106. textBaseline = 'top';
  107. }
  108. else {
  109. textBaseline = 'bottom';
  110. }
  111. }
  112. else if (labelAlign === 'horizontal') {
  113. if (inRange(tickAngle, 90, 270, false)) {
  114. textAlign = 'end';
  115. }
  116. else if (inRange(tickAngle, 270, 360, false) || inRange(tickAngle, 0, 90)) {
  117. textAlign = 'start';
  118. }
  119. }
  120. else if (labelAlign === 'perpendicular') {
  121. if (inRange(tickAngle, 90, 270)) {
  122. textAlign = 'end';
  123. }
  124. else {
  125. textAlign = 'start';
  126. }
  127. }
  128. }
  129. return { textAlign: textAlign, textBaseline: textBaseline };
  130. }
  131. function setRotateAndAdjustLabelAlign(rotate, group, attr) {
  132. group.setLocalEulerAngles(rotate);
  133. var value = group.__data__.value;
  134. var textStyle = getLabelStyle(value, rotate, attr);
  135. var label = group.querySelector(CLASS_NAMES.labelItem.class);
  136. if (label)
  137. applyTextStyle(label, textStyle);
  138. }
  139. function getLabelPos(datum, data, attr) {
  140. var showTick = attr.showTick, tickLength = attr.tickLength, tickDirection = attr.tickDirection, labelDirection = attr.labelDirection, labelSpacing = attr.labelSpacing;
  141. var index = data.indexOf(datum);
  142. var finalLabelSpacing = getCallbackValue(labelSpacing, [datum, index, data]);
  143. var _a = __read([getLabelVector(datum.value, attr), getFactor(labelDirection, tickDirection)], 2), labelVector = _a[0], unionFactor = _a[1];
  144. var extraLength = unionFactor === 1 ? getCallbackValue(showTick ? tickLength : 0, [datum, index, data]) : 0;
  145. var _b = __read(add(scale(labelVector, finalLabelSpacing + extraLength), getValuePos(datum.value, attr)), 2), x = _b[0], y = _b[1];
  146. return { x: x, y: y };
  147. }
  148. function formatter(datum, index, data, attr) {
  149. var labelFormatter = attr.labelFormatter;
  150. var element = isFunction(labelFormatter)
  151. ? function () { return renderExtDo(getCallbackValue(labelFormatter, [datum, index, data, getLabelVector(datum.value, attr)])); }
  152. : function () { return renderExtDo(datum.label || ''); };
  153. return element;
  154. }
  155. function applyTextStyle(node, style) {
  156. if (node.nodeName === 'text')
  157. node.attr(style);
  158. }
  159. function overlapHandler(attr) {
  160. processOverlap(this.node().childNodes, attr, {
  161. hide: hide,
  162. show: show,
  163. rotate: function (label, angle) {
  164. setRotateAndAdjustLabelAlign(+angle, label, attr);
  165. },
  166. ellipsis: function (label, len, suffix) {
  167. if (len === void 0) { len = Infinity; }
  168. label && ellipsisIt(label, len, suffix);
  169. },
  170. wrap: function (label, width, lines) {
  171. label && wrapIt(label, width, lines);
  172. },
  173. getTextShape: function (label) { return label.querySelector('text'); },
  174. });
  175. }
  176. function renderLabel(container, datum, data, style, attr) {
  177. var index = data.indexOf(datum);
  178. var label = select(container)
  179. .append(formatter(datum, index, data, attr))
  180. .attr('className', CLASS_NAMES.labelItem.name)
  181. .node();
  182. var _a = __read(splitStyle(getCallbackStyle(style, [datum, index, data])), 2), labelStyle = _a[0], _b = _a[1], transform = _b.transform, groupStyle = __rest(_b, ["transform"]);
  183. percentTransform(container, transform);
  184. var rotate = getLabelRotation(datum, container, attr);
  185. container.setLocalEulerAngles(+rotate);
  186. applyTextStyle(label, __assign(__assign({}, getLabelStyle(datum.value, rotate, attr)), labelStyle));
  187. // todo G transform 存在问题,需要二次设置
  188. percentTransform(container, transform);
  189. container.attr(groupStyle);
  190. return label;
  191. }
  192. export function renderLabels(container, data, attr, animate) {
  193. var finalData = filterExec(data, attr.labelFilter);
  194. var style = subStyleProps(attr, 'label');
  195. return container
  196. .selectAll(CLASS_NAMES.label.class)
  197. .data(finalData, function (d, i) { return i; })
  198. .join(function (enter) {
  199. return enter
  200. .append('g')
  201. .attr('className', CLASS_NAMES.label.name)
  202. .transition(function (datum) {
  203. renderLabel(this, datum, data, style, attr);
  204. this.attr(getLabelPos(datum, data, attr));
  205. this.__bbox__ = datum.bbox;
  206. return null;
  207. })
  208. .call(function () { return overlapHandler.call(container, attr); });
  209. }, function (update) {
  210. return update
  211. .transition(function (datum) {
  212. var prevLabel = this.querySelector(CLASS_NAMES.labelItem.class);
  213. var label = renderLabel(this, datum, data, style, attr);
  214. var shapeAnimation = transitionShape(prevLabel, label, animate.update);
  215. var animation = transition(this, getLabelPos(datum, data, attr), animate.update);
  216. this.__bbox__ = datum.bbox;
  217. return __spreadArray(__spreadArray([], __read(shapeAnimation), false), [animation], false);
  218. })
  219. .call(function (selection) {
  220. var transitions = get(selection, '_transitions').flat().filter(defined);
  221. onAnimatesFinished(transitions, function () { return overlapHandler.call(container, attr); });
  222. });
  223. }, function (exit) {
  224. return exit.transition(function () {
  225. var _this = this;
  226. var animation = fadeOut(this, animate.exit);
  227. onAnimateFinished(animation, function () { return select(_this).remove(); });
  228. return animation;
  229. });
  230. })
  231. .transitions();
  232. }
  233. //# sourceMappingURL=labels.js.map