GradientManager.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. var Definable = require("./Definable");
  2. var zrUtil = require("../../core/util");
  3. var logError = require("../../core/log");
  4. var colorTool = require("../../tool/color");
  5. /**
  6. * @file Manages SVG gradient elements.
  7. * @author Zhang Wenli
  8. */
  9. /**
  10. * Manages SVG gradient elements.
  11. *
  12. * @class
  13. * @extends Definable
  14. * @param {number} zrId zrender instance id
  15. * @param {SVGElement} svgRoot root of SVG document
  16. */
  17. function GradientManager(zrId, svgRoot) {
  18. Definable.call(this, zrId, svgRoot, ['linearGradient', 'radialGradient'], '__gradient_in_use__');
  19. }
  20. zrUtil.inherits(GradientManager, Definable);
  21. /**
  22. * Create new gradient DOM for fill or stroke if not exist,
  23. * but will not update gradient if exists.
  24. *
  25. * @param {SvgElement} svgElement SVG element to paint
  26. * @param {Displayable} displayable zrender displayable element
  27. */
  28. GradientManager.prototype.addWithoutUpdate = function (svgElement, displayable) {
  29. if (displayable && displayable.style) {
  30. var that = this;
  31. zrUtil.each(['fill', 'stroke'], function (fillOrStroke) {
  32. if (displayable.style[fillOrStroke] && (displayable.style[fillOrStroke].type === 'linear' || displayable.style[fillOrStroke].type === 'radial')) {
  33. var gradient = displayable.style[fillOrStroke];
  34. var defs = that.getDefs(true); // Create dom in <defs> if not exists
  35. var dom;
  36. if (gradient._dom) {
  37. // Gradient exists
  38. dom = gradient._dom;
  39. if (!defs.contains(gradient._dom)) {
  40. // _dom is no longer in defs, recreate
  41. that.addDom(dom);
  42. }
  43. } else {
  44. // New dom
  45. dom = that.add(gradient);
  46. }
  47. that.markUsed(displayable);
  48. var id = dom.getAttribute('id');
  49. svgElement.setAttribute(fillOrStroke, 'url(#' + id + ')');
  50. }
  51. });
  52. }
  53. };
  54. /**
  55. * Add a new gradient tag in <defs>
  56. *
  57. * @param {Gradient} gradient zr gradient instance
  58. * @return {SVGLinearGradientElement | SVGRadialGradientElement}
  59. * created DOM
  60. */
  61. GradientManager.prototype.add = function (gradient) {
  62. var dom;
  63. if (gradient.type === 'linear') {
  64. dom = this.createElement('linearGradient');
  65. } else if (gradient.type === 'radial') {
  66. dom = this.createElement('radialGradient');
  67. } else {
  68. logError('Illegal gradient type.');
  69. return null;
  70. } // Set dom id with gradient id, since each gradient instance
  71. // will have no more than one dom element.
  72. // id may exists before for those dirty elements, in which case
  73. // id should remain the same, and other attributes should be
  74. // updated.
  75. gradient.id = gradient.id || this.nextId++;
  76. dom.setAttribute('id', 'zr' + this._zrId + '-gradient-' + gradient.id);
  77. this.updateDom(gradient, dom);
  78. this.addDom(dom);
  79. return dom;
  80. };
  81. /**
  82. * Update gradient.
  83. *
  84. * @param {Gradient} gradient zr gradient instance
  85. */
  86. GradientManager.prototype.update = function (gradient) {
  87. var that = this;
  88. Definable.prototype.update.call(this, gradient, function () {
  89. var type = gradient.type;
  90. var tagName = gradient._dom.tagName;
  91. if (type === 'linear' && tagName === 'linearGradient' || type === 'radial' && tagName === 'radialGradient') {
  92. // Gradient type is not changed, update gradient
  93. that.updateDom(gradient, gradient._dom);
  94. } else {
  95. // Remove and re-create if type is changed
  96. that.removeDom(gradient);
  97. that.add(gradient);
  98. }
  99. });
  100. };
  101. /**
  102. * Update gradient dom
  103. *
  104. * @param {Gradient} gradient zr gradient instance
  105. * @param {SVGLinearGradientElement | SVGRadialGradientElement} dom
  106. * DOM to update
  107. */
  108. GradientManager.prototype.updateDom = function (gradient, dom) {
  109. if (gradient.type === 'linear') {
  110. dom.setAttribute('x1', gradient.x);
  111. dom.setAttribute('y1', gradient.y);
  112. dom.setAttribute('x2', gradient.x2);
  113. dom.setAttribute('y2', gradient.y2);
  114. } else if (gradient.type === 'radial') {
  115. dom.setAttribute('cx', gradient.x);
  116. dom.setAttribute('cy', gradient.y);
  117. dom.setAttribute('r', gradient.r);
  118. } else {
  119. logError('Illegal gradient type.');
  120. return;
  121. }
  122. if (gradient.global) {
  123. // x1, x2, y1, y2 in range of 0 to canvas width or height
  124. dom.setAttribute('gradientUnits', 'userSpaceOnUse');
  125. } else {
  126. // x1, x2, y1, y2 in range of 0 to 1
  127. dom.setAttribute('gradientUnits', 'objectBoundingBox');
  128. } // Remove color stops if exists
  129. dom.innerHTML = ''; // Add color stops
  130. var colors = gradient.colorStops;
  131. for (var i = 0, len = colors.length; i < len; ++i) {
  132. var stop = this.createElement('stop');
  133. stop.setAttribute('offset', colors[i].offset * 100 + '%');
  134. var color = colors[i].color;
  135. if (color.indexOf('rgba') > -1) {
  136. // Fix Safari bug that stop-color not recognizing alpha #9014
  137. var opacity = colorTool.parse(color)[3];
  138. var hex = colorTool.toHex(color); // stop-color cannot be color, since:
  139. // The opacity value used for the gradient calculation is the
  140. // *product* of the value of stop-opacity and the opacity of the
  141. // value of stop-color.
  142. // See https://www.w3.org/TR/SVG2/pservers.html#StopOpacityProperty
  143. stop.setAttribute('stop-color', '#' + hex);
  144. stop.setAttribute('stop-opacity', opacity);
  145. } else {
  146. stop.setAttribute('stop-color', colors[i].color);
  147. }
  148. dom.appendChild(stop);
  149. } // Store dom element in gradient, to avoid creating multiple
  150. // dom instances for the same gradient element
  151. gradient._dom = dom;
  152. };
  153. /**
  154. * Mark a single gradient to be used
  155. *
  156. * @param {Displayable} displayable displayable element
  157. */
  158. GradientManager.prototype.markUsed = function (displayable) {
  159. if (displayable.style) {
  160. var gradient = displayable.style.fill;
  161. if (gradient && gradient._dom) {
  162. Definable.prototype.markUsed.call(this, gradient._dom);
  163. }
  164. gradient = displayable.style.stroke;
  165. if (gradient && gradient._dom) {
  166. Definable.prototype.markUsed.call(this, gradient._dom);
  167. }
  168. }
  169. };
  170. var _default = GradientManager;
  171. module.exports = _default;