Painter.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. var _core = require("./core");
  2. var createElement = _core.createElement;
  3. var util = require("../core/util");
  4. var each = util.each;
  5. var zrLog = require("../core/log");
  6. var Path = require("../graphic/Path");
  7. var ZImage = require("../graphic/Image");
  8. var ZText = require("../graphic/Text");
  9. var arrayDiff = require("../core/arrayDiff2");
  10. var GradientManager = require("./helper/GradientManager");
  11. var ClippathManager = require("./helper/ClippathManager");
  12. var _graphic = require("./graphic");
  13. var svgPath = _graphic.path;
  14. var svgImage = _graphic.image;
  15. var svgText = _graphic.text;
  16. /**
  17. * SVG Painter
  18. * @module zrender/svg/Painter
  19. */
  20. function parseInt10(val) {
  21. return parseInt(val, 10);
  22. }
  23. function getSvgProxy(el) {
  24. if (el instanceof Path) {
  25. return svgPath;
  26. } else if (el instanceof ZImage) {
  27. return svgImage;
  28. } else if (el instanceof ZText) {
  29. return svgText;
  30. } else {
  31. return svgPath;
  32. }
  33. }
  34. function checkParentAvailable(parent, child) {
  35. return child && parent && child.parentNode !== parent;
  36. }
  37. function insertAfter(parent, child, prevSibling) {
  38. if (checkParentAvailable(parent, child) && prevSibling) {
  39. var nextSibling = prevSibling.nextSibling;
  40. nextSibling ? parent.insertBefore(child, nextSibling) : parent.appendChild(child);
  41. }
  42. }
  43. function prepend(parent, child) {
  44. if (checkParentAvailable(parent, child)) {
  45. var firstChild = parent.firstChild;
  46. firstChild ? parent.insertBefore(child, firstChild) : parent.appendChild(child);
  47. }
  48. }
  49. function append(parent, child) {
  50. if (checkParentAvailable(parent, child)) {
  51. parent.appendChild(child);
  52. }
  53. }
  54. function remove(parent, child) {
  55. if (child && parent && child.parentNode === parent) {
  56. parent.removeChild(child);
  57. }
  58. }
  59. function getTextSvgElement(displayable) {
  60. return displayable.__textSvgEl;
  61. }
  62. function getSvgElement(displayable) {
  63. return displayable.__svgEl;
  64. }
  65. /**
  66. * @alias module:zrender/svg/Painter
  67. * @constructor
  68. * @param {HTMLElement} root 绘图容器
  69. * @param {module:zrender/Storage} storage
  70. * @param {Object} opts
  71. */
  72. var SVGPainter = function (root, storage, opts) {
  73. this.root = root;
  74. this.storage = storage;
  75. this._opts = opts = util.extend({}, opts || {});
  76. var svgRoot = createElement('svg');
  77. svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
  78. svgRoot.setAttribute('version', '1.1');
  79. svgRoot.setAttribute('baseProfile', 'full');
  80. svgRoot.style['user-select'] = 'none';
  81. svgRoot.style.cssText = 'position:absolute;left:0;top:0;';
  82. this.gradientManager = new GradientManager(svgRoot);
  83. this.clipPathManager = new ClippathManager(svgRoot);
  84. var viewport = document.createElement('div');
  85. viewport.style.cssText = 'overflow:hidden;position:relative';
  86. this._svgRoot = svgRoot;
  87. this._viewport = viewport;
  88. root.appendChild(viewport);
  89. viewport.appendChild(svgRoot);
  90. this.resize(opts.width, opts.height);
  91. this._visibleList = [];
  92. };
  93. SVGPainter.prototype = {
  94. constructor: SVGPainter,
  95. getType: function () {
  96. return 'svg';
  97. },
  98. getViewportRoot: function () {
  99. return this._viewport;
  100. },
  101. getViewportRootOffset: function () {
  102. var viewportRoot = this.getViewportRoot();
  103. if (viewportRoot) {
  104. return {
  105. offsetLeft: viewportRoot.offsetLeft || 0,
  106. offsetTop: viewportRoot.offsetTop || 0
  107. };
  108. }
  109. },
  110. refresh: function () {
  111. var list = this.storage.getDisplayList(true);
  112. this._paintList(list);
  113. },
  114. _paintList: function (list) {
  115. this.gradientManager.markAllUnused();
  116. this.clipPathManager.markAllUnused();
  117. var svgRoot = this._svgRoot;
  118. var visibleList = this._visibleList;
  119. var listLen = list.length;
  120. var newVisibleList = [];
  121. var i;
  122. for (i = 0; i < listLen; i++) {
  123. var displayable = list[i];
  124. var svgProxy = getSvgProxy(displayable);
  125. if (!displayable.invisible) {
  126. if (displayable.__dirty) {
  127. svgProxy && svgProxy.brush(displayable); // Update clipPath
  128. this.clipPathManager.update(displayable); // Update gradient
  129. if (displayable.style) {
  130. this.gradientManager.update(displayable.style.fill);
  131. this.gradientManager.update(displayable.style.stroke);
  132. }
  133. displayable.__dirty = false;
  134. }
  135. newVisibleList.push(displayable);
  136. }
  137. }
  138. var diff = arrayDiff(visibleList, newVisibleList);
  139. var prevSvgElement; // First do remove, in case element moved to the head and do remove
  140. // after add
  141. for (i = 0; i < diff.length; i++) {
  142. var item = diff[i];
  143. if (item.removed) {
  144. for (var k = 0; k < item.count; k++) {
  145. var displayable = visibleList[item.indices[k]];
  146. var svgElement = getSvgElement(displayable);
  147. var textSvgElement = getTextSvgElement(displayable);
  148. remove(svgRoot, svgElement);
  149. remove(svgRoot, textSvgElement);
  150. }
  151. }
  152. }
  153. for (i = 0; i < diff.length; i++) {
  154. var item = diff[i];
  155. if (item.added) {
  156. for (var k = 0; k < item.count; k++) {
  157. var displayable = newVisibleList[item.indices[k]];
  158. var svgElement = getSvgElement(displayable);
  159. var textSvgElement = getTextSvgElement(displayable);
  160. prevSvgElement ? insertAfter(svgRoot, svgElement, prevSvgElement) : prepend(svgRoot, svgElement);
  161. if (svgElement) {
  162. insertAfter(svgRoot, textSvgElement, svgElement);
  163. } else if (prevSvgElement) {
  164. insertAfter(svgRoot, textSvgElement, prevSvgElement);
  165. } else {
  166. prepend(svgRoot, textSvgElement);
  167. } // Insert text
  168. insertAfter(svgRoot, textSvgElement, svgElement);
  169. prevSvgElement = textSvgElement || svgElement || prevSvgElement;
  170. this.gradientManager.addWithoutUpdate(svgElement, displayable);
  171. this.clipPathManager.markUsed(displayable);
  172. }
  173. } else if (!item.removed) {
  174. for (var k = 0; k < item.count; k++) {
  175. var displayable = newVisibleList[item.indices[k]];
  176. prevSvgElement = svgElement = getTextSvgElement(displayable) || getSvgElement(displayable) || prevSvgElement;
  177. this.gradientManager.markUsed(displayable);
  178. this.gradientManager.addWithoutUpdate(svgElement, displayable);
  179. this.clipPathManager.markUsed(displayable);
  180. }
  181. }
  182. }
  183. this.gradientManager.removeUnused();
  184. this.clipPathManager.removeUnused();
  185. this._visibleList = newVisibleList;
  186. },
  187. _getDefs: function (isForceCreating) {
  188. var svgRoot = this._svgRoot;
  189. var defs = this._svgRoot.getElementsByTagName('defs');
  190. if (defs.length === 0) {
  191. // Not exist
  192. if (isForceCreating) {
  193. var defs = svgRoot.insertBefore(createElement('defs'), // Create new tag
  194. svgRoot.firstChild // Insert in the front of svg
  195. );
  196. if (!defs.contains) {
  197. // IE doesn't support contains method
  198. defs.contains = function (el) {
  199. var children = defs.children;
  200. if (!children) {
  201. return false;
  202. }
  203. for (var i = children.length - 1; i >= 0; --i) {
  204. if (children[i] === el) {
  205. return true;
  206. }
  207. }
  208. return false;
  209. };
  210. }
  211. return defs;
  212. } else {
  213. return null;
  214. }
  215. } else {
  216. return defs[0];
  217. }
  218. },
  219. resize: function (width, height) {
  220. var viewport = this._viewport; // FIXME Why ?
  221. viewport.style.display = 'none'; // Save input w/h
  222. var opts = this._opts;
  223. width != null && (opts.width = width);
  224. height != null && (opts.height = height);
  225. width = this._getSize(0);
  226. height = this._getSize(1);
  227. viewport.style.display = '';
  228. if (this._width !== width && this._height !== height) {
  229. this._width = width;
  230. this._height = height;
  231. var viewportStyle = viewport.style;
  232. viewportStyle.width = width + 'px';
  233. viewportStyle.height = height + 'px';
  234. var svgRoot = this._svgRoot; // Set width by 'svgRoot.width = width' is invalid
  235. svgRoot.setAttribute('width', width);
  236. svgRoot.setAttribute('height', height);
  237. }
  238. },
  239. /**
  240. * 获取绘图区域宽度
  241. */
  242. getWidth: function () {
  243. return this._width;
  244. },
  245. /**
  246. * 获取绘图区域高度
  247. */
  248. getHeight: function () {
  249. return this._height;
  250. },
  251. _getSize: function (whIdx) {
  252. var opts = this._opts;
  253. var wh = ['width', 'height'][whIdx];
  254. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  255. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  256. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  257. if (opts[wh] != null && opts[wh] !== 'auto') {
  258. return parseFloat(opts[wh]);
  259. }
  260. var root = this.root; // IE8 does not support getComputedStyle, but it use VML.
  261. var stl = document.defaultView.getComputedStyle(root);
  262. return (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) - (parseInt10(stl[plt]) || 0) - (parseInt10(stl[prb]) || 0) | 0;
  263. },
  264. dispose: function () {
  265. this.root.innerHTML = '';
  266. this._svgRoot = this._viewport = this.storage = null;
  267. },
  268. clear: function () {
  269. if (this._viewport) {
  270. this.root.removeChild(this._viewport);
  271. }
  272. },
  273. pathToSvg: function () {
  274. this.refresh();
  275. var html = this._svgRoot.outerHTML;
  276. return 'data:img/svg+xml;utf-8,' + unescape(html);
  277. }
  278. }; // Not supported methods
  279. function createMethodNotSupport(method) {
  280. return function () {
  281. zrLog('In SVG mode painter not support method "' + method + '"');
  282. };
  283. } // Unsuppoted methods
  284. each(['getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'], function (name) {
  285. SVGPainter.prototype[name] = createMethodNotSupport(name);
  286. });
  287. var _default = SVGPainter;
  288. module.exports = _default;