You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2018/06/25 15:38:40 UTC
[incubator-echarts] 03/03: Support event query on components, series,
data items. Support event on element of custom series.
This is an automated email from the ASF dual-hosted git repository.
sushuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit fd064123626c97b36cbd6da1b5fc73385c280abd
Author: sushuang <su...@gmail.com>
AuthorDate: Mon Jun 25 23:38:00 2018 +0800
Support event query on components, series, data items.
Support event on element of custom series.
---
src/chart/custom.js | 22 +++++-
src/echarts.js | 129 +++++++++++++++++++++++++++++-
src/view/Chart.js | 11 ++-
src/view/Component.js | 11 ++-
test/custom-feature.html | 129 ++++++++++++++++++++++++++++++
test/ec-event.html | 202 +++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 499 insertions(+), 5 deletions(-)
diff --git a/src/chart/custom.js b/src/chart/custom.js
index ae15147..056ae8a 100644
--- a/src/chart/custom.js
+++ b/src/chart/custom.js
@@ -169,7 +169,27 @@ echarts.extendChartView({
/**
* @override
*/
- dispose: zrUtil.noop
+ dispose: zrUtil.noop,
+
+ /**
+ * @override
+ */
+ filterForExposedEvent: function (eventType, query, targetEl, packedEvent) {
+ var targetName = query.target;
+ if (targetName == null || targetEl.name === targetName) {
+ return true;
+ }
+
+ // Enable to give a name on a group made by `renderItem`, and listen
+ // events that triggerd by its descendents.
+ while ((targetEl = targetEl.parent) && targetEl !== this.group) {
+ if (targetEl.name === targetName) {
+ return true;
+ }
+ }
+
+ return false;
+ }
});
diff --git a/src/echarts.js b/src/echarts.js
index dec7148..8283d52 100644
--- a/src/echarts.js
+++ b/src/echarts.js
@@ -222,7 +222,7 @@ function ECharts(dom, theme, opts) {
*/
this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs);
- Eventful.call(this);
+ Eventful.call(this, this._ecEventProcessor = makeEventProcessor(this));
/**
* @type {module:echarts~MessageCenter}
@@ -1510,9 +1510,10 @@ echartsProto._initEvents = function () {
var ecModel = this.getModel();
var el = e.target;
var params;
+ var isGlobalOut = eveName === 'globalout';
// no e.target when 'globalout'.
- if (eveName === 'globalout') {
+ if (isGlobalOut) {
params = {};
}
else if (el && el.dataIndex != null) {
@@ -1525,8 +1526,30 @@ echartsProto._initEvents = function () {
}
if (params) {
+ var componentType = params.componentType;
+ var componentIndex = params[componentType + 'Index'];
+ var model = componentType && componentIndex != null
+ && ecModel.getComponent(componentType, componentIndex);
+ var view = model && this[
+ model.mainType === 'series' ? '_chartsMap' : '_componentsMap'
+ ][model.__viewId];
+
+ if (__DEV__) {
+ // `event.componentType` and `event[componentTpype + 'Index']` must not
+ // be missed, otherwise there is no way to distinguish source component.
+ // See `dataFormat.getDataParams`.
+ zrUtil.assert(isGlobalOut || (model && view));
+ }
+
params.event = e;
params.type = eveName;
+
+ var ecEventProcessor = this._ecEventProcessor;
+ ecEventProcessor.targetEl = el;
+ ecEventProcessor.packedEvent = params;
+ ecEventProcessor.model = model;
+ ecEventProcessor.view = view;
+
this.trigger(eveName, params);
}
@@ -1668,6 +1691,108 @@ function createExtensionAPI(ecInstance) {
}
/**
+ * Usage of query:
+ * `chart.on('click', query, handler);`
+ * The `query` can be:
+ * + The component type query string, only `mainType` or `mainType.subType`,
+ * like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.
+ * + The component query object, like:
+ * `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,
+ * `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.
+ * + The element query object, like:
+ * `{targetName: 'some'}` (only available in custom series).
+ *
+ * Caveat: If a prop in the `query` object is `null/undefined`, it is the
+ * same as there is no such prop in the `query` object.
+ */
+function makeEventProcessor(ecIns) {
+ return {
+
+ normalizeQuery: function (query) {
+ var cptQuery = {};
+ var dataQuery = {};
+ var otherQuery = {};
+
+ // `query` is `mainType` or `mainType.subType` of component.
+ if (zrUtil.isString(query)) {
+ var condCptType = parseClassType(query);
+ // `.main` and `.sub` may be ''.
+ cptQuery.mainType = condCptType.main || null;
+ cptQuery.subType = condCptType.sub || null;
+ }
+ // `query` is an object, convert to {mainType, index, name, id}.
+ else {
+ // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,
+ // can not be used in `compomentModel.filterForExposedEvent`.
+ var suffixes = ['Index', 'Name', 'Id'];
+ var dataKeys = {name: 1, dataIndex: 1, dataType: 1};
+ zrUtil.each(query, function (val, key) {
+ var reserved;
+ for (var i = 0; i < suffixes.length; i++) {
+ var propSuffix = suffixes[i];
+ var suffixPos = key.lastIndexOf(propSuffix);
+ if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
+ var mainType = key.slice(0, suffixPos);
+ // Consider `dataIndex`.
+ if (mainType !== 'data') {
+ cptQuery.mainType = mainType;
+ cptQuery[propSuffix.toLowerCase()] = val;
+ reserved = true;
+ }
+ }
+ }
+ if (dataKeys.hasOwnProperty(key)) {
+ dataQuery[key] = val;
+ reserved = true;
+ }
+ if (!reserved) {
+ otherQuery[key] = val;
+ }
+ });
+ }
+
+ return {
+ cptQuery: cptQuery,
+ dataQuery: dataQuery,
+ otherQuery: otherQuery
+ };
+ },
+
+ filter: function (eventType, query, args) {
+ // They should be assigned before each trigger call.
+ var targetEl = this.targetEl;
+ var packedEvent = this.packedEvent;
+ var model = this.model;
+ var view = this.view;
+
+ // For event like 'globalout'.
+ if (!model || !view) {
+ return true;
+ }
+
+ var cptQuery = query.cptQuery;
+ var dataQuery = query.dataQuery;
+
+ return check(cptQuery, model, 'mainType')
+ && check(cptQuery, model, 'subType')
+ && check(cptQuery, model, 'index', 'componentIndex')
+ && check(cptQuery, model, 'name')
+ && check(cptQuery, model, 'id')
+ && check(dataQuery, packedEvent, 'name')
+ && check(dataQuery, packedEvent, 'dataIndex')
+ && check(dataQuery, packedEvent, 'dataType')
+ && (!view.filterForExposedEvent || view.filterForExposedEvent(
+ eventType, query.otherQuery, targetEl, packedEvent
+ ));
+ }
+ };
+
+ function check(query, host, prop, propOnHost) {
+ return query[prop] == null || host[propOnHost || prop] === query[prop];
+ }
+}
+
+/**
* @type {Object} key: actionType.
* @inner
*/
diff --git a/src/view/Chart.js b/src/view/Chart.js
index 67a1c37..47485d8 100644
--- a/src/view/Chart.js
+++ b/src/view/Chart.js
@@ -134,7 +134,7 @@ Chart.prototype = {
* @param {Object} payload
* @return {Object} {update: true}
*/
- updateTransform: null
+ updateTransform: null,
/**
* The view contains the given point.
@@ -144,6 +144,15 @@ Chart.prototype = {
*/
// containPoint: function () {}
+ /**
+ * @param {string} eventType
+ * @param {Object} query
+ * @param {module:zrender/Element} targetEl
+ * @param {Object} packedEvent
+ * @return {boolen} Pass only when return `true`.
+ */
+ filterForExposedEvent: null
+
};
var chartProto = Chart.prototype;
diff --git a/src/view/Component.js b/src/view/Component.js
index cd09fd5..180c502 100644
--- a/src/view/Component.js
+++ b/src/view/Component.js
@@ -43,7 +43,16 @@ Component.prototype = {
render: function (componentModel, ecModel, api, payload) {},
- dispose: function () {}
+ dispose: function () {},
+
+ /**
+ * @param {string} eventType
+ * @param {Object} query
+ * @param {module:zrender/Element} targetEl
+ * @param {Object} packedEvent
+ * @return {boolen} Pass only when return `true`.
+ */
+ filterForExposedEvent: null
};
diff --git a/test/custom-feature.html b/test/custom-feature.html
new file mode 100644
index 0000000..fe979db
--- /dev/null
+++ b/test/custom-feature.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <script src="lib/esl.js"></script>
+ <script src="lib/config.js"></script>
+ <script src="lib/jquery.min.js"></script>
+ <script src="lib/facePrint.js"></script>
+ <script src="lib/testHelper.js"></script>
+ <link rel="stylesheet" href="lib/reset.css" />
+ </head>
+ <body>
+ <style>
+ .test-title {
+ background: #146402;
+ color: #fff;
+ }
+ </style>
+
+
+ <div id="main0"></div>
+
+
+ <script>
+
+ require([
+ 'echarts'/*, 'map/js/china' */
+ ], function (echarts) {
+
+ var option = {
+ xAxis: {},
+ yAxis: {},
+ dataZoom: [{}, {type: 'inside'}],
+ series: [{
+ type: 'custom',
+ renderItem: function (params, api) {
+ return {
+ type: 'group',
+ position: api.coord([api.value(0), api.value(1)]),
+ children: [{
+ type: 'rect',
+ shape: {
+ x: -100,
+ y: -100,
+ width: 200,
+ height: 200,
+ r: 10
+ },
+ style: {
+ fill: 'rgba(102,241,98,0.4)'
+ },
+ silent: true,
+ styleEmphasis: false
+ }, {
+ type: 'group',
+ name: 'innerGroup',
+ children: [{
+ type: 'circle',
+ shape: {
+ r: 30,
+ cx: 0,
+ cy: 0
+ },
+ style: {
+ fill: 'red',
+ text: 'dataIndex: ' + params.dataIndex,
+ textStroke: '#fff',
+ textStrokeWidth: 1
+ }
+ }, {
+ type: 'rect',
+ shape: {
+ x: 50,
+ y: 60,
+ width: 80,
+ height: 80
+ },
+ style: {
+ fill: '#ff3391'
+ }
+ }]
+ }]
+ };
+ },
+ data: [[121, 333], [29, 212]]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'main0', {
+ title: [
+ 'Eventful: ',
+ '+ red shapes trigger events, but green shapes not. ',
+ '+ Only dataIndex: 1 trigger event',
+ 'events (click, mousedown, mousemove, mouseup) are printed in console log.'
+ ],
+ option: option
+ });
+
+ echarts.util.each(['click', 'mousedown', 'mousemove', 'mouseup'], function (eventName) {
+ chart.on(eventName, {dataIndex: 1, target: 'innerGroup'}, function (e) {
+ console.log(e.type, e);
+ });
+ });
+ });
+
+ </script>
+ </body>
+</html>
\ No newline at end of file
diff --git a/test/ec-event.html b/test/ec-event.html
new file mode 100644
index 0000000..a80844c
--- /dev/null
+++ b/test/ec-event.html
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <script src="lib/esl.js"></script>
+ <script src="lib/config.js"></script>
+ <script src="lib/jquery.min.js"></script>
+ <script src="lib/facePrint.js"></script>
+ <script src="lib/testHelper.js"></script>
+ <link rel="stylesheet" href="lib/reset.css" />
+ </head>
+ <body>
+ <style>
+ .test-title {
+ background: #146402;
+ color: #fff;
+ }
+ </style>
+
+
+ <div id="main0"></div>
+ <div id="main1"></div>
+ <div id="main2"></div>
+ <h2><a href="./custom-feature.html">custom element event case.</a></h2>
+ <div id="main3"></div>
+
+
+ <script>
+
+ require([
+ 'echarts'/*, 'map/js/china' */
+ ], function (echarts) {
+
+ var option = {
+ xAxis: {
+ triggerEvent: true,
+ data: ['aaaa', 'bbbb', 'cccc', 'dddd', 'eeee']
+ },
+ yAxis: [{
+ triggerEvent: true
+ }, {
+ triggerEvent: true,
+ type: 'category'
+ }],
+ series: [{
+ type: 'line',
+ data: [121, 442, 55],
+ }, {
+ type: 'scatter',
+ yAxisIndex: 1,
+ data: ['asdf', 'zxcv', 'qwer']
+ }, {
+ type: 'line',
+ data: [234, 213, 255],
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'main0', {
+ title: [
+ 'Check event log on console:',
+ 'click | xAxis click | yAxis.category click | series click | series scatter click',
+ ],
+ option: option
+ });
+
+ chart.on('click', function () {
+ console.log('click');
+ });
+ chart.on('click', 'xAxis', function () {
+ console.log('click xAxis');
+ });
+ chart.on('click', 'yAxis.category', function () {
+ console.log('click yAxis.category');
+ });
+ chart.on('click', 'series', function () {
+ console.log('click series');
+ });
+ chart.on('click', 'series.scatter', function () {
+ console.log('click series.scatter');
+ });
+
+ });
+
+ </script>
+
+
+
+
+
+
+
+ <script>
+
+ require([
+ 'echarts'/*, 'map/js/china' */
+ ], function (echarts) {
+
+ var option = {
+ xAxis: {
+ triggerEvent: true,
+ data: ['aaaa', 'bbbb', 'cccc', 'dddd', 'eeee']
+ },
+ yAxis: [{
+ id: 'left',
+ triggerEvent: true
+ }, {
+ triggerEvent: true,
+ type: 'category'
+ }],
+ series: [{
+ type: 'line',
+ name: 'line-all',
+ symbolSize: 20,
+ data: [121, 442, 55],
+ }, {
+ type: 'scatter',
+ yAxisIndex: 1,
+ label: {
+ show: true,
+ formatter: 'name: {b}'
+ },
+ symbolSize: 20,
+ data: [
+ 'asdf',
+ {name: 'ff', value: 'zxcv'},
+ 'qwer'
+ ]
+ }, {
+ type: 'line',
+ name: 'line-all',
+ symbolSize: 20,
+ label: {
+ show: true,
+ formatter: 'name: {b}'
+ },
+ data: [
+ {name: 'ee', value: 234},
+ {name: 'ff', value: 113},
+ {name: 'gg', value: 255}
+ ]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'main1', {
+ title: [
+ 'Check event log on console:',
+ 'click | click yAxisIndex: 1 | mouseover yAxisId: "left" | click seriesName: "line-all"',
+ 'click data name "ff" | mouseup seriesName: "line-all", dataIndex: 1'
+ ],
+ option: option
+ });
+
+ chart.on('click', function () {
+ console.log('click');
+ });
+ chart.on('click', {yAxisIndex: 1}, function () {
+ console.log('click yAxisIndex: 1');
+ });
+ chart.on('mouseover', {yAxisId: 'left'}, function () {
+ console.log('mouseover yAxisId: "x"');
+ });
+ chart.on('click', {seriesName: 'line-all'}, function () {
+ console.log('click seriesName: "line-all"');
+ });
+ chart.on('click', {name: 'ff'}, function () {
+ console.log('click {name: "ff"}');
+ });
+ chart.on('mouseup', {seriesName: 'line-all', dataIndex: 1}, function () {
+ console.log('mouseup {seriesName: "line-all", dataIndex: 1}');
+ });
+ });
+
+ </script>
+
+
+
+
+
+
+ </body>
+</html>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org