Blame view

node_modules/zrender/src/canvas/helper.ts 3.78 KB
bd028579   易尊强   2/28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  import { LinearGradientObject } from '../graphic/LinearGradient';
  import { RadialGradientObject } from '../graphic/RadialGradient';
  import { GradientObject } from '../graphic/Gradient';
  import { RectLike } from '../core/BoundingRect';
  import Path from '../graphic/Path';
  
  function isSafeNum(num: number) {
      // NaN、Infinity、undefined、'xx'
      return isFinite(num);
  }
  
  export function createLinearGradient(
      this: void,
      ctx: CanvasRenderingContext2D,
      obj: LinearGradientObject,
      rect: RectLike
  ) {
      let x = obj.x == null ? 0 : obj.x;
      let x2 = obj.x2 == null ? 1 : obj.x2;
      let y = obj.y == null ? 0 : obj.y;
      let y2 = obj.y2 == null ? 0 : obj.y2;
  
      if (!obj.global) {
          x = x * rect.width + rect.x;
          x2 = x2 * rect.width + rect.x;
          y = y * rect.height + rect.y;
          y2 = y2 * rect.height + rect.y;
      }
  
      // Fix NaN when rect is Infinity
      x = isSafeNum(x) ? x : 0;
      x2 = isSafeNum(x2) ? x2 : 1;
      y = isSafeNum(y) ? y : 0;
      y2 = isSafeNum(y2) ? y2 : 0;
  
      const canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
  
      return canvasGradient;
  }
  
  export function createRadialGradient(
      this: void,
      ctx: CanvasRenderingContext2D,
      obj: RadialGradientObject,
      rect: RectLike
  ) {
      const width = rect.width;
      const height = rect.height;
      const min = Math.min(width, height);
  
      let x = obj.x == null ? 0.5 : obj.x;
      let y = obj.y == null ? 0.5 : obj.y;
      let r = obj.r == null ? 0.5 : obj.r;
  
      if (!obj.global) {
          x = x * width + rect.x;
          y = y * height + rect.y;
          r = r * min;
      }
  
      x = isSafeNum(x) ? x : 0.5;
      y = isSafeNum(y) ? y : 0.5;
      r = r >= 0 && isSafeNum(r) ? r : 0.5;
  
      const canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
  
      return canvasGradient;
  }
  
  export function getCanvasGradient(this: void, ctx: CanvasRenderingContext2D, obj: GradientObject, rect: RectLike) {
      // TODO Cache?
      const canvasGradient = obj.type === 'radial'
          ? createRadialGradient(ctx, obj as RadialGradientObject, rect)
          : createLinearGradient(ctx, obj as LinearGradientObject, rect);
  
      const colorStops = obj.colorStops;
      for (let i = 0; i < colorStops.length; i++) {
          canvasGradient.addColorStop(
              colorStops[i].offset, colorStops[i].color
          );
      }
      return canvasGradient;
  }
  
  export function isClipPathChanged(clipPaths: Path[], prevClipPaths: Path[]): boolean {
      // displayable.__clipPaths can only be `null`/`undefined` or an non-empty array.
      if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) {
          return false;
      }
      if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {
          return true;
      }
      for (let i = 0; i < clipPaths.length; i++) {
          if (clipPaths[i] !== prevClipPaths[i]) {
              return true;
          }
      }
      return false;
  }
  
  function parseInt10(val: string) {
      return parseInt(val, 10);
  }
  export function getSize(
      root: HTMLElement,
      whIdx: number,
      opts: { width?: number | string, height?: number | string}
  ) {
  
      const wh = ['width', 'height'][whIdx] as 'width' | 'height';
      const cwh = ['clientWidth', 'clientHeight'][whIdx] as 'clientWidth' | 'clientHeight';
      const plt = ['paddingLeft', 'paddingTop'][whIdx] as 'paddingLeft' | 'paddingTop';
      const prb = ['paddingRight', 'paddingBottom'][whIdx] as 'paddingRight' | 'paddingBottom';
  
      if (opts[wh] != null && opts[wh] !== 'auto') {
          return parseFloat(opts[wh] as string);
      }
  
      // IE8 does not support getComputedStyle, but it use VML.
      const stl = document.defaultView.getComputedStyle(root);
  
      return (
          (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
          - (parseInt10(stl[plt]) || 0)
          - (parseInt10(stl[prb]) || 0)
      ) | 0;
  }