@visactor/vgrammar-core
Version:
VGrammar is a visual grammar library
278 lines (252 loc) • 15.2 kB
JavaScript
import { clampRadian, getAngleByPoint, isString, merge } from "@visactor/vutils";
import { CircleCrosshair, LineCrosshair, PolygonCrosshair, RectCrosshair, SectorCrosshair } from "@visactor/vrender-components";
import { BaseInteraction } from "./base";
import { CrosshairEnum } from "../graph/enums";
import { isContinuous, isDiscrete } from "@visactor/vscale";
import { invokeFunctionType } from "../parse/util";
const computeCrosshairStartEnd = (point, scale, type, groupSize, config, offset = 0) => {
var _a, _b;
const start = {
x: 0,
y: 0
}, end = {
x: 0,
y: 0
}, radius = "angle" === type ? null !== (_a = null == config ? void 0 : config.radius) && void 0 !== _a ? _a : Math.min(groupSize.width, groupSize.height) / 2 : null, center = "angle" === type ? null !== (_b = null == config ? void 0 : config.center) && void 0 !== _b ? _b : {
x: groupSize.width / 2,
y: groupSize.height / 2
} : null;
let current = 0;
if (isDiscrete(scale.type)) {
if ("x" === type) current = scale.scale(scale.invert(point.x)); else if ("y" === type) current = scale.scale(scale.invert(point.y)); else if ("angle" === type) {
const angle = clampRadian(getAngleByPoint(center, point) + 2 * Math.PI);
current = scale.scale(scale.invert(angle));
}
} else isContinuous(scale.type) && ("x" === type ? current = point.x : "y" === type ? current = point.y : "angle" === type && (current = getAngleByPoint(center, point)));
switch (current += offset, type) {
case "x":
start.x = current, start.y = 0, end.x = current, end.y = groupSize.height;
break;
case "y":
start.x = 0, start.y = current, end.x = groupSize.width, end.y = current;
break;
case "angle":
start.x = center.x, start.y = center.y, end.x = center.x + radius * Math.cos(current),
end.y = center.y + radius * Math.sin(current);
}
return {
start: start,
end: end
};
}, computeRadiusOfTangential = (point, scale, type, groupSize, config, addition) => {
var _a, _b, _c;
const center = null !== (_b = null !== (_a = null == addition ? void 0 : addition.center) && void 0 !== _a ? _a : null == config ? void 0 : config.center) && void 0 !== _b ? _b : {
x: groupSize.width / 2,
y: groupSize.height / 2
};
let currentRadius = 0;
if (isDiscrete(scale.type)) {
const offset = "band" === scale.type ? scale.bandwidth() / 2 : 0, radius = Math.sqrt((point.x - center.x) ** 2 + (point.y - center.y) ** 2);
currentRadius = scale.scale(scale.invert(radius)) + offset;
} else if (isContinuous(scale.type)) {
const maxRadius = null !== (_c = null == config ? void 0 : config.radius) && void 0 !== _c ? _c : Math.min(groupSize.width, groupSize.height) / 2;
currentRadius = Math.min(maxRadius, Math.sqrt((point.x - center.x) ** 2 + (point.y - center.y) ** 2));
}
return {
radius: currentRadius,
center: center
};
};
export const generateLineCrosshairAttributes = (point, scale, type, groupSize, config, theme, addition) => {
var _a, _b, _c;
const crosshairTheme = null === (_a = null == theme ? void 0 : theme.components) || void 0 === _a ? void 0 : _a.lineCrosshair, offset = "band" === scale.type ? scale.bandwidth() / 2 : 0, points = computeCrosshairStartEnd(point, scale, type, groupSize, {
radius: null !== (_b = null == addition ? void 0 : addition.radius) && void 0 !== _b ? _b : null == config ? void 0 : config.radius,
center: null !== (_c = null == addition ? void 0 : addition.center) && void 0 !== _c ? _c : null == config ? void 0 : config.center
}, offset);
return merge({}, crosshairTheme, points, null != addition ? addition : {});
};
export const generateRectCrosshairAttributes = (point, scale, type, groupSize, config, theme, addition) => {
var _a, _b, _c;
const crosshairTheme = null === (_a = null == theme ? void 0 : theme.components) || void 0 === _a ? void 0 : _a.rectCrosshair, defaultSize = "band" === scale.type || "point" === scale.type ? scale.step() : void 0, customRectStyle = null == addition ? void 0 : addition.rectStyle, size = null != defaultSize ? defaultSize : "y" === type ? null !== (_b = null == customRectStyle ? void 0 : customRectStyle.width) && void 0 !== _b ? _b : crosshairTheme.rectStyle.width : null !== (_c = null == customRectStyle ? void 0 : customRectStyle.height) && void 0 !== _c ? _c : crosshairTheme.rectStyle.height, points = computeCrosshairStartEnd(point, scale, type, groupSize, config, "band" === scale.type ? 0 : -size / 2), rectStyle = {};
"x" === type ? rectStyle.width = size : rectStyle.height = size;
const attribute = merge({}, crosshairTheme, {
start: points.start,
end: points.end,
rectStyle: rectStyle
}, null != addition ? addition : {});
return "x" === type ? attribute.rectStyle.height = attribute.end.y - attribute.start.y : attribute.rectStyle.width = attribute.end.x - attribute.start.x,
attribute;
};
export const generateRingCrosshairAttributes = (point, scale, type, groupSize, config, theme, addition) => {
var _a;
const crosshairTheme = null === (_a = null == theme ? void 0 : theme.components) || void 0 === _a ? void 0 : _a.circleCrosshair, {center: center, radius: radius} = computeRadiusOfTangential(point, scale, 0, groupSize, config, addition), startAngle = crosshairTheme.startAngle, endAngle = crosshairTheme.endAngle, deltaRadius = "band" === scale.type || "point" === scale.type ? scale.step() : 0;
return merge({}, crosshairTheme, {
center: center,
innerRadius: radius - deltaRadius / 2,
radius: radius + deltaRadius / 2,
startAngle: startAngle,
endAngle: endAngle
}, null != addition ? addition : {});
};
export const generateSectorCrosshairAttributes = (point, scale, type, groupSize, config, theme, addition) => {
var _a, _b, _c, _d, _e;
const crosshairTheme = null === (_a = null == theme ? void 0 : theme.components) || void 0 === _a ? void 0 : _a.sectorCrosshair, radius = null !== (_c = null !== (_b = null == addition ? void 0 : addition.radius) && void 0 !== _b ? _b : null == config ? void 0 : config.radius) && void 0 !== _c ? _c : Math.min(groupSize.width, groupSize.height) / 2, center = null !== (_e = null !== (_d = null == addition ? void 0 : addition.center) && void 0 !== _d ? _d : null == config ? void 0 : config.center) && void 0 !== _e ? _e : {
x: groupSize.width / 2,
y: groupSize.height / 2
}, defaultAngle = crosshairTheme.endAngle - crosshairTheme.startAngle, angle = "band" === scale.type || "point" === scale.type ? scale.step() : defaultAngle;
let currentAngle = 0;
if (isDiscrete(scale.type)) {
const angle = clampRadian(getAngleByPoint(center, point) + 2 * Math.PI);
currentAngle = scale.scale(scale.invert(angle)) + ("band" === scale.type ? scale.bandwidth() / 2 : 0);
} else isContinuous(scale.type) && (currentAngle = getAngleByPoint(center, point));
return merge({}, crosshairTheme, {
center: center,
radius: radius,
startAngle: currentAngle - angle / 2,
endAngle: currentAngle + angle / 2
}, null != addition ? addition : {});
};
export const generateCircleCrosshairAttributes = (point, scale, type, groupSize, config, theme, addition) => {
var _a;
const crosshairTheme = null === (_a = null == theme ? void 0 : theme.components) || void 0 === _a ? void 0 : _a.circleCrosshair, {center: center, radius: radius} = computeRadiusOfTangential(point, scale, 0, groupSize, config, addition), startAngle = crosshairTheme.startAngle, endAngle = crosshairTheme.endAngle;
return merge({}, crosshairTheme, {
center: center,
radius: radius,
startAngle: startAngle,
endAngle: endAngle
}, null != addition ? addition : {});
};
export const generatePolygonCrosshairAttributes = (point, scale, type, groupSize, config, theme, addition) => {
var _a;
const crosshairTheme = null === (_a = null == theme ? void 0 : theme.components) || void 0 === _a ? void 0 : _a.circleCrosshair, {center: center, radius: radius} = computeRadiusOfTangential(point, scale, 0, groupSize, config, addition), startAngle = crosshairTheme.startAngle, endAngle = crosshairTheme.endAngle;
return merge({}, crosshairTheme, {
center: center,
radius: radius,
startAngle: startAngle,
endAngle: endAngle
}, null != addition ? addition : {});
};
export class Crosshair extends BaseInteraction {
constructor(view, options) {
var _a, _b;
super(view, options), this.type = Crosshair.type, this.handleCrosshairShow = event => {
var _a;
if (!this._crosshairComponent) return;
const groupGraphicItem = this._container.getGroupGraphicItem(), point = {
x: 0,
y: 0
}, globalTransMatrix = groupGraphicItem.globalTransMatrix, containerPoint = {
x: globalTransMatrix.e,
y: globalTransMatrix.f
};
if (globalTransMatrix.transformPoint(event.canvas, point), point.x < 0 || point.x > groupGraphicItem.attribute.width || point.y < 0 || point.y > groupGraphicItem.attribute.height) return void this._crosshairComponent.hideAll();
const crosshairType = null !== (_a = this.options.crosshairType) && void 0 !== _a ? _a : "x", groupSize = {
width: groupGraphicItem.attribute.width,
height: groupGraphicItem.attribute.height
}, scale = (isString(this.options.scale) ? this.view.getScaleById(this.options.scale) : this.options.scale).getScale(), config = {
center: this.options.center,
radius: this.options.radius
}, theme = this.view.getCurrentTheme(), addition = invokeFunctionType(this.options.attributes, this.parameters(), {}, {});
let attributes = {};
switch (this.getCrosshairComponentType()) {
case CrosshairEnum.lineCrosshair:
attributes = generateLineCrosshairAttributes(point, scale, crosshairType, groupSize, config, theme, addition);
break;
case CrosshairEnum.rectCrosshair:
attributes = generateRectCrosshairAttributes(point, scale, crosshairType, groupSize, config, theme, addition);
break;
case CrosshairEnum.sectorCrosshair:
attributes = generateSectorCrosshairAttributes(point, scale, 0, groupSize, config, theme, addition);
break;
case CrosshairEnum.circleCrosshair:
attributes = generateCircleCrosshairAttributes(point, scale, 0, groupSize, config, theme, addition);
break;
case CrosshairEnum.polygonCrosshair:
attributes = generatePolygonCrosshairAttributes(point, scale, 0, groupSize, config, theme, addition);
break;
case CrosshairEnum.ringCrosshair:
attributes = generateRingCrosshairAttributes(point, scale, 0, groupSize, config, theme, addition);
}
this.getCrosshairComponentType() !== CrosshairEnum.circleCrosshair && (attributes.x = containerPoint.x,
attributes.y = containerPoint.y), this._crosshairComponent.showAll(), this._crosshairComponent.setAttributes(attributes);
}, this.handleCrosshairHide = () => {
this._crosshairComponent && this._crosshairComponent.hideAll();
}, this.options = Object.assign({}, Crosshair.defaultOptions, options), this._container = null !== (_b = null === (_a = view.getMarksBySelector(this.options.container)) || void 0 === _a ? void 0 : _a[0]) && void 0 !== _b ? _b : view.rootMark;
}
getEvents() {
return [ {
type: this.options.trigger,
handler: this.handleCrosshairShow
}, {
type: this.options.triggerOff,
handler: this.handleCrosshairHide
} ];
}
getCrosshairComponentType() {
var _a, _b;
if (this._crosshairComponentType) return this._crosshairComponentType;
const shape = null !== (_a = this.options.crosshairShape) && void 0 !== _a ? _a : "line", type = null !== (_b = this.options.crosshairType) && void 0 !== _b ? _b : "x";
return this._crosshairComponentType = "rect" === shape ? "angle" === type ? CrosshairEnum.sectorCrosshair : "radius" === type ? CrosshairEnum.ringCrosshair : "radius-polygon" === type ? CrosshairEnum.polygonCrosshair : CrosshairEnum.rectCrosshair : "radius" === type ? CrosshairEnum.circleCrosshair : "radius-polygon" === type ? CrosshairEnum.polygonCrosshair : CrosshairEnum.lineCrosshair,
this._crosshairComponentType;
}
getDefaultCrosshairAttribute() {
var _a;
const type = null !== (_a = this.options.crosshairType) && void 0 !== _a ? _a : "x";
return "radius" === type || "radius-polygon" === type ? {
center: {
x: 0,
y: 0
},
zIndex: -1
} : {
start: {
x: 0,
y: 0
},
end: {
x: 0,
y: 0
},
zIndex: -1
};
}
bind() {
super.bind();
const stage = this.view.renderer.stage();
if (!this._crosshairComponent && stage) {
switch (this.getCrosshairComponentType()) {
case CrosshairEnum.lineCrosshair:
this._crosshairComponent = new LineCrosshair(this.getDefaultCrosshairAttribute());
break;
case CrosshairEnum.rectCrosshair:
this._crosshairComponent = new RectCrosshair(this.getDefaultCrosshairAttribute());
break;
case CrosshairEnum.sectorCrosshair:
this._crosshairComponent = new SectorCrosshair(this.getDefaultCrosshairAttribute());
break;
case CrosshairEnum.circleCrosshair:
this._crosshairComponent = new CircleCrosshair(this.getDefaultCrosshairAttribute());
break;
case CrosshairEnum.polygonCrosshair:
this._crosshairComponent = new PolygonCrosshair(this.getDefaultCrosshairAttribute());
break;
case CrosshairEnum.ringCrosshair:
this._crosshairComponent = new SectorCrosshair(this.getDefaultCrosshairAttribute());
}
stage.defaultLayer.appendChild(this._crosshairComponent);
}
}
unbind() {
super.unbind();
const stage = this.view.renderer.stage();
this._crosshairComponent && stage && (stage.defaultLayer.removeChild(this._crosshairComponent),
this._crosshairComponent.release(), this._crosshairComponent = null);
}
}
Crosshair.type = "crosshair", Crosshair.defaultOptions = {
trigger: "pointermove",
triggerOff: "pointerleave",
crosshairType: "x",
crosshairShape: "line"
};
//# sourceMappingURL=crosshair.js.map