Painter.js 11 KB

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