|
import Path, { DEFAULT_PATH_STYLE, PathStyleProps } from '../graphic/Path';
|
import ZRImage, { ImageStyleProps } from '../graphic/Image';
|
import TSpan, { TSpanStyleProps } from '../graphic/TSpan';
|
import { getLineDash } from '../canvas/dashStyle';
|
import { map } from '../core/util';
|
import { normalizeColor } from './helper';
|
|
type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps;
|
|
const NONE = 'none';
|
const mathRound = Math.round;
|
|
function pathHasFill(style: AllStyleOption): style is PathStyleProps {
|
const fill = (style as PathStyleProps).fill;
|
return fill != null && fill !== NONE;
|
}
|
|
function pathHasStroke(style: AllStyleOption): style is PathStyleProps {
|
const stroke = (style as PathStyleProps).stroke;
|
return stroke != null && stroke !== NONE;
|
}
|
|
const strokeProps = ['lineCap', 'miterLimit', 'lineJoin'] as const;
|
const svgStrokeProps = map(strokeProps, prop => `stroke-${prop.toLowerCase()}`);
|
|
export default function mapStyleToAttrs(
|
updateAttr: (key: string, val: string | number) => void,
|
style: AllStyleOption,
|
el: Path | TSpan | ZRImage,
|
/**
|
* Will try not to set the attribute if it's using default value if not using forceUpdate.
|
* Mainly for reduce the generated size in svg-ssr mode.
|
*/
|
forceUpdate: boolean
|
): void {
|
const opacity = style.opacity == null ? 1 : style.opacity;
|
|
// only set opacity. stroke and fill cannot be applied to svg image
|
if (el instanceof ZRImage) {
|
updateAttr('opacity', opacity);
|
return;
|
}
|
|
if (pathHasFill(style)) {
|
const fill = normalizeColor(style.fill as string);
|
updateAttr('fill', fill.color);
|
const fillOpacity = style.fillOpacity != null
|
? style.fillOpacity * fill.opacity * opacity
|
: fill.opacity * opacity;
|
if (forceUpdate || fillOpacity < 1) {
|
updateAttr('fill-opacity', fillOpacity);
|
}
|
}
|
else {
|
updateAttr('fill', NONE);
|
}
|
|
if (pathHasStroke(style)) {
|
const stroke = normalizeColor(style.stroke as string);
|
updateAttr('stroke', stroke.color);
|
const strokeScale = style.strokeNoScale
|
? (el as Path).getLineScale()
|
: 1;
|
const strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0);
|
const strokeOpacity = style.strokeOpacity != null
|
? style.strokeOpacity * stroke.opacity * opacity
|
: stroke.opacity * opacity;
|
const strokeFirst = style.strokeFirst;
|
|
if (forceUpdate || strokeWidth !== 1) {
|
updateAttr('stroke-width', strokeWidth);
|
}
|
// stroke then fill for text; fill then stroke for others
|
if (forceUpdate || strokeFirst) {
|
updateAttr('paint-order', strokeFirst ? 'stroke' : 'fill');
|
}
|
if (forceUpdate || strokeOpacity < 1) {
|
updateAttr('stroke-opacity', strokeOpacity);
|
}
|
|
if (style.lineDash) {
|
let [lineDash, lineDashOffset] = getLineDash(el);
|
if (lineDash) {
|
lineDashOffset = mathRound(lineDashOffset || 0);
|
updateAttr('stroke-dasharray', lineDash.join(','));
|
if (lineDashOffset || forceUpdate) {
|
updateAttr('stroke-dashoffset', lineDashOffset);
|
}
|
}
|
}
|
else if (forceUpdate) {
|
// Reset if force update.
|
updateAttr('stroke-dasharray', NONE);
|
}
|
|
// PENDING reset
|
for (let i = 0; i < strokeProps.length; i++) {
|
const propName = strokeProps[i];
|
if (forceUpdate || style[propName] !== DEFAULT_PATH_STYLE[propName]) {
|
const val = style[propName] || DEFAULT_PATH_STYLE[propName];
|
// TODO reset
|
val && updateAttr(svgStrokeProps[i], val);
|
}
|
}
|
}
|
else if (forceUpdate) {
|
updateAttr('stroke', NONE);
|
}
|
}
|