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

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-beta",
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",
23
+ "@leafer-ui/color": "1.0.0-beta"
24
+ },
21
25
  "devDependencies": {
22
- "@leafer/interface": "1.0.0-alpha.9",
23
- "@leafer-ui/interface": "1.0.0-alpha.9"
26
+ "@leafer/interface": "1.0.0-beta",
27
+ "@leafer-ui/interface": "1.0.0-beta"
24
28
  }
25
29
  }
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
@@ -2,19 +2,44 @@ import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
3
  import { ILeafPaint, IUI } from '@leafer-ui/interface'
4
4
 
5
+ import { drawText } from './FillText'
6
+
5
7
 
6
8
  export function fill(ui: IUI, canvas: ILeaferCanvas, fill: string | object): void {
7
9
  canvas.fillStyle = fill
8
- canvas.fill(ui.__.windingRule)
9
-
10
+ ui.__.__font ? drawText(ui, canvas) : canvas.fill(ui.__.windingRule)
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,50 @@
1
+ import { IBoundsData, ILeaferCanvas, IRenderOptions, IMatrix } from '@leafer/interface'
2
+ import { BoundsHelper } from '@leafer/core'
3
+
4
+ import { IUI, ICachedShape } from '@leafer-ui/interface'
5
+
6
+
7
+ const { getSpread, getOuterOf, getByMove, getIntersectData } = BoundsHelper
8
+
9
+ export function shape(ui: IUI, current: ILeaferCanvas, options: IRenderOptions): ICachedShape {
10
+ const canvas = current.getSameCanvas()
11
+
12
+ let bounds: IBoundsData, matrix: IMatrix, shapeBounds: IBoundsData
13
+ let worldCanvas: ILeaferCanvas
14
+
15
+ const { __world } = ui
16
+ let { a: scaleX, d: scaleY } = __world
17
+
18
+ if (!current.bounds.includes(__world, options.matrix)) {
19
+
20
+ const { renderShapeSpread: spread } = ui.__layout
21
+ const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, spread * scaleX) : current.bounds, __world, options.matrix)
22
+ matrix = current.bounds.getFitMatrix(worldClipBounds)
23
+
24
+ if (matrix.a < 1) {
25
+ worldCanvas = current.getSameCanvas()
26
+ ui.__renderShape(worldCanvas, options)
27
+
28
+ scaleX *= matrix.a
29
+ scaleY *= matrix.d
30
+ }
31
+
32
+ shapeBounds = getOuterOf(__world, matrix)
33
+ bounds = getByMove(shapeBounds, -matrix.e, -matrix.f)
34
+
35
+ if (options.matrix) matrix.multiply(options.matrix)
36
+ options = { ...options, matrix }
37
+
38
+ } else {
39
+
40
+ bounds = shapeBounds = __world
41
+ worldCanvas = canvas
42
+ }
43
+
44
+ ui.__renderShape(canvas, options)
45
+
46
+ return {
47
+ canvas, matrix, bounds,
48
+ worldCanvas, shapeBounds, scaleX, scaleY
49
+ }
50
+ }
package/src/Stroke.ts CHANGED
@@ -2,85 +2,103 @@ import { ILeaferCanvas } from '@leafer/interface'
2
2
 
3
3
  import { IUI, ILeafStrokePaint, ILeafPaint } from '@leafer-ui/interface'
4
4
 
5
+ import { strokeText, strokesText } from './StrokeText'
6
+
5
7
 
6
8
  export function stroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
7
9
  const options = ui.__
8
- const { strokeWidth, strokeAlign } = options
10
+ const { strokeWidth, strokeAlign, __font } = options
9
11
 
10
- switch (strokeAlign) {
12
+ if (__font) {
11
13
 
12
- case 'center':
13
- canvas.setStroke(undefined, strokeWidth, options)
14
- canvas.strokeStyle = stroke
15
- canvas.stroke()
16
- break
14
+ strokeText(ui, canvas, stroke)
17
15
 
18
- case 'inside':
19
- canvas.save()
20
- canvas.setStroke(undefined, strokeWidth * 2, options)
21
- canvas.clip(options.windingRule)
16
+ } else {
22
17
 
23
- canvas.strokeStyle = stroke
24
- canvas.stroke()
18
+ switch (strokeAlign) {
25
19
 
26
- canvas.restore()
27
- break
20
+ case 'center':
28
21
 
29
- case 'outside':
30
- const out = canvas.getSameCanvas(true)
31
- ui.__drawRenderPath(out)
22
+ canvas.setStroke(stroke, strokeWidth, options)
23
+ canvas.stroke()
24
+ break
32
25
 
33
- out.setStroke(undefined, strokeWidth * 2, ui.__)
26
+ case 'inside':
34
27
 
35
- out.strokeStyle = stroke
36
- out.stroke()
28
+ canvas.save()
29
+ canvas.setStroke(stroke, strokeWidth * 2, options)
37
30
 
38
- out.clip(options.windingRule)
39
- out.clearBounds(ui.__layout.renderBounds)
31
+ canvas.clip(options.windingRule)
32
+ canvas.stroke()
40
33
 
41
- canvas.copyWorldToLocal(out, ui.__world, ui.__layout.renderBounds)
42
- out.recycle()
43
- break
44
- }
34
+ canvas.restore()
35
+
36
+ break
37
+
38
+ case 'outside':
39
+ const out = canvas.getSameCanvas(true)
40
+ out.setStroke(stroke, strokeWidth * 2, ui.__)
41
+
42
+ ui.__drawRenderPath(out)
43
+
44
+ out.stroke()
45
+
46
+ out.clip(options.windingRule)
47
+ out.clearWorld(ui.__layout.renderBounds)
48
+
49
+ canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
50
+ out.recycle()
45
51
 
52
+ break
53
+ }
54
+
55
+ }
46
56
  }
47
57
 
48
58
  export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]): void {
49
59
  const options = ui.__
50
- const { strokeWidth, strokeAlign } = options
60
+ const { strokeWidth, strokeAlign, __font } = options
61
+
62
+ if (__font) {
63
+
64
+ strokesText(ui, canvas, strokes)
65
+
66
+ } else {
51
67
 
52
- switch (strokeAlign) {
68
+ switch (strokeAlign) {
53
69
 
54
- case 'center':
55
- canvas.setStroke(undefined, strokeWidth, options)
56
- drawStrokesStyle(strokes, canvas)
57
- break
70
+ case 'center':
71
+ canvas.setStroke(undefined, strokeWidth, options)
72
+ drawStrokesStyle(strokes, canvas)
73
+ break
58
74
 
59
- case 'inside':
60
- canvas.save()
61
- canvas.setStroke(undefined, strokeWidth * 2, options)
62
- canvas.clip(options.windingRule)
75
+ case 'inside':
76
+ canvas.save()
77
+ canvas.setStroke(undefined, strokeWidth * 2, options)
78
+ canvas.clip(options.windingRule)
63
79
 
64
- drawStrokesStyle(strokes, canvas)
80
+ drawStrokesStyle(strokes, canvas)
65
81
 
66
- canvas.restore()
67
- break
82
+ canvas.restore()
83
+ break
68
84
 
69
- case 'outside':
70
- const { renderBounds } = ui.__layout
71
- const out = canvas.getSameCanvas(true)
72
- ui.__drawRenderPath(out)
85
+ case 'outside':
86
+ const { renderBounds } = ui.__layout
87
+ const out = canvas.getSameCanvas(true)
88
+ ui.__drawRenderPath(out)
73
89
 
74
- out.setStroke(undefined, strokeWidth * 2, ui.__)
90
+ out.setStroke(undefined, strokeWidth * 2, ui.__)
75
91
 
76
- drawStrokesStyle(strokes, out)
92
+ drawStrokesStyle(strokes, out)
77
93
 
78
- out.clip(options.windingRule)
79
- out.clearBounds(renderBounds)
94
+ out.clip(options.windingRule)
95
+ out.clearWorld(renderBounds)
96
+
97
+ canvas.copyWorldToInner(out, ui.__world, renderBounds)
98
+ out.recycle()
99
+ break
100
+ }
80
101
 
81
- canvas.copyWorldToLocal(out, ui.__world, renderBounds)
82
- out.recycle()
83
- break
84
102
  }
85
103
 
86
104
  }
@@ -88,6 +106,14 @@ export function strokes(ui: IUI, canvas: ILeaferCanvas, strokes: ILeafPaint[]):
88
106
  function drawStrokesStyle(strokes: ILeafStrokePaint[], canvas: ILeaferCanvas): void {
89
107
  strokes.forEach((item: ILeafStrokePaint) => {
90
108
  canvas.strokeStyle = item.style
91
- canvas.stroke()
109
+
110
+ if (item.blendMode) {
111
+ canvas.saveBlendMode(item.blendMode)
112
+ canvas.stroke()
113
+ canvas.restoreBlendMode()
114
+ } else {
115
+ canvas.stroke()
116
+ }
117
+
92
118
  })
93
119
  }
@@ -0,0 +1,110 @@
1
+ import { ILeaferCanvas } from '@leafer/interface'
2
+
3
+ import { IUI, ITextRowData, ILeafStrokePaint, ILeafPaint, IStrokeAlign } from '@leafer-ui/interface'
4
+
5
+ import { drawText } from './FillText'
6
+
7
+
8
+ export function strokeText(ui: IUI, canvas: ILeaferCanvas, stroke: string | object): void {
9
+ const { strokeAlign } = ui.__
10
+ switch (strokeAlign) {
11
+ case 'center':
12
+ canvas.setStroke(stroke, ui.__.strokeWidth, ui.__)
13
+ drawTextStroke(ui, canvas)
14
+ break
15
+ case 'inside':
16
+ drawAlignStroke(ui, canvas, stroke, 'inside')
17
+ break
18
+ case 'outside':
19
+ drawAlignStroke(ui, canvas, stroke, 'outside')
20
+ break
21
+ }
22
+ }
23
+
24
+ function drawAlignStroke(ui: IUI, canvas: ILeaferCanvas, stroke: string | object, align: IStrokeAlign): void {
25
+ const { strokeWidth, __font } = ui.__
26
+
27
+ const out = canvas.getSameCanvas(true)
28
+ out.setStroke(stroke, strokeWidth * 2, ui.__)
29
+
30
+ out.font = __font
31
+ drawTextStroke(ui, out)
32
+
33
+ out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in'
34
+ drawText(ui, out)
35
+ out.blendMode = 'normal'
36
+
37
+ canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds)
38
+ out.recycle()
39
+ }
40
+
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
+ export function drawTextStroke(ui: IUI, canvas: ILeaferCanvas): void {
92
+
93
+ let row: ITextRowData
94
+ const { rows, decorationY, decorationHeight } = ui.__.__textDrawData
95
+
96
+ for (let i = 0, len = rows.length; i < len; i++) {
97
+ row = rows[i]
98
+
99
+ if (row.text) {
100
+ canvas.strokeText(row.text, row.x, row.y)
101
+ } else if (row.data) {
102
+ row.data.forEach(charData => {
103
+ canvas.strokeText(charData.char, charData.x, row.y)
104
+ })
105
+ }
106
+
107
+ if (decorationY) canvas.strokeRect(row.x, row.y + decorationY, row.width, decorationHeight)
108
+ }
109
+
110
+ }
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,51 @@
1
+ import { IMatrixData, IPointData, IBoundsData } from '@leafer/interface'
2
+ import { Platform, PointHelper, MatrixHelper } from '@leafer/core'
3
+
4
+ import { IGradientPaint, ILeafPaint } 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 conicGradient(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
+
27
+ set(realFrom, x + from.x * width, y + from.y * height)
28
+ set(realTo, x + to.x * width, y + to.y * height)
29
+
30
+ const transform: IMatrixData = get()
31
+ const angle = getAngle(realFrom, realTo)
32
+
33
+ if (Platform.conicGradientRotate90) {
34
+ scaleOf(transform, realFrom, width / height * (stretch || 1), 1)
35
+ rotateOf(transform, realFrom, angle + 90)
36
+ } else {
37
+ scaleOf(transform, realFrom, 1, width / height * (stretch || 1))
38
+ rotateOf(transform, realFrom, angle)
39
+ }
40
+
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
+ applyStops(style, paint.stops, opacity)
43
+
44
+ return {
45
+ type,
46
+ blendMode,
47
+ style,
48
+ transform
49
+ }
50
+
51
+ }
@@ -0,0 +1,117 @@
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
+ }
@@ -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
+ }