@visactor/vchart
Version:
charts lib based @visactor/VGrammar
187 lines (177 loc) • 11.2 kB
JavaScript
import { isContinuous, isDiscrete } from "@visactor/vscale";
import { getDatumByValue } from "./common";
import { getAxisLabelOffset } from "../../axis/util";
import { isValid } from "@visactor/vutils";
import { getFormatFunction } from "../../util";
export const layoutByValue = (stateByField, series, layoutStartPoint, enableRemain = !1) => (layoutStartPoint || (layoutStartPoint = {
x: 0,
y: 0
}), Object.keys(stateByField).forEach((field => {
var _a, _b, _c, _d, _e;
const {currentValue: currentValue, cacheInfo: cacheInfo, labelsComp: labelsComp, attributes: attributes, coordKey: coordKey} = stateByField[field];
let axis = null, coord = 0, axisLabel = null;
if (currentValue.size) {
const item = Array.from(currentValue.values())[0];
coord = item.axis.getScale().scale(item.datum) + item.axis.getLayoutStartPoint()[coordKey] - layoutStartPoint[coordKey],
axis = item.axis, axisLabel = null === (_e = null === (_d = null === (_c = null === (_b = null === (_a = axis.getVRenderComponents()[0]) || void 0 === _a ? void 0 : _a.children[0]) || void 0 === _b ? void 0 : _b.children[0]) || void 0 === _c ? void 0 : _c.getChildByName("axis-label-container")) || void 0 === _d ? void 0 : _d.getChildByName("axis-label-container-layer-0")) || void 0 === _e ? void 0 : _e.children[0];
}
const isVisible = !!currentValue.size && Number.isFinite(coord) && !Number.isNaN(coord), useCache = enableRemain && !isVisible && isValid(cacheInfo), newCacheInfo = useCache ? cacheInfo : {
coordRange: [ 0, 0 ],
sizeRange: [ 0, 0 ],
coord: coord,
labelsTextStyle: {},
labels: labelsComp ? Object.keys(labelsComp).reduce(((res, labelKey) => (res[labelKey] = {
visible: !1,
text: "",
dx: 0,
dy: 0
}, res)), {}) : null,
visible: isVisible,
axis: axis,
axisLabel: axisLabel
};
let bandSize;
newCacheInfo && (newCacheInfo._isCache = useCache);
let offsetSize = 0;
if (attributes && currentValue.forEach((({axis: axis, datum: value = ""}) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
let niceLabelFormatter = null;
const scale = axis.getScale();
if (isDiscrete(scale.type)) bandSize = scale.bandwidth(), 0 === bandSize && scale.step && (offsetSize = scale.step()); else if (isContinuous(scale.type)) {
const field1 = "xField" === field ? series.fieldX[0] : series.fieldY[0], field2 = "xField" === field ? series.fieldX2 : series.fieldY2, datum = getDatumByValue(series.getViewData().latestData, +value, field1, field2);
if (datum) {
const startX = "xField" === field ? series.dataToPositionX(datum) : series.dataToPositionY(datum);
field2 ? (bandSize = Math.abs(startX - ("xField" === field ? series.dataToPositionX1(datum) : series.dataToPositionY1(datum))),
value = `${datum[field1]} ~ ${datum[field2]}`) : bandSize = 1, coord = startX;
}
niceLabelFormatter = axis.niceLabelFormatter;
}
if (newCacheInfo && (null === (_a = attributes.label) || void 0 === _a ? void 0 : _a.visible) && !useCache) {
const labelOffset = getAxisLabelOffset(axis.getSpec()), axisOrient = axis.getOrient(), syncAxisLabelAngle = null === (_b = attributes.label) || void 0 === _b ? void 0 : _b.syncAxisLabelAngle;
newCacheInfo.labels[axisOrient] && (newCacheInfo.labels[axisOrient].visible = !0,
newCacheInfo.labels[axisOrient].text = value, "left" === axisOrient ? (newCacheInfo.labels[axisOrient].dx = -labelOffset,
newCacheInfo.labelsTextStyle[axisOrient] = {
textAlign: syncAxisLabelAngle && axisLabel && null !== (_c = axisLabel.attribute.textAlign) && void 0 !== _c ? _c : "right",
textBaseline: syncAxisLabelAngle && axisLabel && null !== (_d = axisLabel.attribute.textBaseline) && void 0 !== _d ? _d : "middle"
}) : "right" === axisOrient ? (newCacheInfo.labels[axisOrient].dx = labelOffset,
newCacheInfo.labelsTextStyle[axisOrient] = {
textAlign: syncAxisLabelAngle && axisLabel && null !== (_e = axisLabel.attribute.textAlign) && void 0 !== _e ? _e : "left",
textBaseline: syncAxisLabelAngle && axisLabel && null !== (_f = axisLabel.attribute.textBaseline) && void 0 !== _f ? _f : "middle"
}) : "top" === axisOrient ? (newCacheInfo.labels[axisOrient].y = 0, newCacheInfo.labels[axisOrient].dy = -labelOffset,
newCacheInfo.labelsTextStyle[axisOrient] = {
textAlign: syncAxisLabelAngle && axisLabel && null !== (_g = axisLabel.attribute.textAlign) && void 0 !== _g ? _g : "center",
textBaseline: syncAxisLabelAngle && axisLabel && null !== (_h = axisLabel.attribute.textBaseline) && void 0 !== _h ? _h : "bottom"
}) : "bottom" === axisOrient && (newCacheInfo.labels[axisOrient].dy = labelOffset,
newCacheInfo.labelsTextStyle[axisOrient] = {
textAlign: syncAxisLabelAngle && axisLabel && null !== (_j = axisLabel.attribute.textAlign) && void 0 !== _j ? _j : "center",
textBaseline: syncAxisLabelAngle && axisLabel && null !== (_k = axisLabel.attribute.textBaseline) && void 0 !== _k ? _k : "top"
}), newCacheInfo.labels[axisOrient].defaultFormatter = niceLabelFormatter);
}
})), newCacheInfo && !useCache) {
const region = {
x1: 1 / 0,
y1: 1 / 0,
x2: -1 / 0,
y2: -1 / 0
};
setRegionArea(region, currentValue), "xField" === field ? (newCacheInfo.coordRange = [ region.x1, region.x2 ],
newCacheInfo.sizeRange = [ region.y1, region.y2 ], newCacheInfo.coord = coord + layoutStartPoint.x,
newCacheInfo.labels && (newCacheInfo.labels.top.y = region.y1, newCacheInfo.labels.bottom.y = region.y2)) : (newCacheInfo.coordRange = [ region.y1, region.y2 ],
newCacheInfo.sizeRange = [ region.x1, region.x2 ], newCacheInfo.coord = coord + layoutStartPoint.y,
newCacheInfo.labels && (newCacheInfo.labels.left.x = region.x1, newCacheInfo.labels.right.x = region.x2)),
(newCacheInfo.coord < newCacheInfo.coordRange[0] || newCacheInfo.coord > newCacheInfo.coordRange[1]) && (newCacheInfo.visible = !1),
attributes && attributes.label && Object.keys(newCacheInfo.labels).forEach((labelKey => {
newCacheInfo.labels[labelKey].visible && setFormattedCrosshairLabel(newCacheInfo.labels[labelKey], labelKey, attributes.label);
}));
}
stateByField[field].bandSize = null != bandSize ? bandSize : 0, stateByField[field].offsetSize = offsetSize,
stateByField[field].cacheInfo = newCacheInfo;
})), stateByField);
const setFormattedCrosshairLabel = (labelInfo, position, labelSpec) => {
const {formatMethod: formatMethod, formatter: formatter} = labelSpec, {formatFunc: formatFunc, args: args} = getFormatFunction(formatMethod, formatter, labelInfo.text, {
label: labelInfo.text,
position: position
});
formatFunc ? labelInfo.text = formatFunc(...args) : labelInfo.defaultFormatter && (labelInfo.text = labelInfo.defaultFormatter(labelInfo.text));
}, setRegionArea = (outRegion, currentValue) => {
currentValue.forEach((({axis: axis}) => {
axis.getRegions().forEach((r => {
const {x: x, y: y} = r.getLayoutStartPoint(), {width: width, height: height} = r.getLayoutRect();
outRegion.x1 = Math.min(outRegion.x1, x), outRegion.y1 = Math.min(outRegion.y1, y),
outRegion.x2 = Math.max(outRegion.x2, x + width), outRegion.y2 = Math.max(outRegion.y2, y + height);
}));
}));
};
export const layoutCrosshair = stateItem => {
const {cacheInfo: cacheInfo, attributes: attributes, bandSize: bandSize, offsetSize: offsetSize, coordKey: coordKey, anotherAxisKey: anotherAxisKey} = stateItem, {coord: coord, sizeRange: sizeRange} = cacheInfo, type = attributes.type;
let positionAttribute;
if ("line" === type) {
const pos = coord + bandSize / 2;
positionAttribute = {
visible: !0,
start: {
[coordKey]: pos,
[anotherAxisKey]: sizeRange[0]
},
end: {
[coordKey]: pos,
[anotherAxisKey]: sizeRange[1]
}
};
} else if ("rect" === type) {
const [offset0, offset1] = getRectSize(attributes, bandSize, offsetSize, cacheInfo.axis), {coordRange: coordRange} = cacheInfo;
positionAttribute = {
visible: !0,
start: {
[coordKey]: Math.max(coord + offset0, coordRange[0]),
[anotherAxisKey]: sizeRange[0]
},
end: {
[coordKey]: Math.min(coord + offset1, coordRange[1]),
[anotherAxisKey]: sizeRange[1]
}
};
}
return positionAttribute;
};
const getRectSize = (hair, bandSize, offsetSize, axis) => {
var _a, _b, _c;
const visualSize = 0 === bandSize ? offsetSize : bandSize;
let size = visualSize;
if (null === (_a = hair.style) || void 0 === _a ? void 0 : _a.sizePercent) size = visualSize * hair.style.sizePercent; else if ("number" == typeof (null === (_b = hair.style) || void 0 === _b ? void 0 : _b.size)) size = hair.style.size; else if ("function" == typeof (null === (_c = hair.style) || void 0 === _c ? void 0 : _c.size)) {
const axisRect = axis.getLayoutRect();
size = hair.style.size(axisRect, axis);
}
return 0 === bandSize ? [ -size / 2, size / 2 ] : [ bandSize / 2 - size / 2, size / 2 + bandSize / 2 ];
};
export const getCartesianCrosshairRect = (dimensionData, layoutStartPoint) => {
var _a;
const currValueX = new Map, currValueY = new Map, {series: series, datum: datum} = dimensionData, isHorizontal = "horizontal" === series.direction, axisId = (isHorizontal ? series.getYAxisHelper() : series.getXAxisHelper()).getAxisId(), axis = series.getChart().getComponentsByKey("axes").find((axis => axis.id === axisId));
if (!axis) return;
(isHorizontal ? currValueY : currValueX).set(axis.getSpecIndex(), {
datum: null === (_a = series.getDatumPositionValues(datum[0], series.getDimensionField())) || void 0 === _a ? void 0 : _a[0],
axis: axis
});
const state = {
xField: {
coordKey: "x",
anotherAxisKey: "y",
currentValue: currValueX,
attributes: {
visible: !!currValueX.size,
type: "rect"
}
},
yField: {
coordKey: "y",
anotherAxisKey: "x",
currentValue: currValueY,
attributes: {
visible: !!currValueY.size,
type: "rect"
}
}
};
return layoutByValue(state, series, layoutStartPoint), state.xField.cacheInfo ? layoutCrosshair(state.xField) : state.yField.cacheInfo ? layoutCrosshair(state.yField) : void 0;
};
//# sourceMappingURL=cartesian.js.map