@leafer-ui/paint 1.0.0-beta → 1.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafer-ui/paint",
3
- "version": "1.0.0-beta",
3
+ "version": "1.0.0-beta.11",
4
4
  "description": "@leafer-ui/paint",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -19,11 +19,11 @@
19
19
  "leaferjs"
20
20
  ],
21
21
  "dependencies": {
22
- "@leafer/core": "1.0.0-beta",
23
- "@leafer-ui/color": "1.0.0-beta"
22
+ "@leafer/core": "1.0.0-beta.11",
23
+ "@leafer-ui/color": "1.0.0-beta.11"
24
24
  },
25
25
  "devDependencies": {
26
- "@leafer/interface": "1.0.0-beta",
27
- "@leafer-ui/interface": "1.0.0-beta"
26
+ "@leafer/interface": "1.0.0-beta.11",
27
+ "@leafer-ui/interface": "1.0.0-beta.11"
28
28
  }
29
29
  }
package/src/Compute.ts CHANGED
@@ -1,34 +1,32 @@
1
- import { IUI, IPaint, ILeafPaint, IRGB } from '@leafer-ui/interface'
1
+ import { IUI, IPaint, ILeafPaint, IRGB, IBooleanMap } from '@leafer-ui/interface'
2
2
  import { ColorConvert } from '@leafer-ui/color'
3
3
 
4
- import { image } from "./paint/image"
4
+ import { image } from "./paint/image/image"
5
5
  import { linearGradient } from './paint/linear'
6
6
  import { radialGradient } from "./paint/radial"
7
7
  import { conicGradient } from "./paint/conic"
8
8
 
9
9
 
10
- export function computeFill(ui: IUI): void {
11
- compute(ui, 'fill')
12
- }
13
-
14
- export function computeStroke(ui: IUI): void {
15
- compute(ui, 'stroke')
16
- }
10
+ let recycleMap: IBooleanMap
17
11
 
18
- function compute(ui: IUI, attrName: string): void {
12
+ export function compute(ui: IUI, attrName: string): void {
13
+ const value: ILeafPaint[] = []
14
+ let item: ILeafPaint
19
15
  let paints = ui.__.__input[attrName] as IPaint[]
20
16
 
21
- let item: ILeafPaint
22
- const value: ILeafPaint[] = []
23
17
  if (!(paints instanceof Array)) paints = [paints]
24
18
 
19
+ recycleMap = ui.__.__recycleImage(attrName)
20
+
25
21
  for (let i = 0, len = paints.length; i < len; i++) {
26
22
  item = getLeafPaint(ui, paints[i], attrName)
27
23
  if (item) value.push(item)
28
24
  }
25
+
29
26
  ui.__['_' + attrName] = value.length ? value : undefined
30
27
  }
31
28
 
29
+
32
30
  function getLeafPaint(ui: IUI, paint: IPaint, attrName: string): ILeafPaint {
33
31
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0) return undefined
34
32
  const { boxBounds } = ui.__layout
@@ -38,7 +36,7 @@ function getLeafPaint(ui: IUI, paint: IPaint, attrName: string): ILeafPaint {
38
36
  let { type, blendMode, color, opacity } = paint
39
37
  return { type, blendMode, style: ColorConvert.string(color, opacity) }
40
38
  case 'image':
41
- return image(ui, attrName, paint, boxBounds)
39
+ return image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url])
42
40
  case 'linear':
43
41
  return linearGradient(paint, boxBounds)
44
42
  case 'radial':
package/src/Fill.ts CHANGED
@@ -2,44 +2,52 @@ import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
3
  import { ILeafPaint, IUI } from '@leafer-ui/interface'
4
4
 
5
- import { drawText } from './FillText'
5
+ import { checkImage } from './paint/image'
6
+ import { fillText } from './FillText'
6
7
 
7
8
 
8
- export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string | object): void {
9
+ export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string): void {
9
10
  canvas.fillStyle = fill
10
- ui.__.__font ? drawText(ui, canvas) : canvas.fill(ui.__.windingRule)
11
+ ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill())
11
12
  }
12
13
 
14
+
13
15
  export function fills(ui: IUI, canvas: ILeaferCanvas, fills: ILeafPaint[]): void {
14
16
  let item: ILeafPaint
15
17
  const { windingRule, __font } = ui.__
16
18
  for (let i = 0, len = fills.length; i < len; i++) {
17
19
  item = fills[i]
18
- canvas.fillStyle = item.style
19
20
 
20
- if (item.transform) {
21
- canvas.save()
21
+ if (item.image && checkImage(ui, canvas, item, !__font)) continue
22
+
23
+ if (item.style) {
22
24
 
23
- const { a, b, c, d, e, f } = item.transform
24
- canvas.transform(a, b, c, d, e, f)
25
+ canvas.fillStyle = item.style
25
26
 
26
- if (item.blendMode) canvas.blendMode = item.blendMode
27
+ if (item.transform) {
28
+ canvas.save()
27
29
 
28
- __font ? drawText(ui, canvas) : canvas.fill(windingRule)
30
+ canvas.transform(item.transform)
29
31
 
30
- canvas.restore()
31
- } else {
32
- if (item.blendMode) {
33
- canvas.saveBlendMode(item.blendMode)
32
+ if (item.blendMode) canvas.blendMode = item.blendMode
34
33
 
35
- __font ? drawText(ui, canvas) : canvas.fill(windingRule)
34
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
36
35
 
37
- canvas.restoreBlendMode()
36
+ canvas.restore()
38
37
  } else {
38
+ if (item.blendMode) {
39
+ canvas.saveBlendMode(item.blendMode)
40
+
41
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
39
42
 
40
- __font ? drawText(ui, canvas) : canvas.fill(windingRule)
43
+ canvas.restoreBlendMode()
44
+ } else {
41
45
 
46
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
47
+
48
+ }
42
49
  }
43
50
  }
51
+
44
52
  }
45
53
  }
package/src/FillText.ts CHANGED
@@ -3,7 +3,7 @@ import { ILeaferCanvas } from '@leafer/interface'
3
3
  import { IUI, ITextRowData } from '@leafer-ui/interface'
4
4
 
5
5
 
6
- export function drawText(ui: IUI, canvas: ILeaferCanvas): void {
6
+ export function fillText(ui: IUI, canvas: ILeaferCanvas): void {
7
7
 
8
8
  let row: ITextRowData
9
9
  const { rows, decorationY, decorationHeight } = ui.__.__textDrawData
package/src/Shape.ts CHANGED
@@ -37,7 +37,14 @@ export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions):
37
37
 
38
38
  } else {
39
39
 
40
- bounds = shapeBounds = __world
40
+ if (options.matrix) {
41
+ scaleX *= options.matrix.a
42
+ scaleY *= options.matrix.d
43
+ bounds = shapeBounds = getOuterOf(__world, options.matrix)
44
+ } else {
45
+ bounds = shapeBounds = __world
46
+ }
47
+
41
48
  worldCanvas = canvas
42
49
  }
43
50
 
package/src/Stroke.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
- import { IUI, ILeafStrokePaint, ILeafPaint } from '@leafer-ui/interface'
3
+ import { IUI, ILeafPaint } from '@leafer-ui/interface'
4
4
 
5
- import { strokeText, strokesText } from './StrokeText'
5
+ import { strokeText, drawStrokesStyle } from './StrokeText'
6
6
 
7
7
 
8
- export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
8
+ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string): void {
9
9
  const options = ui.__
10
10
  const { strokeWidth, strokeAlign, __font } = options
11
+ if (!strokeWidth) return
11
12
 
12
13
  if (__font) {
13
14
 
@@ -55,13 +56,15 @@ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object):
55
56
  }
56
57
  }
57
58
 
59
+
58
60
  export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
59
61
  const options = ui.__
60
62
  const { strokeWidth, strokeAlign, __font } = options
63
+ if (!strokeWidth) return
61
64
 
62
65
  if (__font) {
63
66
 
64
- strokesText(ui, canvas, strokes)
67
+ strokeText(ui, canvas, strokes)
65
68
 
66
69
  } else {
67
70
 
@@ -69,7 +72,7 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
69
72
 
70
73
  case 'center':
71
74
  canvas.setStroke(undefined, strokeWidth, options)
72
- drawStrokesStyle(strokes, canvas)
75
+ drawStrokesStyle(ui, strokes, canvas)
73
76
  break
74
77
 
75
78
  case 'inside':
@@ -77,7 +80,7 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
77
80
  canvas.setStroke(undefined, strokeWidth * 2, options)
78
81
  canvas.clip(options.windingRule)
79
82
 
80
- drawStrokesStyle(strokes, canvas)
83
+ drawStrokesStyle(ui, strokes, canvas)
81
84
 
82
85
  canvas.restore()
83
86
  break
@@ -89,7 +92,7 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
89
92
 
90
93
  out.setStroke(undefined, strokeWidth * 2, ui.__)
91
94
 
92
- drawStrokesStyle(strokes, out)
95
+ drawStrokesStyle(ui, strokes, out)
93
96
 
94
97
  out.clip(options.windingRule)
95
98
  out.clearWorld(renderBounds)
@@ -102,18 +105,3 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
102
105
  }
103
106
 
104
107
  }
105
-
106
- function drawStrokesStyle(strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
107
- strokes.forEach((item: ILeafStrokePaint) => {
108
- canvas.strokeStyle = item.style
109
-
110
- if (item.blendMode) {
111
- canvas.saveBlendMode(item.blendMode)
112
- canvas.stroke()
113
- canvas.restoreBlendMode()
114
- } else {
115
- canvas.stroke()
116
- }
117
-
118
- })
119
- }
package/src/StrokeText.ts CHANGED
@@ -1,93 +1,45 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
- import { IUI, ITextRowData, ILeafStrokePaint, ILeafPaint, IStrokeAlign } from '@leafer-ui/interface'
3
+ import { IUI, ITextRowData, ILeafPaint, IStrokeAlign, ILeafStrokePaint } from '@leafer-ui/interface'
4
4
 
5
- import { drawText } from './FillText'
5
+ import { fillText } from './FillText'
6
+ import { checkImage } from './paint/image'
6
7
 
7
8
 
8
- export function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
9
+ export function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | ILeafPaint[]): void {
9
10
  const { strokeAlign } = ui.__
11
+ const isStrokes = typeof stroke !== 'string'
10
12
  switch (strokeAlign) {
11
13
  case 'center':
12
- canvas.setStroke(stroke, ui.__.strokeWidth, ui.__)
13
- drawTextStroke(ui, canvas)
14
+ canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__)
15
+ isStrokes ? drawStrokesStyle(ui, stroke as ILeafPaint[], canvas, true) : drawTextStroke(ui, canvas)
14
16
  break
15
17
  case 'inside':
16
- drawAlignStroke(ui, canvas, stroke, 'inside')
18
+ drawAlignStroke(ui, canvas, stroke, 'inside', isStrokes)
17
19
  break
18
20
  case 'outside':
19
- drawAlignStroke(ui, canvas, stroke, 'outside')
21
+ drawAlignStroke(ui, canvas, stroke, 'outside', isStrokes)
20
22
  break
21
23
  }
22
24
  }
23
25
 
24
- function drawAlignStroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object, align: IStrokeAlign): void {
26
+ function drawAlignStroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | ILeafPaint[], align: IStrokeAlign, isStrokes?: boolean): void {
25
27
  const { strokeWidth, __font } = ui.__
26
28
 
27
29
  const out = canvas.getSameCanvas(true)
28
- out.setStroke(stroke, strokeWidth * 2, ui.__)
30
+ out.setStroke(isStrokes ? undefined : stroke, strokeWidth * 2, ui.__)
29
31
 
30
32
  out.font = __font
31
- drawTextStroke(ui, out)
33
+ isStrokes ? drawStrokesStyle(ui, stroke as ILeafPaint[], out, true) : drawTextStroke(ui, out)
32
34
 
33
35
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in'
34
- drawText(ui, out)
36
+ fillText(ui, out)
35
37
  out.blendMode = 'normal'
36
38
 
37
39
  canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
38
40
  out.recycle()
39
41
  }
40
42
 
41
- export function strokesText(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
42
- const { strokeAlign } = ui.__
43
- switch (strokeAlign) {
44
- case 'center':
45
- canvas.setStroke(undefined, ui.__.strokeWidth, ui.__)
46
- drawStrokesStyle(ui, strokes, canvas)
47
- break
48
- case 'inside':
49
- drawAlignStroke(ui, canvas, strokes, 'inside')
50
- break
51
- case 'outside':
52
- drawAlignStrokes(ui, canvas, strokes, 'outside')
53
- break
54
- }
55
- }
56
-
57
-
58
- function drawAlignStrokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[], align: IStrokeAlign): void {
59
- const { strokeWidth, __font } = ui.__
60
-
61
- const out = canvas.getSameCanvas(true)
62
- out.setStroke(undefined, strokeWidth * 2, ui.__)
63
-
64
- out.font = __font
65
- drawStrokesStyle(ui, strokes, out)
66
-
67
- out.blendMode = align === 'outside' ? 'destination - out' : 'destination -in '
68
- drawText(ui, out)
69
- out.blendMode = 'normal'
70
-
71
- canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
72
- out.recycle()
73
- }
74
-
75
-
76
- function drawStrokesStyle(ui: IUI, strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
77
- strokes.forEach((item: ILeafStrokePaint) => {
78
- canvas.strokeStyle = item.style
79
-
80
- if (item.blendMode) {
81
- canvas.saveBlendMode(item.blendMode)
82
- drawTextStroke(ui, canvas)
83
- canvas.restoreBlendMode()
84
- } else {
85
- drawTextStroke(ui, canvas)
86
- }
87
-
88
- })
89
- }
90
-
91
43
  export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
92
44
 
93
45
  let row: ITextRowData
@@ -108,3 +60,25 @@ export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
108
60
  }
109
61
 
110
62
  }
63
+
64
+ export function drawStrokesStyle(ui: IUI, strokes: ILeafStrokePaint[], canvas: ILeaferCanvas, isText?: boolean): void {
65
+ let item: ILeafStrokePaint
66
+ for (let i = 0, len = strokes.length; i < len; i++) {
67
+ item = strokes[i]
68
+
69
+ if (item.image && checkImage(ui, canvas, item, false)) continue
70
+
71
+ if (item.style) {
72
+
73
+ canvas.strokeStyle = item.style
74
+
75
+ if (item.blendMode) {
76
+ canvas.saveBlendMode(item.blendMode)
77
+ isText ? drawTextStroke(ui, canvas) : canvas.stroke()
78
+ canvas.restoreBlendMode()
79
+ } else {
80
+ isText ? drawTextStroke(ui, canvas) : canvas.stroke()
81
+ }
82
+ }
83
+ }
84
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { fill, fills } from './Fill'
2
- export { drawText } from './FillText'
2
+ export { fillText } from './FillText'
3
3
  export { stroke, strokes } from './Stroke'
4
- export { strokeText, strokesText, drawTextStroke } from './StrokeText'
4
+ export { strokeText, drawTextStroke } from './StrokeText'
5
5
  export { shape } from './Shape'
6
- export { computeFill, computeStroke } from './Compute'
6
+ export { compute } from './Compute'
@@ -7,7 +7,7 @@ import { applyStops } from './linear'
7
7
 
8
8
 
9
9
  const { set, getAngle, getDistance } = PointHelper
10
- const { get, rotateOf, scaleOf } = MatrixHelper
10
+ const { get, rotateOfOuter, scaleOfOuter } = MatrixHelper
11
11
 
12
12
  const defaultFrom = { x: 0.5, y: 0.5 }
13
13
  const defaultTo = { x: 0.5, y: 1 }
@@ -31,21 +31,18 @@ export function conicGradient(paint: IGradientPaint, box: IBoundsData): ILeafPai
31
31
  const angle = getAngle(realFrom, realTo)
32
32
 
33
33
  if (Platform.conicGradientRotate90) {
34
- scaleOf(transform, realFrom, width / height * (stretch || 1), 1)
35
- rotateOf(transform, realFrom, angle + 90)
34
+ scaleOfOuter(transform, realFrom, width / height * (stretch || 1), 1)
35
+ rotateOfOuter(transform, realFrom, angle + 90)
36
36
  } else {
37
- scaleOf(transform, realFrom, 1, width / height * (stretch || 1))
38
- rotateOf(transform, realFrom, angle)
37
+ scaleOfOuter(transform, realFrom, 1, width / height * (stretch || 1))
38
+ rotateOfOuter(transform, realFrom, angle)
39
39
  }
40
40
 
41
41
  const style = Platform.conicGradientSupport ? Platform.canvas.createConicGradient(0, realFrom.x, realFrom.y) : Platform.canvas.createRadialGradient(realFrom.x, realFrom.y, 0, realFrom.x, realFrom.y, getDistance(realFrom, realTo))
42
42
  applyStops(style, paint.stops, opacity)
43
43
 
44
- return {
45
- type,
46
- blendMode,
47
- style,
48
- transform
49
- }
44
+ const data: ILeafPaint = { type, style, transform }
45
+ if (blendMode) data.blendMode = blendMode
46
+ return data
50
47
 
51
48
  }
@@ -0,0 +1,49 @@
1
+
2
+ import { ILeaferCanvas } from '@leafer/interface'
3
+ import { ImageManager } from '@leafer/core'
4
+
5
+ import { IUI, ILeafPaint } from '@leafer-ui/interface'
6
+
7
+ import { createPattern } from './pattern'
8
+
9
+
10
+ export function checkImage(ui: IUI, canvas: ILeaferCanvas, paint: ILeafPaint, allowPaint?: boolean): boolean {
11
+ const { scaleX, scaleY } = ui.__world
12
+
13
+ if (!paint.data || paint.patternId === scaleX + scaleY) {
14
+ return false
15
+ } else {
16
+
17
+ if (allowPaint) {
18
+ if (paint.image.isSVG && paint.data.mode !== 'repeat') {
19
+ let { width, height } = paint.data
20
+ width *= scaleX * canvas.pixelRatio
21
+ height *= scaleY * canvas.pixelRatio
22
+ allowPaint = width > 4096 || height > 4096
23
+ } else {
24
+ allowPaint = false
25
+ }
26
+ }
27
+
28
+ if (allowPaint) {
29
+ canvas.save()
30
+ canvas.clip()
31
+ const { data } = paint
32
+ if (paint.blendMode) canvas.blendMode = paint.blendMode
33
+ if (data.opacity) canvas.opacity *= data.opacity
34
+ if (data.transform) canvas.transform(data.transform)
35
+ canvas.drawImage(paint.image.view as any, 0, 0, data.width, data.height)
36
+ canvas.restore()
37
+ return true
38
+ } else {
39
+ if (!paint.style) {
40
+ createPattern(ui, paint, canvas.pixelRatio)
41
+ } else {
42
+ ImageManager.patternTasker.add(async () => {
43
+ if (canvas.bounds.hit(ui.__world) && createPattern(ui, paint, canvas.pixelRatio)) ui.forceUpdate('surface')
44
+ }, 300)
45
+ }
46
+ return false
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,47 @@
1
+ import { IBoundsData, ILeaferImage } from '@leafer/interface'
2
+ import { MatrixHelper } from '@leafer/core'
3
+
4
+ import { IUI, IImagePaint, ILeafPaint, ILeafPaintPatternData } from '@leafer-ui/interface'
5
+
6
+ import { clipMode, fillOrFitMode, repeatMode } from './mode'
7
+ import { createPattern } from './pattern'
8
+
9
+
10
+ const { get, translate } = MatrixHelper
11
+
12
+ export function createData(ui: IUI, leafPaint: ILeafPaint, image: ILeaferImage, paint: IImagePaint, box: IBoundsData): void {
13
+ let { width, height } = image
14
+
15
+ const { opacity, mode, offset, scale, rotation, blendMode } = paint
16
+ const sameBox = box.width === width && box.height === height
17
+ if (blendMode) leafPaint.blendMode = blendMode
18
+
19
+ const data: ILeafPaintPatternData = leafPaint.data = { mode }
20
+
21
+ switch (mode) {
22
+ case 'strench':
23
+ if (!sameBox) width = box.width, height = box.height
24
+ if (box.x || box.y) {
25
+ data.transform = get()
26
+ translate(data.transform, box.x, box.y)
27
+ }
28
+ break
29
+ case 'clip':
30
+ if (offset || scale || rotation) clipMode(data, box, offset, scale, rotation)
31
+ break
32
+ case 'repeat':
33
+ if (!sameBox || scale || rotation) repeatMode(data, box, width, height, scale as number, rotation)
34
+ break
35
+ case 'fit':
36
+ case 'cover':
37
+ default:
38
+ if (!sameBox || rotation) fillOrFitMode(data, mode, box, width, height, rotation)
39
+ }
40
+
41
+ data.width = width
42
+ data.height = height
43
+ if (opacity) data.opacity = opacity
44
+
45
+ const canvas = ui.leafer && ui.leafer.canvas
46
+ if (canvas && canvas.bounds.hit(ui.__world)) createPattern(ui, leafPaint, canvas.pixelRatio)
47
+ }
@@ -0,0 +1,77 @@
1
+ import { IBoundsData, IImageEvent, ISizeData } from '@leafer/interface'
2
+ import { ImageEvent, ImageManager } from '@leafer/core'
3
+
4
+ import { IUI, IImagePaint, ILeafPaint } from '@leafer-ui/interface'
5
+
6
+ import { createData } from './data'
7
+
8
+
9
+ export function image(ui: IUI, attrName: string, attrValue: IImagePaint, box: IBoundsData, first: boolean): ILeafPaint {
10
+ const leafPaint: ILeafPaint = { type: attrValue.type }
11
+ const image = leafPaint.image = ImageManager.get(attrValue)
12
+
13
+ if (image.ready && hasNaturalSize(ui, attrName, image)) createData(ui, leafPaint, image, attrValue, box)
14
+
15
+ if (first) {
16
+
17
+ const event: IImageEvent = { target: ui, image, attrName, attrValue }
18
+
19
+ if (image.ready) {
20
+
21
+ emit(ImageEvent.LOAD, event)
22
+ emit(ImageEvent.LOADED, event)
23
+
24
+ } else if (image.error) {
25
+
26
+ ui.forceUpdate('surface')
27
+ event.error = image.error
28
+ emit(ImageEvent.ERROR, event)
29
+
30
+ } else {
31
+
32
+ emit(ImageEvent.LOAD, event)
33
+
34
+ leafPaint.loadId = image.load(
35
+ () => {
36
+ if (ui.__) {
37
+
38
+ if (hasNaturalSize(ui, attrName, image)) {
39
+ createData(ui, leafPaint, image, attrValue, box)
40
+ ui.forceUpdate('surface')
41
+ }
42
+
43
+ emit(ImageEvent.LOADED, event)
44
+ }
45
+ },
46
+ (error) => {
47
+ ui.forceUpdate('surface')
48
+ event.error = error
49
+ emit(ImageEvent.ERROR, event)
50
+ }
51
+ )
52
+
53
+ }
54
+
55
+ }
56
+
57
+ return leafPaint
58
+ }
59
+
60
+
61
+ function hasNaturalSize(ui: IUI, attrName: string, image: ISizeData): boolean {
62
+ if (attrName === 'fill' && !ui.__.__naturalWidth) {
63
+ const { __: d } = ui
64
+ d.__naturalWidth = image.width
65
+ d.__naturalHeight = image.height
66
+ if (!d.__getInput('width') || !d.__getInput('height')) {
67
+ ui.forceUpdate('width')
68
+ return false
69
+ }
70
+ }
71
+ return true
72
+ }
73
+
74
+ function emit(type: string, data: IImageEvent): void {
75
+ if (data.target.hasEvent(type)) data.target.emitEvent(new ImageEvent(type, data))
76
+ }
77
+
@@ -0,0 +1,2 @@
1
+ export { image } from './image'
2
+ export { checkImage } from './check'
@@ -0,0 +1,62 @@
1
+ import { IBoundsData } from '@leafer/interface'
2
+ import { MatrixHelper } from '@leafer/core'
3
+
4
+ import { IMatrixData, IImagePaintMode, IPointData, ILeafPaintPatternData } from '@leafer-ui/interface'
5
+
6
+
7
+ const { get, rotateOfOuter, translate, scaleOfOuter, scale: scaleHelper, rotate } = MatrixHelper
8
+
9
+ export function fillOrFitMode(data: ILeafPaintPatternData, mode: IImagePaintMode, box: IBoundsData, width: number, height: number, rotation: number): void {
10
+ const transform: IMatrixData = get()
11
+ const swap = rotation && rotation !== 180
12
+ const sw = box.width / (swap ? height : width)
13
+ const sh = box.height / (swap ? width : height)
14
+ const scale = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh)
15
+ const x = box.x + (box.width - width * scale) / 2
16
+ const y = box.y + (box.height - height * scale) / 2
17
+ translate(transform, x, y)
18
+ scaleHelper(transform, scale)
19
+ if (rotation) rotateOfOuter(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation)
20
+ data.scaleX = data.scaleY = scale
21
+ data.transform = transform
22
+ }
23
+
24
+
25
+ export function clipMode(data: ILeafPaintPatternData, box: IBoundsData, offset: IPointData, scale: number | IPointData, rotation: number): void {
26
+ const transform: IMatrixData = get()
27
+ translate(transform, box.x, box.y)
28
+ if (offset) translate(transform, offset.x, offset.y)
29
+ if (scale) {
30
+ typeof scale === 'number' ? scaleHelper(transform, scale) : scaleHelper(transform, scale.x, scale.y)
31
+ data.scaleX = transform.a
32
+ data.scaleY = transform.d
33
+ }
34
+ if (rotation) rotate(transform, rotation)
35
+ data.transform = transform
36
+ }
37
+
38
+
39
+ export function repeatMode(data: ILeafPaintPatternData, box: IBoundsData, width: number, height: number, scale: number, rotation: number): void {
40
+ const transform = get()
41
+
42
+ if (rotation) {
43
+ rotate(transform, rotation)
44
+ switch (rotation) {
45
+ case 90:
46
+ translate(transform, height, 0)
47
+ break
48
+ case 180:
49
+ translate(transform, width, height)
50
+ break
51
+ case 270:
52
+ translate(transform, 0, width)
53
+ break
54
+ }
55
+ }
56
+ translate(transform, box.x, box.y)
57
+ if (scale) {
58
+ scaleOfOuter(transform, box, scale)
59
+ data.scaleX = data.scaleY = scale
60
+ }
61
+ data.transform = transform
62
+ }
@@ -0,0 +1,81 @@
1
+ import { Platform, MatrixHelper } from '@leafer/core'
2
+
3
+ import { IUI, ILeafPaint, IMatrixData } from '@leafer-ui/interface'
4
+
5
+
6
+ const { get, scale: scaleHelper, copy } = MatrixHelper
7
+
8
+ export function createPattern(ui: IUI, paint: ILeafPaint, pixelRatio: number): boolean {
9
+
10
+ let { scaleX, scaleY } = ui.__world
11
+
12
+ const id = scaleX + scaleY
13
+
14
+ if (paint.patternId !== id) {
15
+
16
+ paint.patternId = id
17
+
18
+ const { image, data } = paint
19
+ const maxWidth = image.isSVG ? 4096 : Math.min(image.width, 4096)
20
+ const maxHeight = image.isSVG ? 4096 : Math.min(image.height, 4096)
21
+ let scale: number, matrix: IMatrixData, { width, height, scaleX: sx, scaleY: sy, opacity, transform, mode } = data
22
+
23
+ if (sx) {
24
+ matrix = get()
25
+ copy(matrix, transform)
26
+ scaleHelper(matrix, 1 / sx, 1 / sy)
27
+ scaleX *= sx
28
+ scaleY *= sy
29
+ }
30
+
31
+ scaleX *= pixelRatio
32
+ scaleY *= pixelRatio
33
+ width *= scaleX
34
+ height *= scaleY
35
+
36
+ if (width > maxWidth || height > maxHeight) {
37
+ scale = Math.max(width / maxWidth, height / maxHeight)
38
+ } else if (width < 32 || height < 32) {
39
+ scale = Math.min(width / 32, height / 32)
40
+ }
41
+
42
+ if (scale) {
43
+ scaleX /= scale
44
+ scaleY /= scale
45
+ width /= scale
46
+ height /= scale
47
+ }
48
+
49
+ if (sx) {
50
+ scaleX /= sx
51
+ scaleY /= sy
52
+ }
53
+
54
+ if (transform || scaleX !== 1 || scaleY !== 1) {
55
+ if (!matrix) {
56
+ matrix = get()
57
+ if (transform) copy(matrix, transform)
58
+ }
59
+ scaleHelper(matrix, 1 / scaleX, 1 / scaleY)
60
+ }
61
+
62
+ const style = Platform.canvas.createPattern(image.getCanvas(width, height, opacity) as any, mode === 'repeat' ? 'repeat' : (Platform.origin.noRepeat || 'no-repeat'))
63
+
64
+ try {
65
+ paint.transform = null
66
+ if (matrix) style.setTransform ? style.setTransform(matrix) : paint.transform = matrix
67
+ } catch (e) {
68
+ paint.transform = matrix
69
+ }
70
+
71
+ paint.style = style
72
+
73
+ return true
74
+
75
+ } else {
76
+
77
+ return false
78
+
79
+ }
80
+
81
+ }
@@ -18,11 +18,9 @@ export function linearGradient(paint: IGradientPaint, box: IBoundsData): ILeafPa
18
18
  const style = Platform.canvas.createLinearGradient(box.x + from.x * box.width, box.y + from.y * box.height, box.x + to.x * box.width, box.y + to.y * box.height)
19
19
  applyStops(style, paint.stops, opacity)
20
20
 
21
- return {
22
- type,
23
- blendMode,
24
- style
25
- }
21
+ const data: ILeafPaint = { type, style }
22
+ if (blendMode) data.blendMode = blendMode
23
+ return data
26
24
 
27
25
  }
28
26
 
@@ -7,7 +7,7 @@ import { applyStops } from './linear'
7
7
 
8
8
 
9
9
  const { set, getAngle, getDistance } = PointHelper
10
- const { get, rotateOf, scaleOf } = MatrixHelper
10
+ const { get, rotateOfOuter, scaleOfOuter } = MatrixHelper
11
11
 
12
12
  const defaultFrom = { x: 0.5, y: 0.5 }
13
13
  const defaultTo = { x: 0.5, y: 1 }
@@ -30,18 +30,15 @@ export function radialGradient(paint: IGradientPaint, box: IBoundsData): ILeafPa
30
30
 
31
31
  if (width !== height || stretch) {
32
32
  transform = get()
33
- scaleOf(transform, realFrom, width / height * (stretch || 1), 1)
34
- rotateOf(transform, realFrom, getAngle(realFrom, realTo) + 90)
33
+ scaleOfOuter(transform, realFrom, width / height * (stretch || 1), 1)
34
+ rotateOfOuter(transform, realFrom, getAngle(realFrom, realTo) + 90)
35
35
  }
36
36
 
37
37
  const style = Platform.canvas.createRadialGradient(realFrom.x, realFrom.y, 0, realFrom.x, realFrom.y, getDistance(realFrom, realTo))
38
38
  applyStops(style, paint.stops, opacity)
39
39
 
40
- return {
41
- type,
42
- blendMode,
43
- style,
44
- transform
45
- }
40
+ const data: ILeafPaint = { type, style, transform }
41
+ if (blendMode) data.blendMode = blendMode
42
+ return data
46
43
 
47
44
  }
@@ -1,117 +0,0 @@
1
- import { IBoundsData } from '@leafer/interface'
2
- import { Platform, MatrixHelper, ImageEvent } from '@leafer/core'
3
-
4
- import { IUI, IImagePaint, ILeafPaint, IMatrixData, IImagePaintMode, IPointData } from '@leafer-ui/interface'
5
-
6
-
7
- const { get, rotateOf, translate, scaleOf, scale: scaleHelper, rotate } = MatrixHelper
8
-
9
- export function image(ui: IUI, attrName: string, paint: IImagePaint, box: IBoundsData): ILeafPaint {
10
- let { type, blendMode } = paint
11
- let leaferPaint: ILeafPaint = {
12
- type,
13
- blendMode,
14
- style: 'rgba(255,255,255,0)'
15
- }
16
-
17
- const image = ui.leafer.imageManager.get(paint)
18
-
19
- if (image.ready) {
20
-
21
- let transform: IMatrixData
22
- let { opacity, mode, offset, scale, rotation } = paint
23
- let { width, height } = image
24
- const sameBox = box.width === width && box.height === height
25
-
26
- switch (mode) {
27
- case 'strench':
28
- if (!sameBox) width = box.width, height = box.height
29
- if (box.x || box.y) {
30
- transform = get()
31
- translate(transform, box.x, box.y)
32
- }
33
- break
34
- case 'clip':
35
- if (offset || scale || rotation) transform = getClipTransform(box, offset, scale, rotation)
36
- break
37
- case 'repeat':
38
- if (!sameBox || scale || rotation) transform = getRepeatTransform(box, width, height, scale as number, rotation)
39
- break
40
- case 'fit':
41
- case 'cover':
42
- default:
43
- if (!sameBox || rotation) transform = getFillOrFitTransform(mode, box, width, height, rotation)
44
- }
45
-
46
- leaferPaint.style = createPattern(image.getCanvas(width, height, opacity), transform, mode === 'repeat')
47
-
48
- } else {
49
-
50
- image.load(() => {
51
- if (!ui.__.__getInput('width')) ui.width = image.width
52
- if (!ui.__.__getInput('height')) ui.height = image.height
53
- ui.forceUpdate('width')
54
- ui.emitEvent(new ImageEvent(ImageEvent.LOADED, ui, image, attrName, paint))
55
- }, (error) => {
56
- ui.emitEvent(new ImageEvent(ImageEvent.ERROR, ui, image, attrName, paint, error))
57
- })
58
-
59
- }
60
-
61
- return leaferPaint
62
- }
63
-
64
- function getFillOrFitTransform(mode: IImagePaintMode, box: IBoundsData, width: number, height: number, rotation: number): IMatrixData {
65
- const transform: IMatrixData = get()
66
- const swap = rotation && rotation !== 180
67
- const sw = box.width / (swap ? height : width)
68
- const sh = box.height / (swap ? width : height)
69
- const scale = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh)
70
- const x = box.x + (box.width - width * scale) / 2
71
- const y = box.y + (box.height - height * scale) / 2
72
- translate(transform, x, y)
73
- scaleHelper(transform, scale)
74
- if (rotation) rotateOf(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation)
75
- return transform
76
- }
77
-
78
- function getClipTransform(box: IBoundsData, offset: IPointData, scale: number | IPointData, rotation: number): IMatrixData {
79
- const transform: IMatrixData = get()
80
- translate(transform, box.x, box.y)
81
- if (offset) translate(transform, offset.x, offset.y)
82
- if (scale) typeof scale === 'number' ? scaleHelper(transform, scale) : scaleHelper(transform, scale.x, scale.y)
83
- if (rotation) rotate(transform, rotation)
84
- return transform
85
- }
86
-
87
- function getRepeatTransform(box: IBoundsData, width: number, height: number, scale: number, rotation: number): IMatrixData {
88
- const transform = get()
89
-
90
- if (rotation) {
91
- rotate(transform, rotation)
92
- switch (rotation) {
93
- case 90:
94
- translate(transform, height, 0)
95
- break
96
- case 180:
97
- translate(transform, width, height)
98
- break
99
- case 270:
100
- translate(transform, 0, width)
101
- break
102
- }
103
- }
104
- translate(transform, box.x, box.y)
105
- if (scale) scaleOf(transform, box, scale)
106
- return transform
107
- }
108
-
109
-
110
- function createPattern(canvas: any, transform?: IMatrixData, repeat?: boolean,): CanvasPattern {
111
- let style = Platform.canvas.createPattern(canvas, repeat ? 'repeat' : 'no-repeat')
112
- if (transform) {
113
- const { a, b, c, d, e, f } = transform
114
- style.setTransform(new DOMMatrix([a, b, c, d, e, f]))
115
- }
116
- return style
117
- }