project/node_modules/zrender/lib/svg/graphic.js

481 lines
17 KiB
JavaScript

import { adjustTextY, getIdURL, getMatrixStr, getPathPrecision, getShadowKey, getSRTTransformString, hasShadow, isAroundZero, isGradient, isImagePattern, isLinearGradient, isPattern, isRadialGradient, normalizeColor, round4, TEXT_ALIGN_TO_ANCHOR } from './helper.js';
import Path from '../graphic/Path.js';
import ZRImage from '../graphic/Image.js';
import { getLineHeight } from '../contain/text.js';
import TSpan from '../graphic/TSpan.js';
import SVGPathRebuilder from './SVGPathRebuilder.js';
import mapStyleToAttrs from './mapStyleToAttrs.js';
import { createVNode, vNodeToString, META_DATA_PREFIX } from './core.js';
import { assert, clone, isFunction, isString, logError, map, retrieve2 } from '../core/util.js';
import { createOrUpdateImage } from '../graphic/helper/image.js';
import { createCSSAnimation } from './cssAnimation.js';
import { hasSeparateFont, parseFontSize } from '../graphic/Text.js';
import { DEFAULT_FONT, DEFAULT_FONT_FAMILY } from '../core/platform.js';
import { createCSSEmphasis } from './cssEmphasis.js';
import { getElementSSRData } from '../zrender.js';
var round = Math.round;
function isImageLike(val) {
return val && isString(val.src);
}
function isCanvasLike(val) {
return val && isFunction(val.toDataURL);
}
function setStyleAttrs(attrs, style, el, scope) {
mapStyleToAttrs(function (key, val) {
var isFillStroke = key === 'fill' || key === 'stroke';
if (isFillStroke && isGradient(val)) {
setGradient(style, attrs, key, scope);
}
else if (isFillStroke && isPattern(val)) {
setPattern(el, attrs, key, scope);
}
else if (isFillStroke && val === 'none') {
attrs[key] = 'transparent';
}
else {
attrs[key] = val;
}
}, style, el, false);
setShadow(el, attrs, scope);
}
function setMetaData(attrs, el) {
var metaData = getElementSSRData(el);
if (metaData) {
metaData.each(function (val, key) {
val != null && (attrs[(META_DATA_PREFIX + key).toLowerCase()] = val + '');
});
if (el.isSilent()) {
attrs[META_DATA_PREFIX + 'silent'] = 'true';
}
}
}
function noRotateScale(m) {
return isAroundZero(m[0] - 1)
&& isAroundZero(m[1])
&& isAroundZero(m[2])
&& isAroundZero(m[3] - 1);
}
function noTranslate(m) {
return isAroundZero(m[4]) && isAroundZero(m[5]);
}
function setTransform(attrs, m, compress) {
if (m && !(noTranslate(m) && noRotateScale(m))) {
var mul = compress ? 10 : 1e4;
attrs.transform = noRotateScale(m)
? "translate(" + round(m[4] * mul) / mul + " " + round(m[5] * mul) / mul + ")" : getMatrixStr(m);
}
}
function convertPolyShape(shape, attrs, mul) {
var points = shape.points;
var strArr = [];
for (var i = 0; i < points.length; i++) {
strArr.push(round(points[i][0] * mul) / mul);
strArr.push(round(points[i][1] * mul) / mul);
}
attrs.points = strArr.join(' ');
}
function validatePolyShape(shape) {
return !shape.smooth;
}
function createAttrsConvert(desc) {
var normalizedDesc = map(desc, function (item) {
return (typeof item === 'string' ? [item, item] : item);
});
return function (shape, attrs, mul) {
for (var i = 0; i < normalizedDesc.length; i++) {
var item = normalizedDesc[i];
var val = shape[item[0]];
if (val != null) {
attrs[item[1]] = round(val * mul) / mul;
}
}
};
}
var builtinShapesDef = {
circle: [createAttrsConvert(['cx', 'cy', 'r'])],
polyline: [convertPolyShape, validatePolyShape],
polygon: [convertPolyShape, validatePolyShape]
};
function hasShapeAnimation(el) {
var animators = el.animators;
for (var i = 0; i < animators.length; i++) {
if (animators[i].targetName === 'shape') {
return true;
}
}
return false;
}
export function brushSVGPath(el, scope) {
var style = el.style;
var shape = el.shape;
var builtinShpDef = builtinShapesDef[el.type];
var attrs = {};
var needsAnimate = scope.animation;
var svgElType = 'path';
var strokePercent = el.style.strokePercent;
var precision = (scope.compress && getPathPrecision(el)) || 4;
if (builtinShpDef
&& !scope.willUpdate
&& !(builtinShpDef[1] && !builtinShpDef[1](shape))
&& !(needsAnimate && hasShapeAnimation(el))
&& !(strokePercent < 1)) {
svgElType = el.type;
var mul = Math.pow(10, precision);
builtinShpDef[0](shape, attrs, mul);
}
else {
var needBuildPath = !el.path || el.shapeChanged();
if (!el.path) {
el.createPathProxy();
}
var path = el.path;
if (needBuildPath) {
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
|| strokePercent !== elExt.__svgPathStrokePercent) {
if (!svgPathBuilder) {
svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder();
}
svgPathBuilder.reset(precision);
path.rebuildPath(svgPathBuilder, strokePercent);
svgPathBuilder.generateStr();
elExt.__svgPathVersion = pathVersion;
elExt.__svgPathStrokePercent = strokePercent;
}
attrs.d = svgPathBuilder.getStr();
}
setTransform(attrs, el.transform);
setStyleAttrs(attrs, style, el, scope);
setMetaData(attrs, el);
scope.animation && createCSSAnimation(el, attrs, scope);
scope.emphasis && createCSSEmphasis(el, attrs, scope);
return createVNode(svgElType, el.id + '', attrs);
}
export function brushSVGImage(el, scope) {
var style = el.style;
var image = style.image;
if (image && !isString(image)) {
if (isImageLike(image)) {
image = image.src;
}
else if (isCanvasLike(image)) {
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 attrs = {
href: image,
width: dw,
height: dh
};
if (x) {
attrs.x = x;
}
if (y) {
attrs.y = y;
}
setTransform(attrs, el.transform);
setStyleAttrs(attrs, style, el, scope);
setMetaData(attrs, el);
scope.animation && createCSSAnimation(el, attrs, scope);
return createVNode('image', el.id + '', attrs);
}
;
export function brushSVGTSpan(el, scope) {
var style = el.style;
var text = style.text;
text != null && (text += '');
if (!text || isNaN(style.x) || isNaN(style.y)) {
return;
}
var font = style.font || DEFAULT_FONT;
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;
var attrs = {
'dominant-baseline': 'central',
'text-anchor': textAlign
};
if (hasSeparateFont(style)) {
var separatedFontStr = '';
var fontStyle = style.fontStyle;
var fontSize = parseFontSize(style.fontSize);
if (!parseFloat(fontSize)) {
return;
}
var fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY;
var fontWeight = style.fontWeight;
separatedFontStr += "font-size:" + fontSize + ";font-family:" + fontFamily + ";";
if (fontStyle && fontStyle !== 'normal') {
separatedFontStr += "font-style:" + fontStyle + ";";
}
if (fontWeight && fontWeight !== 'normal') {
separatedFontStr += "font-weight:" + fontWeight + ";";
}
attrs.style = separatedFontStr;
}
else {
attrs.style = "font: " + font;
}
if (text.match(/\s/)) {
attrs['xml:space'] = 'preserve';
}
if (x) {
attrs.x = x;
}
if (y) {
attrs.y = y;
}
setTransform(attrs, el.transform);
setStyleAttrs(attrs, style, el, scope);
setMetaData(attrs, el);
scope.animation && createCSSAnimation(el, attrs, scope);
return createVNode('text', el.id + '', attrs, undefined, text);
}
export function brush(el, scope) {
if (el instanceof Path) {
return brushSVGPath(el, scope);
}
else if (el instanceof ZRImage) {
return brushSVGImage(el, scope);
}
else if (el instanceof TSpan) {
return brushSVGTSpan(el, scope);
}
}
function setShadow(el, attrs, scope) {
var style = el.style;
if (hasShadow(style)) {
var shadowKey = getShadowKey(el);
var shadowCache = scope.shadowCache;
var shadowId = shadowCache[shadowKey];
if (!shadowId) {
var globalScale = el.getGlobalScale();
var scaleX = globalScale[0];
var scaleY = globalScale[1];
if (!scaleX || !scaleY) {
return;
}
var offsetX = style.shadowOffsetX || 0;
var offsetY = style.shadowOffsetY || 0;
var blur_1 = style.shadowBlur;
var _a = normalizeColor(style.shadowColor), opacity = _a.opacity, color = _a.color;
var stdDx = blur_1 / 2 / scaleX;
var stdDy = blur_1 / 2 / scaleY;
var stdDeviation = stdDx + ' ' + stdDy;
shadowId = scope.zrId + '-s' + scope.shadowIdx++;
scope.defs[shadowId] = createVNode('filter', shadowId, {
'id': shadowId,
'x': '-100%',
'y': '-100%',
'width': '300%',
'height': '300%'
}, [
createVNode('feDropShadow', '', {
'dx': offsetX / scaleX,
'dy': offsetY / scaleY,
'stdDeviation': stdDeviation,
'flood-color': color,
'flood-opacity': opacity
})
]);
shadowCache[shadowKey] = shadowId;
}
attrs.filter = getIdURL(shadowId);
}
}
export function setGradient(style, attrs, target, scope) {
var val = style[target];
var gradientTag;
var gradientAttrs = {
'gradientUnits': val.global
? 'userSpaceOnUse'
: 'objectBoundingBox'
};
if (isLinearGradient(val)) {
gradientTag = 'linearGradient';
gradientAttrs.x1 = val.x;
gradientAttrs.y1 = val.y;
gradientAttrs.x2 = val.x2;
gradientAttrs.y2 = val.y2;
}
else if (isRadialGradient(val)) {
gradientTag = 'radialGradient';
gradientAttrs.cx = retrieve2(val.x, 0.5);
gradientAttrs.cy = retrieve2(val.y, 0.5);
gradientAttrs.r = retrieve2(val.r, 0.5);
}
else {
if (process.env.NODE_ENV !== 'production') {
logError('Illegal gradient type.');
}
return;
}
var colors = val.colorStops;
var colorStops = [];
for (var i = 0, len = colors.length; i < len; ++i) {
var offset = round4(colors[i].offset) * 100 + '%';
var stopColor = colors[i].color;
var _a = normalizeColor(stopColor), color = _a.color, opacity = _a.opacity;
var stopsAttrs = {
'offset': offset
};
stopsAttrs['stop-color'] = color;
if (opacity < 1) {
stopsAttrs['stop-opacity'] = opacity;
}
colorStops.push(createVNode('stop', i + '', stopsAttrs));
}
var gradientVNode = createVNode(gradientTag, '', gradientAttrs, colorStops);
var gradientKey = vNodeToString(gradientVNode);
var gradientCache = scope.gradientCache;
var gradientId = gradientCache[gradientKey];
if (!gradientId) {
gradientId = scope.zrId + '-g' + scope.gradientIdx++;
gradientCache[gradientKey] = gradientId;
gradientAttrs.id = gradientId;
scope.defs[gradientId] = createVNode(gradientTag, gradientId, gradientAttrs, colorStops);
}
attrs[target] = getIdURL(gradientId);
}
export function setPattern(el, attrs, target, scope) {
var val = el.style[target];
var boundingRect = el.getBoundingRect();
var patternAttrs = {};
var repeat = val.repeat;
var noRepeat = repeat === 'no-repeat';
var repeatX = repeat === 'repeat-x';
var repeatY = repeat === 'repeat-y';
var child;
if (isImagePattern(val)) {
var imageWidth_1 = val.imageWidth;
var imageHeight_1 = val.imageHeight;
var imageSrc = void 0;
var patternImage = val.image;
if (isString(patternImage)) {
imageSrc = patternImage;
}
else if (isImageLike(patternImage)) {
imageSrc = patternImage.src;
}
else if (isCanvasLike(patternImage)) {
imageSrc = patternImage.toDataURL();
}
if (typeof Image === 'undefined') {
var errMsg = 'Image width/height must been given explictly in svg-ssr renderer.';
assert(imageWidth_1, errMsg);
assert(imageHeight_1, errMsg);
}
else if (imageWidth_1 == null || imageHeight_1 == null) {
var setSizeToVNode_1 = function (vNode, img) {
if (vNode) {
var svgEl = vNode.elm;
var width = imageWidth_1 || img.width;
var height = imageHeight_1 || img.height;
if (vNode.tag === 'pattern') {
if (repeatX) {
height = 1;
width /= boundingRect.width;
}
else if (repeatY) {
width = 1;
height /= boundingRect.height;
}
}
vNode.attrs.width = width;
vNode.attrs.height = height;
if (svgEl) {
svgEl.setAttribute('width', width);
svgEl.setAttribute('height', height);
}
}
};
var createdImage = createOrUpdateImage(imageSrc, null, el, function (img) {
noRepeat || setSizeToVNode_1(patternVNode, img);
setSizeToVNode_1(child, img);
});
if (createdImage && createdImage.width && createdImage.height) {
imageWidth_1 = imageWidth_1 || createdImage.width;
imageHeight_1 = imageHeight_1 || createdImage.height;
}
}
child = createVNode('image', 'img', {
href: imageSrc,
width: imageWidth_1,
height: imageHeight_1
});
patternAttrs.width = imageWidth_1;
patternAttrs.height = imageHeight_1;
}
else if (val.svgElement) {
child = clone(val.svgElement);
patternAttrs.width = val.svgWidth;
patternAttrs.height = val.svgHeight;
}
if (!child) {
return;
}
var patternWidth;
var patternHeight;
if (noRepeat) {
patternWidth = patternHeight = 1;
}
else if (repeatX) {
patternHeight = 1;
patternWidth = patternAttrs.width / boundingRect.width;
}
else if (repeatY) {
patternWidth = 1;
patternHeight = patternAttrs.height / boundingRect.height;
}
else {
patternAttrs.patternUnits = 'userSpaceOnUse';
}
if (patternWidth != null && !isNaN(patternWidth)) {
patternAttrs.width = patternWidth;
}
if (patternHeight != null && !isNaN(patternHeight)) {
patternAttrs.height = patternHeight;
}
var patternTransform = getSRTTransformString(val);
patternTransform && (patternAttrs.patternTransform = patternTransform);
var patternVNode = createVNode('pattern', '', patternAttrs, [child]);
var patternKey = vNodeToString(patternVNode);
var patternCache = scope.patternCache;
var patternId = patternCache[patternKey];
if (!patternId) {
patternId = scope.zrId + '-p' + scope.patternIdx++;
patternCache[patternKey] = patternId;
patternAttrs.id = patternId;
patternVNode = scope.defs[patternId] = createVNode('pattern', patternId, patternAttrs, [child]);
}
attrs[target] = getIdURL(patternId);
}
export function setClipPath(clipPath, attrs, scope) {
var clipPathCache = scope.clipPathCache, defs = scope.defs;
var clipPathId = clipPathCache[clipPath.id];
if (!clipPathId) {
clipPathId = scope.zrId + '-c' + scope.clipPathIdx++;
var clipPathAttrs = {
id: clipPathId
};
clipPathCache[clipPath.id] = clipPathId;
defs[clipPathId] = createVNode('clipPath', clipPathId, clipPathAttrs, [brushSVGPath(clipPath, scope)]);
}
attrs['clip-path'] = getIdURL(clipPathId);
}