UNPKG

@visactor/vchart

Version:

charts lib based @visactor/VGrammar

382 lines (368 loc) 18.7 kB
var __rest = this && this.__rest || function(s, e) { var t = {}; for (var p in s) Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0 && (t[p] = s[p]); if (null != s && "function" == typeof Object.getOwnPropertySymbols) { var i = 0; for (p = Object.getOwnPropertySymbols(s); i < p.length; i++) e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]) && (t[p[i]] = s[p[i]]); } return t; }; import { Tag } from "@visactor/vrender-components"; import { throttle, PointService, isEqual, isArray, isNumber, get, isBoolean, isObject, array } from "@visactor/vutils"; import { RenderModeEnum } from "../../typings/spec/common"; import { BaseComponent } from "../base/base-component"; import { outOfBounds } from "../../util/math"; import { ChartEvent, Event_Bubble_Level, Event_Source_Type } from "../../constant/event"; import { LayoutZIndex } from "../../constant/layout"; import { getDefaultCrosshairTriggerEventByMode } from "./config"; import { limitTagInBounds } from "./utils/common"; import { isDiscrete } from "@visactor/vscale"; const ORIENT_MAP = { x: [ "top", "bottom" ], y: [ "left", "right" ], category: [ "angle" ], value: [ "radius" ] }; export class BaseCrossHair extends BaseComponent { get enableRemain() { return "none" === this.triggerOff; } constructor(spec, options) { super(spec, options), this.specKey = "crosshair", this.layoutType = "none", this.gridZIndex = LayoutZIndex.CrossHair_Grid, this.labelZIndex = LayoutZIndex.CrossHair, this.trigger = "hover", this._handleIn = params => { if (!this._option) return; const {x: x, y: y} = this.calculateTriggerPoint(params); this.showDefault = !1, this._layoutCrosshair(x, y); const components = this._getNeedClearVRenderComponents(); this._hasActive = components.some((comp => comp && !1 !== comp.attribute.visible)); }, this._handleClickInEvent = params => { if (!this._hasActive || !this._spec.lockAfterClick || this._clickLock) return this._clickLock ? (this._clickLock = !1, void this._handleOutEvent()) : void (this._onlyLockClick || (this._handleIn(params), isNumber(this.triggerOff) && (this._timer && clearTimeout(this._timer), this._timer = setTimeout((() => { this._handleOutEvent(); }), this.triggerOff)))); this._clickLock = !0; }, this._handleHoverInEvent = throttle((params => { this._clickLock || this._handleIn(params); }), 10), this._handleOutEvent = () => { this.enableRemain || this._clickLock || !this._hasActive || (this.clearOutEvent(), this.hide()); }, this._handleTooltipShow = params => { const tooltipData = params.tooltipData; if (params.isEmptyTooltip || !tooltipData || !tooltipData.length) return void this._handleTooltipHideOrRelease(); if (isObject(this._spec.followTooltip) && !1 === this._spec.followTooltip[params.activeType]) return void this._handleTooltipHideOrRelease(); const {x: x, y: y} = this.calculateTriggerPoint(params); this.showDefault = !1, this._layoutCrosshair(x, y, tooltipData, params.activeType); const components = this._getNeedClearVRenderComponents(); this._hasActive = components.some((comp => comp && !1 !== comp.attribute.visible)); }, this._handleTooltipHideOrRelease = () => { this.clearOutEvent(), this.hide(); }, this.enable = !0, this.showDefault = !0; } _setAllAxisValues(axisMap, point, field) { let discrete = !1; if (axisMap.forEach((item => { isDiscrete(item.axis.getScale().type) && (discrete ? this.enable = !1 : discrete = !0); })), !this.enable) return !1; const {currentValue: currentValue} = this._stateByField[field]; return axisMap.forEach(((item, id) => { const axis = item.axis; currentValue.set(id, { datum: this._getDatumAtPoint(axis, point), axis: axis }); })), !0; } clearAxisValue() { Object.keys(this._stateByField).forEach((field => { this._stateByField[field].currentValue.clear(); })); } hideCrosshair() { this.clearAxisValue(), this.hide(); } showCrosshair(dimInfo) { dimInfo && dimInfo.length && (this.showDefault = !1, this.clearAxisValue(), dimInfo.forEach((d => { const {axis: axis, value: value} = d; this.setAxisValue(value, axis); })), this.layoutByValue(!1)); } _getLimitBounds() { var _a, _b; if (!this._limitBounds) { const {width: width, height: height} = null !== (_b = null === (_a = this._option.globalInstance.getChart()) || void 0 === _a ? void 0 : _a.getViewRect()) && void 0 !== _b ? _b : { width: 0, height: 0 }; this._limitBounds = { x1: 0, y1: 0, x2: width, y2: height }; } return this._limitBounds; } _showDefaultCrosshairBySpec() { Object.keys(this._stateByField).forEach((field => { const fieldSpec = this._spec[field]; if (fieldSpec && fieldSpec.visible && fieldSpec.defaultSelect) { const {axisIndex: axisIndex = 0, datum: datum} = fieldSpec.defaultSelect, axis = this._option.getComponentsByKey("axes").find((c => c.getSpecIndex() === axisIndex)); axis && (this._stateByField[field].currentValue.clear(), this._stateByField[field].currentValue.set(axisIndex, { axis: axis, datum: datum })); } })); } _updateVisibleCrosshair() { let hasVisible = !1; Object.keys(this._stateByField).forEach((field => { const fieldSpec = this._spec[field]; fieldSpec && fieldSpec.visible && this._stateByField[field].currentValue.size ? hasVisible = !0 : this._hideByField(field); })), hasVisible && this.layoutByValue(!1); } _showDefaultCrosshair() { this.showDefault ? (this._showDefaultCrosshairBySpec(), this.layoutByValue(!1)) : this._updateVisibleCrosshair(); } setAttrFromSpec() { super.setAttrFromSpec(), this._parseCrosshairSpec(); } created() { super.created(), this._initEvent(); } _compareSpec(spec, prevSpec) { const result = super._compareSpec(spec, prevSpec); return result.reMake || isEqual(prevSpec, spec) || (result.reRender = !0, result.reMake = !0), result; } _initEvent() { if (!this._option.disableTriggerEvent) if (this._spec.followTooltip) this._registerTooltipEvent(); else { const triggerConfig = this._getTriggerEvent(); triggerConfig && triggerConfig.forEach((cfg => { this._registerEvent(cfg.in, !1, cfg.click), cfg.out && this._registerEvent(cfg.out, !0); })); } } _registerEvent(eventName, isOut, click) { const handler = isOut ? this._handleOutEvent : click ? this._handleClickInEvent : this._handleHoverInEvent, cfg = isOut ? { level: Event_Bubble_Level.chart } : { source: Event_Source_Type.chart }; isArray(eventName) ? eventName.forEach((evt => { this.event.on(evt, cfg, handler); })) : this.event.on(eventName, cfg, handler); } _eventOff(eventName, isOut, click) { const handler = isOut ? this._handleOutEvent : click ? this._handleClickInEvent : this._handleHoverInEvent; isArray(eventName) ? eventName.forEach((evt => { this.event.off(evt, handler); })) : this.event.off(eventName, handler); } updateLayoutAttribute() { this._limitBounds = null, this._showDefaultCrosshair(); } calculateTriggerPoint(params) { const {event: event} = params, layer = this._option.getCompiler().getStage().getLayer(void 0), point = { x: event.viewX, y: event.viewY }; return layer.globalTransMatrix.transformPoint({ x: event.viewX, y: event.viewY }, point), { x: point.x - this.getLayoutStartPoint().x, y: point.y - this.getLayoutStartPoint().y }; } _getTriggerEvent() { const {mode: mode = RenderModeEnum["desktop-browser"]} = this._option, triggerConfig = getDefaultCrosshairTriggerEventByMode(mode); if (triggerConfig) { const trigger = array(this.trigger || "hover"), outTrigger = inTrigger => "click" === inTrigger ? "none" === this.triggerOff ? null : triggerConfig.clickOut : triggerConfig.hoverOut; this._spec.lockAfterClick && !trigger.includes("click") ? (trigger.push("click"), this._onlyLockClick = !0) : this._onlyLockClick = !1; const res = []; return trigger.forEach((item => { res.push({ click: "click" === item, in: triggerConfig[item], out: outTrigger(item) }); })), res; } return null; } _registerTooltipEvent() { this.event.on(ChartEvent.tooltipHide, { source: Event_Source_Type.chart }, this._handleTooltipHideOrRelease), this.event.on(ChartEvent.tooltipShow, { source: Event_Source_Type.chart }, this._handleTooltipShow), this.event.on(ChartEvent.tooltipRelease, { source: Event_Source_Type.chart }, this._handleTooltipHideOrRelease); } _getAxisInfoByField(field) { var _a, _b; const axesComponents = null === (_b = (_a = this._option).getComponentsByKey) || void 0 === _b ? void 0 : _b.call(_a, "axes"); if (!(null == axesComponents ? void 0 : axesComponents.length)) return null; let bindingAxesIndex = get(this._spec, `${field}Field.bindingAxesIndex`); if (bindingAxesIndex || (bindingAxesIndex = [], axesComponents.forEach(((item, index) => { ORIENT_MAP[field].includes(item.getOrient()) && bindingAxesIndex.push(index); }))), !bindingAxesIndex.length) return null; const map = new Map; let x1 = 1 / 0, y1 = 1 / 0, x2 = -1 / 0, y2 = -1 / 0; const {x: sx, y: sy} = this.getLayoutStartPoint(); return bindingAxesIndex.forEach((idx => { x1 = 1 / 0, y1 = 1 / 0, x2 = -1 / 0, y2 = -1 / 0; const axis = axesComponents.find((axis => axis.getSpecIndex() === idx)); if (!axis) return; axis.getRegions().forEach((r => { const {x: regionStartX, y: regionStartY} = r.getLayoutStartPoint(); x1 = Math.min(x1, regionStartX - sx), y1 = Math.min(y1, regionStartY - sy), x2 = Math.max(x2, regionStartX + r.getLayoutRect().width - sx), y2 = Math.max(y2, regionStartY + r.getLayoutRect().height - sy); })), map.set(idx, { x1: x1, y1: y1, x2: x2, y2: y2, axis: axis }); })), map; } onLayoutEnd() { const region = this._regions[0]; this.setLayoutRect(region.getLayoutRect()), this.setLayoutStartPosition(region.getLayoutStartPoint()), super.onLayoutEnd(); } _releaseEvent() { this.clearOutEvent(); const triggerConfig = this._getTriggerEvent(); triggerConfig && triggerConfig.forEach((cfg => { this._eventOff(cfg.in, !1, cfg.click), cfg.out && this._eventOff(cfg.out, !0); })); } _parseFieldInfo() { Object.keys(this._stateByField).forEach((field => { const fieldSpec = this._spec[field], {crosshairComp: crosshairComp} = this._stateByField[field]; if (fieldSpec && fieldSpec.visible) { if (this._stateByField[field].attributes = this._parseField(fieldSpec, field), crosshairComp) { const {style: style, type: type} = this._stateByField[field].attributes, styleKey = "rect" === type ? "rectStyle" : "lineStyle"; crosshairComp.setAttributes({ [styleKey]: style }); } } else crosshairComp && crosshairComp.parent && crosshairComp.parent.removeChild(crosshairComp); })); } _parseCrosshairSpec() { this._parseFieldInfo(); const {trigger: trigger, triggerOff: triggerOff, labelZIndex: labelZIndex, gridZIndex: gridZIndex} = this._spec; trigger && (this.trigger = trigger), ("none" === triggerOff || isNumber(triggerOff) && triggerOff > 0) && (this.triggerOff = triggerOff), void 0 !== labelZIndex && (this.labelZIndex = labelZIndex), void 0 !== gridZIndex && (this.gridZIndex = gridZIndex); } _parseField(field, fieldName) { var _a, _b, _c; const hair = {}, {line: line = {}, label: label = {}, visible: visible} = field; if (hair.visible = visible, hair.type = line.type || "line", !1 === line.visible) hair.style = { visible: !1 }; else { const style = line.style || {}, {stroke: stroke, fill: fill, lineWidth: lineWidth} = style, _d = style, {strokeOpacity: strokeOpacity, fillOpacity: fillOpacity, opacity: opacity} = _d, restStyle = __rest(_d, [ "strokeOpacity", "fillOpacity", "opacity" ]), isLineType = "line" === hair.type; let finalOpacity = isLineType ? strokeOpacity : fillOpacity; if (isNumber(opacity) && (finalOpacity = (null != finalOpacity ? finalOpacity : 1) * opacity), hair.style = Object.assign({ opacity: finalOpacity, pickable: !1, visible: !0 }, restStyle), isLineType) hair.style.stroke = stroke || fill, hair.style.lineWidth = get(line, "width", lineWidth || 2); else { hair.style.fill = fill || stroke, (null === (_b = null === (_a = null == field ? void 0 : field.line) || void 0 === _a ? void 0 : _a.style) || void 0 === _b ? void 0 : _b.stroke) && (hair.style.stroke = field.line.style.stroke); const rectSize = get(line, "width"); if ("string" == typeof rectSize) { const percent = parseInt(rectSize.substring(0, rectSize.length - 1), 10) / 100; hair.style.sizePercent = percent; } else "number" != typeof rectSize && "function" != typeof rectSize || (hair.style.size = rectSize); } } if (label.visible) { const labelBackground = label.labelBackground || {}, syncAxisLabelAngle = null !== (_c = label.syncAxisLabelAngle) && void 0 !== _c && _c, labelStyle = label.style || {}, _e = labelBackground.style || {}, {fill: rectFill = "rgba(47, 59, 82, 0.9)", stroke: rectStroke, outerBorder: outerBorder} = _e, rectStyle = __rest(_e, [ "fill", "stroke", "outerBorder" ]); hair.label = { visible: !0, formatMethod: label.formatMethod, formatter: label.formatter, minWidth: labelBackground.minWidth, maxWidth: labelBackground.maxWidth, padding: labelBackground.padding, syncAxisLabelAngle: syncAxisLabelAngle, textStyle: Object.assign(Object.assign({ fontSize: 14, pickable: !1 }, labelStyle), { fill: labelStyle.fill || "#fff", stroke: get(labelStyle, "stroke") }), panel: (isBoolean(labelBackground.visible) ? labelBackground.visible : labelBackground) ? Object.assign({ visible: !0, pickable: !1, fill: rectFill, stroke: rectStroke, outerBorder: Object.assign({ stroke: rectFill, distance: 0, lineWidth: 3 }, outerBorder) }, rectStyle) : { visible: !1 }, zIndex: this.labelZIndex, childrenPickable: !1, pickable: !1 }; } else hair.label = { visible: !1 }; return hair; } _filterAxisByPoint(axisMap, relativeX, relativeY) { return axisMap && axisMap.forEach((item => { const axis = item.axis; if (outOfBounds(item, relativeX, relativeY) && axisMap.delete(axis.getSpecIndex()), axis.type.startsWith("polarAxis")) { const center = axis.getCenter(), innerRadius = axis.getInnerRadius(), outerRadius = axis.getOuterRadius(), distance = PointService.distancePP({ x: relativeX, y: relativeY }, center); (distance > outerRadius || distance < innerRadius) && axisMap.delete(axis.getSpecIndex()); } })), axisMap; } _updateCrosshairLabel(label, labelAttrs, callback) { const container = this.getContainer(); label ? label.setAttributes(labelAttrs) : (label = new Tag(labelAttrs), null == container || container.add(label), callback(label)), limitTagInBounds(label, this._getLimitBounds()); } clearOutEvent() { this._timer && (clearTimeout(this._timer), this._timer = null), this._clickLock && (this._clickLock = null), this._hasActive && (this._hasActive = null); } _hideByField(field) { const {crosshairComp: crosshairComp, labelsComp: labelsComp} = this._stateByField[field]; crosshairComp && crosshairComp.hideAll(), labelsComp && Object.keys(labelsComp).forEach((key => { labelsComp[key] && labelsComp[key].hideAll(); })); } hide() { Object.keys(this._stateByField).forEach((field => { this._hideByField(field); })); } _getNeedClearVRenderComponents() { return Object.keys(this._stateByField).reduce(((res, field) => { const {crosshairComp: crosshairComp, labelsComp: labelsComp} = this._stateByField[field]; return crosshairComp && res.push(crosshairComp), labelsComp && Object.keys(labelsComp).forEach((key => { labelsComp[key] && res.push(labelsComp[key]); })), res; }), []); } } BaseCrossHair.specKey = "crosshair"; //# sourceMappingURL=base.js.map