@antv/g2
Version:
the Grammar of Graphics in Javascript
143 lines • 5.65 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { isNumber } from '@antv/util';
import { Marker } from '@antv/component';
import { line } from 'd3-shape';
import { createElement } from '../../utils/createElement';
import { applyStyle } from '../utils';
import { subObject } from '../../utils/helper';
import { select } from '../../utils/selection';
import { dist } from '../../utils/vector';
function getConnectorPoint(shape) {
const { min: [x0, y0], max: [x1, y1], } = shape.getLocalBounds();
let x = 0;
let y = 0;
if (x0 > 0)
x = x0;
if (x1 < 0)
x = x1;
if (y0 > 0)
y = y0;
if (y1 < 0)
y = y1;
return [x, y];
}
function inferBackgroundBounds(textShape, padding = []) {
const [top = 0, right = 0, bottom = top, left = right] = padding;
const container = textShape.parentNode;
const angle = container.getEulerAngles();
container.setEulerAngles(0);
const { min, halfExtents } = textShape.getLocalBounds();
const [x, y] = min;
const [hw, hh] = halfExtents;
container.setEulerAngles(angle);
return {
x: x - left,
y: y - top,
width: hw * 2 + left + right,
height: hh * 2 + top + bottom,
};
}
const cos = (p0, p1, p2) => {
const a = dist(p0, p1);
const b = dist(p1, p2);
const c = dist(p2, p0);
return (Math.pow(a, 2) + Math.pow(b, 2) - Math.pow(c, 2)) / (2 * a * b);
};
function inferConnectorPath(shape, points, controlPoints, coordCenter) {
const [[x0, y0], [x1, y1]] = points;
const [x, y] = getConnectorPoint(shape);
// Straight connector line.
if (x0 === x1 && y0 === y1) {
return line()([
[0, 0],
[x, y],
]);
}
const P = [[x0 - x1, y0 - y1]].concat(controlPoints.length ? controlPoints : [[0, 0]]);
const p0 = [coordCenter[0] - x1, coordCenter[1] - y1];
const [p1, p2] = P;
// If angle is smaller than 90, which will cause connector overlap with element.
if (cos(p0, p1, p2) > 0) {
const x2 = (() => {
const { min, max } = shape.getLocalBounds();
// A(x1,y2) perpendicular to B(x2,y2) => x1*x2 + y1*y2 = 0
const vx = p1[0] + ((p1[1] - p0[1]) * (p1[1] - 0)) / (p1[0] - p0[0]);
if (max[0] < p0[0])
return Math.min(max[0], vx);
return Math.max(min[0], vx);
})();
P.splice(1, 1, [x2, 0]);
}
return line()(P);
}
export const Advance = createElement((g) => {
const _a = g.attributes, { className,
// Do not pass className
class: _c, transform, rotate, labelTransform, labelTransformOrigin, x, y, x0 = x, y0 = y, text, background, connector, startMarker, endMarker, coordCenter, innerHTML } = _a, rest = __rest(_a, ["className", "class", "transform", "rotate", "labelTransform", "labelTransformOrigin", "x", "y", "x0", "y0", "text", "background", "connector", "startMarker", "endMarker", "coordCenter", "innerHTML"]);
g.style.transform = `translate(${x}, ${y})`;
// Position is invalid, do not render the UI,
// or clear previous elements.
if ([x, y, x0, y0].some((v) => !isNumber(v))) {
g.children.forEach((d) => d.remove());
return;
}
const _b = subObject(rest, 'background'), { padding } = _b, backgroundStyle = __rest(_b, ["padding"]);
const _d = subObject(rest, 'connector'), { points = [] } = _d, connectorStyle = __rest(_d, ["points"]);
const endPoints = [
[+x0, +y0],
[+x, +y],
];
let textShape;
// Use html to customize advance text.
if (innerHTML) {
textShape = select(g)
.maybeAppend('html', 'html', className)
.style('zIndex', 0)
.style('innerHTML', innerHTML)
.call(applyStyle, Object.assign({ transform: labelTransform, transformOrigin: labelTransformOrigin }, rest))
.node();
}
else {
textShape = select(g)
.maybeAppend('text', 'text')
.style('zIndex', 0)
.style('text', text)
.call(applyStyle, Object.assign({ textBaseline: 'middle', transform: labelTransform, transformOrigin: labelTransformOrigin }, rest))
.node();
}
const rect = select(g)
.maybeAppend('background', 'rect')
.style('zIndex', -1)
.call(applyStyle, inferBackgroundBounds(textShape, padding))
.call(applyStyle, background ? backgroundStyle : {})
.node();
const connectorPath = inferConnectorPath(rect, endPoints, points, coordCenter);
const markerStart = startMarker &&
new Marker({
id: 'startMarker',
style: Object.assign({ x: 0, y: 0 }, subObject(rest, 'startMarker')),
});
const markerEnd = endMarker &&
new Marker({
id: 'endMarker',
style: Object.assign({ x: 0, y: 0 }, subObject(rest, 'endMarker')),
});
select(g)
.maybeAppend('connector', 'path')
.style('zIndex', 0)
.style('d', connectorPath)
.style('markerStart', markerStart)
.style('markerEnd', markerEnd)
.call(applyStyle, connector ? connectorStyle : {});
});
//# sourceMappingURL=advance.js.map