@visactor/vchart
Version:
charts lib based @visactor/VGrammar
421 lines (409 loc) • 21.5 kB
JavaScript
import { ChartEvent, Event_Source_Type } from "./../constant/event";
import { LayoutState } from "./interface";
import { isMobileLikeMode, isTrueBrowser } from "../util/env";
import { isClass, isString } from "../util/type";
import { isArray, isObject, isValid } from "@visactor/vutils";
import { createGroup, Stage, vglobal, waitForAllSubLayers } from "@visactor/vrender-core";
import { Factory } from "../core/factory";
import { findMarkGraphic, getDatumOfGraphic } from "../util/mark";
import { diffMarks, findSimpleMarks, toRenderMode, traverseGroupMark } from "./util";
import { log } from "../util/debug";
import { AnimationStateEnum } from "../animation/interface";
import { BuiltIn_DISAPPEAR_ANIMATE_NAME } from "../constant/animate";
export class Compiler {
get stateAnimationConfig() {
return this._stateAnimationConfig;
}
getRootGroup() {
return this._rootGroup;
}
getOption() {
return this._option;
}
constructor(container, option) {
this._count = 0, this._rootMarks = [], this._viewListeners = new Map, this._windowListeners = new Map,
this._canvasListeners = new Map, this.isInited = !1, this._released = !1, this._compileChart = null,
this.handleLayoutEnd = () => {
var _a, _b;
null === (_b = null === (_a = this._compileChart) || void 0 === _a ? void 0 : _a.getEvent()) || void 0 === _b || _b.emit(ChartEvent.afterMarkLayoutEnd, {
chart: this._compileChart
});
}, this.handleStageRender = () => {
var _a, _b;
null === (_b = null === (_a = this._compileChart) || void 0 === _a ? void 0 : _a.getEvent()) || void 0 === _b || _b.emit(ChartEvent.afterRender, {
chart: this._compileChart
});
}, this._handleAfterNextRender = () => {
var _a, _b, _c, _d;
this._stage && !this._option.disableDirtyBounds && this._stage.enableDirtyBounds(),
this._compileChart && (null === (_a = this._compileChart.getEvent()) || void 0 === _a || _a.emit(ChartEvent.renderFinished, {
chart: this._compileChart,
vchart: null === (_b = this._compileChart.getOption()) || void 0 === _b ? void 0 : _b.globalInstance
})), null === (_d = null === (_c = this._option.performanceHook) || void 0 === _c ? void 0 : _c.afterVRenderDraw) || void 0 === _d || _d.call(_c, this._compileChart.getOption().globalInstance);
}, this.handleProgressiveFrame = () => {
this._progressiveMarks.length && this._progressiveMarks.forEach((mark => {
mark.isDoingProgressive() && mark.renderProgressive();
})), this.doPreProgressive();
}, this._container = container, this._option = option;
}
getChart() {
return this._compileChart;
}
getCanvas() {
var _a;
return null === (_a = this._stage) || void 0 === _a ? void 0 : _a.window.getNativeHandler().nativeCanvas;
}
getStage() {
return this._stage;
}
initView() {
var _a, _b, _c, _d;
if (this._released) return;
if (this.isInited = !0, this._stage) return;
const {autoRefreshDpr: autoRefreshDpr, dpr: dpr, mode: mode, modeParams: modeParams, gestureConfig: gestureConfig, interactive: interactive, clickInterval: clickInterval, autoPreventDefault: autoPreventDefault, background: background} = this._option;
vglobal.setEnv(toRenderMode(mode), null != modeParams ? modeParams : {}), this._stage = null !== (_a = this._option.stage) && void 0 !== _a ? _a : new Stage(Object.assign({
background: background,
width: this._width,
height: this._height,
container: null !== (_b = this._container.dom) && void 0 !== _b ? _b : null,
canvas: null !== (_c = this._container.canvas) && void 0 !== _c ? _c : null,
dpr: dpr,
viewBox: this._option.viewBox,
canvasControled: this._option.canvasControled,
beforeRender: stage => {
var _a, _b, _c;
null === (_a = this._compileChart) || void 0 === _a || _a.onBeforeRender(), null === (_c = (_b = this._option).beforeRender) || void 0 === _c || _c.call(_b, stage);
},
afterRender: this._option.afterRender,
disableDirtyBounds: !0,
autoRender: !0,
ticker: this._option.ticker,
pluginList: this._option.pluginList,
enableHtmlAttribute: this._option.enableHtmlAttribute,
optimize: this._option.optimize,
supportsTouchEvents: this._option.supportsTouchEvents,
supportsPointerEvents: this._option.supportsPointerEvents,
event: {
clickInterval: clickInterval,
autoPreventDefault: autoPreventDefault
},
ReactDOM: this._option.ReactDOM,
autoRefresh: isValid(autoRefreshDpr) ? autoRefreshDpr : !isValid(dpr)
}, null !== (_d = this._option.renderHooks) && void 0 !== _d ? _d : {})), this._stage.enableIncrementalAutoRender(),
this._stage.setTheme({
symbol: {
shape: "circle",
size: 8
},
text: {
fontSize: 14,
fill: "#000000"
}
});
const group = createGroup({
x: 0,
y: 0,
width: this._width,
height: this._height
});
group.name = "root", this._stage.defaultLayer.appendChild(group), this._rootGroup = group;
const GestureController = (isValid(gestureConfig) ? gestureConfig : isMobileLikeMode(mode)) && !1 !== interactive && Factory.getStageEventPlugin("gesture");
GestureController && (this._gestureController = new GestureController(this._stage, isObject(gestureConfig) ? gestureConfig : {})),
this._setCanvasStyle(), this.getStage().hooks.afterRender.tap("chart-event", this.handleStageRender),
!1 !== interactive && this._viewListeners.forEach((listener => {}));
}
getLayoutState() {
return this._layoutState;
}
updateLayoutTag() {
this._layoutState = LayoutState.before;
}
_setCanvasStyle() {
if (this._stage && this._container.dom && !isString(this._container.dom)) {
this._container.dom.style.display = "block", this._container.dom.style.position = "relative";
const canvas = this.getCanvas();
canvas && (canvas.style.display = "block");
}
}
compile(ctx, option) {
if (this._released) return;
const {chart: chart} = ctx;
this._compileChart = chart, this.initView(), this._stage && ("render" !== (null == option ? void 0 : option.actionSource) && this._cachedMarks && (this.reuseOrMorphing(option.morphConfig),
this._cachedMarks = null), chart.compile(), chart.afterCompile());
}
clearNextRender() {
return !!this._nextRafId && (vglobal.getSpecifiedCancelAnimationFrame(10)(this._nextRafId),
this._nextRafId = null, !0);
}
clear(ctx) {
const {chart: chart} = ctx;
this.clearNextRender(), chart.clear();
}
renderNextTick(morphConfig) {
this._released || (this._nextRafId && this.clearNextRender(), this._nextRafId = vglobal.getSpecifiedRequestAnimationFrame(10)((() => {
this._nextRafId = null, this.render(morphConfig);
})));
}
_commitedAll() {
return this._rootMarks.some((mark => traverseGroupMark(mark, (m => m.commit()))));
}
_hasCommitedMark() {
return this._rootMarks.some((mark => traverseGroupMark(mark, (m => m.isCommited()), null, null, !0)));
}
_doRender(immediately) {
var _a, _b, _c, _d, _e, _f;
null === (_b = null === (_a = this._option.performanceHook) || void 0 === _a ? void 0 : _a.beforeDoRender) || void 0 === _b || _b.call(_a, this._compileChart.getOption().globalInstance),
this._stage && (this._rootMarks.forEach((g => {
traverseGroupMark(g, (m => {
m.needClear && (this._progressiveMarks || m.runAnimation(), m.clearExitGraphics(),
m.needClear = !1);
}), null, !0);
})), null === (_d = null === (_c = this._option.performanceHook) || void 0 === _c ? void 0 : _c.beforeVRenderDraw) || void 0 === _d || _d.call(_c, this._compileChart.getOption().globalInstance),
this._stage.disableDirtyBounds(), this._stage.afterNextRender(this._handleAfterNextRender),
immediately && (this._stage.render(), null === (_f = null === (_e = this._option.performanceHook) || void 0 === _e ? void 0 : _e.afterVRenderDraw) || void 0 === _f || _f.call(_e, this._compileChart.getOption().globalInstance)));
}
renderMarks() {
var _a;
this._hasCommitedMark() && (log(`--- start of renderMarks(${this._count}) ---`),
this.clearProgressive(), this._rootMarks.forEach((mark => {
mark.render();
})), this._layoutState === LayoutState.before && (this._layoutState = LayoutState.layouting,
null === (_a = this._compileChart) || void 0 === _a || _a.onLayout(), this._layoutState = LayoutState.reevaluate,
this._hasCommitedMark() && this._rootMarks.forEach((mark => {
mark.render();
})), this.handleLayoutEnd()), this.findProgressiveMarks(), this.updateStateAnimation(),
this._doRender(!0), this.doPreProgressive(), log(`--- start of renderMarks(${this._count}) ---`),
this._count++);
}
reuseOrMorphing(morphConfig = {}) {
const {reuse: reuse = !0, morph: morph = !0, morphAll: morphAll = !1, animation: animation = {}, enableExitAnimation: enableExitAnimation = !1} = morphConfig, newMarks = findSimpleMarks(this._rootMarks), {update: update, exit: exit} = diffMarks(this._cachedMarks, newMarks, {
morph: morph,
morphAll: morphAll,
reuse: reuse
});
update.forEach((({prev: prev, next: next}) => {
if (reuse && 1 === prev.length && 1 === next.length && prev[0].type === next[0].type) next[0].reuse(prev[0]); else {
const prevMark = prev.filter((item => item.getMarkConfig().morph))[0];
prevMark && next.forEach((item => {
item.getMarkConfig().morph && item.prepareMorph(prevMark);
}));
}
})), exit.forEach((({prev: prev}) => {
prev.forEach((m => {
m.removeProduct();
}));
}));
}
render(morphConfig) {
if (this._released) return;
if (this.clearNextRender(), this.initView(), !this._stage) return;
const {width: width, height: height} = this._rootGroup.attribute;
this._width === width && this._height === height || this._rootGroup.setAttributes({
width: this._width,
height: this._height
}), this.renderMarks(), this.clearNextRender() && this.renderMarks();
}
setStageAnimationConfig(config) {
const animationConfig = {};
Object.keys(config).forEach((key => {
const value = config[key];
isArray(value) ? animationConfig[key] = "disappear" === key ? value.map((item => isClass(item.callBack) ? Object.assign(Object.assign({}, item), {
custom: item.callBack
}) : Object.assign(Object.assign({}, item), {
type: BuiltIn_DISAPPEAR_ANIMATE_NAME,
customParameters: {
callBack: item.callBack
}
}))) : value.map((item => {
var _a;
const options = null !== (_a = item.options) && void 0 !== _a ? _a : {};
return Object.assign(Object.assign({}, item), {
options: (...args) => {
const _options = "function" == typeof options ? options(...args) : options;
return Object.assign({}, _options);
}
});
})) : animationConfig[key] = Object.assign({}, config[key]);
})), this._stateAnimationConfig = animationConfig;
}
updateStateAnimation() {
const allMarks = [];
this._rootMarks.forEach((mark => {
traverseGroupMark(mark, (m => {
allMarks.push(m);
}));
}));
const markAnimationStates = allMarks.map((mark => mark.getAnimationState())), animationState = markAnimationStates.every((state => state === AnimationStateEnum.appear)) ? AnimationStateEnum.appear : markAnimationStates.every((state => state === AnimationStateEnum.disappear)) ? AnimationStateEnum.disappear : AnimationStateEnum.none;
this._stage.context || (this._stage.context = {}), this._stage.context.animationState = animationState;
}
updateViewBox(viewBox, reRender = !0) {
if (!this._stage) return;
const prevViewBox = this._stage.viewBox;
!viewBox || prevViewBox && prevViewBox.x1 === viewBox.x1 && prevViewBox.y1 === viewBox.y1 && prevViewBox.x2 === viewBox.x2 && prevViewBox.y2 === viewBox.y2 || this._stage.setViewBox(viewBox, reRender);
}
resize(width, height, reRender = !0) {
if (!this._stage) return;
const hasChange = this._width !== width || this._height !== height;
this._width = width, this._height = height, hasChange && (this._stage.resize(width, height),
this._commitedAll(), reRender && this.render({
morph: !1
}));
}
setBackground(color) {
this._stage && (this._stage.background = color);
}
setSize(width, height) {
this._width = width, this._height = height, this._stage;
}
setViewBox(viewBox, reRender = !0) {
this.updateViewBox(viewBox, reRender);
}
addEventListener(source, type, callback) {
var _a, _b;
if (!1 !== this._option.interactive) if (source === Event_Source_Type.chart) {
const rootGroup = this.getRootGroup(), wrappedCallback = function(event) {
var _a;
const graphic = event.target;
let markGraphic = null;
graphic && (markGraphic = isValid(graphic.context) ? graphic : findMarkGraphic(rootGroup, graphic));
const context = null !== (_a = null == markGraphic ? void 0 : markGraphic.context) && void 0 !== _a ? _a : {}, markId = isValid(context.markId) ? context.markId : null, modelId = isValid(context.modelId) ? context.modelId : null, modelUserId = isValid(context.modelUserId) ? context.modelUserId : null, markUserId = isValid(context.markUserId) ? context.markUserId : null, params = {
event: event,
type: type,
source: source,
item: markGraphic,
datum: getDatumOfGraphic(markGraphic),
markId: markId,
modelId: modelId,
markUserId: markUserId,
modelUserId: modelUserId
};
callback.call(null, params);
}.bind(this);
this._viewListeners.set(callback, {
type: type,
callback: wrappedCallback
}), null === (_a = this._stage) || void 0 === _a || _a.addEventListener(type, wrappedCallback);
} else if (source === Event_Source_Type.window) {
const wrappedCallback = function(event) {
const params = {
event: event,
type: type,
source: source,
item: null,
datum: null,
markId: null,
modelId: null,
markUserId: null,
modelUserId: null
};
callback.call(null, params);
}.bind(this);
this._windowListeners.set(callback, {
type: type,
callback: wrappedCallback
});
const windowObject = this._getGlobalThis();
null == windowObject || windowObject.addEventListener(type, wrappedCallback);
} else if (source === Event_Source_Type.canvas) {
const wrappedCallback = function(event) {
const params = {
event: event,
type: type,
source: source,
item: null,
datum: null,
markId: null,
modelId: null,
markUserId: null,
modelUserId: null
};
callback.call(null, params);
}.bind(this);
this._canvasListeners.set(callback, {
type: type,
callback: wrappedCallback
});
const canvasObject = null === (_b = this.getStage()) || void 0 === _b ? void 0 : _b.window;
null == canvasObject || canvasObject.addEventListener(type, wrappedCallback);
}
}
removeEventListener(source, type, callback) {
var _a, _b, _c, _d, _e;
if (!1 !== this._option.interactive) if (source === Event_Source_Type.chart) {
const wrappedCallback = null === (_a = this._viewListeners.get(callback)) || void 0 === _a ? void 0 : _a.callback;
wrappedCallback && (null === (_b = this._stage) || void 0 === _b || _b.removeEventListener(type, wrappedCallback)),
this._viewListeners.delete(callback);
} else if (source === Event_Source_Type.window) {
const windowObject = this._getGlobalThis(), wrappedCallback = null === (_c = this._windowListeners.get(callback)) || void 0 === _c ? void 0 : _c.callback;
wrappedCallback && (null == windowObject || windowObject.removeEventListener(type, wrappedCallback)),
this._windowListeners.delete(callback);
} else if (source === Event_Source_Type.canvas) {
const canvasObject = null === (_d = this.getStage()) || void 0 === _d ? void 0 : _d.window, wrappedCallback = null === (_e = this._canvasListeners.get(callback)) || void 0 === _e ? void 0 : _e.callback;
canvasObject && wrappedCallback && (null == canvasObject || canvasObject.removeEventListener(type, wrappedCallback)),
this._canvasListeners.delete(callback);
}
}
releaseEvent() {
const stage = this.getStage();
stage && stage.hooks.afterRender.unTap("chart-event", this.handleStageRender), this._viewListeners.clear(),
this._windowListeners.clear(), this._canvasListeners.clear();
}
release() {
var _a;
this.clearNextRender(), this.releaseEvent(), this._option = this._container = null,
this.releaseGrammar(!0), this._stage !== (null === (_a = this._option) || void 0 === _a ? void 0 : _a.stage) && this._stage.release(),
this._stage = null, this.isInited = !1, this._compileChart = null, this._released = !0;
}
releaseGrammar(removeGraphicItems = !1) {
removeGraphicItems ? this._rootMarks.forEach((g => {
traverseGroupMark(g, (m => {
m.removeProduct();
}), null, !0);
})) : this._cachedMarks = findSimpleMarks(this._rootMarks), this._rootMarks = [];
}
addRootMark(mark) {
this._rootMarks.includes(mark) || this._rootMarks.push(mark);
}
getRootMarks() {
return this._rootMarks;
}
removeRootMark(mark) {
const index = this._rootMarks.findIndex((m => m === mark));
return index >= 0 && (this._rootMarks.splice(index, 1), !0);
}
_getGlobalThis() {
var _a;
return isTrueBrowser(this._option.mode) ? globalThis : null === (_a = this.getStage()) || void 0 === _a ? void 0 : _a.window;
}
_combineIncrementalLayers() {
this._stage && waitForAllSubLayers(this._stage).then((() => {
this._stage && this._stage.defaultLayer.combineSubLayer();
}));
}
findProgressiveMarks() {
const marks = [];
return this._rootMarks.forEach((mark => {
traverseGroupMark(mark, (m => {
m.isProgressive() && marks.push(m);
}));
})), marks.length ? (this._progressiveMarks = marks, this._combineIncrementalLayers(),
marks) : (this._progressiveMarks = null, null);
}
doPreProgressive() {
if (this._progressiveMarks && this._progressiveMarks.some((mark => mark.isDoingProgressive()))) {
const raf = vglobal.getSpecifiedRequestAnimationFrame(10);
this._progressiveRafId = raf(this.handleProgressiveFrame);
} else this._progressiveMarks && this._progressiveMarks.every((mark => mark.canAnimateAfterProgressive())) ? this._progressiveMarks.forEach((mark => {
mark.runAnimation(), mark.clearExitGraphics();
})) : this._progressiveMarks && (this._progressiveMarks = null);
}
clearProgressive() {
if (this._progressiveRafId) {
vglobal.getSpecifiedCancelAnimationFrame(10)(this._progressiveRafId);
}
this._progressiveMarks && this._progressiveMarks.length && (this._progressiveMarks.forEach((entry => {
entry.clearProgressive();
})), this._progressiveMarks = null);
}
}
//# sourceMappingURL=compiler.js.map