Skip to content

Commit

Permalink
feat: add chart.guide().tag().
Browse files Browse the repository at this point in the history
  • Loading branch information
sima.zhang1990@gmail.com committed Mar 6, 2018
1 parent b29b6fc commit f4c8833
Show file tree
Hide file tree
Showing 9 changed files with 436 additions and 13 deletions.
16 changes: 11 additions & 5 deletions demos/area-00.html
Expand Up @@ -58,15 +58,21 @@
items[0].name = items[0].title;
}
});
chart.guide().tag({
position: [6, 2800],
content: '最高点',
offsetY: -2,
direct: 'tc'
});
chart.area().position('time*tem');
chart.line().position('time*tem');
chart.render();

// const point = chart.getPosition({
// time: 'Apr.',
// tem: 2600
// });
// chart.showTooltip(point);
const point = chart.getPosition({
time: 'Apr.',
tem: 2600
});
chart.showTooltip(point);
</script>
</body>
</html>
109 changes: 109 additions & 0 deletions docs/api/guide.md
Expand Up @@ -17,6 +17,7 @@ require('@antv/f2/lib/component/guide/html'); // 只加载 Guide.Html 组件
require('@antv/f2/lib/component/guide/text'); // 只加载 Guide.Text 组件
require('@antv/f2/lib/component/guide/rect'); // 只加载 Guide.Rect 组件
require('@antv/f2/lib/component/guide/line'); // 只加载 Guide.Line 组件
require('@antv/f2/lib/component/guide/tag'); // 只加载 Guide.Tag 组件

// 第二步:加载插件 Guide
const Guide = require('@antv/f2/lib/plugin/guide');
Expand Down Expand Up @@ -167,6 +168,113 @@ chart.guide().text({

设置辅助文本 y 方向的偏移量。

### Tag

`chart.guide().tag({})`

绘制辅助 Tag。

<img src="https://gw.alipayobjects.com/zos/rmsportal/kaPGgxRvqETVwUCRBQAG.png" width="50%;">


```ja
chart.guide().tag({
top: {Boolean}, // 指定 guide 是否绘制在 canvas 最上层,默认为 true, 即绘制在最上层
position: {Function} | {Array}, // Tag 的起始位置,值为原始数据值,支持 callback
content: {String}, // tag 的文本内容,支持文本换行,只需要在文本中写入 '\n',如 '最大值\n200'
direct: {String}, // 箭头朝向,默认自动计算,也可以手动指定方向,'tl'、'tc'、'tr'、'cl'、'cr'、'bl'、'bc'、'br'
side: {Number}, // 三角标的边长,默认为 4
offsetX: {Number}, // X 轴偏移,默认为 0
offsetY: {Number}, // Y 轴偏移,默认为 0
backgroud: {
padding: [ 4, 6 ], // tag 内边距,使用同 css 盒模型的 padding
radius: 2, // tag 圆角
fill: '#1890FF', // tag 背景色
}, // tag 背景样式
textStyle: {
fontSize: 12,
fill: '#fff'
} // tag 文本样式
});
```

#### 参数

- `top`: Boolean

指定 guide 是否绘制在 canvas 最上层,默认为 true, 即绘制在最上层。

- `position`: Array/Function

指定辅助 Tag 的显示位置,该值的类型如下:

+ Array: 数组来配置位置 [ x, y ],根据数组中的值的存在以下几种形式:
* x,y 都是原始数据 [ '2010-01-01', 200 ];
* x,y 可以使用原始数据的替代字符串 'min', 'max', 'median' , 例如:[ 'median', 200 ]
* x, y 都是用百分比的形式,在绘图区域定位,字符串中存在 '%', 例如 [ '50%', '50%'] 使得辅助元素居中
+ Function: 回调函数,可以动态的确定辅助元素的位置,应用于数据动态更新,辅助元素的位置根据数据变化的场景

```js
chart.guide().tag({
/**
* 设置辅助文本的显示位置
* @param {Scale} xScale x 轴对应的度量
* @param {Array} yScales y 轴对应的度量的数组集合
* @return {Array} 返回值必须为数组格式
*/
position(xScale, yScales) {
return []; // 位置信息
},
content: '最大值'
});
```

- `content`: String

辅助 tag 的显示内容。

- `direct`: String

Tag 箭头的方向,默认自动计算,用户也可以手动设置,该方向相对于 point,可设置值为:'tl'、'tc'、'tr'、'cl'、'cr'、'bl'、'bc'、'br',如下如所示:

<img src="https://gw.alipayobjects.com/zos/rmsportal/hyRzDvMdRVwukHVfmGWL.png" width="50%">

- `side`: Number

Tag 箭头的边长,默认为 4px。

- `offsetX`: Number

设置 Tag x 方向的偏移量。

- `offsetY`: Number

设置 Tag y 方向的偏移量。

- `backgroud`: Object

Tag 的背景样式设置,可设置的属性如下,详见[绘图属性](./canvas.md)

```js
backgroud: {
padding: [ 4, 6 ], // tag 内边距,用法同 css 盒模型的 padding
radius: 2, // tag 圆角
fill: '#1890FF', // tag 背景填充颜色
// 其他绘图属性
}
```

- `textStyle`: Object

Tag 的字体样式设置,可设置的属性如下,详见[绘图属性](./canvas.md)

```js
textStyle: {
fontSize: 12, // 字体大小
fill: '#fff' // 字体颜色
}
```

### Rect

`chart.guide.rect({})`
Expand Down Expand Up @@ -360,6 +468,7 @@ chart.guide().arc({

设置圆弧的显示样式,详见绘图属性。


### 清空 guides

```js
Expand Down
1 change: 1 addition & 0 deletions docs/getting-started/require-on-demand.md
Expand Up @@ -100,6 +100,7 @@ require('@antv/f2/lib/component/guide/html'); // 只加载 Guide.Html 组件
require('@antv/f2/lib/component/guide/text'); // 只加载 Guide.Text 组件
require('@antv/f2/lib/component/guide/rect'); // 只加载 Guide.Rect 组件
require('@antv/f2/lib/component/guide/line'); // 只加载 Guide.Line 组件
require('@antv/f2/lib/component/guide/tag'); // 只加载 Guide.Tag 组件

// 第二步:加载插件 Guide
const Guide = require('@antv/f2/lib/plugin/guide');
Expand Down
3 changes: 2 additions & 1 deletion src/component/guide/index.js
Expand Up @@ -3,5 +3,6 @@ module.exports = {
Line: require('./line'),
Arc: require('./arc'),
Rect: require('./rect'),
Html: require('./html')
Html: require('./html'),
Tag: require('./tag')
};
193 changes: 193 additions & 0 deletions src/component/guide/tag.js
@@ -0,0 +1,193 @@
const Util = require('../../util/common');
const GuideBase = require('./base');

class Tag extends GuideBase {
_initDefaultCfg() {
this.type = 'tag';
this.position = null;
this.content = null;
this.direct = 'auto'; // 默认自动计算,如果用户设置了就按照用户设置的渲染
this.offsetX = 0;
this.offsetY = 0;
this.side = 4; // 三角标的边长
this.background = {
padding: 5, // tag 内边距
radius: 2, // tag 圆角
fill: '#1890FF' // tag 背景色
};
this.textStyle = {
fontSize: 12,
fill: '#fff',
textAlign: 'center',
textBaseline: 'middle'
};
}

_getDirect(container, point, tagWidth, tagHeight) {
let direct = this.direct;
if (direct === 'auto') { // 自动计算
const side = this.side;
const canvas = container.get('canvas');
const clientWidth = canvas.getWidth();
const clientHeight = canvas.getHeight();
const { x, y } = point;

let vertical = 't';
let horizontal = 'l';

if (y - side - tagHeight < 0) {
vertical = 'b';
}

if (vertical === 'b') {
if (y + side + tagHeight > clientHeight) {
vertical = 't';
}
}

const diff = vertical === 'c' ? side : 0;
if (x - diff - tagWidth < 0) {
horizontal = 'r';
}
if (horizontal === 'r') {
const diff = vertical === 'c' ? side : 0;
if (x + diff + tagWidth > clientWidth) {
horizontal = 'l';
}
}
direct = vertical + horizontal;
this.direct = direct;
}

return direct;
}

render(coord, container) {
const position = this.parsePoint(coord, this.position);
const { content, background, textStyle } = this;

const tagContainer = container.addGroup({
className: 'guide-tag'
});
// 绘制文本
const tagText = tagContainer.addShape('text', {
className: 'guide-tag-text',
zIndex: 1,
attrs: Util.mix({
x: 0,
y: 0,
text: content
}, textStyle)
});

// 绘制背景框
const textBBox = tagText.getBBox();
const padding = Util.parsePadding(background.padding);
const tagWidth = textBBox.width + padding[1] + padding[3];
const tagHeight = textBBox.height + padding[0] + padding[2];
const yMin = textBBox.minY - padding[0];
const xMin = textBBox.minX - padding[3];
const tagBg = tagContainer.addShape('rect', {
className: 'guide-tag-bg',
zIndex: -1,
attrs: Util.mix({
x: xMin,
y: yMin,
width: tagWidth,
height: tagHeight
}, background)
});
const direct = this._getDirect(container, position, tagWidth, tagHeight);
const side = this.side;
let x = position.x + this.offsetX;
let y = position.y + this.offsetY;
let arrowPoints;
const radius = Util.parsePadding(background.radius);
if (direct === 'tl') {
arrowPoints = [
{ x: tagWidth - side + xMin, y: tagHeight + yMin - 1 }, // 这个 1 是为了防止出现白边
{ x: tagWidth + xMin, y: tagHeight + yMin - 1 },
{ x: tagWidth + xMin, y: tagHeight + side + yMin }
];
radius[2] = 0;
x = x - tagWidth;
y = y - side - tagHeight;
} else if (direct === 'cl') {
arrowPoints = [
{ x: tagWidth + xMin - 1, y: (tagHeight - side) / 2 + yMin },
{ x: tagWidth + xMin - 1, y: (tagHeight + side) / 2 + yMin },
{ x: tagWidth + side + xMin, y: tagHeight / 2 + yMin }
];

x = x - tagWidth - side;
y = y - tagHeight / 2;
} else if (direct === 'bl') {
arrowPoints = [
{ x: tagWidth + xMin, y: -side + yMin },
{ x: tagWidth - side + xMin, y: yMin + 1 },
{ x: tagWidth + xMin, y: yMin + 1 }
];
radius[1] = 0;

x = x - tagWidth;
y = y + side;
} else if (direct === 'bc') {
arrowPoints = [
{ x: tagWidth / 2 + xMin, y: -side + yMin },
{ x: (tagWidth - side) / 2 + xMin, y: yMin + 1 },
{ x: (tagWidth + side) / 2 + xMin, y: yMin + 1 }
];
x = x - tagWidth / 2;
y = y + side;
} else if (direct === 'br') {
arrowPoints = [
{ x: xMin, y: -side + yMin },
{ x: xMin, y: yMin + 1 },
{ x: side + xMin, y: yMin + 1 }
];
radius[0] = 0;
y = y + side;
} else if (direct === 'cr') {
arrowPoints = [
{ x: -side + xMin, y: tagHeight / 2 + yMin },
{ x: xMin + 1, y: (tagHeight - side) / 2 + yMin },
{ x: xMin + 1, y: (tagHeight + side) / 2 + yMin }
];
x = x + side;
y = y - tagHeight / 2;
} else if (direct === 'tr') {
arrowPoints = [
{ x: 0 + xMin, y: tagHeight + side + yMin },
{ x: 0 + xMin, y: tagHeight + yMin - 1 },
{ x: side + xMin, y: tagHeight + yMin - 1 }
];
radius[3] = 0;

y = y - tagHeight - side;
} else if (direct === 'tc') {
arrowPoints = [
{ x: (tagWidth - side) / 2 + xMin, y: tagHeight + yMin - 1 },
{ x: (tagWidth + side) / 2 + xMin, y: tagHeight + yMin - 1 },
{ x: tagWidth / 2 + xMin, y: tagHeight + side + yMin }
];
x = x - tagWidth / 2;
y = y - tagHeight - side;
}

tagContainer.addShape('Polygon', {
zIndex: 0,
attrs: {
points: arrowPoints,
fill: background.fill
}
});
tagBg.attr('radius', radius);
tagContainer.moveTo(x - xMin, y - yMin);
tagContainer.sort();

this.element = tagContainer;
}
}

GuideBase.Tag = Tag;
module.exports = Tag;
2 changes: 1 addition & 1 deletion src/component/guide/text.js
Expand Up @@ -6,7 +6,7 @@ class Text extends GuideBase {
this.type = 'text';
/**
* 辅助文本的位置
* @type {Object | Function | Array}
* @type {Function | Array}
*/
this.position = null;
/**
Expand Down

0 comments on commit f4c8833

Please sign in to comment.