// Shared methods of svg and svg-ssr
|
|
import { MatrixArray } from '../core/matrix';
|
import Transformable, { TransformProp } from '../core/Transformable';
|
import { RADIAN_TO_DEGREE, retrieve2, logError, isFunction } from '../core/util';
|
import Displayable from '../graphic/Displayable';
|
import { GradientObject } from '../graphic/Gradient';
|
import { LinearGradientObject } from '../graphic/LinearGradient';
|
import Path from '../graphic/Path';
|
import { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern';
|
import { RadialGradientObject } from '../graphic/RadialGradient';
|
import { parse } from '../tool/color';
|
import env from '../core/env';
|
|
const mathRound = Math.round;
|
|
export function normalizeColor(color: string): { color: string; opacity: number; } {
|
let opacity;
|
if (!color || color === 'transparent') {
|
color = 'none';
|
}
|
else if (typeof color === 'string' && color.indexOf('rgba') > -1) {
|
const arr = parse(color);
|
if (arr) {
|
// TODO use hex?
|
color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')';
|
opacity = arr[3];
|
}
|
}
|
return {
|
color,
|
opacity: opacity == null ? 1 : opacity
|
};
|
}
|
const EPSILON = 1e-4;
|
export function isAroundZero(transform: number) {
|
return transform < EPSILON && transform > -EPSILON;
|
}
|
|
export function round3(transform: number) {
|
return mathRound(transform * 1e3) / 1e3;
|
}
|
export function round4(transform: number) {
|
return mathRound(transform * 1e4) / 1e4;
|
}
|
export function round1(transform: number) {
|
return mathRound(transform * 10) / 10;
|
}
|
|
export function getMatrixStr(m: MatrixArray) {
|
return 'matrix('
|
// Avoid large string of matrix
|
// PENDING If have precision issue when scaled
|
+ round3(m[0]) + ','
|
+ round3(m[1]) + ','
|
+ round3(m[2]) + ','
|
+ round3(m[3]) + ','
|
+ round4(m[4]) + ','
|
+ round4(m[5])
|
+ ')';
|
}
|
|
export const TEXT_ALIGN_TO_ANCHOR = {
|
left: 'start',
|
right: 'end',
|
center: 'middle',
|
middle: 'middle'
|
};
|
|
export function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number {
|
// TODO Other baselines.
|
if (textBaseline === 'top') {
|
y += lineHeight / 2;
|
}
|
else if (textBaseline === 'bottom') {
|
y -= lineHeight / 2;
|
}
|
return y;
|
}
|
|
|
export function hasShadow(style: Displayable['style']) {
|
// TODO: textBoxShadowBlur is not supported yet
|
return style
|
&& (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY);
|
}
|
|
export function getShadowKey(displayable: Displayable) {
|
const style = displayable.style;
|
const globalScale = displayable.getGlobalScale();
|
return [
|
style.shadowColor,
|
(style.shadowBlur || 0).toFixed(2), // Reduce the precision
|
(style.shadowOffsetX || 0).toFixed(2),
|
(style.shadowOffsetY || 0).toFixed(2),
|
globalScale[0],
|
globalScale[1]
|
].join(',');
|
}
|
|
export function getClipPathsKey(clipPaths: Path[]) {
|
let key: number[] = [];
|
if (clipPaths) {
|
for (let i = 0; i < clipPaths.length; i++) {
|
const clipPath = clipPaths[i];
|
key.push(clipPath.id);
|
}
|
}
|
return key.join(',');
|
}
|
|
export function isImagePattern(val: any): val is ImagePatternObject {
|
return val && (!!(val as ImagePatternObject).image);
|
}
|
export function isSVGPattern(val: any): val is SVGPatternObject {
|
return val && (!!(val as SVGPatternObject).svgElement);
|
}
|
export function isPattern(val: any): val is PatternObject {
|
return isImagePattern(val) || isSVGPattern(val);
|
}
|
|
export function isLinearGradient(val: GradientObject): val is LinearGradientObject {
|
return val.type === 'linear';
|
}
|
|
export function isRadialGradient(val: GradientObject): val is RadialGradientObject {
|
return val.type === 'radial';
|
}
|
|
export function isGradient(val: any): val is GradientObject {
|
return val && (
|
(val as GradientObject).type === 'linear'
|
|| (val as GradientObject).type === 'radial'
|
);
|
}
|
|
export function getIdURL(id: string) {
|
return `url(#${id})`;
|
}
|
|
export function getPathPrecision(el: Path) {
|
const scale = el.getGlobalScale();
|
const size = Math.max(scale[0], scale[1]);
|
return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1);
|
}
|
|
export function getSRTTransformString(
|
transform: Partial<Pick<Transformable, TransformProp>>
|
) {
|
const x = transform.x || 0;
|
const y = transform.y || 0;
|
const rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE;
|
const scaleX = retrieve2(transform.scaleX, 1);
|
const scaleY = retrieve2(transform.scaleY, 1);
|
const skewX = transform.skewX || 0;
|
const skewY = transform.skewY || 0;
|
const res = [];
|
if (x || y) {
|
// TODO not using px unit?
|
res.push(`translate(${x}px,${y}px)`);
|
}
|
if (rotation) {
|
res.push(`rotate(${rotation})`);
|
}
|
if (scaleX !== 1 || scaleY !== 1) {
|
res.push(`scale(${scaleX},${scaleY})`);
|
}
|
if (skewX || skewY) {
|
res.push(`skew(${mathRound(skewX * RADIAN_TO_DEGREE)}deg, ${mathRound(skewY * RADIAN_TO_DEGREE)}deg)`);
|
}
|
|
return res.join(' ');
|
}
|
|
export const encodeBase64 = (function () {
|
if (env.hasGlobalWindow && isFunction(window.btoa)) {
|
return function (str: string) {
|
return window.btoa(unescape(str));
|
};
|
}
|
if (typeof Buffer !== 'undefined') {
|
return function (str: string) {
|
return Buffer.from(str).toString('base64');
|
};
|
}
|
return function (str: string): string {
|
if (process.env.NODE_ENV !== 'production') {
|
logError('Base64 isn\'t natively supported in the current environment.');
|
}
|
return null;
|
};
|
})();
|