├── .eslintignore
├── CHANGELOG.md
├── ISSUE_TEMPLATE.md
├── index.js
├── .babelrc
├── .torch.compile.opts.js
├── .gitlab-ci.yml
├── webpack-dev.config.js
├── .travis.yml
├── .eslintrc
├── .editorconfig
├── webpack.config.js
├── LICENSE
├── .gitignore
├── .npmignore
├── package.json
├── README.md
├── test
├── unit
│ └── index-spec.js
└── fixtures
│ └── candleSticks.json
└── src
├── index.js
└── component
└── range.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | coverage/
3 | dist/
4 | mocks/
5 | node_modules/
6 | demos/
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # History
2 |
3 | ---
4 |
5 | ## 2.0.0
6 |
7 | 配合 G2@3.0.0 版本升级。
8 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Summary
2 |
3 | ### Environment
4 |
5 | ### Expected behavior
6 |
7 | ### Actual behavior
8 |
9 | ### Steps to reproduce the behavior
10 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const Slider = require('./src/index');
2 | if (window && !window.G2) {
3 | console.err('Please load the G2 script first!');
4 | }
5 |
6 | module.exports = Slider;
7 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "transform-remove-strict-mode"
4 | ],
5 | "presets": [
6 | [
7 | "@babel/preset-env",
8 | {
9 | "loose": true,
10 | "modules": false
11 | }
12 | ]
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.torch.compile.opts.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | babelrc: {
3 | presets: [
4 | '@babel/preset-env'
5 | ],
6 | sourceMaps: 'inline'
7 | },
8 | extensions: ['.js'],
9 | exclude: [
10 | 'bower_components/**/*.js',
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | before_script:
2 | - Xvfb :99 &
3 | - export DISPLAY=:99.0;
4 | - echo $PATH
5 | test:
6 | image: reg.docker.alibaba-inc.com/dockerlab/macaca-electron:0.2.0
7 | script:
8 | - time tnpm i --no-cache
9 | - time tnpm run ci
10 | tags:
11 | - swarm
12 |
--------------------------------------------------------------------------------
/webpack-dev.config.js:
--------------------------------------------------------------------------------
1 | const webpackConfig = require('./webpack.config');
2 | const _ = require('lodash');
3 |
4 | module.exports = _.merge({
5 | devtool: 'cheap-source-map',
6 | watch: true,
7 | watchOptions: {
8 | aggregateTimeout: 300,
9 | poll: 1000
10 | }
11 | }, webpackConfig);
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "0.10"
5 |
6 | install:
7 | - npm install spm coveralls
8 |
9 | before_script:
10 | - node_modules/spm/bin/spm-install
11 |
12 | script:
13 | - node_modules/spm/bin/spm-test
14 |
15 | after_success:
16 | - node_modules/spm/bin/spm-test --coveralls | node_modules/.bin/coveralls
17 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "egg"
4 | ],
5 | "parser": "babel-eslint",
6 | "parserOptions": {
7 | "sourceType": "module"
8 | },
9 | "rules": {
10 | "no-bitwise": [
11 | 0
12 | ],
13 | "experimentalDecorators": [
14 | 0
15 | ],
16 | "comma-dangle": [
17 | "error",
18 | "never"
19 | ]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | charset = utf-8
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [Makefile]
16 | indent_style = tab
17 | indent_size = 1
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const {
3 | resolve
4 | } = require('path');
5 |
6 | module.exports = {
7 | entry: {
8 | 'g2-plugin-slider': './index.js'
9 | },
10 | output: {
11 | filename: '[name].js',
12 | library: 'Slider',
13 | libraryTarget: 'umd',
14 | path: resolve(__dirname, 'build/')
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | use: {
21 | loader: 'babel-loader',
22 | options: {
23 | babelrc: true
24 | }
25 | }
26 | }
27 | ]
28 | },
29 | plugins: [
30 | new webpack.NoEmitOnErrorsPlugin(),
31 | new webpack.optimize.AggressiveMergingPlugin()
32 | ]
33 | };
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Alipay.inc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # lock
9 | package-lock.json
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # Bower dependency directory (https://bower.io/)
30 | bower_components
31 |
32 | # node-waf configuration
33 | .lock-wscript
34 |
35 | # Compiled binary addons (http://nodejs.org/api/addons.html)
36 | build/Release
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # Typescript v1 declaration files
43 | typings/
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # Yarn Integrity file
58 | .yarn-integrity
59 |
60 | # dotenv environment variables file
61 | .env
62 |
63 | build
64 | dist
65 | temp
66 | .DS_Store
67 | .idea
68 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # lock
9 | package-lock.json
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # Bower dependency directory (https://bower.io/)
30 | bower_components
31 |
32 | # node-waf configuration
33 | .lock-wscript
34 |
35 | # Compiled binary addons (http://nodejs.org/api/addons.html)
36 | build/Release
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # Typescript v1 declaration files
43 | typings/
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # Yarn Integrity file
58 | .yarn-integrity
59 |
60 | # dotenv environment variables file
61 | .env
62 |
63 | # build
64 | .DS_Store
65 |
66 | # npmignore - content above this line is automatically generated and modifications may be omitted
67 | # see npmjs.com/npmignore for more details.
68 | test
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@antv/g2-plugin-slider",
3 | "version": "2.1.1",
4 | "description": "A datazoom slider plugin for G2.",
5 | "keywords": [
6 | "g2",
7 | "slider",
8 | "plugin",
9 | "datazoom"
10 | ],
11 | "main": "build/g2-plugin-slider.js",
12 | "browser": "build/g2-plugin-slider.js",
13 | "module": "index.js",
14 | "homepage": "https://github.com/antvis/g2-plugin-slider",
15 | "author": "sima.zhang1990@gmail.com",
16 | "repository": {
17 | "type": "git",
18 | "url": "git@github.com:antvis/g2-plugin-slider.git"
19 | },
20 | "bugs": {
21 | "url": "http://gitlab.alibaba-inc.com/datavis/g2-plugin-slider/issues"
22 | },
23 | "peerDependencies": {
24 | "@antv/g2": ">=3.2.8"
25 | },
26 | "devDependencies": {
27 | "@antv/data-set": "~0.9.6",
28 | "@antv/g2": "~3.2.8",
29 | "@babel/core": "~7.0.0",
30 | "@babel/preset-env": "~7.0.0",
31 | "torchjs": "~2.1.0",
32 | "babel-eslint": "~7.2.3",
33 | "babel-loader": "~8.0.0",
34 | "babel-plugin-transform-remove-strict-mode": "0.0.2",
35 | "chai": "~4.0.1",
36 | "electron": "~1.8.2-beta5",
37 | "eslint": "~3.19.0",
38 | "eslint-config-airbnb": "~15.0.1",
39 | "eslint-config-egg": "~4.2.0",
40 | "event-simulate": "~1.0.0",
41 | "jquery": "~3.3.1",
42 | "pre-commit": "~1.2.2",
43 | "shelljs": "~0.7.8",
44 | "uglify-js": "~3.0.15",
45 | "webpack": "~3.4.1"
46 | },
47 | "scripts": {
48 | "build": "webpack",
49 | "ci": "npm run lint && npm run test",
50 | "compress": "uglifyjs --compress --mangle --output dist/g2-plugin-slider.min.js -- build/g2-plugin-slider.js",
51 | "coverage": "npm run coverage-generator && npm run coverage-viewer",
52 | "coverage-viewer": "torch-coverage",
53 | "coverage-generator": "torch --coverage --compile --renderer --recursive test/unit",
54 | "dev": "webpack --config webpack-dev.config.js",
55 | "dist": "rm -rf dist && mkdir dist && npm run build && npm run compress",
56 | "prepublishOnly": "npm run dist",
57 | "lint": "eslint ./",
58 | "lint-fix": "eslint --fix ./",
59 | "test": "torch --compile --renderer --recursive ./test/unit",
60 | "test-live": "torch --compile --renderer --interactive --recursive ./test/unit"
61 | },
62 | "pre-commit": {
63 | "run": [
64 | "lint",
65 | "test"
66 | ],
67 | "silent": false
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # g2-plugin-slider
2 |
3 | [](https://www.npmjs.com/package/@antv/g2-plugin-slider)
4 | [](https://npmjs.org/package/@antv/g2-plugin-slider)
5 | [](http://isitmaintained.com/project/antvis/g2-plugin-slider "Percentage of issues still open")
6 |
7 | A datazoom slider plugin for G2.
8 |
9 | ## Install
10 |
11 | **首先**,你先要确保 [G2](https://github.com/antvis/g2) 已经加载。
12 |
13 | 如果你使用 npm,直接 `npm i @antv/g2-plugin-slider`。否则,直接下载[最新脚本](http://unpkg.alipay.com/@antv/g2-plugin-slider)。
14 |
15 | ```javascript
16 | import G2 from '@antv/g2';
17 | import Slider from '@antv/g2-plugin-slider';
18 |
19 | const slider = new Slider({
20 |
21 | });
22 | ```
23 |
24 |
25 | ```html
26 |
27 |
28 |
29 |
34 | ```
35 |
36 | ## API Reference
37 |
38 | ### Create an instance
39 |
40 | ```js
41 | new Slider({
42 | container: {string} | {HTMLElement},
43 | width?: {number} | {string},
44 | height?: {number},
45 | padding?: {object} | {number} | {array},
46 | xAxis: {string},
47 | yAxis: {string},
48 | start: {string} | {number},
49 | end: {string} | {number},
50 | startRadio?: {number},
51 | endRadio?: {number},
52 | minSpan: {number},
53 | maxSpan: {number},
54 | data: {array} | {dataview},
55 | fillerStyle?: {object},
56 | backgroundStyle?: {object},
57 | textStyle?: {object},
58 | handleStyle?: {object},
59 | backgroundChart?: {object}
60 | });
61 | ```
62 |
63 | - `container`
64 |
65 | (string | HTMLElement)
66 |
67 | 对应 slider 的 DOM 容器,可以传入该 DOM 的 id 或者直接传入容器的 html 节点对象。
68 |
69 | - `width`
70 |
71 | (string | number)
72 |
73 | 设置 slider 组件的宽度,默认为 `auto`,表示自适应容器的宽度。
74 |
75 | - `height`
76 |
77 | (number)
78 |
79 | 设置 slider 组件的高度,默认为 26,单位为 'px'。
80 |
81 | - `padding`
82 |
83 | 设置 slider 组件所在画布 canvas 的内边距,用于与图表对齐(默认图表的 canvas 容器也是带了内边距),默认值同 G2 默认主题的 padding 相同,[ 20, 20, 95, 80 ]。
84 |
85 | - `xAxis`
86 |
87 | (string)
88 |
89 | **必须声明**,我们的 Slider 是带有背景图表的滑动条组件,该字段用于声明该背景图表的横轴映射字段,同时该字段也是数据过滤字段。
90 |
91 | - `yAxis`
92 |
93 | (string)
94 |
95 | **必须声明**,我们的 Slider 是带有背景图表的滑动条组件,该字段用于声明该背景图表的纵轴轴映射字段。
96 |
97 | - `data`
98 |
99 | (array | dataview)
100 |
101 | **必须声明**,数据源。
102 |
103 | - `startRadio`
104 |
105 | (number)
106 |
107 | 声明滑动条起始滑块的位置对应的范围边界值,值介于 [0, 1]。
108 |
109 | 注意:`startRadio` 和 `start` 同时声明时,以 `startRadio` 为准。
110 |
111 | - `endRadio`
112 |
113 | (number)
114 |
115 | 声明滑动条结束滑块的位置对应的范围边界值,值介于 [0, 1]。
116 |
117 | 注意:`endRadio` 和 `end` 同时声明时,以 `endRadio` 为准。
118 |
119 |
120 | - `start`
121 |
122 | (number | string)
123 |
124 | 声明滑动条起始滑块的位置对应的数据值,默认为最小值。
125 |
126 | - `end`
127 |
128 | (number | string)
129 |
130 | 声明滑动条结束滑块的位置对应的数据值,默认为最大值。
131 |
132 | - `minSpan`
133 |
134 | (number)
135 |
136 | 筛选的最小范围限制,必须对应原始数据的范围,如果是时间,请使用时间戳。
137 |
138 | - `maxSpan`
139 |
140 | (number)
141 |
142 | 筛选的最大范围限制,必须对应原始数据的范围,如果是时间,请使用时间戳。
143 |
144 | - `scales`
145 |
146 | (object)
147 |
148 | 用于对 `xAxis` 和 `yAxis` 字段进行[列定义](/zh-cn/g2/3.x/tutorial/how-to-scale.htm),用于同操作的图表中对应的列定义相同。
149 |
150 | 示例代码:
151 |
152 | ```js
153 | scales: {
154 | [`${xAxis}`]: {
155 | type: 'time',
156 | mask: 'MM-DD'
157 | }
158 | }
159 | ```
160 |
161 | - `onChange`
162 |
163 | (function)
164 |
165 | 当滑动条滑块发生变化时,触发该回调函数,主要用于更新 ds 的状态量。该回调函数会提供一个参数,该参数是一个对象,包含如下属性:
166 |
167 | ```js
168 | onChange: (obj) => {
169 | const { startValue, endValue, startText, endText, startRadio, endRadio } = obj;
170 | }
171 | ```
172 |
173 | * `startValue` 起点滑块当前对应的原始数据值,如果是 `time` 或者 `timeCat` 类型是,该值为时间戳,请注意。
174 | * `endValue` 终点滑块当前对应的原始数据值,如果是 `time` 或者 `timeCat` 类型是,该值为时间戳,请注意。
175 | * `startText` 起点滑块当前的显示文本值
176 | * `endText` 终点滑块当前的显示文本值
177 | * `startRadio` 起点滑块当前对应的范围边界值,值介于 [0, 1]
178 | * `endRadio` 终点滑块当前对应的范围边界值,值介于 [0, 1]
179 |
180 | > 说明1:之所以区分 text 和 value,是因为大部分情况下用户会对数值进行格式化,所以在设置状态量和更新状态量时,需要保证前后数值类型的一致。
181 | > 说明2:若数据并非有序排列,则可以通过 `[startRadio, endRadio]` 获取到滑块起点和终点选中的范围
182 |
183 | - `fillerStyle`
184 |
185 | (object)
186 |
187 | 选中区域的样式配置,默认配置如下:
188 |
189 | ```js
190 | {
191 | fill: '#BDCCED',
192 | fillOpacity: 0.3
193 | }
194 | ```
195 |
196 | 图中红框框选区域:
197 |
198 | - `backgroundStyle`
199 |
200 | (object)
201 |
202 | slider 整体背景样式。
203 |
204 | - `textStyle`
205 |
206 | (object)
207 |
208 | slider 辅助文本字体样式配置。
209 |
210 | - `handleStyle`
211 |
212 | (object)
213 |
214 | slider 滑块的样式配置,可配置的属性如下:
215 |
216 | ```js
217 | {
218 | img: 'https://gw.alipayobjects.com/zos/rmsportal/QXtfhORGlDuRvLXFzpsQ.png', // 可以使图片地址也可以是 data urls
219 | width: 5,
220 | height: 26
221 | }
222 | ```
223 |
224 | - `backgroundChart`
225 |
226 | (object)
227 |
228 | slider 滑块的背景图表配置,可配置其图表类型以及颜色:
229 |
230 | ```js
231 | {
232 | type: [ 'area' ], // 图表的类型,可以是字符串也可是是数组
233 | color: '#CCD6EC'
234 | }
235 | ```
236 |
237 | ### Functions
238 |
239 | - `slider.render()`
240 |
241 | `slider.render()` 渲染组件,即将其绘制到页面上。
242 |
243 | - `slider.changeData()`
244 |
245 | `slider.changeData(data)` 更新数据源。
246 |
247 | - `slider.repaint()`
248 |
249 | `slider.repaint()` 重绘。
250 |
251 | - `slider.destroy()`
252 |
253 | `slider.destroy()` 销毁。
254 |
--------------------------------------------------------------------------------
/test/unit/index-spec.js:
--------------------------------------------------------------------------------
1 | const expect = require('chai').expect;
2 | const G2 = require('@antv/g2');
3 | const DataSet = require('@antv/data-set');
4 | const Slider = require('../../index');
5 | const PekingAQI = require('../fixtures/peking-aqi.json');
6 | const kData = require('../fixtures/candleSticks.json');
7 | const topData = require('../fixtures/top2000.json');
8 |
9 | const div = document.createElement('div');
10 | div.id = 'c1';
11 | document.body.appendChild(div);
12 |
13 | const range = document.createElement('div');
14 | range.id = 'range';
15 | document.body.appendChild(range);
16 |
17 | describe('Test cases', function() {
18 | it('changeData && autoWidth', function() {
19 | // 设置状态量
20 | const ds = new DataSet({
21 | state: {
22 | start: '2004-01-01',
23 | end: '2007-09-24'
24 | }
25 | });
26 | const dv = ds.createView();
27 | dv.source(PekingAQI)
28 | .transform({
29 | type: 'filter',
30 | callback: obj => {
31 | return obj.date <= ds.state.end && obj.date >= ds.state.start;
32 | }
33 | });
34 | const chart = new G2.Chart({
35 | id: 'c1',
36 | forceFit: true,
37 | height: 400,
38 | animate: false
39 | });
40 |
41 | chart.scale({
42 | date: {
43 | type: 'time',
44 | mask: 'MM-DD',
45 | alias: '日期'
46 | }
47 | });
48 |
49 | const view1 = chart.view({
50 | start: {
51 | x: 0,
52 | y: 0
53 | },
54 | end: {
55 | x: 1,
56 | y: 0.45
57 | }
58 | });
59 | view1.source(dv);
60 | view1.line().position('date*aqi');
61 |
62 | const view2 = chart.view({
63 | start: {
64 | x: 0,
65 | y: 0.55
66 | },
67 | end: {
68 | x: 1,
69 | y: 1
70 | }
71 | });
72 | view2.source(dv);
73 | view2.interval().position('date*aqi');
74 | chart.render();
75 |
76 | const slider = new Slider({
77 | container: document.getElementById('range'), // DOM id
78 | width: 'auto',
79 | height: 26,
80 | start: '2004-01-01', // 和状态量对应
81 | end: '2007-09-24',
82 | data: PekingAQI, // 源数据
83 | xAxis: 'date', // 背景图的横轴对应字段,同时为数据筛选的字段
84 | yAxis: 'aqi', // 背景图的纵轴对应字段,同时为数据筛选的字段
85 | backgroundChart: {
86 | type: 'line',
87 | color: 'rgba(0, 0, 0, 0.3)'
88 | },
89 | // fillerStyle: {
90 | // fill: 'rgba(0, 0, 0, 0.2)'
91 | // },
92 | // backgroundStyle: {
93 | // stroke: '#f80',
94 | // fill: '#F3F3F3',
95 | // opacity: 0.2,
96 | // lineWidth: 2
97 | // },
98 | onChange: ({ startText, endText }) => {
99 | ds.setState('start', startText);
100 | ds.setState('end', endText);
101 | }
102 | });
103 | slider.render();
104 |
105 | expect(slider.bgChart).not.to.be.empty;
106 |
107 |
108 | setTimeout(function() {
109 | const newData = PekingAQI.slice(10, 90);
110 | ds.setState('start', '2000-06-29');
111 | ds.setState('end', '2000-08-20');
112 | dv.source(newData);
113 | slider.start = '2000-06-29';
114 | slider.end = '2000-08-20';
115 | slider.changeData(newData);
116 | expect(slider.bgChart).not.to.be.empty;
117 | expect(slider.start).equal('2000-06-29');
118 |
119 | }, 2000);
120 | });
121 |
122 | it.only('basic', function() {
123 | // 设置状态量,时间格式建议转换为时间戳
124 | const ds = new DataSet({
125 | state: {
126 | start: '2015-04-07',
127 | end: '2015-07-28'
128 | }
129 | });
130 | const dv = ds.createView();
131 | dv.source(kData)
132 | .transform({
133 | type: 'filter',
134 | callback: obj => {
135 | const date = obj.time;
136 | return date <= ds.state.end && date >= ds.state.start;
137 | }
138 | })
139 | .transform({
140 | type: 'map',
141 | callback: obj => {
142 | obj.trend = (obj.start <= obj.end) ? '上涨' : '下跌';
143 | obj.range = [ obj.start, obj.end, obj.max, obj.min ];
144 | return obj;
145 | }
146 | });
147 | const chart = new G2.Chart({
148 | container: 'c1',
149 | width: 600,
150 | height: 400,
151 | animate: false,
152 | padding: [ 20, 120, 100 ]
153 | });
154 |
155 | chart.source(dv, {
156 | trend: {
157 | alias: '趋势'
158 | },
159 | time: {
160 | type: 'timeCat',
161 | nice: false,
162 | mask: 'MM-DD',
163 | alias: '时间'
164 | // tickCount: 10
165 | },
166 | volumn: { alias: '成交量' },
167 | start: { alias: '开盘价' },
168 | end: { alias: '收盘价' },
169 | max: { alias: '最高价' },
170 | min: { alias: '最低价' },
171 | range: { alias: '股票价格' }
172 | });
173 | chart.tooltip({
174 | showTitle: false,
175 | itemTpl: '
'
176 | + ''
177 | + '{name}
'
178 | + '开盘价:{start}
'
179 | + '收盘价:{end}
'
180 | + '最高价:{max}
'
181 | + '最低价:{min}
'
182 | + '成交量:{volumn}
'
183 | + ''
184 | });
185 | chart.schema()
186 | .position('time*range')
187 | .color('trend', val => {
188 | if (val === '上涨') {
189 | return '#f04864';
190 | }
191 |
192 | if (val === '下跌') {
193 | return '#2fc25b';
194 | }
195 | })
196 | .shape('candle')
197 | .tooltip('time*start*end*max*min*volumn', (time, start, end, max, min, volumn) => {
198 | return {
199 | name: time,
200 | start,
201 | end,
202 | max,
203 | min,
204 | volumn
205 | };
206 | });
207 | chart.render();
208 |
209 | const slider = new Slider({
210 | container: 'range', // DOM id
211 | width: 600,
212 | height: 26,
213 | padding: [ 20, 120, 100 ],
214 | minSpan: 30 * 24 * 60 * 60 * 1000, // 设置最小值
215 | maxSpan: 120 * 24 * 60 * 60 * 1000, // 设置最大值
216 | start: '2015-04-07', // 和状态量对应
217 | end: '2015-07-28',
218 | data: kData, // 源数据
219 | xAxis: 'time', // 背景图的横轴对应字段,同时为数据筛选的字段
220 | yAxis: 'volumn', // 背景图的纵轴对应字段,同时为数据筛选的字段
221 | scales: {
222 | time: {
223 | type: 'timeCat'
224 | }
225 | },
226 | onChange: ({ startText, endText }) => {
227 | ds.setState('start', startText);
228 | ds.setState('end', endText);
229 | }
230 | });
231 | slider.render();
232 | });
233 |
234 | it('linear scale', function() {
235 | const ds = new DataSet({
236 | state: {
237 | start: 1960,
238 | end: 1984
239 | }
240 | });
241 | const dv = ds.createView('test')
242 | .source(topData)
243 | .transform({
244 | as: [ 'count' ],
245 | groupBy: [ 'release' ],
246 | operations: [ 'count' ],
247 | type: 'aggregate'
248 | })
249 | .transform({
250 | type: 'map',
251 | callback(obj) {
252 | obj.release *= 1;
253 | return obj;
254 | }
255 | })
256 | .transform({
257 | type: 'filter',
258 | callback: obj => {
259 | const date = obj.release;
260 | return date <= ds.state.end && date >= ds.state.start;
261 | }
262 | });
263 |
264 | const chart = new G2.Chart({
265 | container: 'c1',
266 | width: 600,
267 | height: 400,
268 | animate: false,
269 | padding: [ 20, 120, 100 ]
270 | });
271 | chart.source(dv);
272 | chart.scale({
273 | count: {
274 | alias: 'top2000 唱片总量'
275 | },
276 | release: {
277 | tickInterval: 5,
278 | alias: '唱片发行年份'
279 | }
280 | });
281 | chart.interval()
282 | .position('release*count')
283 | .color('#e50000');
284 |
285 | chart.render();
286 |
287 | const slider = new Slider({
288 | container: 'range', // DOM id
289 | width: 600,
290 | height: 26,
291 | padding: [ 20, 120, 100 ],
292 | minSpan: 10, // 设置最小值
293 | maxSpan: 50, // 设置最大值
294 | start: 1960, // 和状态量对应
295 | end: 1984,
296 | data: topData, // 源数据
297 | xAxis: 'release', // 背景图的横轴对应字段,同时为数据筛选的字段
298 | yAxis: 'count', // 背景图的纵轴对应字段,同时为数据筛选的字段
299 | onChange: ({ startText, endText }) => {
300 | ds.setState('start', startText);
301 | ds.setState('end', endText);
302 | }
303 | });
304 | slider.render();
305 |
306 |
307 | });
308 | });
309 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview G2's plugin for datazoom.
3 | * @author sima.zhang
4 | */
5 | const Range = require('./component/range');
6 |
7 | const G2 = window && window.G2;
8 | const { Chart, Util, G, Global } = G2;
9 | const { Canvas } = G;
10 | const { DomUtil } = Util;
11 |
12 | const isNumber = val => typeof val === 'number';
13 |
14 | class Slider {
15 | _initProps() {
16 | this.height = 26;
17 | this.width = 'auto'; // 默认自适应
18 | this.padding = Global.plotCfg.padding;
19 | this.container = null;
20 | this.xAxis = null;
21 | this.yAxis = null;
22 | // 选中区域的样式
23 | this.fillerStyle = {
24 | fill: '#BDCCED',
25 | fillOpacity: 0.3
26 | };
27 | // 滑动条背景样式
28 | this.backgroundStyle = {
29 | stroke: '#CCD6EC',
30 | fill: '#CCD6EC',
31 | fillOpacity: 0.3,
32 | lineWidth: 1
33 | };
34 | this.range = [ 0, 100 ];
35 | this.layout = 'horizontal';
36 | // 文本颜色
37 | this.textStyle = {
38 | fill: '#545454'
39 | };
40 | // 滑块的样式
41 | this.handleStyle = {
42 | img: 'https://gw.alipayobjects.com/zos/rmsportal/QXtfhORGlDuRvLXFzpsQ.png',
43 | width: 5
44 | };
45 | // 背景图表的配置,如果为 false 则表示不渲染
46 | this.backgroundChart = {
47 | type: [ 'area' ], // 图表的类型,可以是字符串也可是是数组
48 | color: '#CCD6EC'
49 | };
50 | }
51 |
52 | constructor(cfg) {
53 | this._initProps();
54 | Util.deepMix(this, cfg);
55 | const container = this.container;
56 | if (!container) {
57 | throw new Error('Please specify the container for the Slider!');
58 | }
59 | if (Util.isString(container)) {
60 | this.domContainer = document.getElementById(container);
61 | } else {
62 | this.domContainer = container;
63 | }
64 |
65 | this.handleStyle = Util.mix({
66 | width: this.height,
67 | height: this.height
68 | }, this.handleStyle);
69 | if (this.width === 'auto') { // 宽度自适应
70 | window.addEventListener('resize', Util.wrapBehavior(this, '_initForceFitEvent'));
71 | }
72 | }
73 |
74 | _initForceFitEvent() {
75 | const timer = setTimeout(Util.wrapBehavior(this, 'forceFit'), 200);
76 | clearTimeout(this.resizeTimer);
77 | this.resizeTimer = timer;
78 | }
79 |
80 | forceFit() {
81 | if (!this || this.destroyed) {
82 | return;
83 | }
84 | const width = DomUtil.getWidth(this.domContainer);
85 | const height = this.height;
86 | if (width !== this.domWidth) {
87 | const canvas = this.canvas;
88 | canvas.changeSize(width, height); // 改变画布尺寸
89 | this.bgChart && this.bgChart.changeWidth(width);
90 | canvas.clear();
91 | this._initWidth();
92 | this._initSlider(); // 初始化滑动条
93 | this._bindEvent();
94 | canvas.draw();
95 | }
96 | }
97 |
98 | _initWidth() {
99 | let width;
100 | if (this.width === 'auto') {
101 | width = DomUtil.getWidth(this.domContainer);
102 | } else {
103 | width = this.width;
104 | }
105 | this.domWidth = width;
106 | const padding = Util.toAllPadding(this.padding);
107 |
108 | if (this.layout === 'horizontal') {
109 | this.plotWidth = width - padding[1] - padding[3];
110 | this.plotPadding = padding[3];
111 | this.plotHeight = this.height;
112 | } else if (this.layout === 'vertical') {
113 | this.plotWidth = this.width;
114 | this.plotHeight = this.height - padding[0] - padding[2];
115 | this.plotPadding = padding[0];
116 | }
117 | }
118 |
119 | render() {
120 | this._initWidth();
121 | this._initCanvas(); // 初始化 canvas
122 | this._initBackground(); // 初始化背景图表
123 | this._initSlider(); // 初始化滑动条
124 | this._bindEvent();
125 | this.canvas.draw();
126 | }
127 |
128 | changeData(data) {
129 | this.data = data;
130 | this.repaint();
131 | }
132 |
133 | destroy() {
134 | clearTimeout(this.resizeTimer);
135 | const rangeElement = this.rangeElement;
136 | rangeElement.off('sliderchange');
137 | this.bgChart && this.bgChart.destroy();
138 | this.canvas.destroy();
139 | const container = this.domContainer;
140 | while (container.hasChildNodes()) {
141 | container.removeChild(container.firstChild);
142 | }
143 | window.removeEventListener('resize', Util.getWrapBehavior(this, '_initForceFitEvent'));
144 | this.destroyed = true;
145 | }
146 |
147 | clear() {
148 | this.canvas.clear();
149 | this.bgChart && this.bgChart.destroy();
150 | this.bgChart = null;
151 | this.scale = null;
152 | this.canvas.draw();
153 | }
154 |
155 | repaint() {
156 | this.clear();
157 | this.render();
158 | }
159 |
160 | _initCanvas() {
161 | const width = this.domWidth;
162 | const height = this.height;
163 | const canvas = new Canvas({
164 | width,
165 | height,
166 | containerDOM: this.domContainer,
167 | capture: false
168 | });
169 | const node = canvas.get('el');
170 | node.style.position = 'absolute';
171 | node.style.top = 0;
172 | node.style.left = 0;
173 | node.style.zIndex = 3;
174 | this.canvas = canvas;
175 | }
176 |
177 | _initBackground() {
178 | const data = this.data;
179 | const xAxis = this.xAxis;
180 | const yAxis = this.yAxis;
181 | const scales = Util.deepMix({
182 | [`${xAxis}`]: {
183 | range: [ 0, 1 ]
184 | }
185 | }, this.scales); // 用户列定义
186 | if (!data) { // 没有数据,则不创建
187 | throw new Error('Please specify the data!');
188 | }
189 | if (!xAxis) {
190 | throw new Error('Please specify the xAxis!');
191 | }
192 | if (!yAxis) {
193 | throw new Error('Please specify the yAxis!');
194 | }
195 |
196 | const backgroundChart = this.backgroundChart;
197 | let type = backgroundChart.type;
198 | const color = backgroundChart.color;
199 | if (!Util.isArray(type)) {
200 | type = [ type ];
201 | }
202 |
203 | const padding = Util.toAllPadding(this.padding);
204 | const bgChart = new Chart({
205 | container: this.container,
206 | width: this.domWidth,
207 | height: this.height,
208 | padding: [ 0, padding[1], 0, padding[3] ],
209 | animate: false
210 | });
211 | bgChart.source(data);
212 | bgChart.scale(scales);
213 | bgChart.axis(false);
214 | bgChart.tooltip(false);
215 | bgChart.legend(false);
216 | Util.each(type, eachType => {
217 | bgChart[eachType]()
218 | .position(xAxis + '*' + yAxis)
219 | .color(color)
220 | .opacity(1);
221 | });
222 | bgChart.render();
223 | this.bgChart = bgChart;
224 | this.scale = this.layout === 'horizontal' ? bgChart.getXScale() : bgChart.getYScales()[0];
225 | if (this.layout === 'vertical') {
226 | bgChart.destroy();
227 | }
228 | }
229 |
230 | _initRange() {
231 | const startRadio = this.startRadio;
232 | const endRadio = this.endRadio;
233 | const start = this.start;
234 | const end = this.end;
235 | const scale = this.scale;
236 | let min = 0;
237 | let max = 1;
238 |
239 | // startRadio 优先级高于 start
240 | if (isNumber(startRadio)) {
241 | min = startRadio;
242 | } else if (start) {
243 | min = scale.scale(scale.translate(start));
244 | }
245 |
246 | // endRadio 优先级高于 end
247 | if (isNumber(endRadio)) {
248 | max = endRadio;
249 | } else if (end) {
250 | max = scale.scale(scale.translate(end));
251 | }
252 |
253 | const { minSpan, maxSpan } = this;
254 | let totalSpan = 0;
255 | if (scale.type === 'time' || scale.type === 'timeCat') { // 时间类型已排序
256 | const values = scale.values;
257 | const firstValue = values[0];
258 | const lastValue = values[values.length - 1];
259 | totalSpan = lastValue - firstValue;
260 | } else if (scale.isLinear) {
261 | totalSpan = scale.max - scale.min;
262 | }
263 |
264 | if (totalSpan && minSpan) {
265 | this.minRange = (minSpan / totalSpan) * 100;
266 | }
267 |
268 | if (totalSpan && maxSpan) {
269 | this.maxRange = (maxSpan / totalSpan) * 100;
270 | }
271 |
272 | const range = [ min * 100, max * 100 ];
273 | this.range = range;
274 | return range;
275 | }
276 |
277 | _getHandleValue(type) {
278 | let value;
279 | const range = this.range;
280 | const min = range[0] / 100;
281 | const max = range[1] / 100;
282 | const scale = this.scale;
283 | if (type === 'min') {
284 | value = this.start ? this.start : scale.invert(min);
285 | } else {
286 | value = this.end ? this.end : scale.invert(max);
287 | }
288 | return value;
289 | }
290 |
291 | _initSlider() {
292 | const canvas = this.canvas;
293 | const range = this._initRange();
294 | const scale = this.scale;
295 | const rangeElement = canvas.addGroup(Range, {
296 | middleAttr: this.fillerStyle,
297 | range,
298 | minRange: this.minRange,
299 | maxRange: this.maxRange,
300 | layout: this.layout,
301 | width: this.plotWidth,
302 | height: this.plotHeight,
303 | backgroundStyle: this.backgroundStyle,
304 | textStyle: this.textStyle,
305 | handleStyle: this.handleStyle,
306 | minText: scale.getText(this._getHandleValue('min')),
307 | maxText: scale.getText(this._getHandleValue('max'))
308 | });
309 | if (this.layout === 'horizontal') {
310 | rangeElement.translate(this.plotPadding, 0);
311 | } else if (this.layout === 'vertical') {
312 | rangeElement.translate(0, this.plotPadding);
313 | }
314 | this.rangeElement = rangeElement;
315 | }
316 |
317 | _bindEvent() {
318 | const self = this;
319 | const rangeElement = self.rangeElement;
320 | rangeElement.on('sliderchange', function(ev) {
321 | const range = ev.range;
322 | const minRatio = range[0] / 100;
323 | const maxRatio = range[1] / 100;
324 | self._updateElement(minRatio, maxRatio);
325 | });
326 | }
327 |
328 | _updateElement(minRatio, maxRatio) {
329 | const scale = this.scale;
330 | const rangeElement = this.rangeElement;
331 | const minTextElement = rangeElement.get('minTextElement');
332 | const maxTextElement = rangeElement.get('maxTextElement');
333 | const min = scale.invert(minRatio);
334 | const max = scale.invert(maxRatio);
335 | const minText = scale.getText(min);
336 | const maxText = scale.getText(max);
337 | minTextElement.attr('text', minText);
338 | maxTextElement.attr('text', maxText);
339 |
340 | this.start = min;
341 | this.end = max;
342 |
343 | if (this.onChange) {
344 | this.onChange({
345 | startText: minText,
346 | endText: maxText,
347 | startValue: min,
348 | endValue: max,
349 | startRadio: minRatio,
350 | endRadio: maxRatio
351 | });
352 | }
353 | }
354 | }
355 |
356 | module.exports = Slider;
357 |
--------------------------------------------------------------------------------
/src/component/range.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview The class of slider
3 | * @author sima.zhang
4 | */
5 | const G2 = window && window.G2;
6 | const { Util, G } = G2;
7 | const { Group } = G;
8 | const { DomUtil } = Util;
9 | const OFFSET = 5;
10 |
11 |
12 | class Range extends Group {
13 | getDefaultCfg() {
14 | return {
15 | /**
16 | * 范围
17 | * @type {Array}
18 | */
19 | range: null,
20 | /**
21 | * 中滑块属性
22 | * @type {ATTRS}
23 | */
24 | middleAttr: null,
25 | /**
26 | * 背景
27 | * @type {G-Element}
28 | */
29 | backgroundElement: null,
30 | /**
31 | * 下滑块
32 | * @type {G-Element}
33 | */
34 | minHandleElement: null,
35 | /**
36 | * 上滑块
37 | * @type {G-Element}
38 | */
39 | maxHandleElement: null,
40 | /**
41 | * 中块
42 | * @type {G-Element}
43 | */
44 | middleHandleElement: null,
45 | /**
46 | * 当前的激活的元素
47 | * @type {G-Element}
48 | */
49 | currentTarget: null,
50 | /**
51 | * 布局方式: horizontal,vertical
52 | * @type {String}
53 | */
54 | layout: 'vertical',
55 | /**
56 | * 宽
57 | * @type {Number}
58 | */
59 | width: null,
60 | /**
61 | * 高
62 | * @type {Number}
63 | */
64 | height: null,
65 | /**
66 | * 当前的PageX
67 | * @type {Number}
68 | */
69 | pageX: null,
70 | /**
71 | * 当前的PageY
72 | * @type {Number}
73 | */
74 | pageY: null
75 | };
76 | }
77 |
78 | _initHandle(type) {
79 | const handle = this.addGroup();
80 | const layout = this.get('layout');
81 | const handleStyle = this.get('handleStyle');
82 | const img = handleStyle.img;
83 | const iconWidth = handleStyle.width;
84 | const iconHeight = handleStyle.height;
85 |
86 | let text;
87 | let handleIcon;
88 | let triggerCursor;
89 |
90 |
91 | if (layout === 'horizontal') {
92 | const iconWidth = handleStyle.width;
93 | triggerCursor = 'ew-resize';
94 | handleIcon = handle.addShape('Image', {
95 | attrs: {
96 | x: -iconWidth / 2,
97 | y: 0,
98 | width: iconWidth,
99 | height: iconHeight,
100 | img,
101 | cursor: triggerCursor
102 | }
103 | });
104 | text = handle.addShape('Text', {
105 | attrs: Util.mix({
106 | x: (type === 'min') ? -(iconWidth / 2 + OFFSET) : iconWidth / 2 + OFFSET,
107 | y: iconHeight / 2,
108 | textAlign: (type === 'min') ? 'end' : 'start',
109 | textBaseline: 'middle',
110 | text: type === 'min' ? this.get('minText') : this.get('maxText'),
111 | cursor: triggerCursor
112 | }, this.get('textStyle'))
113 | });
114 | } else {
115 | triggerCursor = 'ns-resize';
116 | handleIcon = handle.addShape('Image', {
117 | attrs: {
118 | x: 0,
119 | y: -iconHeight / 2,
120 | width: iconWidth,
121 | height: iconHeight,
122 | img,
123 | cursor: triggerCursor
124 | }
125 | });
126 | text = handle.addShape('Text', {
127 | attrs: Util.mix({
128 | x: iconWidth / 2,
129 | y: (type === 'min') ? (iconHeight / 2 + OFFSET) : -(iconHeight / 2 + OFFSET),
130 | textAlign: 'center',
131 | textBaseline: 'middle',
132 | text: type === 'min' ? this.get('minText') : this.get('maxText'),
133 | cursor: triggerCursor
134 | }, this.get('textStyle'))
135 | });
136 | }
137 |
138 | this.set(type + 'TextElement', text);
139 | this.set(type + 'IconElement', handleIcon);
140 | return handle;
141 | }
142 |
143 | _initSliderBackground() {
144 | const backgroundElement = this.addGroup();
145 | backgroundElement.initTransform();
146 | backgroundElement.translate(0, 0);
147 | backgroundElement.addShape('Rect', {
148 | attrs: Util.mix({
149 | x: 0,
150 | y: 0,
151 | width: this.get('width'),
152 | height: this.get('height')
153 | }, this.get('backgroundStyle'))
154 | });
155 | return backgroundElement;
156 | }
157 |
158 | _beforeRenderUI() {
159 | const backgroundElement = this._initSliderBackground();
160 | const minHandleElement = this._initHandle('min');
161 | const maxHandleElement = this._initHandle('max');
162 | const middleHandleElement = this.addShape('rect', {
163 | attrs: this.get('middleAttr')
164 | });
165 |
166 | this.set('middleHandleElement', middleHandleElement);
167 | this.set('minHandleElement', minHandleElement);
168 | this.set('maxHandleElement', maxHandleElement);
169 | this.set('backgroundElement', backgroundElement);
170 | backgroundElement.set('zIndex', 0);
171 | middleHandleElement.set('zIndex', 1);
172 | minHandleElement.set('zIndex', 2);
173 | maxHandleElement.set('zIndex', 2);
174 | middleHandleElement.attr('cursor', 'move');
175 | this.sort();
176 | }
177 |
178 | _renderUI() {
179 | if (this.get('layout') === 'horizontal') {
180 | this._renderHorizontal();
181 | } else {
182 | this._renderVertical();
183 | }
184 | }
185 |
186 | _transform(layout) {
187 | const range = this.get('range');
188 | const minRatio = range[0] / 100;
189 | const maxRatio = range[1] / 100;
190 | const width = this.get('width');
191 | const height = this.get('height');
192 | const minHandleElement = this.get('minHandleElement');
193 | const maxHandleElement = this.get('maxHandleElement');
194 | const middleHandleElement = this.get('middleHandleElement');
195 | if (minHandleElement.resetMatrix) {
196 | minHandleElement.resetMatrix();
197 | maxHandleElement.resetMatrix();
198 | } else {
199 | minHandleElement.initTransform();
200 | maxHandleElement.initTransform();
201 | }
202 | if (layout === 'horizontal') {
203 | middleHandleElement.attr({
204 | x: width * minRatio,
205 | y: 0,
206 | width: (maxRatio - minRatio) * width,
207 | height
208 | });
209 |
210 | minHandleElement.translate(minRatio * width, 0);
211 | maxHandleElement.translate(maxRatio * width, 0);
212 | } else {
213 | middleHandleElement.attr({
214 | x: 0,
215 | y: height * (1 - maxRatio),
216 | width,
217 | height: (maxRatio - minRatio) * height
218 | });
219 | minHandleElement.translate(0, (1 - minRatio) * height);
220 | maxHandleElement.translate(0, (1 - maxRatio) * height);
221 | }
222 | }
223 |
224 | _renderHorizontal() {
225 | this._transform('horizontal');
226 | }
227 |
228 | _renderVertical() {
229 | this._transform('vertical');
230 | }
231 |
232 | _bindUI() {
233 | this.on('mousedown', Util.wrapBehavior(this, '_onMouseDown'));
234 | }
235 |
236 | _isElement(target, name) { // 判断是否是该元素
237 | const element = this.get(name);
238 | if (target === element) {
239 | return true;
240 | }
241 | if (element.isGroup) {
242 | const elementChildren = element.get('children');
243 | return elementChildren.indexOf(target) > -1;
244 | }
245 | return false;
246 | }
247 |
248 | _getRange(diff, range) {
249 | let rst = diff + range;
250 | rst = rst > 100 ? 100 : rst;
251 | rst = rst < 0 ? 0 : rst;
252 | return rst;
253 | }
254 |
255 | _limitRange(diff, limit, range) {
256 | range[0] = this._getRange(diff, range[0]);
257 | range[1] = range[0] + limit;
258 | if (range[1] > 100) {
259 | range[1] = 100;
260 | range[0] = range[1] - limit;
261 | }
262 | }
263 |
264 | _updateStatus(dim, ev) {
265 | const totalLength = dim === 'x' ? this.get('width') : this.get('height');
266 | dim = Util.upperFirst(dim);
267 | const range = this.get('range');
268 | const page = this.get('page' + dim);
269 | const currentTarget = this.get('currentTarget');
270 | const rangeStash = this.get('rangeStash');
271 | const layout = this.get('layout');
272 | const sign = layout === 'vertical' ? -1 : 1;
273 | const currentPage = ev[ 'page' + dim ];
274 | const diffPage = currentPage - page;
275 | const diffRange = (diffPage / totalLength) * 100 * sign;
276 | let diffStashRange;
277 |
278 | const minRange = this.get('minRange');
279 | const maxRange = this.get('maxRange');
280 |
281 | if (range[1] <= range[0]) {
282 | if (this._isElement(currentTarget, 'minHandleElement') || this._isElement(currentTarget, 'maxHandleElement')) {
283 | range[0] = this._getRange(diffRange, range[0]);
284 | range[1] = this._getRange(diffRange, range[0]);
285 | }
286 | } else {
287 | if (this._isElement(currentTarget, 'minHandleElement')) {
288 | range[0] = this._getRange(diffRange, range[0]);
289 | if (minRange) { // 设置了最小范围
290 | if ((range[1] - range[0]) <= minRange) {
291 | this._limitRange(diffRange, minRange, range);
292 | }
293 | }
294 |
295 | if (maxRange) { // 设置了最大范围
296 | if (range[1] - range[0] >= maxRange) {
297 | this._limitRange(diffRange, maxRange, range);
298 | }
299 | }
300 | }
301 | if (this._isElement(currentTarget, 'maxHandleElement')) {
302 | range[1] = this._getRange(diffRange, range[1]);
303 |
304 | if (minRange) { // 设置了最小范围
305 | if ((range[1] - range[0]) <= minRange) {
306 | this._limitRange(diffRange, minRange, range);
307 | }
308 | }
309 |
310 | if (maxRange) { // 设置了最大范围
311 | if (range[1] - range[0] >= maxRange) {
312 | this._limitRange(diffRange, maxRange, range);
313 | }
314 | }
315 | }
316 | }
317 |
318 | if (this._isElement(currentTarget, 'middleHandleElement')) {
319 | diffStashRange = (rangeStash[1] - rangeStash[0]);
320 | this._limitRange(diffRange, diffStashRange, range);
321 | }
322 |
323 | this.emit('sliderchange', {
324 | range
325 | });
326 |
327 | this.set('page' + dim, currentPage);
328 | this._renderUI();
329 | this.get('canvas').draw(); // need delete
330 | return;
331 | }
332 |
333 | _onMouseDown(ev) {
334 | const currentTarget = ev.currentTarget;
335 | const originEvent = ev.event;
336 | const range = this.get('range');
337 | originEvent.stopPropagation();
338 | originEvent.preventDefault();
339 | this.set('pageX', originEvent.pageX);
340 | this.set('pageY', originEvent.pageY);
341 | this.set('currentTarget', currentTarget);
342 | this.set('rangeStash', [ range[0], range[1] ]);
343 | this._bindCanvasEvents();
344 | }
345 |
346 | _bindCanvasEvents() {
347 | const containerDOM = this.get('canvas').get('containerDOM');
348 | this.onMouseMoveListener = DomUtil.addEventListener(containerDOM, 'mousemove', Util.wrapBehavior(this, '_onCanvasMouseMove'));
349 | this.onMouseUpListener = DomUtil.addEventListener(containerDOM, 'mouseup', Util.wrapBehavior(this, '_onCanvasMouseUp'));
350 | // @2018-06-06 by blue.lb 添加mouseleave事件监听,让用户在操作出滑块区域后有一个“正常”的效果,可以正常重新触发滑块的操作流程
351 | this.onMouseLeaveListener = DomUtil.addEventListener(containerDOM, 'mouseleave', Util.wrapBehavior(this, '_onCanvasMouseUp'));
352 | }
353 |
354 | _onCanvasMouseMove(ev) {
355 | const layout = this.get('layout');
356 | if (layout === 'horizontal') {
357 | this._updateStatus('x', ev);
358 | } else {
359 | this._updateStatus('y', ev);
360 | }
361 | }
362 |
363 | _onCanvasMouseUp() {
364 | this._removeDocumentEvents();
365 | }
366 |
367 | _removeDocumentEvents() {
368 | this.onMouseMoveListener.remove();
369 | this.onMouseUpListener.remove();
370 | this.onMouseLeaveListener.remove();
371 | }
372 | }
373 |
374 | module.exports = Range;
375 |
--------------------------------------------------------------------------------
/test/fixtures/candleSticks.json:
--------------------------------------------------------------------------------
1 | [{"time":"2015-11-19","start":8.18,"max":8.33,"min":7.98,"end":8.32,"volumn":1810,"money":14723.56},{"time":"2015-11-18","start":8.37,"max":8.6,"min":8.03,"end":8.09,"volumn":2790.37,"money":23309.19},{"time":"2015-11-17","start":8.7,"max":8.78,"min":8.32,"end":8.37,"volumn":3729.04,"money":31709.71},{"time":"2015-11-16","start":8.18,"max":8.69,"min":8.05,"end":8.62,"volumn":3095.44,"money":26100.69},{"time":"2015-11-13","start":8.01,"max":8.75,"min":7.97,"end":8.41,"volumn":5815.58,"money":48562.37},{"time":"2015-11-12","start":7.76,"max":8.18,"min":7.61,"end":8.15,"volumn":4742.6,"money":37565.36},{"time":"2015-11-11","start":7.55,"max":7.81,"min":7.49,"end":7.8,"volumn":3133.82,"money":24065.42},{"time":"2015-11-10","start":7.5,"max":7.68,"min":7.44,"end":7.57,"volumn":2670.35,"money":20210.58},{"time":"2015-11-09","start":7.65,"max":7.66,"min":7.3,"end":7.58,"volumn":2841.79,"money":21344.36},{"time":"2015-11-06","start":7.52,"max":7.71,"min":7.48,"end":7.64,"volumn":2725.44,"money":20721.51},{"time":"2015-11-05","start":7.48,"max":7.57,"min":7.29,"end":7.48,"volumn":3520.85,"money":26140.83},{"time":"2015-11-04","start":7.01,"max":7.5,"min":7.01,"end":7.46,"volumn":3591.47,"money":26285.52},{"time":"2015-11-03","start":7.1,"max":7.17,"min":6.82,"end":7,"volumn":2029.21,"money":14202.33},{"time":"2015-11-02","start":7.09,"max":7.44,"min":6.93,"end":7.17,"volumn":3191.31,"money":23205.11},{"time":"2015-10-30","start":6.98,"max":7.27,"min":6.84,"end":7.18,"volumn":3522.61,"money":25083.44},{"time":"2015-10-29","start":6.94,"max":7.2,"min":6.8,"end":7.05,"volumn":2752.27,"money":19328.44},{"time":"2015-10-28","start":7.01,"max":7.14,"min":6.8,"end":6.85,"volumn":2311.11,"money":16137.32},{"time":"2015-10-27","start":6.91,"max":7.31,"min":6.48,"end":7.18,"volumn":3172.9,"money":21827.3},{"time":"2015-10-26","start":6.9,"max":7.08,"min":6.87,"end":6.95,"volumn":2769.31,"money":19337.44},{"time":"2015-10-23","start":6.71,"max":6.85,"min":6.58,"end":6.79,"volumn":2483.18,"money":16714.31},{"time":"2015-10-22","start":6.38,"max":6.67,"min":6.34,"end":6.65,"volumn":2225.88,"money":14465.56},{"time":"2015-10-21","start":7.08,"max":7.1,"min":6.41,"end":6.41,"volumn":2891.47,"money":19585.98},{"time":"2015-10-20","start":6.88,"max":7.19,"min":6.85,"end":7.12,"volumn":2389.62,"money":16813.58},{"time":"2015-10-19","start":7.1,"max":7.14,"min":6.8,"end":6.94,"volumn":2786.61,"money":19474.72},{"time":"2015-10-16","start":6.92,"max":7.38,"min":6.73,"end":7.15,"volumn":3289.27,"money":22963.97},{"time":"2015-10-15","start":6.63,"max":6.9,"min":6.6,"end":6.89,"volumn":2440.37,"money":16575.84},{"time":"2015-10-14","start":6.7,"max":6.99,"min":6.61,"end":6.66,"volumn":2496.38,"money":16858.28},{"time":"2015-10-13","start":6.55,"max":6.81,"min":6.55,"end":6.75,"volumn":2299.83,"money":15338.24},{"time":"2015-10-12","start":6.29,"max":6.89,"min":6.29,"end":6.69,"volumn":3147.58,"money":20738.35},{"time":"2015-10-09","start":6.1,"max":6.44,"min":6.08,"end":6.34,"volumn":2664.04,"money":16811.14},{"time":"2015-10-08","start":6.11,"max":6.28,"min":6,"end":6.12,"volumn":2197.23,"money":13440.92},{"time":"2015-09-30","start":5.93,"max":6.12,"min":5.81,"end":5.83,"volumn":1459.64,"money":8659.52},{"time":"2015-09-29","start":5.96,"max":5.98,"min":5.73,"end":5.83,"volumn":1047.42,"money":6132.72},{"time":"2015-09-28","start":5.86,"max":6.13,"min":5.85,"end":6.07,"volumn":952.45,"money":5717.33},{"time":"2015-09-25","start":6.23,"max":6.28,"min":5.85,"end":5.96,"volumn":1395.27,"money":8465.95},{"time":"2015-09-24","start":6.16,"max":6.32,"min":6.1,"end":6.27,"volumn":1434.38,"money":8920.88},{"time":"2015-09-23","start":6.18,"max":6.32,"min":6.02,"end":6.12,"volumn":1558.54,"money":9631.38},{"time":"2015-09-22","start":6.35,"max":6.4,"min":6.15,"end":6.25,"volumn":1893.83,"money":11901.51},{"time":"2015-09-21","start":5.83,"max":6.32,"min":5.83,"end":6.31,"volumn":1748.35,"money":10807.66},{"time":"2015-09-18","start":6,"max":6.1,"min":5.81,"end":6.02,"volumn":1507.09,"money":8999.44},{"time":"2015-09-17","start":6.1,"max":6.34,"min":5.8,"end":5.83,"volumn":2135.64,"money":13039.56},{"time":"2015-09-16","start":5.58,"max":6.07,"min":5.4,"end":6.07,"volumn":1758.57,"money":10132.25},{"time":"2015-09-15","start":5.81,"max":6.09,"min":5.52,"end":5.52,"volumn":1617.12,"money":9248.69},{"time":"2015-09-14","start":6.98,"max":7.06,"min":6.13,"end":6.13,"volumn":1982.9,"money":12989.01},{"time":"2015-09-11","start":6.87,"max":7.01,"min":6.68,"end":6.81,"volumn":1371.77,"money":9370.49},{"time":"2015-09-10","start":6.7,"max":7.17,"min":6.65,"end":6.86,"volumn":2181.33,"money":15169.4},{"time":"2015-09-09","start":6.76,"max":7.03,"min":6.65,"end":6.93,"volumn":2105.28,"money":14426.76},{"time":"2015-09-08","start":6.26,"max":6.7,"min":6.01,"end":6.64,"volumn":1506.53,"money":9556.54},{"time":"2015-09-07","start":6.19,"max":6.45,"min":6.09,"end":6.2,"volumn":1605.85,"money":10091.26},{"time":"2015-09-02","start":6.2,"max":6.84,"min":5.98,"end":5.99,"volumn":1934.95,"money":12225.68},{"time":"2015-09-01","start":7.22,"max":7.28,"min":6.64,"end":6.64,"volumn":2645.87,"money":18017.91},{"time":"2015-08-31","start":7.83,"max":8,"min":7.35,"end":7.38,"volumn":2984.03,"money":23031.24},{"time":"2015-08-28","start":7.3,"max":7.77,"min":7.1,"end":7.77,"volumn":3092.6,"money":23121.49},{"time":"2015-08-27","start":6.75,"max":7.1,"min":6.43,"end":7.06,"volumn":2903.76,"money":19848.87},{"time":"2015-08-26","start":7.13,"max":7.43,"min":6.42,"end":6.58,"volumn":4212.04,"money":29069.25},{"time":"2015-08-25","start":7.13,"max":7.29,"min":7.13,"end":7.13,"volumn":1838.59,"money":13140.8},{"time":"2015-08-24","start":8.4,"max":8.4,"min":7.92,"end":7.92,"volumn":1766.39,"money":14168.86},{"time":"2015-08-21","start":9,"max":9.61,"min":8.53,"end":8.8,"volumn":3388.27,"money":30952.11},{"time":"2015-08-20","start":10.02,"max":10.24,"min":9.32,"end":9.33,"volumn":3902.17,"money":38391.68},{"time":"2015-08-19","start":9.3,"max":10.59,"min":9.01,"end":10.35,"volumn":5568.96,"money":53928.79},{"time":"2015-08-18","start":11.18,"max":11.5,"min":10,"end":10,"volumn":7332.7,"money":78439.61},{"time":"2015-08-17","start":10.2,"max":11.11,"min":9.9,"end":11.11,"volumn":8121.86,"money":86298.92},{"time":"2015-08-14","start":9.58,"max":10.37,"min":9.37,"end":10.1,"volumn":6001.61,"money":58425.11},{"time":"2015-08-13","start":9.14,"max":9.5,"min":8.91,"end":9.49,"volumn":3854.19,"money":35696.27},{"time":"2015-08-12","start":9.09,"max":9.68,"min":8.98,"end":9.29,"volumn":4238.57,"money":39909.3},{"time":"2015-08-11","start":9.23,"max":9.47,"min":9,"end":9.15,"volumn":4294.85,"money":39674.71},{"time":"2015-08-10","start":8.9,"max":9.38,"min":8.76,"end":9.2,"volumn":4013.11,"money":36287.89},{"time":"2015-08-07","start":8.36,"max":8.8,"min":8.31,"end":8.7,"volumn":3092.66,"money":26336.76},{"time":"2015-08-06","start":8.03,"max":8.42,"min":7.95,"end":8.25,"volumn":2072.21,"money":17060.11},{"time":"2015-08-05","start":8.5,"max":8.69,"min":8.08,"end":8.28,"volumn":3533.94,"money":29796.96},{"time":"2015-08-04","start":7.65,"max":8.39,"min":7.65,"end":8.39,"volumn":3067.22,"money":24773.88},{"time":"2015-08-03","start":8.15,"max":8.4,"min":7.6,"end":7.63,"volumn":3098.33,"money":24382.99},{"time":"2015-07-31","start":8.7,"max":9.01,"min":8.25,"end":8.44,"volumn":3500.61,"money":30289.83},{"time":"2015-07-30","start":8.92,"max":9.65,"min":8.7,"end":8.97,"volumn":6022.8,"money":55624.85},{"time":"2015-07-29","start":8.35,"max":8.91,"min":7.78,"end":8.88,"volumn":4177.18,"money":34893.2},{"time":"2015-07-28","start":8,"max":9,"min":7.92,"end":8.1,"volumn":5114.55,"money":42095.99},{"time":"2015-07-27","start":9.4,"max":9.99,"min":8.8,"end":8.8,"volumn":6001.51,"money":56962.25},{"time":"2015-07-24","start":9.27,"max":10.28,"min":9.11,"end":9.78,"volumn":8446.76,"money":81991.19},{"time":"2015-07-23","start":9,"max":9.78,"min":8.74,"end":9.46,"volumn":8496.1,"money":77551.01},{"time":"2015-07-22","start":8.08,"max":8.97,"min":8.05,"end":8.97,"volumn":8676.75,"money":75751.1},{"time":"2015-07-21","start":7.8,"max":8.33,"min":7.65,"end":8.15,"volumn":4631.15,"money":37450.78},{"time":"2015-07-20","start":7.72,"max":8.27,"min":7.63,"end":8.05,"volumn":6428.2,"money":51127.98},{"time":"2015-07-17","start":6.94,"max":7.73,"min":6.94,"end":7.73,"volumn":4835.44,"money":36666.58},{"time":"2015-07-16","start":6.53,"max":7.48,"min":6.42,"end":7.03,"volumn":3605.77,"money":25031.15},{"time":"2015-07-15","start":7.57,"max":7.83,"min":7.13,"end":7.13,"volumn":2729.59,"money":20041.75},{"time":"2015-07-14","start":8.2,"max":8.7,"min":7.66,"end":7.92,"volumn":7073.81,"money":58131.16},{"time":"2015-07-13","start":7.5,"max":8.1,"min":7.5,"end":8.1,"volumn":4573.92,"money":36083.69},{"time":"2015-07-10","start":6.9,"max":7.36,"min":6.88,"end":7.36,"volumn":4183.37,"money":30411.19},{"time":"2015-07-09","start":5.47,"max":6.69,"min":5.47,"end":6.69,"volumn":6661.78,"money":40398.96},{"time":"2015-07-08","start":6.08,"max":6.08,"min":6.08,"end":6.08,"volumn":158.16,"money":961.61},{"time":"2015-07-07","start":6.77,"max":6.88,"min":6.75,"end":6.75,"volumn":383.45,"money":2590.85},{"time":"2015-07-06","start":9.1,"max":9.1,"min":7.5,"end":7.5,"volumn":2968.98,"money":24002.6},{"time":"2015-07-03","start":8.38,"max":8.87,"min":8.33,"end":8.33,"volumn":2641.73,"money":22223.44},{"time":"2015-07-02","start":10.38,"max":10.38,"min":9.26,"end":9.26,"volumn":2611.06,"money":24620.81},{"time":"2015-07-01","start":11.31,"max":11.61,"min":10.29,"end":10.29,"volumn":3401.45,"money":37212.87},{"time":"2015-06-30","start":10.08,"max":11.52,"min":10.01,"end":11.43,"volumn":4205.99,"money":45381.06},{"time":"2015-06-29","start":12.96,"max":12.96,"min":11.12,"end":11.12,"volumn":3745.68,"money":44252.47},{"time":"2015-06-26","start":13.15,"max":13.41,"min":12.36,"end":12.36,"volumn":3675.91,"money":46759.29},{"time":"2015-06-25","start":13.71,"max":14.46,"min":13.3,"end":13.73,"volumn":4907.6,"money":68290.5},{"time":"2015-06-24","start":13.35,"max":13.85,"min":12.9,"end":13.71,"volumn":3656.8,"money":49219.92},{"time":"2015-06-23","start":13.26,"max":13.64,"min":12.26,"end":13.2,"volumn":3566.35,"money":45904.78},{"time":"2015-06-19","start":14.45,"max":14.97,"min":13.62,"end":13.62,"volumn":3621.43,"money":51108.31},{"time":"2015-06-18","start":14.5,"max":16,"min":14.3,"end":15.13,"volumn":5046.59,"money":77208.53},{"time":"2015-06-17","start":14.46,"max":15,"min":14,"end":14.6,"volumn":3483.7,"money":50495.84},{"time":"2015-06-16","start":14,"max":15.1,"min":13.42,"end":14.8,"volumn":4844.74,"money":68953.77},{"time":"2015-06-15","start":14.5,"max":15.09,"min":14.1,"end":14.39,"volumn":4008.2,"money":58703.24},{"time":"2015-06-12","start":14.07,"max":14.97,"min":14,"end":14.37,"volumn":5399.47,"money":78592.45},{"time":"2015-06-11","start":13.4,"max":14.5,"min":13.19,"end":14.13,"volumn":5472.93,"money":76037.31},{"time":"2015-06-10","start":12.95,"max":13.47,"min":12.71,"end":13.37,"volumn":4087.74,"money":53951.64},{"time":"2015-06-09","start":13.46,"max":13.46,"min":12.85,"end":13.12,"volumn":4371.67,"money":57352.21},{"time":"2015-06-08","start":12.88,"max":13.69,"min":12.59,"end":13.61,"volumn":7406.58,"money":98236.3},{"time":"2015-06-05","start":12.38,"max":12.94,"min":12.24,"end":12.77,"volumn":5386.66,"money":68208.51},{"time":"2015-06-04","start":12.55,"max":12.81,"min":11.29,"end":12.31,"volumn":3905.22,"money":47415.64},{"time":"2015-06-03","start":13,"max":13.15,"min":12.2,"end":12.54,"volumn":4559.61,"money":57928.58},{"time":"2015-06-02","start":11.84,"max":12.77,"min":11.48,"end":12.73,"volumn":4405.17,"money":52747.92},{"time":"2015-06-01","start":11.29,"max":11.8,"min":11,"end":11.74,"volumn":3308.94,"money":38060.2},{"time":"2015-05-29","start":11.3,"max":11.65,"min":10.31,"end":11.11,"volumn":3434.12,"money":38143.88},{"time":"2015-05-28","start":12.79,"max":12.99,"min":11.39,"end":11.4,"volumn":4979.63,"money":61398.36},{"time":"2015-05-27","start":12.89,"max":13.18,"min":12.5,"end":12.66,"volumn":4886.86,"money":62349.63},{"time":"2015-05-26","start":12.4,"max":13.08,"min":11.96,"end":12.92,"volumn":5838.51,"money":73409.96},{"time":"2015-05-25","start":11.7,"max":12.87,"min":11.61,"end":12.3,"volumn":6405.2,"money":78937.32},{"time":"2015-05-22","start":11.39,"max":11.89,"min":11.18,"end":11.71,"volumn":5519.87,"money":63515.93},{"time":"2015-05-21","start":11.27,"max":11.35,"min":11.05,"end":11.33,"volumn":4132.8,"money":46318.65},{"time":"2015-05-20","start":11.23,"max":11.48,"min":10.81,"end":11.32,"volumn":6859.76,"money":76273.65},{"time":"2015-05-19","start":11.5,"max":11.78,"min":11,"end":11.33,"volumn":6864.05,"money":77731.34},{"time":"2015-05-18","start":11.68,"max":12.25,"min":11.45,"end":12.15,"volumn":4236.5,"money":50728.6},{"time":"2015-05-15","start":11.19,"max":12.28,"min":10.8,"end":11.69,"volumn":5515.66,"money":64496.32},{"time":"2015-05-14","start":10.18,"max":11.19,"min":10.11,"end":11.19,"volumn":4181.77,"money":45399.19},{"time":"2015-05-13","start":10.2,"max":10.32,"min":10,"end":10.17,"volumn":2247.39,"money":22781.23},{"time":"2015-05-12","start":10.3,"max":10.36,"min":10.01,"end":10.28,"volumn":2010.65,"money":20480.63},{"time":"2015-05-11","start":9.98,"max":10.36,"min":9.89,"end":10.3,"volumn":2101.26,"money":21367.53},{"time":"2015-05-08","start":9.82,"max":10.08,"min":9.65,"end":9.94,"volumn":1609.43,"money":15845.56},{"time":"2015-05-07","start":9.62,"max":9.84,"min":9.45,"end":9.6,"volumn":1270.86,"money":12241.17},{"time":"2015-05-06","start":10.18,"max":10.25,"min":9.6,"end":9.66,"volumn":1754.7,"money":17347.05},{"time":"2015-05-05","start":10.68,"max":10.68,"min":10,"end":10.02,"volumn":1903.5,"money":19598.64},{"time":"2015-05-04","start":10.61,"max":10.84,"min":10.55,"end":10.72,"volumn":1554.93,"money":16624.43},{"time":"2015-04-30","start":10.4,"max":11.05,"min":10.4,"end":10.63,"volumn":2169.06,"money":23333.06},{"time":"2015-04-29","start":10.31,"max":10.64,"min":10.25,"end":10.4,"volumn":1614.77,"money":16910.96},{"time":"2015-04-28","start":11.07,"max":11.25,"min":10.46,"end":10.49,"volumn":2552.21,"money":27515.88},{"time":"2015-04-27","start":10.6,"max":11.67,"min":10.6,"end":11.06,"volumn":4216.46,"money":47534.53},{"time":"2015-04-24","start":10.5,"max":10.85,"min":10.25,"end":10.61,"volumn":2326.42,"money":24599.63},{"time":"2015-04-23","start":10.26,"max":10.93,"min":10.11,"end":10.7,"volumn":3767.77,"money":39643.72},{"time":"2015-04-22","start":10.22,"max":10.42,"min":10.08,"end":10.23,"volumn":2868.77,"money":29316.49},{"time":"2015-04-21","start":9.56,"max":10.2,"min":9.4,"end":10.19,"volumn":3493.61,"money":34865.01},{"time":"2015-04-20","start":9.71,"max":9.99,"min":9.42,"end":9.6,"volumn":2462.09,"money":23769.5},{"time":"2015-04-17","start":9.79,"max":10.09,"min":9.16,"end":9.82,"volumn":4473.33,"money":43367.29},{"time":"2015-04-16","start":9.36,"max":10.04,"min":8.9,"end":9.66,"volumn":2851.79,"money":27210.03},{"time":"2015-04-15","start":10.03,"max":10.28,"min":9.37,"end":9.43,"volumn":3138.11,"money":30713.13},{"time":"2015-04-14","start":10.33,"max":10.33,"min":9.98,"end":10.03,"volumn":2951.59,"money":29803.4},{"time":"2015-04-13","start":10.3,"max":10.63,"min":10.2,"end":10.33,"volumn":3196.99,"money":33351.76},{"time":"2015-04-10","start":10.25,"max":10.5,"min":10,"end":10.28,"volumn":2565.64,"money":26337.81},{"time":"2015-04-09","start":9.78,"max":10.48,"min":9.58,"end":10.22,"volumn":4316.86,"money":43647.33},{"time":"2015-04-08","start":9.46,"max":9.86,"min":9.02,"end":9.78,"volumn":3683.43,"money":34664.66},{"time":"2015-04-07","start":9.53,"max":9.87,"min":9.38,"end":9.44,"volumn":3874.06,"money":37076.79},{"time":"2015-04-03","start":8.6,"max":9.48,"min":8.4,"end":9.48,"volumn":3760.78,"money":34361.28},{"time":"2015-04-02","start":8.45,"max":8.74,"min":8.18,"end":8.62,"volumn":3076.83,"money":26112.98},{"time":"2015-04-01","start":8.16,"max":8.61,"min":8.06,"end":8.45,"volumn":2396.89,"money":20000.88},{"time":"2015-03-31","start":8.18,"max":8.5,"min":8.13,"end":8.16,"volumn":1938,"money":15989.33},{"time":"2015-03-30","start":8.2,"max":8.53,"min":8.11,"end":8.26,"volumn":2820.79,"money":23532.99},{"time":"2015-03-27","start":8.4,"max":8.4,"min":8.01,"end":8.28,"volumn":4634.57,"money":38032.68},{"time":"2015-03-26","start":7.39,"max":8.12,"min":7.32,"end":8.12,"volumn":4209.29,"money":33643.03},{"time":"2015-03-25","start":7.36,"max":7.6,"min":7.2,"end":7.38,"volumn":1845.49,"money":13550.21},{"time":"2015-03-24","start":7.62,"max":7.62,"min":7.2,"end":7.35,"volumn":2264.5,"money":16699.5},{"time":"2015-03-23","start":7.54,"max":7.68,"min":7.46,"end":7.59,"volumn":1834.28,"money":13855.41},{"time":"2015-03-20","start":7.33,"max":7.65,"min":7.25,"end":7.55,"volumn":2470.71,"money":18588.13},{"time":"2015-03-19","start":7.38,"max":7.66,"min":7.26,"end":7.37,"volumn":2450.54,"money":18247.82},{"time":"2015-03-18","start":7.12,"max":7.46,"min":7.1,"end":7.37,"volumn":2854.4,"money":20828.88},{"time":"2015-03-17","start":6.95,"max":7.13,"min":6.87,"end":7.09,"volumn":2457.13,"money":17162.55},{"time":"2015-03-16","start":6.8,"max":7.06,"min":6.79,"end":6.95,"volumn":1858.78,"money":12924.21},{"time":"2015-03-13","start":6.85,"max":6.93,"min":6.69,"end":6.79,"volumn":1167.06,"money":7909.64},{"time":"2015-03-12","start":6.84,"max":7.06,"min":6.71,"end":6.85,"volumn":2152.85,"money":14835.41},{"time":"2015-03-11","start":6.98,"max":7.04,"min":6.77,"end":6.84,"volumn":1445.77,"money":9886.53},{"time":"2015-03-10","start":6.73,"max":6.99,"min":6.7,"end":6.97,"volumn":1999.93,"money":13770.37},{"time":"2015-03-09","start":6.59,"max":6.88,"min":6.4,"end":6.72,"volumn":2243.1,"money":14951.1},{"time":"2015-03-06","start":6.47,"max":6.6,"min":6.35,"end":6.5,"volumn":1270.49,"money":8229.96},{"time":"2015-03-05","start":6.43,"max":6.54,"min":6.34,"end":6.47,"volumn":1363.09,"money":8789.45},{"time":"2015-03-04","start":6.35,"max":6.45,"min":6.32,"end":6.41,"volumn":1295.42,"money":8265.63},{"time":"2015-03-03","start":6.16,"max":6.47,"min":6.07,"end":6.42,"volumn":2266.82,"money":14214.79},{"time":"2015-03-02","start":6.22,"max":6.25,"min":6.07,"end":6.17,"volumn":1277.88,"money":7850.34},{"time":"2015-02-27","start":6.16,"max":6.33,"min":6.15,"end":6.19,"volumn":908.98,"money":5663.74},{"time":"2015-02-26","start":6.12,"max":6.18,"min":6.1,"end":6.16,"volumn":703.72,"money":4328.56},{"time":"2015-02-25","start":6.09,"max":6.18,"min":6.03,"end":6.12,"volumn":766.56,"money":4678.73},{"time":"2015-02-17","start":6.11,"max":6.15,"min":6.06,"end":6.08,"volumn":766.73,"money":4677.31},{"time":"2015-02-16","start":6.03,"max":6.14,"min":6.01,"end":6.11,"volumn":814.71,"money":4948.33},{"time":"2015-02-13","start":5.98,"max":6.34,"min":5.93,"end":6.08,"volumn":1992.56,"money":12135.01},{"time":"2015-02-12","start":5.72,"max":6.1,"min":5.66,"end":6.01,"volumn":2572.38,"money":15312.73},{"time":"2015-02-11","start":5.69,"max":5.77,"min":5.67,"end":5.72,"volumn":602.66,"money":3443.99},{"time":"2015-02-10","start":5.46,"max":5.75,"min":5.43,"end":5.73,"volumn":1298.63,"money":7307.42},{"time":"2015-02-09","start":5.59,"max":5.59,"min":5.47,"end":5.48,"volumn":435.98,"money":2410.09},{"time":"2015-02-06","start":5.5,"max":5.62,"min":5.48,"end":5.61,"volumn":630.6,"money":3490.13},{"time":"2015-02-05","start":5.58,"max":5.59,"min":5.47,"end":5.48,"volumn":636.7,"money":3521.89},{"time":"2015-02-04","start":5.63,"max":5.67,"min":5.52,"end":5.52,"volumn":635.38,"money":3548.96},{"time":"2015-02-03","start":5.63,"max":5.67,"min":5.56,"end":5.65,"volumn":434.34,"money":2439.08},{"time":"2015-02-02","start":5.55,"max":5.65,"min":5.51,"end":5.61,"volumn":338.71,"money":1896.01},{"time":"2015-01-30","start":5.78,"max":5.85,"min":5.6,"end":5.65,"volumn":574.74,"money":3270.25},{"time":"2015-01-29","start":5.8,"max":5.87,"min":5.74,"end":5.78,"volumn":605.55,"money":3516.14},{"time":"2015-01-28","start":5.89,"max":5.95,"min":5.82,"end":5.85,"volumn":653.47,"money":3846.52},{"time":"2015-01-27","start":5.72,"max":5.94,"min":5.7,"end":5.89,"volumn":1398.84,"money":8194.18},{"time":"2015-01-26","start":5.65,"max":5.73,"min":5.58,"end":5.72,"volumn":930.19,"money":5247.01},{"time":"2015-01-23","start":5.68,"max":5.72,"min":5.6,"end":5.62,"volumn":758.13,"money":4284.8},{"time":"2015-01-22","start":5.49,"max":5.78,"min":5.41,"end":5.71,"volumn":1139.94,"money":6386.11},{"time":"2015-01-21","start":5.36,"max":5.58,"min":5.33,"end":5.55,"volumn":701.11,"money":3840.84},{"time":"2015-01-20","start":5.23,"max":5.35,"min":5.22,"end":5.33,"volumn":817.97,"money":4326.47},{"time":"2015-01-19","start":5.6,"max":5.67,"min":5.12,"end":5.16,"volumn":1248.82,"money":6669.96},{"time":"2015-01-16","start":5.67,"max":5.73,"min":5.66,"end":5.69,"volumn":399.54,"money":2274.94},{"time":"2015-01-15","start":5.6,"max":5.67,"min":5.57,"end":5.67,"volumn":361.28,"money":2031.66},{"time":"2015-01-14","start":5.62,"max":5.69,"min":5.61,"end":5.62,"volumn":321.27,"money":1812.93},{"time":"2015-01-13","start":5.64,"max":5.71,"min":5.58,"end":5.65,"volumn":375.35,"money":2120.87},{"time":"2015-01-12","start":5.79,"max":5.79,"min":5.58,"end":5.6,"volumn":516.19,"money":2921.05},{"time":"2015-01-09","start":5.95,"max":5.97,"min":5.8,"end":5.82,"volumn":701.39,"money":4123.5},{"time":"2015-01-08","start":5.95,"max":6.06,"min":5.91,"end":5.97,"volumn":676.75,"money":4056.12},{"time":"2015-01-07","start":6,"max":6.04,"min":5.92,"end":5.96,"volumn":546.93,"money":3267.16},{"time":"2015-01-06","start":5.89,"max":6.09,"min":5.84,"end":6.07,"volumn":1169.3,"money":6980.48},{"time":"2015-01-05","start":5.89,"max":6,"min":5.75,"end":5.94,"volumn":806.1,"money":4751.15}]
2 |
--------------------------------------------------------------------------------