var _core = require("./core"); var createElement = _core.createElement; var dataUtil = require("../utils/data_structure_util"); var Path = require("../graphic/Path"); var QImage = require("../graphic/Image"); var QText = require("../graphic/Text"); var arrayDiff = require("../utils/array_diff2"); var GradientManager = require("./GradientManager"); var ClippathManager = require("./ClippathManager"); var ShadowManager = require("./ShadowManager"); var _graphic = require("./graphic"); var svgPath = _graphic.path; var svgImage = _graphic.image; var svgText = _graphic.text; /* eslint-disable no-unused-vars */ /** * @class qrenderer.svg.SVGPainter * * SVG 画笔。 * * @docauthor 大漠穷秋 damoqiongqiu@126.com */ /** * @private * @method getSvgProxy * * QImage 映射成 svgImage,ZText 映射成 svgText,其它所有都映射成 svgPath。 * * @param {Element} el */ function getSvgProxy(el) { if (el instanceof Path) { return svgPath; } else if (el instanceof QImage) { return svgImage; } else if (el instanceof QText) { return svgText; } return svgPath; } function checkParentAvailable(parent, child) { return child && parent && child.parentNode !== parent; } function insertAfter(parent, child, prevSibling) { if (checkParentAvailable(parent, child) && prevSibling) { var nextSibling = prevSibling.nextSibling; nextSibling ? parent.insertBefore(child, nextSibling) : parent.appendChild(child); } } function prepend(parent, child) { if (checkParentAvailable(parent, child)) { var firstChild = parent.firstChild; firstChild ? parent.insertBefore(child, firstChild) : parent.appendChild(child); } } function remove(parent, child) { if (child && parent && child.parentNode === parent) { parent.removeChild(child); } } function getTextSvgElement(displayable) { return displayable.__textSvgEl; } function getSvgElement(displayable) { return displayable.__svgEl; } /** * @method constructor SVGPainter * @param {HTMLElement} host * @param {qrenderer.core.Storage} storage * @param {Object} opts */ var SVGPainter = function SVGPainter(host, storage) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var qrId = arguments.length > 3 ? arguments[3] : undefined; opts = dataUtil.extend({}, opts); this._opts = opts; this.host = host; this.storage = storage; this._visibleList = []; var svgRoot = createElement('svg'); svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svgRoot.setAttribute('version', '1.1'); svgRoot.setAttribute('baseProfile', 'full'); svgRoot.style.cssText = 'user-select:none;position:absolute;left:0;top:0;'; this.gradientManager = new GradientManager(qrId, svgRoot); this.clipPathManager = new ClippathManager(qrId, svgRoot); this.shadowManager = new ShadowManager(qrId, svgRoot); this._svgRoot = svgRoot; var div = document.createElement('div'); div.style.cssText = 'overflow:hidden;position:relative'; this._host = div; this.host.appendChild(div); this._host.appendChild(svgRoot); this.resize(opts.width, opts.height); }; SVGPainter.prototype = { constructor: SVGPainter, /** * @method getType */ getType: function getType() { return 'svg'; }, /** * @method getHost */ getHost: function getHost() { return this._host; }, /** * @method getViewportRootOffset */ getViewportRootOffset: function getViewportRootOffset() { var viewportRoot = this.getViewportRoot(); if (viewportRoot) { return { offsetLeft: viewportRoot.offsetLeft || 0, offsetTop: viewportRoot.offsetTop || 0 }; } }, /** * @method refresh */ refresh: function refresh() { var list = this.storage.getDisplayList(true); this._paintList(list); }, /** * @method setBackgroundColor */ setBackgroundColor: function setBackgroundColor(backgroundColor) { // TODO gradient this._host.style.background = backgroundColor; }, /** * @private * @method _paintList */ _paintList: function _paintList(list) { this.gradientManager.markAllUnused(); this.clipPathManager.markAllUnused(); this.shadowManager.markAllUnused(); var svgRoot = this._svgRoot; var visibleList = this._visibleList; var listLen = list.length; var newVisibleList = []; var i; var svgElement; var textSvgElement; for (i = 0; i < listLen; i++) { var displayable = list[i]; var svgProxy = getSvgProxy(displayable); svgElement = getSvgElement(displayable) || getTextSvgElement(displayable); if (!displayable.invisible) { if (displayable.__dirty) { svgProxy && svgProxy.render(displayable); // Update clipPath this.clipPathManager.update(displayable); // Update gradient and shadow if (displayable.style.fill && displayable.style.stroke) { this.gradientManager.update(displayable.style.fill); this.gradientManager.update(displayable.style.stroke); } this.shadowManager.update(svgElement, displayable); displayable.__dirty = false; } newVisibleList.push(displayable); } } var diff = arrayDiff(visibleList, newVisibleList); var prevSvgElement; // First do remove, in case element moved to the head and do remove // after add for (i = 0; i < diff.length; i++) { var item = diff[i]; if (item.removed) { for (var k = 0; k < item.count; k++) { var _displayable = visibleList[item.indices[k]]; svgElement = getSvgElement(_displayable); textSvgElement = getTextSvgElement(_displayable); remove(svgRoot, svgElement); remove(svgRoot, textSvgElement); } } } for (i = 0; i < diff.length; i++) { var _item = diff[i]; if (_item.added) { for (var _k = 0; _k < _item.count; _k++) { var _displayable2 = newVisibleList[_item.indices[_k]]; svgElement = getSvgElement(_displayable2); textSvgElement = getTextSvgElement(_displayable2); prevSvgElement ? insertAfter(svgRoot, svgElement, prevSvgElement) : prepend(svgRoot, svgElement); if (svgElement) { insertAfter(svgRoot, textSvgElement, svgElement); } else if (prevSvgElement) { insertAfter(svgRoot, textSvgElement, prevSvgElement); } else { prepend(svgRoot, textSvgElement); } // Insert text insertAfter(svgRoot, textSvgElement, svgElement); prevSvgElement = textSvgElement || svgElement || prevSvgElement; // qrenderer.Text only create textSvgElement. this.gradientManager.addWithoutUpdate(svgElement || textSvgElement, _displayable2); this.shadowManager.addWithoutUpdate(svgElement || textSvgElement, _displayable2); this.clipPathManager.markUsed(_displayable2); } } else if (!_item.removed) { for (var _k2 = 0; _k2 < _item.count; _k2++) { var _displayable3 = newVisibleList[_item.indices[_k2]]; svgElement = getSvgElement(_displayable3); textSvgElement = getTextSvgElement(_displayable3); svgElement = getSvgElement(_displayable3); textSvgElement = getTextSvgElement(_displayable3); this.gradientManager.markUsed(_displayable3); this.gradientManager.addWithoutUpdate(svgElement || textSvgElement, _displayable3); this.shadowManager.markUsed(_displayable3); this.shadowManager.addWithoutUpdate(svgElement || textSvgElement, _displayable3); this.clipPathManager.markUsed(_displayable3); if (textSvgElement) { // Insert text. insertAfter(svgRoot, textSvgElement, svgElement); } prevSvgElement = svgElement || textSvgElement || prevSvgElement; } } } this.gradientManager.removeUnused(); this.clipPathManager.removeUnused(); this.shadowManager.removeUnused(); this._visibleList = newVisibleList; }, /** * @private * @method _paintList */ _getDefs: function _getDefs(isForceCreating) { var svgRoot = this._svgRoot; var defs = this._svgRoot.getElementsByTagName('defs'); if (defs.length !== 0) { return defs[0]; } // Not exist if (!isForceCreating) { return null; } defs = svgRoot.insertBefore(createElement('defs'), // Create new tag svgRoot.firstChild // Insert in the front of svg ); if (!defs.contains) { // IE doesn't support contains method defs.contains = function (el) { var children = defs.children; if (!children) { return false; } for (var i = children.length - 1; i >= 0; --i) { if (children[i] === el) { return true; } } return false; }; } return defs; }, /** * @method resize */ resize: function resize(width, height) { var viewport = this._host; // FIXME Why ? viewport.style.display = 'none'; // Save input w/h var opts = this._opts; width != null && (opts.width = width); height != null && (opts.height = height); width = this._getSize(0); height = this._getSize(1); viewport.style.display = ''; if (this._width !== width || this._height !== height) { this._width = width; this._height = height; var viewportStyle = viewport.style; viewportStyle.width = width + 'px'; viewportStyle.height = height + 'px'; var svgRoot = this._svgRoot; // Set width by 'svgRoot.width = width' is invalid svgRoot.setAttribute('width', width); svgRoot.setAttribute('height', height); } }, /** * @method getWidth * 获取绘图区域宽度 */ getWidth: function getWidth() { return this._width; }, /** * @method getHeight * 获取绘图区域高度 */ getHeight: function getHeight() { return this._height; }, /** * @private * @method _getSize */ _getSize: function _getSize(whIdx) { var opts = this._opts; var wh = ['width', 'height'][whIdx]; var cwh = ['clientWidth', 'clientHeight'][whIdx]; var plt = ['paddingLeft', 'paddingTop'][whIdx]; var prb = ['paddingRight', 'paddingBottom'][whIdx]; if (opts[wh] != null && opts[wh] !== 'auto') { return parseFloat(opts[wh]); } var host = this.host; // IE8 does not support getComputedStyle, but it use VML. var stl = document.defaultView.getComputedStyle(host); return (host[cwh] || dataUtil.parseInt10(stl[wh]) || dataUtil.parseInt10(host.style[wh])) - (dataUtil.parseInt10(stl[plt]) || 0) - (dataUtil.parseInt10(stl[prb]) || 0) | 0; }, /** * @method dispose */ dispose: function dispose() { this.host.innerHTML = ''; this._svgRoot = this._host = this.storage = null; }, /** * @method clear */ clear: function clear() { if (this._host) { this.host.removeChild(this._host); } }, /** * @method pathToDataUrl */ pathToDataUrl: function pathToDataUrl() { this.refresh(); var html = this._svgRoot.outerHTML; return 'data:image/svg+xml;charset=UTF-8,' + html; } }; // Not supported methods function createMethodNotSupport(method) { return function () { console.log('In SVG mode painter not support method "' + method + '"'); }; } // Unsuppoted methods ['getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'].forEach(function (name, index) { SVGPainter.prototype[name] = createMethodNotSupport(name); }); var _default = SVGPainter; module.exports = _default;