Skip to content
This repository was archived by the owner on Jul 15, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,70 @@ slider 滑块的背景图表配置,可配置其图表类型以及颜色:
- `slider.destroy()`

`slider.destroy()` 销毁。


### 新增时间滚动应用

```js
this.ds = new DataSet({
// 这里需要计算,可以直接赋值x轴的第一个值和最后一个值
state: {
from: '开始位置(需要计算)',
to: '结束位置(需要计算)'
}
});
const dv = this.ds.createView(DATA_VIEW_NAME);
// 需要传入你的数据
dv.source(data);
this.slider = new Slider({
container: sliderDom, // 用于显示该组件的 dom 容器 ID
rangeWidthRatio: 0.2, // 设置滑块占总背景的宽度
oneScreenScale: this._getOneScreenScale(), // 注意这里需要自己计算,因为每张图对应的最小一屏展示都是不同的,如果大于1则不用初始化slider
width: 'auto', // 可选,指定滑块的宽度,如果不指定则默认同 chart 相同,如果 chart 自适应宽度,slider 也会自适应宽度复制代码
height: 30, // 指定滑块的高度
data: this.ds.getView(DATA_VIEW_NAME).origin,
padding: [0, 0, 0, 0],
xAxis: 'xAxis', // 滑块控制的维度
yAxis: 'max', // 滑块背景图表对应的 Y 轴维度, 没有传入则默认不绘制背景图
// 是否隐藏左右两边的文案
hideTextElement: true,
// 是否开启范围选择模式
rangeMode: true,
onChange: ({ startText, endText }) => {
// !!! 更新状态量
this.ds.setState('from', startText);
this.ds.setState('to', endText);
},
// 当图表发生resize时,需要重新制定一屏在总屏中的占比
resizeSlider: (o) => {
const osScale = this._getOneScreenScale();
o.setOneScreenScale(osScale);
}
});
this.slider.render();
```

### 提供一种计算一屏应该展示多少的方法

```js
_getOneScreenScale() {
const chartWidth = this.chart.get('width');
// 用一个labelWidth,全局的储存住当前x轴所有坐标的宽度之和
if (!this.labelWidth) {
const padding = this.chart.get('plot').get('padding');
const xAxes = this.chart.get('axisController').axes[0];
const labelsGroup = xAxes.get('labelsGroup');
const labels = labelsGroup.get('children');
this.labelWidth = padding[1] + padding[3];
labels.forEach((label) => {
// !!!这里示例在更新数据以后会直接导致无法获取label的示例,源码里面可以实时计算容器的位置
const bbox = label.calculateBox();
if (bbox) {
const width = bbox.maxX - bbox.minX + SPACE;
this.labelWidth += width;
}
});
}
return chartWidth / this.labelWidth;
}
```
88 changes: 57 additions & 31 deletions src/component/range.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ const OFFSET = 5;
class Range extends Group {
getDefaultCfg() {
return {
/**
* 是否开启范围选择模式
* @type {Boolean}
*/
rangeMode: false,
/**
* 滑块宽度的占总背景宽的比例
* @type {Number}
*/
rangeWidthRatio: 0.2,
/**
* 是否显示左右两侧的文案
* @type {Boolean}
*/
hideTextElement: false,
/**
* 范围
* @type {Array}
Expand Down Expand Up @@ -82,14 +97,12 @@ class Range extends Group {
const iconWidth = handleStyle.width;
const iconHeight = handleStyle.height;

let text;
let handleIcon;
let triggerCursor;


if (layout === 'horizontal') {
const iconWidth = handleStyle.width;
triggerCursor = 'ew-resize';
triggerCursor = this.get('rangeMode') ? 'move' : 'ew-resize';
handleIcon = handle.addShape('Image', {
attrs: {
x: -iconWidth / 2,
Expand All @@ -100,18 +113,8 @@ class Range extends Group {
cursor: triggerCursor
}
});
text = handle.addShape('Text', {
attrs: Util.mix({
x: (type === 'min') ? -(iconWidth / 2 + OFFSET) : iconWidth / 2 + OFFSET,
y: iconHeight / 2,
textAlign: (type === 'min') ? 'end' : 'start',
textBaseline: 'middle',
text: type === 'min' ? this.get('minText') : this.get('maxText'),
cursor: triggerCursor
}, this.get('textStyle'))
});
} else {
triggerCursor = 'ns-resize';
triggerCursor = this.get('rangeMode') ? 'move' : 'ns-resize';
handleIcon = handle.addShape('Image', {
attrs: {
x: 0,
Expand All @@ -122,20 +125,36 @@ class Range extends Group {
cursor: triggerCursor
}
});
text = handle.addShape('Text', {
attrs: Util.mix({
x: iconWidth / 2,
y: (type === 'min') ? (iconHeight / 2 + OFFSET) : -(iconHeight / 2 + OFFSET),
textAlign: 'center',
textBaseline: 'middle',
text: type === 'min' ? this.get('minText') : this.get('maxText'),
cursor: triggerCursor
}, this.get('textStyle'))
});
}

this.set(type + 'TextElement', text);
this.set(type + 'IconElement', handleIcon);
// @2018-06-22 by blue.lb 范围选择模式,不要显示内容
if (!this.get('hideTextElement')) {
let text;
if (layout === 'horizontal') {
text = handle.addShape('Text', {
attrs: Util.mix({
x: (type === 'min') ? -(iconWidth / 2 + OFFSET) : iconWidth / 2 + OFFSET,
y: iconHeight / 2,
textAlign: (type === 'min') ? 'end' : 'start',
textBaseline: 'middle',
text: type === 'min' ? this.get('minText') : this.get('maxText'),
cursor: triggerCursor
}, this.get('textStyle'))
});
} else {
text = handle.addShape('Text', {
attrs: Util.mix({
x: iconWidth / 2,
y: (type === 'min') ? (iconHeight / 2 + OFFSET) : -(iconHeight / 2 + OFFSET),
textAlign: 'center',
textBaseline: 'middle',
text: type === 'min' ? this.get('minText') : this.get('maxText'),
cursor: triggerCursor
}, this.get('textStyle'))
});
}
this.set(type + 'TextElement', text);
}
return handle;
}

Expand Down Expand Up @@ -200,7 +219,6 @@ class Range extends Group {
width: (maxRatio - minRatio) * width,
height
});

minHandleElement.translate(minRatio * width, 0);
maxHandleElement.translate(maxRatio * width, 0);
} else {
Expand Down Expand Up @@ -277,7 +295,7 @@ class Range extends Group {
range[0] = this._getRange(diffRange, range[0]);
range[1] = this._getRange(diffRange, range[0]);
}
} else {
} else if (!this.get('rangeMode')) {
if (this._isElement(currentTarget, 'minHandleElement')) {
range[0] = this._getRange(diffRange, range[0]);
if (minRange) { // 设置了最小范围
Expand Down Expand Up @@ -308,8 +326,14 @@ class Range extends Group {
}
}
}

if (this._isElement(currentTarget, 'middleHandleElement')) {
// @2018-06-22 by blue.lb 这里判断如果是范围选择,则这里选择上、下滑块与选择中滑块效果一致
if (this.get('rangeMode') && (this._isElement(currentTarget, 'maxHandleElement') || this._isElement(currentTarget, 'minHandleElement') || this._isElement(currentTarget, 'middleHandleElement'))) {
range[0] = this._getRange(diffRange, range[0]);
if (range[0] > 100 - this.get('rangeWidthRatio') * 100) {
range[0] = 100 - this.get('rangeWidthRatio') * 100;
}
range[1] = range[0] + this.get('rangeWidthRatio') * 100;
} else if (this._isElement(currentTarget, 'middleHandleElement')) {
diffStashRange = (rangeStash[1] - rangeStash[0]);
this._limitRange(diffRange, diffStashRange, range);
}
Expand All @@ -321,7 +345,6 @@ class Range extends Group {
this.set('page' + dim, currentPage);
this._renderUI();
this.get('canvas').draw(); // need delete
return;
}

_onMouseDown(ev) {
Expand All @@ -341,6 +364,8 @@ class Range extends Group {
const containerDOM = this.get('canvas').get('containerDOM');
this.onMouseMoveListener = DomUtil.addEventListener(containerDOM, 'mousemove', Util.wrapBehavior(this, '_onCanvasMouseMove'));
this.onMouseUpListener = DomUtil.addEventListener(containerDOM, 'mouseup', Util.wrapBehavior(this, '_onCanvasMouseUp'));
// @2018-06-06 by blue.lb 添加mouseleave事件监听,让用户在操作出滑块区域后有一个“正常”的效果,可以正常重新触发滑块的操作流程
this.onMouseLeaveListener = DomUtil.addEventListener(containerDOM, 'mouseleave', Util.wrapBehavior(this, '_onCanvasMouseUp'));
}

_onCanvasMouseMove(ev) {
Expand All @@ -359,6 +384,7 @@ class Range extends Group {
_removeDocumentEvents() {
this.onMouseMoveListener.remove();
this.onMouseUpListener.remove();
this.onMouseLeaveListener.remove();
}
}

Expand Down
65 changes: 58 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ class Slider {
lineWidth: 1
};
this.range = [ 0, 100 ];
// @2018-06-25 by blue.lb 构建时间范围选择器所需要的配置项
// 是否隐藏左右两边的文案
this.hideTextElement = false;
// 是否开启范围选择模式
this.rangeMode = false;
// 一屏占比
this.oneScreenScale = 0;
// 中间滑块的宽度占用总宽度的比例
this.rangeWidthRatio = 1;
this.layout = 'horizontal';
// 文本颜色
this.textStyle = {
Expand Down Expand Up @@ -73,6 +82,12 @@ class Slider {
this.resizeTimer = timer;
}

// @2018-06-25 by blue.lb 外部方法允许重写设置一屏相对于整个数据宽度的比例
setOneScreenScale(scale) {
this.oneScreenScale = scale;
this.start = 0;
this.end = this.rangeWidthRatio;
}
forceFit() {
if (!this || this.destroyed) {
return;
Expand All @@ -85,6 +100,10 @@ class Slider {
this.bgChart && this.bgChart.changeWidth(width);
canvas.clear();
this._initWidth();
// @2018-07-18 by blue.lb 在resize时需要计算出新的宽度比例
if (this.resizeSlider) {
this.resizeSlider(this);
}
this._initSlider(); // 初始化滑动条
this._bindEvent();
canvas.draw();
Expand Down Expand Up @@ -227,6 +246,8 @@ class Slider {
const start = this.start;
const end = this.end;
const scale = this.scale;
// @2018-07-18 by blue.lb 获取新增的属性
const rangeWidthRatio = this.rangeWidthRatio;
let min = 0;
let max = 1;
if (start) {
Expand All @@ -235,7 +256,10 @@ class Slider {
if (end) {
max = scale.scale(scale.translate(end));
}

// @2018-07-18 by blue.lb 赋值给最大值,这里是为了固定滑块的宽度
if (rangeWidthRatio) {
max = rangeWidthRatio;
}
const { minSpan, maxSpan } = this;
let totalSpan = 0;
if (scale.type === 'time' || scale.type === 'timeCat') { // 时间类型已排序
Expand Down Expand Up @@ -281,6 +305,10 @@ class Slider {
const rangeElement = canvas.addGroup(Range, {
middleAttr: this.fillerStyle,
range,
// @2018-07-18 by blue.lb 传入给滑块示例属性
rangeMode: this.rangeMode,
rangeWidthRatio: this.rangeWidthRatio,
hideTextElement: this.hideTextElement,
minRange: this.minRange,
maxRange: this.maxRange,
layout: this.layout,
Expand All @@ -305,23 +333,46 @@ class Slider {
const rangeElement = self.rangeElement;
rangeElement.on('sliderchange', function(ev) {
const range = ev.range;
const minRatio = range[0] / 100;
const maxRatio = range[1] / 100;
// @2018-07-18 by blue.lb 首先获取滑块的对应的范围
let x = range[0];
let y = range[1];
// @2018-07-18 by blue.lb 判断仅应用为滑动展示时间的工具,根据最大值换算当前滑动块占用范围对应的位置
if (self.rangeMode && self.oneScreenScale > 0) {
const rate = 100 * (1 - self.rangeWidthRatio);
const indexNum = self.oneScreenScale * 100;
const rangeX = (range[0] * 100 / rate);
const index = Math.floor(rangeX / indexNum);
x = index * indexNum;
y = (index + 1) * indexNum;
if (y > 100) {
y = 100;
x = 100 - indexNum;
}
}
const minRatio = x / 100;
const maxRatio = y / 100;
self._updateElement(minRatio, maxRatio);
});
if (self.rangeMode) {
const minRatio = 0;
const maxRatio = self.oneScreenScale;
self._updateElement(minRatio, maxRatio);
}
}

_updateElement(minRatio, maxRatio) {
const scale = this.scale;
const rangeElement = this.rangeElement;
const minTextElement = rangeElement.get('minTextElement');
const maxTextElement = rangeElement.get('maxTextElement');
const min = scale.invert(minRatio);
const max = scale.invert(maxRatio);
const minText = scale.getText(min);
const maxText = scale.getText(max);
minTextElement.attr('text', minText);
maxTextElement.attr('text', maxText);
if (!this.hideTextElement) {
const minTextElement = rangeElement.get('minTextElement');
const maxTextElement = rangeElement.get('maxTextElement');
minTextElement.attr('text', minText);
maxTextElement.attr('text', maxText);
}

this.start = minText;
this.end = maxText;
Expand Down