@leafer-ui/paint 1.0.0-bate → 1.0.0-beta.10

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-bate",
3
+ "version": "1.0.0-beta.10",
4
4
  "description": "@leafer-ui/paint",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -18,8 +18,12 @@
18
18
  "leafer-ui",
19
19
  "leaferjs"
20
20
  ],
21
+ "dependencies": {
22
+ "@leafer/core": "1.0.0-beta.10",
23
+ "@leafer-ui/color": "1.0.0-beta.10"
24
+ },
21
25
  "devDependencies": {
22
- "@leafer/interface": "1.0.0-bate",
23
- "@leafer-ui/interface": "1.0.0-bate"
26
+ "@leafer/interface": "1.0.0-beta.10",
27
+ "@leafer-ui/interface": "1.0.0-beta.10"
24
28
  }
25
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
@@ -1,44 +1,53 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
3
  import { ILeafPaint, IUI } from '@leafer-ui/interface'
4
- import { drawText } from './FillText'
5
4
 
5
+ import { checkImage } from './paint/image'
6
+ import { fillText } from './FillText'
6
7
 
7
- export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string | object): void {
8
+
9
+ export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string): void {
8
10
  canvas.fillStyle = fill
9
- ui.__.__font ? drawText(ui, canvas) : canvas.fill(ui.__.windingRule)
11
+ ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill())
10
12
  }
11
13
 
14
+
12
15
  export function fills(ui: IUI, canvas: ILeaferCanvas, fills: ILeafPaint[]): void {
13
16
  let item: ILeafPaint
14
17
  const { windingRule, __font } = ui.__
15
18
  for (let i = 0, len = fills.length; i < len; i++) {
16
19
  item = fills[i]
17
- canvas.fillStyle = item.style
18
20
 
19
- if (item.transform) {
20
- canvas.save()
21
+ if (item.image && checkImage(ui, canvas, item, !__font)) continue
22
+
23
+ if (item.style) {
21
24
 
22
- const { a, b, c, d, e, f } = item.transform
23
- canvas.transform(a, b, c, d, e, f)
25
+ canvas.fillStyle = item.style
24
26
 
25
- if (item.blendMode) canvas.blendMode = item.blendMode
27
+ if (item.transform) {
28
+ canvas.save()
26
29
 
27
- __font ? drawText(ui, canvas) : canvas.fill(windingRule)
30
+ canvas.transform(item.transform)
28
31
 
29
- canvas.restore()
30
- } else {
31
- if (item.blendMode) {
32
- canvas.saveBlendMode(item.blendMode)
32
+ if (item.blendMode) canvas.blendMode = item.blendMode
33
33
 
34
- __font ? drawText(ui, canvas) : canvas.fill(windingRule)
34
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
35
35
 
36
- canvas.restoreBlendMode()
36
+ canvas.restore()
37
37
  } else {
38
+ if (item.blendMode) {
39
+ canvas.saveBlendMode(item.blendMode)
38
40
 
39
- __font ? drawText(ui, canvas) : canvas.fill(windingRule)
41
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
40
42
 
43
+ canvas.restoreBlendMode()
44
+ } else {
45
+
46
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill())
47
+
48
+ }
41
49
  }
42
50
  }
51
+
43
52
  }
44
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
@@ -1,8 +1,9 @@
1
1
  import { IBoundsData, ILeaferCanvas, IRenderOptions, IMatrix } from '@leafer/interface'
2
- import { BoundsHelper } from '@leafer/math'
2
+ import { BoundsHelper } from '@leafer/core'
3
3
 
4
4
  import { IUI, ICachedShape } from '@leafer-ui/interface'
5
5
 
6
+
6
7
  const { getSpread, getOuterOf, getByMove, getIntersectData } = BoundsHelper
7
8
 
8
9
  export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions): ICachedShape {
@@ -16,7 +17,7 @@ export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions):
16
17
 
17
18
  if (!current.bounds.includes(__world, options.matrix)) {
18
19
 
19
- const { shapeRenderSpread: spread } = ui.__layout
20
+ const { renderShapeSpread: spread } = ui.__layout
20
21
  const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, spread * scaleX) : current.bounds, __world, options.matrix)
21
22
  matrix = current.bounds.getFitMatrix(worldClipBounds)
22
23
 
@@ -36,7 +37,14 @@ export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions):
36
37
 
37
38
  } else {
38
39
 
39
- 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
+
40
48
  worldCanvas = canvas
41
49
  }
42
50
 
package/src/Stroke.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
- import { IUI, ILeafStrokePaint, ILeafPaint } from '@leafer-ui/interface'
4
- import { strokeText, strokesText } from './StrokeText'
3
+ import { IUI, ILeafPaint } from '@leafer-ui/interface'
5
4
 
5
+ import { strokeText, drawStrokesStyle } from './StrokeText'
6
6
 
7
- export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
7
+
8
+ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string): void {
8
9
  const options = ui.__
9
10
  const { strokeWidth, strokeAlign, __font } = options
11
+ if (!strokeWidth) return
10
12
 
11
13
  if (__font) {
12
14
 
@@ -54,13 +56,15 @@ export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object):
54
56
  }
55
57
  }
56
58
 
59
+
57
60
  export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
58
61
  const options = ui.__
59
62
  const { strokeWidth, strokeAlign, __font } = options
63
+ if (!strokeWidth) return
60
64
 
61
65
  if (__font) {
62
66
 
63
- strokesText(ui, canvas, strokes)
67
+ strokeText(ui, canvas, strokes)
64
68
 
65
69
  } else {
66
70
 
@@ -68,7 +72,7 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
68
72
 
69
73
  case 'center':
70
74
  canvas.setStroke(undefined, strokeWidth, options)
71
- drawStrokesStyle(strokes, canvas)
75
+ drawStrokesStyle(ui, strokes, canvas)
72
76
  break
73
77
 
74
78
  case 'inside':
@@ -76,7 +80,7 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
76
80
  canvas.setStroke(undefined, strokeWidth * 2, options)
77
81
  canvas.clip(options.windingRule)
78
82
 
79
- drawStrokesStyle(strokes, canvas)
83
+ drawStrokesStyle(ui, strokes, canvas)
80
84
 
81
85
  canvas.restore()
82
86
  break
@@ -88,7 +92,7 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
88
92
 
89
93
  out.setStroke(undefined, strokeWidth * 2, ui.__)
90
94
 
91
- drawStrokesStyle(strokes, out)
95
+ drawStrokesStyle(ui, strokes, out)
92
96
 
93
97
  out.clip(options.windingRule)
94
98
  out.clearWorld(renderBounds)
@@ -101,18 +105,3 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
101
105
  }
102
106
 
103
107
  }
104
-
105
- function drawStrokesStyle(strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
106
- strokes.forEach((item: ILeafStrokePaint) => {
107
- canvas.strokeStyle = item.style
108
-
109
- if (item.blendMode) {
110
- canvas.saveBlendMode(item.blendMode)
111
- canvas.stroke()
112
- canvas.restoreBlendMode()
113
- } else {
114
- canvas.stroke()
115
- }
116
-
117
- })
118
- }
package/src/StrokeText.ts CHANGED
@@ -1,92 +1,45 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
- import { IUI, ITextRowData, ILeafStrokePaint, ILeafPaint, IStrokeAlign } from '@leafer-ui/interface'
4
- import { drawText } from './FillText'
3
+ import { IUI, ITextRowData, ILeafPaint, IStrokeAlign, ILeafStrokePaint } from '@leafer-ui/interface'
5
4
 
5
+ import { fillText } from './FillText'
6
+ import { checkImage } from './paint/image'
6
7
 
7
- export function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
8
- const { strokeAlign } = ui.__
9
- switch (strokeAlign) {
10
- case 'center':
11
- canvas.setStroke(stroke, ui.__.strokeWidth, ui.__)
12
- drawTextStroke(ui, canvas)
13
- break
14
- case 'inside':
15
- drawAlignStroke(ui, canvas, stroke, 'inside')
16
- break
17
- case 'outside':
18
- drawAlignStroke(ui, canvas, stroke, 'outside')
19
- break
20
- }
21
- }
22
-
23
- function drawAlignStroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object, align: IStrokeAlign): void {
24
- const { strokeWidth, __font } = ui.__
25
-
26
- const out = canvas.getSameCanvas(true)
27
- out.setStroke(stroke, strokeWidth * 2, ui.__)
28
-
29
- out.font = __font
30
- drawTextStroke(ui, out)
31
-
32
- out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in'
33
- drawText(ui, out)
34
- out.blendMode = 'normal'
35
8
 
36
- canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
37
- out.recycle()
38
- }
39
-
40
- export function strokesText(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
9
+ export function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | ILeafPaint[]): void {
41
10
  const { strokeAlign } = ui.__
11
+ const isStrokes = typeof stroke !== 'string'
42
12
  switch (strokeAlign) {
43
13
  case 'center':
44
- canvas.setStroke(undefined, ui.__.strokeWidth, ui.__)
45
- drawStrokesStyle(ui, strokes, canvas)
14
+ canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__)
15
+ isStrokes ? drawStrokesStyle(ui, stroke as ILeafPaint[], canvas, true) : drawTextStroke(ui, canvas)
46
16
  break
47
17
  case 'inside':
48
- drawAlignStroke(ui, canvas, strokes, 'inside')
18
+ drawAlignStroke(ui, canvas, stroke, 'inside', isStrokes)
49
19
  break
50
20
  case 'outside':
51
- drawAlignStrokes(ui, canvas, strokes, 'outside')
21
+ drawAlignStroke(ui, canvas, stroke, 'outside', isStrokes)
52
22
  break
53
23
  }
54
24
  }
55
25
 
56
-
57
- function drawAlignStrokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[], align: IStrokeAlign): void {
26
+ function drawAlignStroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | ILeafPaint[], align: IStrokeAlign, isStrokes?: boolean): void {
58
27
  const { strokeWidth, __font } = ui.__
59
28
 
60
29
  const out = canvas.getSameCanvas(true)
61
- out.setStroke(undefined, strokeWidth * 2, ui.__)
30
+ out.setStroke(isStrokes ? undefined : stroke, strokeWidth * 2, ui.__)
62
31
 
63
32
  out.font = __font
64
- drawStrokesStyle(ui, strokes, out)
33
+ isStrokes ? drawStrokesStyle(ui, stroke as ILeafPaint[], out, true) : drawTextStroke(ui, out)
65
34
 
66
- out.blendMode = align === 'outside' ? 'destination - out' : 'destination -in '
67
- drawText(ui, out)
35
+ out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in'
36
+ fillText(ui, out)
68
37
  out.blendMode = 'normal'
69
38
 
70
39
  canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
71
40
  out.recycle()
72
41
  }
73
42
 
74
-
75
- function drawStrokesStyle(ui: IUI, strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
76
- strokes.forEach((item: ILeafStrokePaint) => {
77
- canvas.strokeStyle = item.style
78
-
79
- if (item.blendMode) {
80
- canvas.saveBlendMode(item.blendMode)
81
- drawTextStroke(ui, canvas)
82
- canvas.restoreBlendMode()
83
- } else {
84
- drawTextStroke(ui, canvas)
85
- }
86
-
87
- })
88
- }
89
-
90
43
  export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
91
44
 
92
45
  let row: ITextRowData
@@ -107,3 +60,25 @@ export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
107
60
  }
108
61
 
109
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'
@@ -1,11 +1,13 @@
1
1
  import { IMatrixData, IPointData, IBoundsData } from '@leafer/interface'
2
- import { IGradientPaint, ILeafPaint } from '@leafer-ui/interface'
3
2
  import { Platform, PointHelper, MatrixHelper } from '@leafer/core'
3
+
4
+ import { IGradientPaint, ILeafPaint } from '@leafer-ui/interface'
5
+
4
6
  import { applyStops } from './linear'
5
7
 
6
8
 
7
9
  const { set, getAngle, getDistance } = PointHelper
8
- const { get, rotateOf, scaleOf } = MatrixHelper
10
+ const { get, rotateOfOuter, scaleOfOuter } = MatrixHelper
9
11
 
10
12
  const defaultFrom = { x: 0.5, y: 0.5 }
11
13
  const defaultTo = { x: 0.5, y: 1 }
@@ -29,21 +31,18 @@ export function conicGradient(paint: IGradientPaint, box: IBoundsData): ILeafPai
29
31
  const angle = getAngle(realFrom, realTo)
30
32
 
31
33
  if (Platform.conicGradientRotate90) {
32
- scaleOf(transform, realFrom, width / height * (stretch || 1), 1)
33
- rotateOf(transform, realFrom, angle + 90)
34
+ scaleOfOuter(transform, realFrom, width / height * (stretch || 1), 1)
35
+ rotateOfOuter(transform, realFrom, angle + 90)
34
36
  } else {
35
- scaleOf(transform, realFrom, 1, width / height * (stretch || 1))
36
- rotateOf(transform, realFrom, angle)
37
+ scaleOfOuter(transform, realFrom, 1, width / height * (stretch || 1))
38
+ rotateOfOuter(transform, realFrom, angle)
37
39
  }
38
40
 
39
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))
40
42
  applyStops(style, paint.stops, opacity)
41
43
 
42
- return {
43
- type,
44
- blendMode,
45
- style,
46
- transform
47
- }
44
+ const data: ILeafPaint = { type, style, transform }
45
+ if (blendMode) data.blendMode = blendMode
46
+ return data
48
47
 
49
48
  }
@@ -0,0 +1,47 @@
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
+ let { width, height } = ui.__world
12
+
13
+ if (!paint.data || paint.patternId === width + height) {
14
+ return false
15
+ } else {
16
+
17
+ if (allowPaint) {
18
+ if (paint.image.isSVG && paint.data.mode !== 'repeat') {
19
+ width *= canvas.pixelRatio
20
+ height *= canvas.pixelRatio
21
+ allowPaint = width > 4096 || height > 4096
22
+ } else {
23
+ allowPaint = false
24
+ }
25
+ }
26
+
27
+ if (allowPaint) {
28
+ canvas.save()
29
+ canvas.clip()
30
+ const { data } = paint
31
+ if (paint.blendMode) canvas.blendMode = paint.blendMode
32
+ if (data.opacity) canvas.opacity *= data.opacity
33
+ if (data.transform) canvas.transform(data.transform)
34
+ canvas.drawImage(paint.image.view as any, 0, 0, data.width, data.height)
35
+ canvas.restore()
36
+ return true
37
+ } else {
38
+ ImageManager.patternTasker.addParallel(() => {
39
+ if (canvas.bounds.hit(ui.__world)) {
40
+ createPattern(ui, paint, canvas.pixelRatio)
41
+ ui.forceUpdate('surface')
42
+ }
43
+ }, null, true)
44
+ return false
45
+ }
46
+ }
47
+ }
@@ -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,68 @@
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): void {
9
+ const id = ui.__world.width + ui.__world.height
10
+
11
+ if (paint.patternId !== id) {
12
+
13
+ paint.patternId = id
14
+
15
+ let scale: number, matrix: IMatrixData, { a, d } = ui.__world, { width, height, scaleX, scaleY, opacity, transform, mode } = paint.data
16
+
17
+ if (scaleX) {
18
+ matrix = get()
19
+ copy(matrix, transform)
20
+ scaleHelper(matrix, 1 / scaleX, 1 / scaleY)
21
+ a *= scaleX
22
+ d *= scaleY
23
+ }
24
+
25
+ a *= pixelRatio
26
+ d *= pixelRatio
27
+ width *= a
28
+ height *= d
29
+
30
+ const { image } = paint
31
+ const maxWidth = image.isSVG ? 4096 : Math.min(image.width, 4096)
32
+ const maxHeight = image.isSVG ? 4096 : Math.min(image.height, 4096)
33
+
34
+ if (width > maxWidth || height > maxHeight) {
35
+ scale = Math.max(width / maxWidth, height / maxHeight)
36
+
37
+ a /= scale
38
+ d /= scale
39
+ width /= scale
40
+ height /= scale
41
+ }
42
+
43
+ if (scaleX) {
44
+ a /= scaleX
45
+ d /= scaleY
46
+ }
47
+
48
+ if (transform || a !== 1 || d !== 1) {
49
+ if (!matrix) {
50
+ matrix = get()
51
+ if (transform) copy(matrix, transform)
52
+ }
53
+ scaleHelper(matrix, 1 / a, 1 / d)
54
+ }
55
+
56
+ const style = Platform.canvas.createPattern(paint.image.getCanvas(width, height, opacity) as any, mode === 'repeat' ? 'repeat' : (Platform.origin.noRepeat || 'no-repeat'))
57
+
58
+ paint.transform = null
59
+
60
+ try {
61
+ if (matrix) style.setTransform ? style.setTransform(matrix) : paint.transform = matrix
62
+ } catch (e) {
63
+ paint.transform = matrix
64
+ }
65
+
66
+ paint.style = style
67
+ }
68
+ }
@@ -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,116 +0,0 @@
1
- import { IBoundsData } from '@leafer/interface'
2
- import { IUI, IImagePaint, ILeafPaint, IMatrixData, IImagePaintMode, IPointData } from '@leafer-ui/interface'
3
- import { Platform, MatrixHelper, ImageEvent } from '@leafer/core'
4
-
5
-
6
- const { get, rotateOf, translate, scaleOf, scale: scaleHelper, rotate } = MatrixHelper
7
-
8
- export function image(ui: IUI, attrName: string, paint: IImagePaint, box: IBoundsData): ILeafPaint {
9
- let { type, blendMode } = paint
10
- let leaferPaint: ILeafPaint = {
11
- type,
12
- blendMode,
13
- style: 'rgba(255,255,255,0)'
14
- }
15
-
16
- const image = ui.leafer.imageManager.get(paint)
17
-
18
- if (image.ready) {
19
-
20
- let transform: IMatrixData
21
- let { opacity, mode, offset, scale, rotation } = paint
22
- let { width, height } = image
23
- const sameBox = box.width === width && box.height === height
24
-
25
- switch (mode) {
26
- case 'strench':
27
- if (!sameBox) width = box.width, height = box.height
28
- if (box.x || box.y) {
29
- transform = get()
30
- translate(transform, box.x, box.y)
31
- }
32
- break
33
- case 'clip':
34
- if (offset || scale || rotation) transform = getClipTransform(box, offset, scale, rotation)
35
- break
36
- case 'repeat':
37
- if (!sameBox || scale || rotation) transform = getRepeatTransform(box, width, height, scale as number, rotation)
38
- break
39
- case 'fit':
40
- case 'cover':
41
- default:
42
- if (!sameBox || rotation) transform = getFillOrFitTransform(mode, box, width, height, rotation)
43
- }
44
-
45
- leaferPaint.style = createPattern(image.getCanvas(width, height, opacity), transform, mode === 'repeat')
46
-
47
- } else {
48
-
49
- image.load(() => {
50
- if (!ui.__.__getInput('width')) ui.width = image.width
51
- if (!ui.__.__getInput('height')) ui.height = image.height
52
- ui.forceUpdate('width')
53
- ui.emitEvent(new ImageEvent(ImageEvent.LOADED, ui, image, attrName, paint))
54
- }, (error) => {
55
- ui.emitEvent(new ImageEvent(ImageEvent.ERROR, ui, image, attrName, paint, error))
56
- })
57
-
58
- }
59
-
60
- return leaferPaint
61
- }
62
-
63
- function getFillOrFitTransform(mode: IImagePaintMode, box: IBoundsData, width: number, height: number, rotation: number): IMatrixData {
64
- const transform: IMatrixData = get()
65
- const swap = rotation && rotation !== 180
66
- const sw = box.width / (swap ? height : width)
67
- const sh = box.height / (swap ? width : height)
68
- const scale = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh)
69
- const x = box.x + (box.width - width * scale) / 2
70
- const y = box.y + (box.height - height * scale) / 2
71
- translate(transform, x, y)
72
- scaleHelper(transform, scale)
73
- if (rotation) rotateOf(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation)
74
- return transform
75
- }
76
-
77
- function getClipTransform(box: IBoundsData, offset: IPointData, scale: number | IPointData, rotation: number): IMatrixData {
78
- const transform: IMatrixData = get()
79
- translate(transform, box.x, box.y)
80
- if (offset) translate(transform, offset.x, offset.y)
81
- if (scale) typeof scale === 'number' ? scaleHelper(transform, scale) : scaleHelper(transform, scale.x, scale.y)
82
- if (rotation) rotate(transform, rotation)
83
- return transform
84
- }
85
-
86
- function getRepeatTransform(box: IBoundsData, width: number, height: number, scale: number, rotation: number): IMatrixData {
87
- const transform = get()
88
-
89
- if (rotation) {
90
- rotate(transform, rotation)
91
- switch (rotation) {
92
- case 90:
93
- translate(transform, height, 0)
94
- break
95
- case 180:
96
- translate(transform, width, height)
97
- break
98
- case 270:
99
- translate(transform, 0, width)
100
- break
101
- }
102
- }
103
- translate(transform, box.x, box.y)
104
- if (scale) scaleOf(transform, box, scale)
105
- return transform
106
- }
107
-
108
-
109
- function createPattern(canvas: any, transform?: IMatrixData, repeat?: boolean,): CanvasPattern {
110
- let style = Platform.canvas.createPattern(canvas, repeat ? 'repeat' : 'no-repeat')
111
- if (transform) {
112
- const { a, b, c, d, e, f } = transform
113
- style.setTransform(new DOMMatrix([a, b, c, d, e, f]))
114
- }
115
- return style
116
- }