@leafer-ui/paint 1.0.0-alpha.9 → 1.0.0-bate

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.9",
3
+ "version": "1.0.0-bate",
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.9",
23
- "@leafer-ui/interface": "1.0.0-alpha.9"
22
+ "@leafer/interface": "1.0.0-bate",
23
+ "@leafer-ui/interface": "1.0.0-bate"
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,44 @@
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
-
9
+ ui.__.__font ? drawText(ui, canvas) : canvas.fill(ui.__.windingRule)
10
10
  }
11
11
 
12
12
  export function fills(ui: IUI, canvas: ILeaferCanvas, fills: ILeafPaint[]): void {
13
13
  let item: ILeafPaint
14
- const { windingRule } = ui.__
14
+ const { windingRule, __font } = ui.__
15
15
  for (let i = 0, len = fills.length; i < len; i++) {
16
16
  item = fills[i]
17
17
  canvas.fillStyle = item.style
18
- canvas.fill(windingRule)
18
+
19
+ if (item.transform) {
20
+ canvas.save()
21
+
22
+ const { a, b, c, d, e, f } = item.transform
23
+ canvas.transform(a, b, c, d, e, f)
24
+
25
+ if (item.blendMode) canvas.blendMode = item.blendMode
26
+
27
+ __font ? drawText(ui, canvas) : canvas.fill(windingRule)
28
+
29
+ canvas.restore()
30
+ } else {
31
+ if (item.blendMode) {
32
+ canvas.saveBlendMode(item.blendMode)
33
+
34
+ __font ? drawText(ui, canvas) : canvas.fill(windingRule)
35
+
36
+ canvas.restoreBlendMode()
37
+ } else {
38
+
39
+ __font ? drawText(ui, canvas) : canvas.fill(windingRule)
40
+
41
+ }
42
+ }
19
43
  }
20
44
  }
@@ -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,49 @@
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
+ const { getSpread, getOuterOf, getByMove, getIntersectData } = BoundsHelper
7
+
8
+ export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions): ICachedShape {
9
+ const canvas = current.getSameCanvas()
10
+
11
+ let bounds: IBoundsData, matrix: IMatrix, shapeBounds: IBoundsData
12
+ let worldCanvas: ILeaferCanvas
13
+
14
+ const { __world } = ui
15
+ let { a: scaleX, d: scaleY } = __world
16
+
17
+ if (!current.bounds.includes(__world, options.matrix)) {
18
+
19
+ const { shapeRenderSpread: spread } = ui.__layout
20
+ const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, spread * scaleX) : current.bounds, __world, options.matrix)
21
+ matrix = current.bounds.getFitMatrix(worldClipBounds)
22
+
23
+ if (matrix.a < 1) {
24
+ worldCanvas = current.getSameCanvas()
25
+ ui.__renderShape(worldCanvas, options)
26
+
27
+ scaleX *= matrix.a
28
+ scaleY *= matrix.d
29
+ }
30
+
31
+ shapeBounds = getOuterOf(__world, matrix)
32
+ bounds = getByMove(shapeBounds, -matrix.e, -matrix.f)
33
+
34
+ if (options.matrix) matrix.multiply(options.matrix)
35
+ options = { ...options, matrix }
36
+
37
+ } else {
38
+
39
+ bounds = shapeBounds = __world
40
+ worldCanvas = canvas
41
+ }
42
+
43
+ ui.__renderShape(canvas, options)
44
+
45
+ return {
46
+ canvas, matrix, bounds,
47
+ worldCanvas, shapeBounds, scaleX, scaleY
48
+ }
49
+ }
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.clearBounds(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()
50
+
51
+ break
52
+ }
45
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 {
66
+
67
+ switch (strokeAlign) {
51
68
 
52
- switch (strokeAlign) {
69
+ case 'center':
70
+ canvas.setStroke(undefined, strokeWidth, options)
71
+ drawStrokesStyle(strokes, canvas)
72
+ break
53
73
 
54
- case 'center':
55
- canvas.setStroke(undefined, strokeWidth, options)
56
- drawStrokesStyle(strokes, canvas)
57
- break
74
+ case 'inside':
75
+ canvas.save()
76
+ canvas.setStroke(undefined, strokeWidth * 2, options)
77
+ canvas.clip(options.windingRule)
58
78
 
59
- case 'inside':
60
- canvas.save()
61
- canvas.setStroke(undefined, strokeWidth * 2, options)
62
- canvas.clip(options.windingRule)
79
+ drawStrokesStyle(strokes, canvas)
63
80
 
64
- drawStrokesStyle(strokes, canvas)
81
+ canvas.restore()
82
+ break
65
83
 
66
- canvas.restore()
67
- break
84
+ case 'outside':
85
+ const { renderBounds } = ui.__layout
86
+ const out = canvas.getSameCanvas(true)
87
+ ui.__drawRenderPath(out)
68
88
 
69
- case 'outside':
70
- const { renderBounds } = ui.__layout
71
- const out = canvas.getSameCanvas(true)
72
- ui.__drawRenderPath(out)
89
+ out.setStroke(undefined, strokeWidth * 2, ui.__)
73
90
 
74
- out.setStroke(undefined, strokeWidth * 2, ui.__)
91
+ drawStrokesStyle(strokes, out)
75
92
 
76
- drawStrokesStyle(strokes, out)
93
+ out.clip(options.windingRule)
94
+ out.clearWorld(renderBounds)
77
95
 
78
- out.clip(options.windingRule)
79
- out.clearBounds(renderBounds)
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,14 @@ 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.blendMode) {
110
+ canvas.saveBlendMode(item.blendMode)
111
+ canvas.stroke()
112
+ canvas.restoreBlendMode()
113
+ } else {
114
+ canvas.stroke()
115
+ }
116
+
92
117
  })
93
118
  }
@@ -0,0 +1,109 @@
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.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
+ export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
91
+
92
+ let row: ITextRowData
93
+ const { rows, decorationY, decorationHeight } = ui.__.__textDrawData
94
+
95
+ for (let i = 0, len = rows.length; i < len; i++) {
96
+ row = rows[i]
97
+
98
+ if (row.text) {
99
+ canvas.strokeText(row.text, row.x, row.y)
100
+ } else if (row.data) {
101
+ row.data.forEach(charData => {
102
+ canvas.strokeText(charData.char, charData.x, row.y)
103
+ })
104
+ }
105
+
106
+ if (decorationY) canvas.strokeRect(row.x, row.y + decorationY, row.width, decorationHeight)
107
+ }
108
+
109
+ }
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, getDistance } = 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.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
+ 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
+ }