@visactor/vchart
Version:
charts lib based @visactor/VGrammar
348 lines (315 loc) • 17.2 kB
JavaScript
import { degreeToRadian, isValid } from "@visactor/vutils";
import { DataView } from "@visactor/vdataset";
import { ARC_START_ANGLE, ARC_END_ANGLE, ARC_RATIO, ARC_MIDDLE_ANGLE, ARC_RADIAN, ARC_QUADRANT, ARC_K, POLAR_START_RADIAN, POLAR_END_RADIAN } from "../../constant/polar";
import { AttributeLevel } from "../../constant/attribute";
import { DEFAULT_DATA_KEY, DEFAULT_DATA_INDEX } from "../../constant/data";
import { PREFIX } from "../../constant/base";
import { normalizeStartEndAngle } from "../../util/math";
import { isSpecValueWithScale } from "../../util/scale";
import { field } from "../../util/object";
import { PolarSeries } from "../polar/polar";
import { SeriesTypeEnum } from "../interface/type";
import { pie } from "../../data/transforms/pie";
import { registerDataSetInstanceTransform } from "../../data/register";
import { registerEmptyCircleAnimation, registerPieAnimation } from "./animation/animation";
import { animationConfig, shouldMarkDoMorph, userAnimationConfig } from "../../animation/utils";
import { AnimationStateEnum } from "../../animation/interface";
import { centerOffsetConfig } from "./animation/centerOffset";
import { registerArcMark } from "../../mark/arc";
import { pieSeriesMark } from "./constant";
import { Factory } from "../../core/factory";
import { isNil, polarToCartesian } from "@visactor/vutils";
import { PieSeriesSpecTransformer } from "./pie-transformer";
import { DEFAULT_LABEL_VISIBLE } from "../../constant/label";
import { ChartEvent } from "../../constant/event";
import { computeLayoutRadius } from "../../component/axis/polar/util/common";
import { calcLayoutNumber } from "../../util/space";
import { CompilableData } from "../../compile/data";
import { pie as pieTheme } from "../../theme/builtin/common/series/pie";
export class BasePieSeries extends PolarSeries {
constructor() {
super(...arguments), this.transformerConstructor = PieSeriesSpecTransformer, this._pieMarkName = "pie",
this._pieMarkType = "arc", this.getCenter = () => {
var _a, _b;
const layoutRect = this._region.getLayoutRect();
return {
x: calcLayoutNumber(null === (_a = this._spec) || void 0 === _a ? void 0 : _a.centerX, layoutRect.width, layoutRect, layoutRect.width / 2),
y: calcLayoutNumber(null === (_b = this._spec) || void 0 === _b ? void 0 : _b.centerY, layoutRect.height, layoutRect, layoutRect.height / 2)
};
}, this._startAngle = POLAR_START_RADIAN, this._endAngle = POLAR_END_RADIAN, this._pieMark = null,
this._labelMark = null, this._labelLineMark = null, this._emptyArcMark = null, this.dataToCentralPosition = datum => {
const angle = datum[ARC_MIDDLE_ANGLE];
if (isNil(angle)) return null;
const radius = this.computeDatumRadius(datum), innerRadius = this.computeDatumInnerRadius(datum), center = this.computeCenter(datum);
return polarToCartesian(center, (radius + innerRadius) / 2, angle);
};
}
_buildMarkAttributeContext() {
super._buildMarkAttributeContext(), this._markAttributeContext.getCenter = () => ({
x: () => this.getCenter().x,
y: () => this.getCenter().y
}), this._markAttributeContext.startAngleScale = datum => this.startAngleScale(datum),
this._markAttributeContext.endAngleScale = datum => this.endAngleScale(datum);
}
setAttrFromSpec() {
var _a, _b, _c, _d, _e, _f;
super.setAttrFromSpec(), this._centerOffset = null !== (_a = this._spec.centerOffset) && void 0 !== _a ? _a : 0,
this._cornerRadius = null !== (_b = this._spec.cornerRadius) && void 0 !== _b ? _b : 0;
const normalized = normalizeStartEndAngle(isValid(this._spec.startAngle) ? degreeToRadian(this._spec.startAngle) : this._startAngle, isValid(this._spec.endAngle) ? degreeToRadian(this._spec.endAngle) : this._endAngle);
this._startAngle = normalized.startAngle, this._endAngle = normalized.endAngle,
this._padAngle = isValid(this._spec.padAngle) ? degreeToRadian(this._spec.padAngle) : 0,
this.setAngleField(this._spec.valueField || this._spec.angleField), this._spec.categoryField && this.setSeriesField(this._spec.categoryField),
this._radiusField = [], this._specAngleField = this._angleField.slice(), this._specRadiusField = [],
this._showEmptyCircle = null !== (_d = null === (_c = this._spec.emptyPlaceholder) || void 0 === _c ? void 0 : _c.showEmptyCircle) && void 0 !== _d && _d,
this._showAllZero = null !== (_e = this._spec.showAllZero) && void 0 !== _e && _e,
this._supportNegative = null !== (_f = this._spec.supportNegative) && void 0 !== _f && _f;
}
initData() {
super.initData();
const viewData = this.getViewData();
if (!viewData) return;
registerDataSetInstanceTransform(this._dataSet, "pie", pie), viewData.transform({
type: "pie",
options: {
angleField: () => this._angleField[0],
startAngle: () => this._startAngle,
endAngle: () => this._endAngle,
minAngle: () => isValid(this._spec.minAngle) ? degreeToRadian(this._spec.minAngle) : 0,
asStartAngle: ARC_START_ANGLE,
asEndAngle: ARC_END_ANGLE,
asRatio: ARC_RATIO,
asMiddleAngle: ARC_MIDDLE_ANGLE,
asRadian: ARC_RADIAN,
asQuadrant: ARC_QUADRANT,
asK: ARC_K,
showAllZero: this._showAllZero,
supportNegative: this._supportNegative,
showEmptyCircle: this._showEmptyCircle
}
}, !1);
const viewDataLabel = new DataView(this._dataSet, {
name: `${PREFIX}_series_${this.id}_viewDataLabel`
});
viewDataLabel.parse([ this.getViewData() ], {
type: "dataview"
}), this._viewDataLabel = new CompilableData(this._option, viewDataLabel);
}
compileData() {
var _a;
super.compileData(), null === (_a = this._viewDataLabel) || void 0 === _a || _a.compile();
}
initMark() {
this._pieMark = this._createMark(Object.assign(Object.assign({}, BasePieSeries.mark.pie), {
name: this._pieMarkName,
type: this._pieMarkType
}), {
key: DEFAULT_DATA_KEY,
groupKey: this._seriesField,
skipBeforeLayouted: !0,
isSeriesMark: !0
}, {
morph: shouldMarkDoMorph(this._spec, this._pieMarkName),
morphElementKey: this._seriesField
}), this._showEmptyCircle && (this._emptyArcMark = this._createMark({
name: "emptyCircle",
type: "arc"
}, {
dataView: !1
}), this._data.addRelatedMark(this._emptyArcMark));
}
startAngleScale(datum) {
return field(ARC_START_ANGLE)(datum);
}
endAngleScale(datum) {
return field(ARC_END_ANGLE)(datum);
}
_computeLayoutRadius() {
return computeLayoutRadius((() => this._spec.layoutRadius), this.getLayoutRect, this.getCenter, (() => ({
startAngle: this._startAngle,
endAngle: this._endAngle
})));
}
initMarkStyle() {
const initialStyle = {
x: () => this.getCenter().x,
y: () => this.getCenter().y,
fill: this.getColorAttribute(),
outerRadius: isSpecValueWithScale(this._outerRadius) ? this._outerRadius : () => this._computeLayoutRadius() * this._outerRadius,
innerRadius: isSpecValueWithScale(this._innerRadius) ? this._innerRadius : () => this._computeLayoutRadius() * this._innerRadius,
cornerRadius: () => this._computeLayoutRadius() * this._cornerRadius,
startAngle: datum => this.startAngleScale(datum),
endAngle: datum => this.endAngleScale(datum),
padAngle: this._padAngle,
centerOffset: this._centerOffset
}, pieMark = this._pieMark;
pieMark && this.setMarkStyle(pieMark, initialStyle, "normal", AttributeLevel.Series);
const emptyPieMark = this._emptyArcMark;
emptyPieMark && this.setMarkStyle(emptyPieMark, Object.assign(Object.assign({}, initialStyle), {
visible: () => {
const data = this.getViewData().latestData;
return !data || !data.length;
}
}), "normal", AttributeLevel.Series);
}
getInteractionTriggers() {
return this._parseInteractionConfig(this._pieMark ? [ this._pieMark ] : []);
}
initTooltip() {
super.initTooltip(), this._pieMark && this._tooltipHelper.activeTriggerSet.mark.add(this._pieMark);
}
initMarkStyleWithSpec(mark, spec) {
if (super.initMarkStyleWithSpec(mark, spec), mark.name === this._pieMarkName) {
const pieSpec = this.getSpec()[mark.name];
if (pieSpec) for (const state in pieSpec.state || {}) this.setMarkStyle(mark, this.generateRadiusStyle(pieSpec.state[state]), state, AttributeLevel.User_Mark);
}
"emptyCircle" === mark.name && this.setMarkStyle(mark, this.generateRadiusStyle(spec.style), "normal", AttributeLevel.User_Mark);
}
initLabelMarkStyle(textMark) {
textMark && this.setMarkStyle(textMark, {
visible: field(DEFAULT_LABEL_VISIBLE).bind(this),
text: datum => datum[this.getDimensionField()[0]],
fill: this.getColorAttribute(),
z: this.dataToPositionZ.bind(this)
});
}
afterInitMark() {
super.afterInitMark();
}
initEvent() {
var _a;
super.initEvent(), null === (_a = this._viewDataLabel.getDataView()) || void 0 === _a || _a.target.addListener("change", this.viewDataLabelUpdate.bind(this));
}
initGroups() {}
onLayoutEnd() {
this._viewDataLabel.getDataView().reRunAllTransform(), this.onMarkPositionUpdate(),
super.onLayoutEnd();
}
getDimensionField() {
return this._seriesField ? [ this._seriesField ] : [];
}
getMeasureField() {
return this._specAngleField;
}
viewDataLabelUpdate() {
this.event.emit(ChartEvent.viewDataLabelUpdate, {
model: this
}), this._viewDataLabel.updateData();
}
generateRadiusStyle(spec) {
if (!spec) return;
const style = {};
return spec.outerRadius && (style.outerRadius = () => this._computeLayoutRadius() * spec.outerRadius),
spec.innerRadius && (style.innerRadius = () => this._computeLayoutRadius() * spec.innerRadius),
spec.cornerRadius && (style.cornerRadius = () => this._computeLayoutRadius() * spec.cornerRadius),
style;
}
computeCenter(datum) {
return {
x: this._pieMark.getAttribute("x", datum, "normal"),
y: this._pieMark.getAttribute("y", datum, "normal")
};
}
getRadius(state = "normal") {
var _a, _b, _c, _d, _e, _f, _g;
const styleRadius = "normal" === state ? null === (_c = null === (_b = this.getSpec()[(null === (_a = this._pieMark) || void 0 === _a ? void 0 : _a.name) || "pie"]) || void 0 === _b ? void 0 : _b.style) || void 0 === _c ? void 0 : _c.outerRadius : null === (_g = null === (_f = null === (_e = this.getSpec()[(null === (_d = this._pieMark) || void 0 === _d ? void 0 : _d.name) || "pie"]) || void 0 === _e ? void 0 : _e.state) || void 0 === _f ? void 0 : _f[state]) || void 0 === _g ? void 0 : _g.outerRadius;
return null != styleRadius ? styleRadius : this._outerRadius;
}
getInnerRadius(state = "normal") {
var _a, _b, _c, _d, _e, _f, _g;
const styleRadius = "normal" === state ? null === (_c = null === (_b = this.getSpec()[(null === (_a = this._pieMark) || void 0 === _a ? void 0 : _a.name) || "pie"]) || void 0 === _b ? void 0 : _b.style) || void 0 === _c ? void 0 : _c.innerRadius : null === (_g = null === (_f = null === (_e = this.getSpec()[(null === (_d = this._pieMark) || void 0 === _d ? void 0 : _d.name) || "pie"]) || void 0 === _e ? void 0 : _e.state) || void 0 === _f ? void 0 : _f[state]) || void 0 === _g ? void 0 : _g.innerRadius;
return null != styleRadius ? styleRadius : this._innerRadius;
}
computeRadius(r, k) {
return this._computeLayoutRadius() * r * (isNil(k) ? 1 : k) + this._centerOffset;
}
computeDatumRadius(datum, state) {
return this._computeLayoutRadius() * this.getRadius(state) + this._centerOffset;
}
_compareSpec(spec, prevSpec, ignoreCheckKeys) {
ignoreCheckKeys = null != ignoreCheckKeys ? ignoreCheckKeys : {
data: !0
};
const defaultIgnoreKeys = [ "centerX", "centerY", "centerOffset", "radius", "innerRadius", "cornerRadius", "startAngle", "endAngle", "padAngle" ];
defaultIgnoreKeys.forEach((key => {
ignoreCheckKeys[key] = !0;
}));
const result = super._compareSpec(spec, prevSpec, ignoreCheckKeys);
return spec = null != spec ? spec : {}, defaultIgnoreKeys.some((key => spec[key] !== prevSpec[key])) && (result.reRender = !0,
result.change = !0), result;
}
computeDatumInnerRadius(datum, state) {
return this._computeLayoutRadius() * this.getInnerRadius(state) + this._centerOffset;
}
dataToPosition(datum, checkInViewData) {
const angle = datum[ARC_MIDDLE_ANGLE];
if (isNil(angle)) return null;
if (checkInViewData && !this.isDatumInViewData(datum)) return null;
const radius = this.computeDatumRadius(datum), center = this.computeCenter(datum);
return polarToCartesian(center, radius, angle);
}
initAnimation() {
var _a, _b, _c;
const animationParams = {
growFrom: (datum, graphic, state) => {
var _a, _b;
if (state === AnimationStateEnum.appear) return this._startAngle;
if (state === AnimationStateEnum.disappear) return this._endAngle;
const outState = [ AnimationStateEnum.disappear, AnimationStateEnum.exit ], markElements = this._pieMark.getGraphics(), data = datum, dataIndex = null == data ? void 0 : data[DEFAULT_DATA_INDEX];
if (void 0 === markElements.find((e => {
var _a;
return (null === (_a = e.context.data[0]) || void 0 === _a ? void 0 : _a[DEFAULT_DATA_INDEX]) < dataIndex;
}))) return this._startAngle;
if (void 0 === markElements.find((e => {
var _a;
return (null === (_a = e.context.data[0]) || void 0 === _a ? void 0 : _a[DEFAULT_DATA_INDEX]) > dataIndex;
}))) return this._endAngle;
const prevMarkElement = [ ...markElements ].reverse().find((e => {
var _a;
return (null === (_a = e.context.data[0]) || void 0 === _a ? void 0 : _a[DEFAULT_DATA_INDEX]) < dataIndex;
}));
return outState.includes(state) ? null === (_a = null == prevMarkElement ? void 0 : prevMarkElement.getFinalAttribute()) || void 0 === _a ? void 0 : _a.endAngle : null === (_b = null == prevMarkElement ? void 0 : prevMarkElement.attribute) || void 0 === _b ? void 0 : _b.endAngle;
}
}, appearPreset = null === (_a = this._spec.animationAppear) || void 0 === _a ? void 0 : _a.preset;
if (this._pieMark) {
const pieAnimationConfig = animationConfig(null === (_b = Factory.getAnimationInKey("pie")) || void 0 === _b ? void 0 : _b(animationParams, appearPreset), userAnimationConfig("pie", this._spec, this._markAttributeContext));
pieAnimationConfig.normal && pieAnimationConfig.normal.type && (pieAnimationConfig.normal = centerOffsetConfig(this._pieMark, pieAnimationConfig.normal)),
this._pieMark.setAnimationConfig(pieAnimationConfig);
}
if (this._emptyArcMark) {
const pieAnimationConfig = animationConfig(null === (_c = Factory.getAnimationInKey("emptyCircle")) || void 0 === _c ? void 0 : _c(animationParams, null != appearPreset ? appearPreset : "fadeIn"));
this._emptyArcMark.setAnimationConfig(pieAnimationConfig);
}
}
getDefaultShapeType() {
return "circle";
}
getGroupFields() {
return [];
}
getStackGroupFields() {
return [];
}
getStackValueField() {
return "";
}
_noAnimationDataKey(datum, index) {
return index;
}
getActiveMarks() {
return [ this._pieMark ];
}
}
BasePieSeries.transformerConstructor = PieSeriesSpecTransformer, BasePieSeries.mark = pieSeriesMark,
BasePieSeries.builtInTheme = {
pie: pieTheme
};
export class PieSeries extends BasePieSeries {
constructor() {
super(...arguments), this.type = SeriesTypeEnum.pie;
}
}
PieSeries.type = SeriesTypeEnum.pie;
export const registerPieSeries = () => {
registerArcMark(), registerPieAnimation(), registerEmptyCircleAnimation(), Factory.registerSeries(PieSeries.type, PieSeries);
};
//# sourceMappingURL=pie.js.map