EffectSymbol.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. var zrUtil = require("zrender/lib/core/util");
  20. var _symbol = require("../../util/symbol");
  21. var createSymbol = _symbol.createSymbol;
  22. var _graphic = require("../../util/graphic");
  23. var Group = _graphic.Group;
  24. var _number = require("../../util/number");
  25. var parsePercent = _number.parsePercent;
  26. var SymbolClz = require("./Symbol");
  27. /*
  28. * Licensed to the Apache Software Foundation (ASF) under one
  29. * or more contributor license agreements. See the NOTICE file
  30. * distributed with this work for additional information
  31. * regarding copyright ownership. The ASF licenses this file
  32. * to you under the Apache License, Version 2.0 (the
  33. * "License"); you may not use this file except in compliance
  34. * with the License. You may obtain a copy of the License at
  35. *
  36. * http://www.apache.org/licenses/LICENSE-2.0
  37. *
  38. * Unless required by applicable law or agreed to in writing,
  39. * software distributed under the License is distributed on an
  40. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  41. * KIND, either express or implied. See the License for the
  42. * specific language governing permissions and limitations
  43. * under the License.
  44. */
  45. /**
  46. * Symbol with ripple effect
  47. * @module echarts/chart/helper/EffectSymbol
  48. */
  49. var EFFECT_RIPPLE_NUMBER = 3;
  50. function normalizeSymbolSize(symbolSize) {
  51. if (!zrUtil.isArray(symbolSize)) {
  52. symbolSize = [+symbolSize, +symbolSize];
  53. }
  54. return symbolSize;
  55. }
  56. function updateRipplePath(rippleGroup, effectCfg) {
  57. var color = effectCfg.rippleEffectColor || effectCfg.color;
  58. rippleGroup.eachChild(function (ripplePath) {
  59. ripplePath.attr({
  60. z: effectCfg.z,
  61. zlevel: effectCfg.zlevel,
  62. style: {
  63. stroke: effectCfg.brushType === 'stroke' ? color : null,
  64. fill: effectCfg.brushType === 'fill' ? color : null
  65. }
  66. });
  67. });
  68. }
  69. /**
  70. * @constructor
  71. * @param {module:echarts/data/List} data
  72. * @param {number} idx
  73. * @extends {module:zrender/graphic/Group}
  74. */
  75. function EffectSymbol(data, idx) {
  76. Group.call(this);
  77. var symbol = new SymbolClz(data, idx);
  78. var rippleGroup = new Group();
  79. this.add(symbol);
  80. this.add(rippleGroup);
  81. rippleGroup.beforeUpdate = function () {
  82. this.attr(symbol.getScale());
  83. };
  84. this.updateData(data, idx);
  85. }
  86. var effectSymbolProto = EffectSymbol.prototype;
  87. effectSymbolProto.stopEffectAnimation = function () {
  88. this.childAt(1).removeAll();
  89. };
  90. effectSymbolProto.startEffectAnimation = function (effectCfg) {
  91. var symbolType = effectCfg.symbolType;
  92. var color = effectCfg.color;
  93. var rippleGroup = this.childAt(1);
  94. for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) {
  95. // If width/height are set too small (e.g., set to 1) on ios10
  96. // and macOS Sierra, a circle stroke become a rect, no matter what
  97. // the scale is set. So we set width/height as 2. See #4136.
  98. var ripplePath = createSymbol(symbolType, -1, -1, 2, 2, color);
  99. ripplePath.attr({
  100. style: {
  101. strokeNoScale: true
  102. },
  103. z2: 99,
  104. silent: true,
  105. scale: [0.5, 0.5]
  106. });
  107. var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset; // TODO Configurable effectCfg.period
  108. ripplePath.animate('', true).when(effectCfg.period, {
  109. scale: [effectCfg.rippleScale / 2, effectCfg.rippleScale / 2]
  110. }).delay(delay).start();
  111. ripplePath.animateStyle(true).when(effectCfg.period, {
  112. opacity: 0
  113. }).delay(delay).start();
  114. rippleGroup.add(ripplePath);
  115. }
  116. updateRipplePath(rippleGroup, effectCfg);
  117. };
  118. /**
  119. * Update effect symbol
  120. */
  121. effectSymbolProto.updateEffectAnimation = function (effectCfg) {
  122. var oldEffectCfg = this._effectCfg;
  123. var rippleGroup = this.childAt(1); // Must reinitialize effect if following configuration changed
  124. var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale'];
  125. for (var i = 0; i < DIFFICULT_PROPS.length; i++) {
  126. var propName = DIFFICULT_PROPS[i];
  127. if (oldEffectCfg[propName] !== effectCfg[propName]) {
  128. this.stopEffectAnimation();
  129. this.startEffectAnimation(effectCfg);
  130. return;
  131. }
  132. }
  133. updateRipplePath(rippleGroup, effectCfg);
  134. };
  135. /**
  136. * Highlight symbol
  137. */
  138. effectSymbolProto.highlight = function () {
  139. this.trigger('emphasis');
  140. };
  141. /**
  142. * Downplay symbol
  143. */
  144. effectSymbolProto.downplay = function () {
  145. this.trigger('normal');
  146. };
  147. /**
  148. * Update symbol properties
  149. * @param {module:echarts/data/List} data
  150. * @param {number} idx
  151. */
  152. effectSymbolProto.updateData = function (data, idx) {
  153. var seriesModel = data.hostModel;
  154. this.childAt(0).updateData(data, idx);
  155. var rippleGroup = this.childAt(1);
  156. var itemModel = data.getItemModel(idx);
  157. var symbolType = data.getItemVisual(idx, 'symbol');
  158. var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
  159. var color = data.getItemVisual(idx, 'color');
  160. rippleGroup.attr('scale', symbolSize);
  161. rippleGroup.traverse(function (ripplePath) {
  162. ripplePath.attr({
  163. fill: color
  164. });
  165. });
  166. var symbolOffset = itemModel.getShallow('symbolOffset');
  167. if (symbolOffset) {
  168. var pos = rippleGroup.position;
  169. pos[0] = parsePercent(symbolOffset[0], symbolSize[0]);
  170. pos[1] = parsePercent(symbolOffset[1], symbolSize[1]);
  171. }
  172. var symbolRotate = data.getItemVisual(idx, 'symbolRotate');
  173. rippleGroup.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;
  174. var effectCfg = {};
  175. effectCfg.showEffectOn = seriesModel.get('showEffectOn');
  176. effectCfg.rippleScale = itemModel.get('rippleEffect.scale');
  177. effectCfg.brushType = itemModel.get('rippleEffect.brushType');
  178. effectCfg.period = itemModel.get('rippleEffect.period') * 1000;
  179. effectCfg.effectOffset = idx / data.count();
  180. effectCfg.z = itemModel.getShallow('z') || 0;
  181. effectCfg.zlevel = itemModel.getShallow('zlevel') || 0;
  182. effectCfg.symbolType = symbolType;
  183. effectCfg.color = color;
  184. effectCfg.rippleEffectColor = itemModel.get('rippleEffect.color');
  185. this.off('mouseover').off('mouseout').off('emphasis').off('normal');
  186. if (effectCfg.showEffectOn === 'render') {
  187. this._effectCfg ? this.updateEffectAnimation(effectCfg) : this.startEffectAnimation(effectCfg);
  188. this._effectCfg = effectCfg;
  189. } else {
  190. // Not keep old effect config
  191. this._effectCfg = null;
  192. this.stopEffectAnimation();
  193. var symbol = this.childAt(0);
  194. var onEmphasis = function () {
  195. symbol.highlight();
  196. if (effectCfg.showEffectOn !== 'render') {
  197. this.startEffectAnimation(effectCfg);
  198. }
  199. };
  200. var onNormal = function () {
  201. symbol.downplay();
  202. if (effectCfg.showEffectOn !== 'render') {
  203. this.stopEffectAnimation();
  204. }
  205. };
  206. this.on('mouseover', onEmphasis, this).on('mouseout', onNormal, this).on('emphasis', onEmphasis, this).on('normal', onNormal, this);
  207. }
  208. this._effectCfg = effectCfg;
  209. };
  210. effectSymbolProto.fadeOut = function (cb) {
  211. this.off('mouseover').off('mouseout').off('emphasis').off('normal');
  212. cb && cb();
  213. };
  214. zrUtil.inherits(EffectSymbol, Group);
  215. var _default = EffectSymbol;
  216. module.exports = _default;