You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
10 KiB
314 lines
10 KiB
import { createElement } from './core';
|
|
import ZRImage from '../graphic/Image';
|
|
import { DEFAULT_FONT, getLineHeight } from '../contain/text';
|
|
import { map } from '../core/util';
|
|
import { normalizeLineDash } from '../graphic/helper/dashStyle';
|
|
var NONE = 'none';
|
|
var mathRound = Math.round;
|
|
var mathSin = Math.sin;
|
|
var mathCos = Math.cos;
|
|
var PI = Math.PI;
|
|
var PI2 = Math.PI * 2;
|
|
var degree = 180 / PI;
|
|
var EPSILON = 1e-4;
|
|
function round3(val) {
|
|
return mathRound(val * 1e3) / 1e3;
|
|
}
|
|
function round4(val) {
|
|
return mathRound(val * 1e4) / 1e4;
|
|
}
|
|
function isAroundZero(val) {
|
|
return val < EPSILON && val > -EPSILON;
|
|
}
|
|
function pathHasFill(style) {
|
|
var fill = style.fill;
|
|
return fill != null && fill !== NONE;
|
|
}
|
|
function pathHasStroke(style) {
|
|
var stroke = style.stroke;
|
|
return stroke != null && stroke !== NONE;
|
|
}
|
|
function setTransform(svgEl, m) {
|
|
if (m) {
|
|
attr(svgEl, 'transform', 'matrix('
|
|
+ round3(m[0]) + ','
|
|
+ round3(m[1]) + ','
|
|
+ round3(m[2]) + ','
|
|
+ round3(m[3]) + ','
|
|
+ round4(m[4]) + ','
|
|
+ round4(m[5])
|
|
+ ')');
|
|
}
|
|
}
|
|
function attr(el, key, val) {
|
|
if (!val || val.type !== 'linear' && val.type !== 'radial') {
|
|
el.setAttribute(key, val);
|
|
}
|
|
}
|
|
function attrXLink(el, key, val) {
|
|
el.setAttributeNS('http://www.w3.org/1999/xlink', key, val);
|
|
}
|
|
function attrXML(el, key, val) {
|
|
el.setAttributeNS('http://www.w3.org/XML/1998/namespace', key, val);
|
|
}
|
|
function bindStyle(svgEl, style, el) {
|
|
var opacity = style.opacity == null ? 1 : style.opacity;
|
|
if (el instanceof ZRImage) {
|
|
svgEl.style.opacity = opacity + '';
|
|
return;
|
|
}
|
|
if (pathHasFill(style)) {
|
|
var fill = style.fill;
|
|
fill = fill === 'transparent' ? NONE : fill;
|
|
attr(svgEl, 'fill', fill);
|
|
attr(svgEl, 'fill-opacity', (style.fillOpacity != null ? style.fillOpacity * opacity : opacity) + '');
|
|
}
|
|
else {
|
|
attr(svgEl, 'fill', NONE);
|
|
}
|
|
if (pathHasStroke(style)) {
|
|
var stroke = style.stroke;
|
|
stroke = stroke === 'transparent' ? NONE : stroke;
|
|
attr(svgEl, 'stroke', stroke);
|
|
var strokeWidth = style.lineWidth;
|
|
var strokeScale_1 = style.strokeNoScale
|
|
? el.getLineScale()
|
|
: 1;
|
|
attr(svgEl, 'stroke-width', (strokeScale_1 ? strokeWidth / strokeScale_1 : 0) + '');
|
|
attr(svgEl, 'paint-order', style.strokeFirst ? 'stroke' : 'fill');
|
|
attr(svgEl, 'stroke-opacity', (style.strokeOpacity != null ? style.strokeOpacity * opacity : opacity) + '');
|
|
var lineDash = style.lineDash && strokeWidth > 0 && normalizeLineDash(style.lineDash, strokeWidth);
|
|
if (lineDash) {
|
|
var lineDashOffset = style.lineDashOffset;
|
|
if (strokeScale_1 && strokeScale_1 !== 1) {
|
|
lineDash = map(lineDash, function (rawVal) {
|
|
return rawVal / strokeScale_1;
|
|
});
|
|
if (lineDashOffset) {
|
|
lineDashOffset /= strokeScale_1;
|
|
lineDashOffset = mathRound(lineDashOffset);
|
|
}
|
|
}
|
|
attr(svgEl, 'stroke-dasharray', lineDash.join(','));
|
|
attr(svgEl, 'stroke-dashoffset', (lineDashOffset || 0) + '');
|
|
}
|
|
else {
|
|
attr(svgEl, 'stroke-dasharray', '');
|
|
}
|
|
style.lineCap && attr(svgEl, 'stroke-linecap', style.lineCap);
|
|
style.lineJoin && attr(svgEl, 'stroke-linejoin', style.lineJoin);
|
|
style.miterLimit && attr(svgEl, 'stroke-miterlimit', style.miterLimit + '');
|
|
}
|
|
else {
|
|
attr(svgEl, 'stroke', NONE);
|
|
}
|
|
}
|
|
var SVGPathRebuilder = (function () {
|
|
function SVGPathRebuilder() {
|
|
}
|
|
SVGPathRebuilder.prototype.reset = function () {
|
|
this._d = [];
|
|
this._str = '';
|
|
};
|
|
SVGPathRebuilder.prototype.moveTo = function (x, y) {
|
|
this._add('M', x, y);
|
|
};
|
|
SVGPathRebuilder.prototype.lineTo = function (x, y) {
|
|
this._add('L', x, y);
|
|
};
|
|
SVGPathRebuilder.prototype.bezierCurveTo = function (x, y, x2, y2, x3, y3) {
|
|
this._add('C', x, y, x2, y2, x3, y3);
|
|
};
|
|
SVGPathRebuilder.prototype.quadraticCurveTo = function (x, y, x2, y2) {
|
|
this._add('Q', x, y, x2, y2);
|
|
};
|
|
SVGPathRebuilder.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {
|
|
this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise);
|
|
};
|
|
SVGPathRebuilder.prototype.ellipse = function (cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise) {
|
|
var firstCmd = this._d.length === 0;
|
|
var dTheta = endAngle - startAngle;
|
|
var clockwise = !anticlockwise;
|
|
var dThetaPositive = Math.abs(dTheta);
|
|
var isCircle = isAroundZero(dThetaPositive - PI2)
|
|
|| (clockwise ? dTheta >= PI2 : -dTheta >= PI2);
|
|
var unifiedTheta = dTheta > 0 ? dTheta % PI2 : (dTheta % PI2 + PI2);
|
|
var large = false;
|
|
if (isCircle) {
|
|
large = true;
|
|
}
|
|
else if (isAroundZero(dThetaPositive)) {
|
|
large = false;
|
|
}
|
|
else {
|
|
large = (unifiedTheta >= PI) === !!clockwise;
|
|
}
|
|
var x0 = round4(cx + rx * mathCos(startAngle));
|
|
var y0 = round4(cy + ry * mathSin(startAngle));
|
|
if (isCircle) {
|
|
if (clockwise) {
|
|
dTheta = PI2 - 1e-4;
|
|
}
|
|
else {
|
|
dTheta = -PI2 + 1e-4;
|
|
}
|
|
large = true;
|
|
if (firstCmd) {
|
|
this._d.push('M', x0, y0);
|
|
}
|
|
}
|
|
var x = round4(cx + rx * mathCos(startAngle + dTheta));
|
|
var y = round4(cy + ry * mathSin(startAngle + dTheta));
|
|
if (isNaN(x0) || isNaN(y0) || isNaN(rx) || isNaN(ry) || isNaN(psi) || isNaN(degree) || isNaN(x) || isNaN(y)) {
|
|
return '';
|
|
}
|
|
this._d.push('A', round4(rx), round4(ry), mathRound(psi * degree), +large, +clockwise, x, y);
|
|
};
|
|
SVGPathRebuilder.prototype.rect = function (x, y, w, h) {
|
|
this._add('M', x, y);
|
|
this._add('L', x + w, y);
|
|
this._add('L', x + w, y + h);
|
|
this._add('L', x, y + h);
|
|
this._add('L', x, y);
|
|
};
|
|
SVGPathRebuilder.prototype.closePath = function () {
|
|
if (this._d.length > 0) {
|
|
this._add('Z');
|
|
}
|
|
};
|
|
SVGPathRebuilder.prototype._add = function (cmd, a, b, c, d, e, f, g, h) {
|
|
this._d.push(cmd);
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
var val = arguments[i];
|
|
if (isNaN(val)) {
|
|
this._invalid = true;
|
|
return;
|
|
}
|
|
this._d.push(round4(val));
|
|
}
|
|
};
|
|
SVGPathRebuilder.prototype.generateStr = function () {
|
|
this._str = this._invalid ? '' : this._d.join(' ');
|
|
this._d = [];
|
|
};
|
|
SVGPathRebuilder.prototype.getStr = function () {
|
|
return this._str;
|
|
};
|
|
return SVGPathRebuilder;
|
|
}());
|
|
var svgPath = {
|
|
brush: function (el) {
|
|
var style = el.style;
|
|
var svgEl = el.__svgEl;
|
|
if (!svgEl) {
|
|
svgEl = createElement('path');
|
|
el.__svgEl = svgEl;
|
|
}
|
|
if (!el.path) {
|
|
el.createPathProxy();
|
|
}
|
|
var path = el.path;
|
|
if (el.shapeChanged()) {
|
|
path.beginPath();
|
|
el.buildPath(path, el.shape);
|
|
el.pathUpdated();
|
|
}
|
|
var pathVersion = path.getVersion();
|
|
var elExt = el;
|
|
var svgPathBuilder = elExt.__svgPathBuilder;
|
|
if (elExt.__svgPathVersion !== pathVersion || !svgPathBuilder || el.style.strokePercent < 1) {
|
|
if (!svgPathBuilder) {
|
|
svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder();
|
|
}
|
|
svgPathBuilder.reset();
|
|
path.rebuildPath(svgPathBuilder, el.style.strokePercent);
|
|
svgPathBuilder.generateStr();
|
|
elExt.__svgPathVersion = pathVersion;
|
|
}
|
|
attr(svgEl, 'd', svgPathBuilder.getStr());
|
|
bindStyle(svgEl, style, el);
|
|
setTransform(svgEl, el.transform);
|
|
}
|
|
};
|
|
export { svgPath as path };
|
|
var svgImage = {
|
|
brush: function (el) {
|
|
var style = el.style;
|
|
var image = style.image;
|
|
if (image instanceof HTMLImageElement) {
|
|
image = image.src;
|
|
}
|
|
else if (image instanceof HTMLCanvasElement) {
|
|
image = image.toDataURL();
|
|
}
|
|
if (!image) {
|
|
return;
|
|
}
|
|
var x = style.x || 0;
|
|
var y = style.y || 0;
|
|
var dw = style.width;
|
|
var dh = style.height;
|
|
var svgEl = el.__svgEl;
|
|
if (!svgEl) {
|
|
svgEl = createElement('image');
|
|
el.__svgEl = svgEl;
|
|
}
|
|
if (image !== el.__imageSrc) {
|
|
attrXLink(svgEl, 'href', image);
|
|
el.__imageSrc = image;
|
|
}
|
|
attr(svgEl, 'width', dw + '');
|
|
attr(svgEl, 'height', dh + '');
|
|
attr(svgEl, 'x', x + '');
|
|
attr(svgEl, 'y', y + '');
|
|
bindStyle(svgEl, style, el);
|
|
setTransform(svgEl, el.transform);
|
|
}
|
|
};
|
|
export { svgImage as image };
|
|
var TEXT_ALIGN_TO_ANCHOR = {
|
|
left: 'start',
|
|
right: 'end',
|
|
center: 'middle',
|
|
middle: 'middle'
|
|
};
|
|
function adjustTextY(y, lineHeight, textBaseline) {
|
|
if (textBaseline === 'top') {
|
|
y += lineHeight / 2;
|
|
}
|
|
else if (textBaseline === 'bottom') {
|
|
y -= lineHeight / 2;
|
|
}
|
|
return y;
|
|
}
|
|
var svgText = {
|
|
brush: function (el) {
|
|
var style = el.style;
|
|
var text = style.text;
|
|
text != null && (text += '');
|
|
if (!text || isNaN(style.x) || isNaN(style.y)) {
|
|
return;
|
|
}
|
|
var textSvgEl = el.__svgEl;
|
|
if (!textSvgEl) {
|
|
textSvgEl = createElement('text');
|
|
attrXML(textSvgEl, 'xml:space', 'preserve');
|
|
el.__svgEl = textSvgEl;
|
|
}
|
|
var font = style.font || DEFAULT_FONT;
|
|
var textSvgElStyle = textSvgEl.style;
|
|
textSvgElStyle.font = font;
|
|
textSvgEl.textContent = text;
|
|
bindStyle(textSvgEl, style, el);
|
|
setTransform(textSvgEl, el.transform);
|
|
var x = style.x || 0;
|
|
var y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline);
|
|
var textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign]
|
|
|| style.textAlign;
|
|
attr(textSvgEl, 'dominant-baseline', 'central');
|
|
attr(textSvgEl, 'text-anchor', textAlign);
|
|
attr(textSvgEl, 'x', x + '');
|
|
attr(textSvgEl, 'y', y + '');
|
|
}
|
|
};
|
|
export { svgText as text };
|
|
|