EffectSymbol.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. var zrUtil = require("zrender/lib/core/util");
  2. var _symbol = require("../../util/symbol");
  3. var createSymbol = _symbol.createSymbol;
  4. var _graphic = require("../../util/graphic");
  5. var Group = _graphic.Group;
  6. var _number = require("../../util/number");
  7. var parsePercent = _number.parsePercent;
  8. var SymbolClz = require("./Symbol");
  9. /**
  10. * Symbol with ripple effect
  11. * @module echarts/chart/helper/EffectSymbol
  12. */
  13. var EFFECT_RIPPLE_NUMBER = 3;
  14. function normalizeSymbolSize(symbolSize) {
  15. if (!zrUtil.isArray(symbolSize)) {
  16. symbolSize = [+symbolSize, +symbolSize];
  17. }
  18. return symbolSize;
  19. }
  20. function updateRipplePath(rippleGroup, effectCfg) {
  21. rippleGroup.eachChild(function (ripplePath) {
  22. ripplePath.attr({
  23. z: effectCfg.z,
  24. zlevel: effectCfg.zlevel,
  25. style: {
  26. stroke: effectCfg.brushType === 'stroke' ? effectCfg.color : null,
  27. fill: effectCfg.brushType === 'fill' ? effectCfg.color : null
  28. }
  29. });
  30. });
  31. }
  32. /**
  33. * @constructor
  34. * @param {module:echarts/data/List} data
  35. * @param {number} idx
  36. * @extends {module:zrender/graphic/Group}
  37. */
  38. function EffectSymbol(data, idx) {
  39. Group.call(this);
  40. var symbol = new SymbolClz(data, idx);
  41. var rippleGroup = new Group();
  42. this.add(symbol);
  43. this.add(rippleGroup);
  44. rippleGroup.beforeUpdate = function () {
  45. this.attr(symbol.getScale());
  46. };
  47. this.updateData(data, idx);
  48. }
  49. var effectSymbolProto = EffectSymbol.prototype;
  50. effectSymbolProto.stopEffectAnimation = function () {
  51. this.childAt(1).removeAll();
  52. };
  53. effectSymbolProto.startEffectAnimation = function (effectCfg) {
  54. var symbolType = effectCfg.symbolType;
  55. var color = effectCfg.color;
  56. var rippleGroup = this.childAt(1);
  57. for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) {
  58. // var ripplePath = createSymbol(
  59. // symbolType, -0.5, -0.5, 1, 1, color
  60. // );
  61. // If width/height are set too small (e.g., set to 1) on ios10
  62. // and macOS Sierra, a circle stroke become a rect, no matter what
  63. // the scale is set. So we set width/height as 2. See #4136.
  64. var ripplePath = createSymbol(symbolType, -1, -1, 2, 2, color);
  65. ripplePath.attr({
  66. style: {
  67. strokeNoScale: true
  68. },
  69. z2: 99,
  70. silent: true,
  71. scale: [0.5, 0.5]
  72. });
  73. var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset; // TODO Configurable effectCfg.period
  74. ripplePath.animate('', true).when(effectCfg.period, {
  75. scale: [effectCfg.rippleScale / 2, effectCfg.rippleScale / 2]
  76. }).delay(delay).start();
  77. ripplePath.animateStyle(true).when(effectCfg.period, {
  78. opacity: 0
  79. }).delay(delay).start();
  80. rippleGroup.add(ripplePath);
  81. }
  82. updateRipplePath(rippleGroup, effectCfg);
  83. };
  84. /**
  85. * Update effect symbol
  86. */
  87. effectSymbolProto.updateEffectAnimation = function (effectCfg) {
  88. var oldEffectCfg = this._effectCfg;
  89. var rippleGroup = this.childAt(1); // Must reinitialize effect if following configuration changed
  90. var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale'];
  91. for (var i = 0; i < DIFFICULT_PROPS.length; i++) {
  92. var propName = DIFFICULT_PROPS[i];
  93. if (oldEffectCfg[propName] !== effectCfg[propName]) {
  94. this.stopEffectAnimation();
  95. this.startEffectAnimation(effectCfg);
  96. return;
  97. }
  98. }
  99. updateRipplePath(rippleGroup, effectCfg);
  100. };
  101. /**
  102. * Highlight symbol
  103. */
  104. effectSymbolProto.highlight = function () {
  105. this.trigger('emphasis');
  106. };
  107. /**
  108. * Downplay symbol
  109. */
  110. effectSymbolProto.downplay = function () {
  111. this.trigger('normal');
  112. };
  113. /**
  114. * Update symbol properties
  115. * @param {module:echarts/data/List} data
  116. * @param {number} idx
  117. */
  118. effectSymbolProto.updateData = function (data, idx) {
  119. var seriesModel = data.hostModel;
  120. this.childAt(0).updateData(data, idx);
  121. var rippleGroup = this.childAt(1);
  122. var itemModel = data.getItemModel(idx);
  123. var symbolType = data.getItemVisual(idx, 'symbol');
  124. var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
  125. var color = data.getItemVisual(idx, 'color');
  126. rippleGroup.attr('scale', symbolSize);
  127. rippleGroup.traverse(function (ripplePath) {
  128. ripplePath.attr({
  129. fill: color
  130. });
  131. });
  132. var symbolOffset = itemModel.getShallow('symbolOffset');
  133. if (symbolOffset) {
  134. var pos = rippleGroup.position;
  135. pos[0] = parsePercent(symbolOffset[0], symbolSize[0]);
  136. pos[1] = parsePercent(symbolOffset[1], symbolSize[1]);
  137. }
  138. rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0;
  139. var effectCfg = {};
  140. effectCfg.showEffectOn = seriesModel.get('showEffectOn');
  141. effectCfg.rippleScale = itemModel.get('rippleEffect.scale');
  142. effectCfg.brushType = itemModel.get('rippleEffect.brushType');
  143. effectCfg.period = itemModel.get('rippleEffect.period') * 1000;
  144. effectCfg.effectOffset = idx / data.count();
  145. effectCfg.z = itemModel.getShallow('z') || 0;
  146. effectCfg.zlevel = itemModel.getShallow('zlevel') || 0;
  147. effectCfg.symbolType = symbolType;
  148. effectCfg.color = color;
  149. this.off('mouseover').off('mouseout').off('emphasis').off('normal');
  150. if (effectCfg.showEffectOn === 'render') {
  151. this._effectCfg ? this.updateEffectAnimation(effectCfg) : this.startEffectAnimation(effectCfg);
  152. this._effectCfg = effectCfg;
  153. } else {
  154. // Not keep old effect config
  155. this._effectCfg = null;
  156. this.stopEffectAnimation();
  157. var symbol = this.childAt(0);
  158. var onEmphasis = function () {
  159. symbol.highlight();
  160. if (effectCfg.showEffectOn !== 'render') {
  161. this.startEffectAnimation(effectCfg);
  162. }
  163. };
  164. var onNormal = function () {
  165. symbol.downplay();
  166. if (effectCfg.showEffectOn !== 'render') {
  167. this.stopEffectAnimation();
  168. }
  169. };
  170. this.on('mouseover', onEmphasis, this).on('mouseout', onNormal, this).on('emphasis', onEmphasis, this).on('normal', onNormal, this);
  171. }
  172. this._effectCfg = effectCfg;
  173. };
  174. effectSymbolProto.fadeOut = function (cb) {
  175. this.off('mouseover').off('mouseout').off('emphasis').off('normal');
  176. cb && cb();
  177. };
  178. zrUtil.inherits(EffectSymbol, Group);
  179. var _default = EffectSymbol;
  180. module.exports = _default;