f8fd082910dfabdfae1a9a6778658396957306b697031e564d0f7ce1503b103fcfb6375dc52f4bc61165f0eccc3306a1d4f4a9539239aa24dbbbb137356ffe 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Shared methods of svg and svg-ssr
  2. import { MatrixArray } from '../core/matrix';
  3. import Transformable, { TransformProp } from '../core/Transformable';
  4. import { RADIAN_TO_DEGREE, retrieve2, logError, isFunction } from '../core/util';
  5. import Displayable from '../graphic/Displayable';
  6. import { GradientObject } from '../graphic/Gradient';
  7. import { LinearGradientObject } from '../graphic/LinearGradient';
  8. import Path from '../graphic/Path';
  9. import { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern';
  10. import { RadialGradientObject } from '../graphic/RadialGradient';
  11. import { parse } from '../tool/color';
  12. import env from '../core/env';
  13. const mathRound = Math.round;
  14. export function normalizeColor(color: string): { color: string; opacity: number; } {
  15. let opacity;
  16. if (!color || color === 'transparent') {
  17. color = 'none';
  18. }
  19. else if (typeof color === 'string' && color.indexOf('rgba') > -1) {
  20. const arr = parse(color);
  21. if (arr) {
  22. // TODO use hex?
  23. color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')';
  24. opacity = arr[3];
  25. }
  26. }
  27. return {
  28. color,
  29. opacity: opacity == null ? 1 : opacity
  30. };
  31. }
  32. const EPSILON = 1e-4;
  33. export function isAroundZero(transform: number) {
  34. return transform < EPSILON && transform > -EPSILON;
  35. }
  36. export function round3(transform: number) {
  37. return mathRound(transform * 1e3) / 1e3;
  38. }
  39. export function round4(transform: number) {
  40. return mathRound(transform * 1e4) / 1e4;
  41. }
  42. export function round1(transform: number) {
  43. return mathRound(transform * 10) / 10;
  44. }
  45. export function getMatrixStr(m: MatrixArray) {
  46. return 'matrix('
  47. // Avoid large string of matrix
  48. // PENDING If have precision issue when scaled
  49. + round3(m[0]) + ','
  50. + round3(m[1]) + ','
  51. + round3(m[2]) + ','
  52. + round3(m[3]) + ','
  53. + round4(m[4]) + ','
  54. + round4(m[5])
  55. + ')';
  56. }
  57. export const TEXT_ALIGN_TO_ANCHOR = {
  58. left: 'start',
  59. right: 'end',
  60. center: 'middle',
  61. middle: 'middle'
  62. };
  63. export function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number {
  64. // TODO Other baselines.
  65. if (textBaseline === 'top') {
  66. y += lineHeight / 2;
  67. }
  68. else if (textBaseline === 'bottom') {
  69. y -= lineHeight / 2;
  70. }
  71. return y;
  72. }
  73. export function hasShadow(style: Displayable['style']) {
  74. // TODO: textBoxShadowBlur is not supported yet
  75. return style
  76. && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY);
  77. }
  78. export function getShadowKey(displayable: Displayable) {
  79. const style = displayable.style;
  80. const globalScale = displayable.getGlobalScale();
  81. return [
  82. style.shadowColor,
  83. (style.shadowBlur || 0).toFixed(2), // Reduce the precision
  84. (style.shadowOffsetX || 0).toFixed(2),
  85. (style.shadowOffsetY || 0).toFixed(2),
  86. globalScale[0],
  87. globalScale[1]
  88. ].join(',');
  89. }
  90. export function getClipPathsKey(clipPaths: Path[]) {
  91. let key: number[] = [];
  92. if (clipPaths) {
  93. for (let i = 0; i < clipPaths.length; i++) {
  94. const clipPath = clipPaths[i];
  95. key.push(clipPath.id);
  96. }
  97. }
  98. return key.join(',');
  99. }
  100. export function isImagePattern(val: any): val is ImagePatternObject {
  101. return val && (!!(val as ImagePatternObject).image);
  102. }
  103. export function isSVGPattern(val: any): val is SVGPatternObject {
  104. return val && (!!(val as SVGPatternObject).svgElement);
  105. }
  106. export function isPattern(val: any): val is PatternObject {
  107. return isImagePattern(val) || isSVGPattern(val);
  108. }
  109. export function isLinearGradient(val: GradientObject): val is LinearGradientObject {
  110. return val.type === 'linear';
  111. }
  112. export function isRadialGradient(val: GradientObject): val is RadialGradientObject {
  113. return val.type === 'radial';
  114. }
  115. export function isGradient(val: any): val is GradientObject {
  116. return val && (
  117. (val as GradientObject).type === 'linear'
  118. || (val as GradientObject).type === 'radial'
  119. );
  120. }
  121. export function getIdURL(id: string) {
  122. return `url(#${id})`;
  123. }
  124. export function getPathPrecision(el: Path) {
  125. const scale = el.getGlobalScale();
  126. const size = Math.max(scale[0], scale[1]);
  127. return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1);
  128. }
  129. export function getSRTTransformString(
  130. transform: Partial<Pick<Transformable, TransformProp>>
  131. ) {
  132. const x = transform.x || 0;
  133. const y = transform.y || 0;
  134. const rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE;
  135. const scaleX = retrieve2(transform.scaleX, 1);
  136. const scaleY = retrieve2(transform.scaleY, 1);
  137. const skewX = transform.skewX || 0;
  138. const skewY = transform.skewY || 0;
  139. const res = [];
  140. if (x || y) {
  141. // TODO not using px unit?
  142. res.push(`translate(${x}px,${y}px)`);
  143. }
  144. if (rotation) {
  145. res.push(`rotate(${rotation})`);
  146. }
  147. if (scaleX !== 1 || scaleY !== 1) {
  148. res.push(`scale(${scaleX},${scaleY})`);
  149. }
  150. if (skewX || skewY) {
  151. res.push(`skew(${mathRound(skewX * RADIAN_TO_DEGREE)}deg, ${mathRound(skewY * RADIAN_TO_DEGREE)}deg)`);
  152. }
  153. return res.join(' ');
  154. }
  155. export const encodeBase64 = (function () {
  156. if (env.hasGlobalWindow && isFunction(window.btoa)) {
  157. return function (str: string) {
  158. return window.btoa(unescape(str));
  159. };
  160. }
  161. if (typeof Buffer !== 'undefined') {
  162. return function (str: string) {
  163. return Buffer.from(str).toString('base64');
  164. };
  165. }
  166. return function (str: string): string {
  167. if (process.env.NODE_ENV !== 'production') {
  168. logError('Base64 isn\'t natively supported in the current environment.');
  169. }
  170. return null;
  171. };
  172. })();