Skip to content

Commit

Permalink
feat: add TagGuide (#1363)
Browse files Browse the repository at this point in the history
  • Loading branch information
El-Chiang authored Feb 16, 2022
1 parent 1169d11 commit a558e0f
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 34 deletions.
4 changes: 3 additions & 1 deletion packages/f2/src/components/guide/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import LineGuideView from './views/Line';
import ArcGuideView from './views/Arc';
import RectGuideView from './views/Rect';
import ImageGuideView from './views/Image';
import TagGuideView from './views/Tag';

const DefaultGuideView = () => null;
const TextGuide = withGuide(TextGuideView);
Expand All @@ -13,7 +14,8 @@ const LineGuide = withGuide(LineGuideView);
const ArcGuide = withGuide(ArcGuideView);
const RectGuide = withGuide(RectGuideView);
const ImageGuide = withGuide(ImageGuideView);
const TagGuide = withGuide(TagGuideView)

export default withGuide(DefaultGuideView);

export { withGuide, TextGuide, PointGuide, ArcGuide, LineGuide, RectGuide, ImageGuide };
export { withGuide, TextGuide, PointGuide, ArcGuide, LineGuide, RectGuide, ImageGuide, TagGuide };
213 changes: 213 additions & 0 deletions packages/f2/src/components/guide/views/Tag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { jsx } from '../../../jsx';
import type { Types } from '@antv/f2-graphic';
interface TagGuideProps {
points?: { x: number; y: number }[] | null;
canvasWidth?: number;
canvasHeight?: number;
guideBBox?: Types.BBox;
offsetX?: number;
offsetY?: number;
autoAdjust?: boolean;
/**
* 箭头的方向
*/
direct?: string;
/**
* 箭头的边长
*/
side?: number;
/**
* 文字内容
*/
content?: string;
/**
* 背景样式设置,见 group 绘图属性
*/
background?: any;
/**
* 文字样式
*/
textStyle?: any;
}

const defaultProps: TagGuideProps = {
offsetX: 0,
offsetY: 0,
points: [],
direct: 'tl',
side: 6,
autoAdjust: true,
};

const defaultStyle = {
container: {
fill: '#1677FF',
radius: 2,
padding: [3, 5],
},
text: {
fontSize: '22px' as `${number}px`,
fill: '#fff',
},
arrow: {
fill: '#1677FF',
},
};

export default (props: TagGuideProps) => {
const cfg = { ...defaultProps, ...props };
const {
points,
content,
offsetX,
offsetY,
direct,
side,
autoAdjust,
canvasWidth,
canvasHeight,
guideBBox,
background,
textStyle,
} = cfg;
const { x, y } = points[0] || {};
const { width: guideWidth, height: guideHeight } = guideBBox || {};

let posX = x + (offsetX || 0);
let posY = y + (offsetY || 0);

const _getDirect = (point) => {
let newDirect = direct;
const { x, y } = point;

let vertical = newDirect[0];
let horizontal = newDirect[1];

// adjust for vertical direction
if (vertical === 't' && y - side - guideHeight < 0) {
vertical = 'b';
} else if (vertical === 'b' && y + side + guideHeight > canvasHeight) {
vertical = 't';
}
// adjust for horizontal direction
const diff = vertical === 'c' ? side : 0;
if (horizontal === 'l' && x - diff - guideWidth < 0) {
horizontal = 'r';
} else if (horizontal === 'r' && x + diff + guideWidth > canvasWidth) {
horizontal = 'l';
} else if (horizontal === 'c') {
if (guideWidth / 2 + x + diff > canvasWidth) {
horizontal = 'l';
} else if (x - guideWidth / 2 - diff < 0) {
horizontal = 'r';
}
}

newDirect = vertical + horizontal;
return newDirect;
};

const _getArrowPoints = (direct) => {
let arrowPoints = [];
const { minX, minY } = guideBBox || {};
if (direct === 'tl') {
arrowPoints = [
{ x: minX, y: minY - 1 }, // 这个 1 是为了防止出现白边
{ x: minX, y: minY + side },
{ x: minX - side - 1, y: minY - 1 },
];
posX -= (guideWidth || 0);
posY = posY - (guideHeight || 0) - side;
} else if (direct === 'cl') {
arrowPoints = [
{ x: minX - 1, y: minY - 1 - side / 2 },
{ x: minX - 1, y: minY + 1 + side / 2 },
{ x: minX + side, y: minY },
];
posX = posX - (guideWidth || 0) - side;
posY -= (guideHeight / 2 || 0);
} else if (direct === 'bl') {
arrowPoints = [
{ x: minX, y: -side + minY },
{ x: minX - side - 1, y: minY + 1 },
{ x: minX, y: minY + 1 },
];
posX = posX - (guideWidth || 0);
posY += side;
} else if (direct === 'bc') {
arrowPoints = [
{ x: guideWidth / 2 + minX, y: -side + minY },
{ x: (guideWidth - side) / 2 + minX - 1, y: minY + 1 },
{ x: (guideWidth + side) / 2 + minX + 1, y: minY + 1 },
];
} else if (direct === 'br') {
arrowPoints = [
{ x: minX, y: minY - side },
{ x: minX, y: minY + 1 },
{ x: minX + side + 1, y: minY + 1 },
];
posY += side;
} else if (direct === 'cr') {
arrowPoints = [
{ x: minX - side, y: minY },
{ x: minX + 1, y: minY - 1 - side / 2 },
{ x: minX + 1, y: minY + 1 + side / 2 },
];
posX += side;
posY -= (guideHeight / 2 || 0);
} else if (direct === 'tr') {
arrowPoints = [
{ x: minX, y: minY + side },
{ x: minX, y: minY - 1 },
{ x: side + minX + 1, y: minY - 1 },
];
posY = posY - (guideHeight || 0) - side;
} else if (direct === 'tc') {
arrowPoints = [
{ x: minX - 1 - side / 2, y: minY - 1 },
{ x: minX + 1 + side / 2, y: minY - 1 },
{ x: minX, y: minY + side },
];
posX -= (guideWidth / 2 || 0);
posY = posY - (guideHeight || 0) - side;
}

return arrowPoints;
};

const dr = autoAdjust ? _getDirect(points[0]) : direct;
const arrowPoints = _getArrowPoints(dr);

return (
<group
attrs={{
fill: defaultStyle.container.fill,
radius: defaultStyle.container.radius,
...background,
}}
style={{
left: posX,
top: posY,
padding: defaultStyle.container.padding,
...background,
}}
>
<text
attrs={{
text: content,
fontSize: defaultStyle.text.fontSize,
fill: defaultStyle.text.fill,
...textStyle,
}}
/>
{guideBBox && (
<polygon
attrs={{
points: arrowPoints,
fill: defaultStyle.arrow.fill,
}}
/>
)}
</group>
);
};
42 changes: 40 additions & 2 deletions packages/f2/src/components/guide/withGuide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Component from '../../base/component';
import { isString, isNil } from '@antv/util';
import { Ref } from '../../types';
import Chart from '../../chart';
import { renderShape } from '../../base/diff';

function isInBBox(bbox, point) {
const { minX, maxX, minY, maxY } = bbox;
Expand All @@ -19,6 +20,12 @@ export default (View) => {
super(props);
// 创建ref
this.triggerRef = {};
this.state = {};
}

willMount() {
super.willMount();
this.getGuideBBox();
}

didMount() {
Expand All @@ -38,6 +45,24 @@ export default (View) => {
});
}

getGuideBBox() {
const shape = renderShape(this, this.render(), false);
const { x, y, width, height } = shape.get('attrs');
// getBBox 没有包含 padding 所以这里手动计算 bbox
const bbox = {
minX: x,
minY: y,
maxX: x + width,
maxY: y + height,
width,
height,
};
this.setState({
guideBBox: bbox,
});
shape.destroy();
}

// 解析record里的模板字符串,如min、max、50%...
parseReplaceStr(value, scale) {
const replaceMap = { min: 0, max: 1, median: 0.5 };
Expand Down Expand Up @@ -86,13 +111,26 @@ export default (View) => {
}

render() {
const { props } = this;
const { props, context } = this;
const { coord, records = [] } = props;
const { width, height } = context;
const points = this.convertPoints(records);
const theme = this.getGuideTheme();
const { guideWidth, guideHeight, guideBBox } = this.state;

return (
<View triggerRef={this.triggerRef} points={points} theme={theme} coord={coord} {...props} />
<View
triggerRef={this.triggerRef}
points={points}
theme={theme}
coord={coord}
{...props}
canvasWidth={width}
canvasHeight={height}
guideWidth={guideWidth}
guideHeight={guideHeight}
guideBBox={guideBBox}
/>
);
}
};
Expand Down
1 change: 1 addition & 0 deletions packages/f2/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
ArcGuide,
RectGuide,
ImageGuide,
TagGuide,
} from './guide';
export { default as Tooltip, withTooltip, TooltipView } from './tooltip';
export { default as Treemap, withTreemap, TreemapView } from './treemap';
Expand Down
Loading

0 comments on commit a558e0f

Please sign in to comment.