@leafer-ui/paint 1.0.0-alpha.21 → 1.0.0-alpha.30

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-alpha.21",
3
+ "version": "1.0.0-alpha.30",
4
4
  "description": "@leafer-ui/paint",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -19,7 +19,7 @@
19
19
  "leaferjs"
20
20
  ],
21
21
  "devDependencies": {
22
- "@leafer/interface": "1.0.0-alpha.21",
23
- "@leafer-ui/interface": "1.0.0-alpha.21"
22
+ "@leafer/interface": "1.0.0-alpha.30",
23
+ "@leafer-ui/interface": "1.0.0-alpha.30"
24
24
  }
25
25
  }
package/src/Compute.ts ADDED
@@ -0,0 +1,51 @@
1
+ import { IUI, IPaint, ILeafPaint, IRGB } from '@leafer-ui/interface'
2
+ import { ColorConvert } from '@leafer-ui/color'
3
+
4
+ import { image } from "./paint/image"
5
+ import { linearGradient } from './paint/linear'
6
+ import { radialGradient } from "./paint/radial"
7
+ import { conicGradient } from "./paint/conic"
8
+
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
+ }
17
+
18
+ function compute(ui: IUI, attrName: string): void {
19
+ let paints = ui.__.__input[attrName] as IPaint[]
20
+
21
+ let item: ILeafPaint
22
+ const value: ILeafPaint[] = []
23
+ if (!(paints instanceof Array)) paints = [paints]
24
+
25
+ for (let i = 0, len = paints.length; i < len; i++) {
26
+ item = getLeafPaint(ui, paints[i], attrName)
27
+ if (item) value.push(item)
28
+ }
29
+ ui.__['_' + attrName] = value.length ? value : undefined
30
+ }
31
+
32
+ function getLeafPaint(ui: IUI, paint: IPaint, attrName: string): ILeafPaint {
33
+ if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0) return undefined
34
+ const { boxBounds } = ui.__layout
35
+
36
+ switch (paint.type) {
37
+ case 'solid':
38
+ let { type, blendMode, color, opacity } = paint
39
+ return { type, blendMode, style: ColorConvert.string(color, opacity) }
40
+ case 'image':
41
+ return image(ui, attrName, paint, boxBounds)
42
+ case 'linear':
43
+ return linearGradient(paint, boxBounds)
44
+ case 'radial':
45
+ return radialGradient(paint, boxBounds)
46
+ case 'angular':
47
+ return conicGradient(paint, boxBounds)
48
+ default:
49
+ return (paint as IRGB).r ? { type: 'solid', style: ColorConvert.string(paint) } : undefined
50
+ }
51
+ }
package/src/Fill.ts CHANGED
@@ -1,20 +1,45 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
3
  import { ILeafPaint, IUI } from '@leafer-ui/interface'
4
+ import { drawText } from './FillText'
4
5
 
5
6
 
6
7
  export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string | object): void {
7
8
  canvas.fillStyle = fill
8
- canvas.fill(ui.__.windingRule)
9
+ ui.__.__font ? drawText(ui, canvas) : canvas.fill(ui.__.windingRule)
9
10
 
10
11
  }
11
12
 
12
13
  export function fills(ui: IUI, canvas: ILeaferCanvas, fills: ILeafPaint[]): void {
13
14
  let item: ILeafPaint
14
- const { windingRule } = ui.__
15
+ const { windingRule, __font } = ui.__
15
16
  for (let i = 0, len = fills.length; i < len; i++) {
16
17
  item = fills[i]
17
18
  canvas.fillStyle = item.style
18
- canvas.fill(windingRule)
19
+
20
+ if (item.transform) {
21
+ canvas.save()
22
+
23
+ const { a, b, c, d, e, f } = item.transform
24
+ canvas.transform(a, b, c, d, e, f)
25
+
26
+ if (item.blendMode) canvas.blendMode = item.blendMode
27
+
28
+ __font ? drawText(ui, canvas) : canvas.fill(windingRule)
29
+
30
+ canvas.restore()
31
+ } else {
32
+ if (item.blendMode) {
33
+ canvas.saveBlendMode(item.blendMode)
34
+
35
+ __font ? drawText(ui, canvas) : canvas.fill(windingRule)
36
+
37
+ canvas.restoreBlendMode()
38
+ } else {
39
+
40
+ __font ? drawText(ui, canvas) : canvas.fill(windingRule)
41
+
42
+ }
43
+ }
19
44
  }
20
45
  }
@@ -0,0 +1,25 @@
1
+ import { ILeaferCanvas } from '@leafer/interface'
2
+
3
+ import { IUI, ITextRowData } from '@leafer-ui/interface'
4
+
5
+
6
+ export function drawText(ui: IUI, canvas: ILeaferCanvas): void {
7
+
8
+ let row: ITextRowData
9
+ const { rows, decorationY, decorationHeight } = ui.__.__textDrawData
10
+
11
+ for (let i = 0, len = rows.length; i < len; i++) {
12
+ row = rows[i]
13
+
14
+ if (row.text) {
15
+ canvas.fillText(row.text, row.x, row.y)
16
+ } else if (row.data) {
17
+ row.data.forEach(charData => {
18
+ canvas.fillText(charData.char, charData.x, row.y)
19
+ })
20
+ }
21
+
22
+ if (decorationY) canvas.fillRect(row.x, row.y + decorationY, row.width, decorationHeight)
23
+ }
24
+
25
+ }
package/src/Shape.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { IBoundsData, ILeaferCanvas, IRenderOptions, IMatrix } from '@leafer/interface'
2
+ import { BoundsHelper } from '@leafer/math'
3
+
4
+ import { IUI, ICachedShape } from '@leafer-ui/interface'
5
+
6
+
7
+ export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions): ICachedShape {
8
+ const canvas = current.getSameCanvas()
9
+
10
+ let bounds: IBoundsData, matrix: IMatrix, shapeBounds: IBoundsData
11
+ let worldCanvas: ILeaferCanvas
12
+
13
+ const { __world } = ui
14
+ let { a: scaleX, d: scaleY } = __world
15
+
16
+ if (!current.bounds.includes(__world, options.matrix)) {
17
+
18
+ const { renderBoundsSpreadWidth } = ui.__layout
19
+ const worldClipBounds = BoundsHelper.getIntersectData(renderBoundsSpreadWidth ? BoundsHelper.getSpread(current.bounds, renderBoundsSpreadWidth * __world.a) : current.bounds, __world, options.matrix)
20
+ matrix = current.bounds.getFitMatrix(worldClipBounds)
21
+
22
+ if (matrix.a < 1) {
23
+ worldCanvas = current.getSameCanvas()
24
+ ui.__renderShape(worldCanvas, options)
25
+
26
+ scaleX *= matrix.a
27
+ scaleY *= matrix.d
28
+ }
29
+
30
+ shapeBounds = BoundsHelper.getOuterOf(__world, matrix)
31
+ bounds = BoundsHelper.getByMove(shapeBounds, -matrix.e, -matrix.f)
32
+
33
+ if (options.matrix) matrix.multiply(options.matrix)
34
+ options = { ...options, matrix }
35
+
36
+ } else {
37
+
38
+ bounds = shapeBounds = __world
39
+ worldCanvas = canvas
40
+ }
41
+
42
+ ui.__renderShape(canvas, options)
43
+
44
+ return {
45
+ canvas, matrix, bounds,
46
+ worldCanvas, shapeBounds, scaleX, scaleY
47
+ }
48
+ }
package/src/Stroke.ts CHANGED
@@ -1,86 +1,103 @@
1
1
  import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
3
  import { IUI, ILeafStrokePaint, ILeafPaint } from '@leafer-ui/interface'
4
+ import { strokeText, strokesText } from './StrokeText'
4
5
 
5
6
 
6
7
  export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
7
8
  const options = ui.__
8
- const { strokeWidth, strokeAlign } = options
9
+ const { strokeWidth, strokeAlign, __font } = options
9
10
 
10
- switch (strokeAlign) {
11
+ if (__font) {
11
12
 
12
- case 'center':
13
- canvas.setStroke(undefined, strokeWidth, options)
14
- canvas.strokeStyle = stroke
15
- canvas.stroke()
16
- break
13
+ strokeText(ui, canvas, stroke)
17
14
 
18
- case 'inside':
19
- canvas.save()
20
- canvas.setStroke(undefined, strokeWidth * 2, options)
21
- canvas.clip(options.windingRule)
15
+ } else {
22
16
 
23
- canvas.strokeStyle = stroke
24
- canvas.stroke()
17
+ switch (strokeAlign) {
25
18
 
26
- canvas.restore()
27
- break
19
+ case 'center':
28
20
 
29
- case 'outside':
30
- const out = canvas.getSameCanvas(true)
31
- ui.__drawRenderPath(out)
21
+ canvas.setStroke(stroke, strokeWidth, options)
22
+ canvas.stroke()
23
+ break
32
24
 
33
- out.setStroke(undefined, strokeWidth * 2, ui.__)
25
+ case 'inside':
34
26
 
35
- out.strokeStyle = stroke
36
- out.stroke()
27
+ canvas.save()
28
+ canvas.setStroke(stroke, strokeWidth * 2, options)
37
29
 
38
- out.clip(options.windingRule)
39
- out.clearWorld(ui.__layout.renderBounds)
30
+ canvas.clip(options.windingRule)
31
+ canvas.stroke()
40
32
 
41
- canvas.copyWorldToLocal(out, ui.__world, ui.__layout.renderBounds)
42
- out.recycle()
43
- break
44
- }
33
+ canvas.restore()
34
+
35
+ break
36
+
37
+ case 'outside':
38
+ const out = canvas.getSameCanvas(true)
39
+ out.setStroke(stroke, strokeWidth * 2, ui.__)
40
+
41
+ ui.__drawRenderPath(out)
42
+
43
+ out.stroke()
44
+
45
+ out.clip(options.windingRule)
46
+ out.clearWorld(ui.__layout.renderBounds)
47
+
48
+ canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
49
+ out.recycle()
45
50
 
51
+ break
52
+ }
53
+
54
+ }
46
55
  }
47
56
 
48
57
  export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
49
58
  const options = ui.__
50
- const { strokeWidth, strokeAlign } = options
59
+ const { strokeWidth, strokeAlign, __font } = options
60
+
61
+ if (__font) {
62
+
63
+ strokesText(ui, canvas, strokes)
64
+
65
+ } else {
51
66
 
52
- switch (strokeAlign) {
67
+ switch (strokeAlign) {
53
68
 
54
- case 'center':
55
- canvas.setStroke(undefined, strokeWidth, options)
56
- drawStrokesStyle(strokes, canvas)
57
- break
69
+ case 'center':
70
+ canvas.setStroke(undefined, strokeWidth, options)
71
+ drawStrokesStyle(strokes, canvas)
72
+ break
58
73
 
59
- case 'inside':
60
- canvas.save()
61
- canvas.setStroke(undefined, strokeWidth * 2, options)
62
- canvas.clip(options.windingRule)
74
+ case 'inside':
75
+ canvas.save()
76
+ canvas.setStroke(undefined, strokeWidth * 2, options)
77
+ canvas.clip(options.windingRule)
63
78
 
64
- drawStrokesStyle(strokes, canvas)
79
+ drawStrokesStyle(strokes, canvas)
65
80
 
66
- canvas.restore()
67
- break
81
+ canvas.restore()
82
+ break
68
83
 
69
- case 'outside':
70
- const { renderBounds } = ui.__layout
71
- const out = canvas.getSameCanvas(true)
72
- ui.__drawRenderPath(out)
84
+ case 'outside':
85
+ const { renderBounds } = ui.__layout
86
+ const out = canvas.getSameCanvas(true)
87
+ ui.__drawRenderPath(out)
73
88
 
74
- out.setStroke(undefined, strokeWidth * 2, ui.__)
89
+ out.setStroke(undefined, strokeWidth * 2, ui.__)
75
90
 
76
- drawStrokesStyle(strokes, out)
91
+ drawStrokesStyle(strokes, out)
77
92
 
78
- out.clip(options.windingRule)
79
- out.clearWorld(renderBounds)
93
+ out.clip(options.windingRule)
94
+ out.clearWorld(renderBounds)
95
+
96
+ canvas.copyWorldToInner(out, ui.__world, renderBounds)
97
+ out.recycle()
98
+ break
99
+ }
80
100
 
81
- canvas.copyWorldToLocal(out, ui.__world, renderBounds)
82
- out.recycle()
83
- break
84
101
  }
85
102
 
86
103
  }
@@ -88,6 +105,23 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
88
105
  function drawStrokesStyle(strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
89
106
  strokes.forEach((item: ILeafStrokePaint) => {
90
107
  canvas.strokeStyle = item.style
91
- canvas.stroke()
108
+
109
+ // if (item.transform) {
110
+ // canvas.save()
111
+ // if (item.blendMode) canvas.blendMode = item.blendMode
112
+ // const { a, b, c, d, e, f } = item.transform
113
+ // canvas.transform(a, b, c, d, e, f)
114
+ // canvas.stroke()
115
+ // canvas.restore()
116
+ // } else {
117
+
118
+ if (item.blendMode) {
119
+ canvas.saveBlendMode(item.blendMode)
120
+ canvas.stroke()
121
+ canvas.restoreBlendMode()
122
+ } else {
123
+ canvas.stroke()
124
+ }
125
+ //}
92
126
  })
93
127
  }
@@ -0,0 +1,118 @@
1
+ import { ILeaferCanvas } from '@leafer/interface'
2
+
3
+ import { IUI, ITextRowData, ILeafStrokePaint, ILeafPaint, IStrokeAlign } from '@leafer-ui/interface'
4
+ import { drawText } from './FillText'
5
+
6
+
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
+
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 {
41
+ const { strokeAlign } = ui.__
42
+ switch (strokeAlign) {
43
+ case 'center':
44
+ canvas.setStroke(undefined, ui.__.strokeWidth, ui.__)
45
+ drawStrokesStyle(ui, strokes, canvas)
46
+ break
47
+ case 'inside':
48
+ drawAlignStroke(ui, canvas, strokes, 'inside')
49
+ break
50
+ case 'outside':
51
+ drawAlignStrokes(ui, canvas, strokes, 'outside')
52
+ break
53
+ }
54
+ }
55
+
56
+
57
+ function drawAlignStrokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[], align: IStrokeAlign): void {
58
+ const { strokeWidth, __font } = ui.__
59
+
60
+ const out = canvas.getSameCanvas(true)
61
+ out.setStroke(undefined, strokeWidth * 2, ui.__)
62
+
63
+ out.font = __font
64
+ drawStrokesStyle(ui, strokes, out)
65
+
66
+ out.blendMode = align === 'outside' ? 'destination - out' : 'destination -in '
67
+ drawText(ui, out)
68
+ out.blendMode = 'normal'
69
+
70
+ canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
71
+ out.recycle()
72
+ }
73
+
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.transform) {
80
+ // canvas.save()
81
+ // if (item.blendMode) canvas.blendMode = item.blendMode
82
+ // const { a, b, c, d, e, f } = item.transform
83
+ // canvas.transform(a, b, c, d, e, f)
84
+ // canvas.stroke()
85
+ // canvas.restore()
86
+ // } else {
87
+
88
+ if (item.blendMode) {
89
+ canvas.saveBlendMode(item.blendMode)
90
+ drawTextStroke(ui, canvas)
91
+ canvas.restoreBlendMode()
92
+ } else {
93
+ drawTextStroke(ui, canvas)
94
+ }
95
+ //}
96
+ })
97
+ }
98
+
99
+ export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
100
+
101
+ let row: ITextRowData
102
+ const { rows, decorationY, decorationHeight } = ui.__.__textDrawData
103
+
104
+ for (let i = 0, len = rows.length; i < len; i++) {
105
+ row = rows[i]
106
+
107
+ if (row.text) {
108
+ canvas.strokeText(row.text, row.x, row.y)
109
+ } else if (row.data) {
110
+ row.data.forEach(charData => {
111
+ canvas.strokeText(charData.char, charData.x, row.y)
112
+ })
113
+ }
114
+
115
+ if (decorationY) canvas.strokeRect(row.x, row.y + decorationY, row.width, decorationHeight)
116
+ }
117
+
118
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  export { fill, fills } from './Fill'
2
+ export { drawText } from './FillText'
2
3
  export { stroke, strokes } from './Stroke'
3
-
4
+ export { strokeText, strokesText, drawTextStroke } from './StrokeText'
5
+ export { shape } from './Shape'
6
+ export { computeFill, computeStroke } from './Compute'
@@ -0,0 +1,49 @@
1
+ import { IMatrixData, IPointData, IBoundsData } from '@leafer/interface'
2
+ import { IGradientPaint, ILeafPaint } from '@leafer-ui/interface'
3
+ import { Platform, PointHelper, MatrixHelper } from '@leafer/core'
4
+ import { applyStops } from './linear'
5
+
6
+
7
+ const { set, getAngle } = PointHelper
8
+ const { get, rotateOf, scaleOf } = MatrixHelper
9
+
10
+ const defaultFrom = { x: 0.5, y: 0.5 }
11
+ const defaultTo = { x: 0.5, y: 1 }
12
+
13
+ const realFrom = {} as IPointData
14
+ const realTo = {} as IPointData
15
+
16
+ export function conicGradient(paint: IGradientPaint, box: IBoundsData): ILeafPaint {
17
+
18
+ let { from, to, type, opacity, blendMode, stretch } = paint
19
+
20
+ from || (from = defaultFrom)
21
+ to || (to = defaultTo)
22
+
23
+ const { x, y, width, height } = box
24
+
25
+ set(realFrom, x + from.x * width, y + from.y * height)
26
+ set(realTo, x + to.x * width, y + to.y * height)
27
+
28
+ const transform: IMatrixData = get()
29
+ const angle = getAngle(realFrom, realTo)
30
+
31
+ if (Platform.conicGradientRotate90) {
32
+ scaleOf(transform, realFrom, width / height * (stretch || 1), 1)
33
+ rotateOf(transform, realFrom, angle + 90)
34
+ } else {
35
+ scaleOf(transform, realFrom, 1, width / height * (stretch || 1))
36
+ rotateOf(transform, realFrom, angle)
37
+ }
38
+
39
+ const style = Platform.canvas.createConicGradient(0, realFrom.x, realFrom.y)
40
+ applyStops(style, paint.stops, opacity)
41
+
42
+ return {
43
+ type,
44
+ blendMode,
45
+ style,
46
+ transform
47
+ }
48
+
49
+ }
@@ -0,0 +1,116 @@
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
+ }
@@ -0,0 +1,35 @@
1
+ import { IObject, IBoundsData } from '@leafer/interface'
2
+ import { Platform } from '@leafer/core'
3
+
4
+ import { IGradientPaint, ILeafPaint, IColorStop } from '@leafer-ui/interface'
5
+ import { ColorConvert } from '@leafer-ui/color'
6
+
7
+
8
+ const defaultFrom = { x: 0.5, y: 0 }
9
+ const defaultTo = { x: 0.5, y: 1 }
10
+
11
+ export function linearGradient(paint: IGradientPaint, box: IBoundsData): ILeafPaint {
12
+
13
+ let { from, to, type, blendMode, opacity } = paint
14
+
15
+ from || (from = defaultFrom)
16
+ to || (to = defaultTo)
17
+
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
+ applyStops(style, paint.stops, opacity)
20
+
21
+ return {
22
+ type,
23
+ blendMode,
24
+ style
25
+ }
26
+
27
+ }
28
+
29
+ export function applyStops(gradient: IObject, stops: IColorStop[], opacity: number): void {
30
+ let stop: IColorStop
31
+ for (let i = 0, len = stops.length; i < len; i++) {
32
+ stop = stops[i]
33
+ gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity))
34
+ }
35
+ }
@@ -0,0 +1,47 @@
1
+ import { IBoundsData } from '@leafer/interface'
2
+ import { Platform, PointHelper, MatrixHelper } from '@leafer/core'
3
+
4
+ import { IGradientPaint, ILeafPaint, IMatrixData, IPointData } from '@leafer-ui/interface'
5
+
6
+ import { applyStops } from './linear'
7
+
8
+
9
+ const { set, getAngle, getDistance } = PointHelper
10
+ const { get, rotateOf, scaleOf } = MatrixHelper
11
+
12
+ const defaultFrom = { x: 0.5, y: 0.5 }
13
+ const defaultTo = { x: 0.5, y: 1 }
14
+
15
+ const realFrom = {} as IPointData
16
+ const realTo = {} as IPointData
17
+
18
+ export function radialGradient(paint: IGradientPaint, box: IBoundsData): ILeafPaint {
19
+
20
+ let { from, to, type, opacity, blendMode, stretch } = paint
21
+
22
+ from || (from = defaultFrom)
23
+ to || (to = defaultTo)
24
+
25
+ const { x, y, width, height } = box
26
+ set(realFrom, x + from.x * width, y + from.y * height)
27
+ set(realTo, x + to.x * width, y + to.y * height)
28
+
29
+ let transform: IMatrixData
30
+
31
+ if (width !== height || stretch) {
32
+ transform = get()
33
+ scaleOf(transform, realFrom, width / height * (stretch || 1), 1)
34
+ rotateOf(transform, realFrom, getAngle(realFrom, realTo) + 90)
35
+ }
36
+
37
+ const style = Platform.canvas.createRadialGradient(realFrom.x, realFrom.y, 0, realFrom.x, realFrom.y, getDistance(realFrom, realTo))
38
+ applyStops(style, paint.stops, opacity)
39
+
40
+ return {
41
+ type,
42
+ blendMode,
43
+ style,
44
+ transform
45
+ }
46
+
47
+ }