@leafer-ui/display 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/display",
3
- "version": "1.0.0-alpha.21",
3
+ "version": "1.0.0-alpha.30",
4
4
  "description": "@leafer-ui/display",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -19,13 +19,13 @@
19
19
  "leaferjs"
20
20
  ],
21
21
  "dependencies": {
22
- "@leafer/core": "1.0.0-alpha.21",
23
- "@leafer-ui/data": "1.0.0-alpha.21",
24
- "@leafer-ui/display-module": "1.0.0-alpha.21",
25
- "@leafer-ui/decorator": "1.0.0-alpha.21"
22
+ "@leafer/core": "1.0.0-alpha.30",
23
+ "@leafer-ui/data": "1.0.0-alpha.30",
24
+ "@leafer-ui/display-module": "1.0.0-alpha.30",
25
+ "@leafer-ui/decorator": "1.0.0-alpha.30"
26
26
  },
27
27
  "devDependencies": {
28
- "@leafer/interface": "1.0.0-alpha.21",
29
- "@leafer-ui/interface": "1.0.0-alpha.21"
28
+ "@leafer/interface": "1.0.0-alpha.30",
29
+ "@leafer-ui/interface": "1.0.0-alpha.30"
30
30
  }
31
31
  }
package/src/Box.ts ADDED
@@ -0,0 +1,105 @@
1
+ import { ILeaferCanvas, IRenderOptions, IPathDrawer, IBoundsData, IPathCommandData, __Boolean } from '@leafer/interface'
2
+ import { rewrite, rewriteAble, registerUI, BoundsHelper, dataProcessor, affectRenderBoundsType } from '@leafer/core'
3
+
4
+ import { IBox, IBoxData, IBoxInputData, IOverflow } from '@leafer-ui/interface'
5
+
6
+ import { Group } from './Group'
7
+ import { Rect } from './Rect'
8
+ import { BoxData } from '@leafer-ui/data'
9
+
10
+
11
+ const rect = Rect.prototype
12
+ const group = Group.prototype
13
+ const bounds = {} as IBoundsData
14
+ const { copy, add } = BoundsHelper
15
+
16
+ @rewriteAble()
17
+ @registerUI()
18
+ export class Box extends Group implements IBox {
19
+
20
+ public get __tag() { return 'Box' }
21
+
22
+ @dataProcessor(BoxData)
23
+ public __: IBoxData
24
+
25
+ @affectRenderBoundsType('show')
26
+ public overflow: IOverflow
27
+
28
+ constructor(data?: IBoxInputData) {
29
+ super(data)
30
+ this.isBranchLeaf = true
31
+ this.__layout.renderBoundsChanged || this.__layout.renderBoundsChange()
32
+ }
33
+
34
+ @rewrite(rect.__updateStrokeBoundsSpreadWidth)
35
+ public __updateStrokeBoundsSpreadWidth(): number { return 0 }
36
+
37
+ @rewrite(rect.__updateRenderBoundsSpreadWidth)
38
+ public __updateRectRenderBoundsSpreadWidth(): number { return 0 }
39
+
40
+ public __updateRenderBoundsSpreadWidth(): number {
41
+ let width = this.__updateRectRenderBoundsSpreadWidth() || super.__updateRenderBoundsSpreadWidth()
42
+ this.__.__drawAfterFill = this.__.overflow === 'hide'
43
+ if (!width) width = this.__.__drawAfterFill ? 0 : 1
44
+ return width
45
+ }
46
+
47
+
48
+ @rewrite(rect.__updateBoxBounds)
49
+ public __updateBoxBounds(): void { }
50
+
51
+ @rewrite(rect.__updateStrokeBounds)
52
+ public __updateStrokeBounds(): void { }
53
+
54
+ public __updateRenderBounds(): void {
55
+ this.__updateRectRenderBounds()
56
+ if (!this.__.__drawAfterFill) {
57
+ const { renderBounds } = this.__layout
58
+ copy(bounds, renderBounds)
59
+ super.__updateRenderBounds()
60
+ add(renderBounds, bounds)
61
+ }
62
+ }
63
+
64
+
65
+ @rewrite(rect.__updateRenderBounds)
66
+ public __updateRectRenderBounds(): void { }
67
+
68
+ @rewrite(rect.__updateChange)
69
+ public __updateRectChange(): void { }
70
+
71
+ public __updateChange(): void {
72
+ super.__updateChange()
73
+ this.__updateRectChange()
74
+ }
75
+
76
+
77
+ @rewrite(rect.__drawPathByData)
78
+ public __drawPathByData(_drawer: IPathDrawer, _data: IPathCommandData): void { }
79
+
80
+
81
+ @rewrite(rect.__render)
82
+ public __renderRect(_canvas: ILeaferCanvas, _options: IRenderOptions): void { }
83
+
84
+ @rewrite(group.__render)
85
+ public __renderGroup(_canvas: ILeaferCanvas, _options: IRenderOptions): void { }
86
+
87
+
88
+ public __render(canvas: ILeaferCanvas, options: IRenderOptions): void {
89
+ if (this.__.__drawAfterFill) {
90
+ this.__renderRect(canvas, options)
91
+ } else {
92
+ this.__renderRect(canvas, options)
93
+ this.__renderGroup(canvas, options)
94
+ }
95
+ }
96
+
97
+ public __drawAfterFill(canvas: ILeaferCanvas, options: IRenderOptions): void {
98
+ canvas.save()
99
+ canvas.clip()
100
+ this.__renderGroup(canvas, options)
101
+ canvas.restore()
102
+ if (this.__.stroke) this.__drawRenderPath(canvas)
103
+ }
104
+
105
+ }
package/src/Canvas.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { ILeaferCanvas, ILeaferCanvasConfig, __String, IRenderOptions, IPointData, ICanvasContext2D, IScreenSizeData } from '@leafer/interface'
2
+ import { Creator, Matrix, boundsType, dataProcessor, registerUI, surfaceType } from '@leafer/core'
3
+
4
+ import { ICanvas, ICanvasData, ICanvasInputData, IUI } from '@leafer-ui/interface'
5
+ import { ImageData } from '@leafer-ui/data'
6
+
7
+ import { Rect } from './Rect'
8
+
9
+
10
+ @registerUI()
11
+ export class Canvas extends Rect implements ICanvas {
12
+
13
+ public get __tag() { return 'Canvas' }
14
+
15
+ @dataProcessor(ImageData)
16
+ public __: ICanvasData
17
+
18
+ @boundsType('')
19
+ public pixelRatio: number
20
+
21
+ @surfaceType('true')
22
+ public smooth: boolean
23
+
24
+ public canvas: ILeaferCanvas
25
+
26
+ public readonly context: ICanvasContext2D
27
+
28
+ constructor(data?: ICanvasInputData) {
29
+ super(data)
30
+ this.canvas = Creator.canvas(data as ILeaferCanvasConfig)
31
+ this.context = this.canvas.context
32
+ this.__.hitType = 'all'
33
+ this.__.pixelRatio = this.canvas.pixelRatio
34
+ }
35
+
36
+ public draw(ui: IUI, offset?: IPointData, scale?: number | IPointData, rotation?: number): void {
37
+ ui.__layout.checkUpdate()
38
+ const matrix = new Matrix(ui.__world)
39
+ matrix.invert()
40
+
41
+ const m = new Matrix()
42
+ if (offset) m.translate(offset.x, offset.y)
43
+ if (scale) typeof scale === 'number' ? m.scale(scale) : m.scale(scale.x, scale.y)
44
+ if (rotation) m.rotate(rotation)
45
+ matrix.preMultiply(m)
46
+
47
+ ui.__render(this.canvas, { matrix })
48
+ this.forceUpdate('fill')
49
+ }
50
+
51
+ public __draw(canvas: ILeaferCanvas, _options: IRenderOptions): void {
52
+ canvas.drawImage(this.canvas.view as any, 0, 0)
53
+ }
54
+
55
+ public __updateBoxBounds(): void {
56
+ super.__updateBoxBounds()
57
+ this.canvas.resize(this.__ as IScreenSizeData)
58
+ }
59
+
60
+ }
package/src/Ellipse.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { __Number } from '@leafer/interface'
2
- import { PathCreator, OneRadian, dataProcessor, pathType, registerUI } from '@leafer/core'
2
+ import { PathCommandDataHelper, dataProcessor, pathType, registerUI } from '@leafer/core'
3
3
 
4
4
  import { IEllipse, IEllipseInputData, IEllipseData } from '@leafer-ui/interface'
5
5
  import { EllipseData } from '@leafer-ui/data'
@@ -7,14 +7,13 @@ import { EllipseData } from '@leafer-ui/data'
7
7
  import { UI } from './UI'
8
8
 
9
9
 
10
-
11
- const { PI } = Math
12
- const { begin, moveTo, close, ellipse } = PathCreator
13
-
10
+ const { moveTo, closePath, ellipse } = PathCommandDataHelper
14
11
 
15
12
  @registerUI()
16
13
  export class Ellipse extends UI implements IEllipse {
17
14
 
15
+ public get __tag() { return 'Ellipse' }
16
+
18
17
  @dataProcessor(EllipseData)
19
18
  public __: IEllipseData
20
19
 
@@ -36,28 +35,30 @@ export class Ellipse extends UI implements IEllipse {
36
35
  const { width, height, innerRadius, startAngle, endAngle } = this.__
37
36
  const rx = width / 2, ry = height / 2
38
37
 
39
- begin(this.__.path = [])
38
+ const path: number[] = this.__.path = []
40
39
 
41
40
  if (innerRadius) {
42
41
 
43
42
  if (startAngle || endAngle) {
44
- ellipse(rx, ry, rx * (1 - innerRadius), ry * (1 - innerRadius), 0, startAngle * OneRadian, endAngle * OneRadian, false)
45
- ellipse(rx, ry, rx, ry, 0, endAngle * OneRadian, startAngle * OneRadian, true)
46
- close()
43
+ if (innerRadius < 1) ellipse(path, rx, ry, rx * innerRadius, ry * innerRadius, 0, startAngle, endAngle, false)
44
+ ellipse(path, rx, ry, rx, ry, 0, endAngle, startAngle, true)
45
+ if (innerRadius < 1) closePath(path)
47
46
  } else {
48
- ellipse(rx, ry, rx * (1 - innerRadius), ry * (1 - innerRadius), 0, 0, 2 * PI, false)
49
- moveTo(width, ry)
50
- ellipse(rx, ry, rx, ry, 0, 0, 2 * PI, true)
47
+ if (innerRadius < 1) {
48
+ ellipse(path, rx, ry, rx * innerRadius, ry * innerRadius)
49
+ moveTo(path, width, ry)
50
+ }
51
+ ellipse(path, rx, ry, rx, ry, 0, 0, 360, true)
51
52
  }
52
53
 
53
54
  } else {
54
55
 
55
56
  if (startAngle || endAngle) {
56
- moveTo(rx, ry)
57
- ellipse(rx, ry, rx, ry, 0, startAngle * OneRadian, endAngle * OneRadian, false)
58
- close()
57
+ moveTo(path, rx, ry)
58
+ ellipse(path, rx, ry, rx, ry, 0, startAngle, endAngle, false)
59
+ closePath(path)
59
60
  } else {
60
- ellipse(rx, ry, rx, ry, 0, 0, 2 * PI, false)
61
+ ellipse(path, rx, ry, rx, ry)
61
62
  }
62
63
 
63
64
  }
package/src/Frame.ts CHANGED
@@ -1,84 +1,25 @@
1
- import { ILeaferCanvas, IRenderOptions, IPathDrawer, IBoundsData, IPathCommandData, __Boolean } from '@leafer/interface'
2
- import { BoundsHelper, dataProcessor, boundsType, rewrite, useModule, rewriteAble, registerUI } from '@leafer/core'
1
+ import { __Boolean } from '@leafer/interface'
2
+ import { dataProcessor, registerUI, affectRenderBoundsType } from '@leafer/core'
3
3
 
4
- import { IFrame, IFrameData, IFrameInputData } from '@leafer-ui/interface'
4
+ import { IFrame, IFrameData, IFrameInputData, IOverflow } from '@leafer-ui/interface'
5
5
  import { FrameData } from '@leafer-ui/data'
6
- import { FrameRender } from '@leafer-ui/display-module'
7
6
 
8
- import { Group } from './Group'
9
- import { Rect } from './Rect'
7
+ import { Box } from './Box'
10
8
 
11
9
 
12
- const rect = Rect.prototype
13
- const group = Group.prototype
14
- const bounds = {} as IBoundsData
15
- const { copy, add } = BoundsHelper
16
-
17
-
18
- @useModule(FrameRender)
19
- @rewriteAble()
20
10
  @registerUI()
21
- export class Frame extends Group implements IFrame {
11
+ export class Frame extends Box implements IFrame {
12
+
13
+ public get __tag() { return 'Frame' }
22
14
 
23
15
  @dataProcessor(FrameData)
24
16
  public __: IFrameData
25
17
 
26
- @boundsType(true)
27
- public clip: __Boolean
18
+ @affectRenderBoundsType('hide')
19
+ public overflow: IOverflow
28
20
 
29
21
  constructor(data?: IFrameInputData) {
30
22
  super(data)
31
- if (!this.fill) this.fill = '#FFFFFF'
32
- this.__isBranchLeaf = true
23
+ if (!this.__.fill) this.__.fill = '#FFFFFF'
33
24
  }
34
-
35
- @rewrite(rect.__drawPathByData)
36
- public __drawPathByData(_drawer: IPathDrawer, _data: IPathCommandData): void { }
37
-
38
- public __updateBoxBounds(): void {
39
- this.__updateRectBoxBounds()
40
- if (!this.__.clip) {
41
- const { boxBounds } = this.__layout
42
- copy(bounds, boxBounds)
43
- super.__updateBoxBounds()
44
- add(boxBounds, bounds)
45
- }
46
- }
47
-
48
- public __updateEventBounds(): void {
49
- this.__updateRectEventBounds()
50
- if (!this.__.clip) {
51
- const { eventBounds } = this.__layout
52
- copy(bounds, eventBounds)
53
- super.__updateEventBounds()
54
- add(eventBounds, bounds)
55
- }
56
- }
57
-
58
- public __updateRenderBounds(): void {
59
- this.__updateRectRenderBounds()
60
- if (!this.__.clip) {
61
- const { renderBounds } = this.__layout
62
- copy(bounds, renderBounds)
63
- super.__updateRenderBounds()
64
- add(renderBounds, bounds)
65
- }
66
- }
67
-
68
- @rewrite(rect.__updateBoxBounds)
69
- public __updateRectBoxBounds(): void { }
70
-
71
- @rewrite(rect.__updateEventBounds)
72
- public __updateRectEventBounds(): void { }
73
-
74
- @rewrite(rect.__updateRenderBounds)
75
- public __updateRectRenderBounds(): void { }
76
-
77
-
78
- @rewrite(rect.__render)
79
- public __renderRect(_canvas: ILeaferCanvas, _options: IRenderOptions): void { }
80
-
81
- @rewrite(group.__render)
82
- public __renderGroup(_canvas: ILeaferCanvas, _options: IRenderOptions): void { }
83
-
84
25
  }
package/src/Group.ts CHANGED
@@ -10,14 +10,27 @@ import { UI } from './UI'
10
10
  @registerUI()
11
11
  export class Group extends UI implements IGroup {
12
12
 
13
+ public get __tag() { return 'Group' }
14
+
13
15
  @dataProcessor(GroupData)
14
16
  public __: IGroupData
15
17
 
16
18
  public children: IUI[]
17
19
 
20
+ public set mask(child: IUI) {
21
+ if (this.__hasMask) this.__removeMask()
22
+ if (child) {
23
+ child.isMask = true
24
+ this.addAt(child, 0)
25
+ }
26
+ }
27
+ public get mask(): IUI {
28
+ return this.children.find(item => item.isMask)
29
+ }
30
+
18
31
  constructor(data?: IGroupInputData) {
19
32
  super(data)
20
- this.__isBranch = true
33
+ this.isBranch = true
21
34
  this.children = []
22
35
  }
23
36
 
package/src/Image.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ILeaferImage, __String } from '@leafer/interface'
2
- import { boundsType, dataProcessor, registerUI, Creator } from '@leafer/core'
2
+ import { ImageEvent, boundsType, dataProcessor, registerUI } from '@leafer/core'
3
3
 
4
- import { IImage, IImageInputData, IImageData } from '@leafer-ui/interface'
4
+ import { IImage, IImageInputData, IImageData, IImagePaint } from '@leafer-ui/interface'
5
5
  import { ImageData } from '@leafer-ui/data'
6
6
 
7
7
  import { Rect } from './Rect'
@@ -10,27 +10,42 @@ import { Rect } from './Rect'
10
10
  @registerUI()
11
11
  export class Image extends Rect implements IImage {
12
12
 
13
+ public get __tag() { return 'Image' }
14
+
13
15
  @dataProcessor(ImageData)
14
16
  public __: IImageData
15
17
 
16
18
  @boundsType('')
17
19
  public url: __String
18
20
 
19
- @boundsType('')
20
- public thumb: __String
21
+ public get ready(): boolean { return this.image?.ready }
21
22
 
22
- public __image: ILeaferImage
23
+ public image: ILeaferImage
23
24
 
24
25
  constructor(data?: IImageInputData) {
25
26
  super(data)
26
27
  }
27
28
 
28
29
  public __updateBoxBounds(): void {
29
- this.__image = Creator.image(this)
30
- }
31
30
 
31
+ let update: boolean
32
+
33
+ const { url } = this
34
+ const fill = this.fill as IImagePaint
35
+
36
+ if (fill) {
37
+ if (fill.url !== url) update = true
38
+ } else {
39
+ if (url) update = true
40
+ }
41
+
42
+ if (update) {
43
+ if (this.image) this.image = null
44
+ this.fill = { type: 'image', mode: 'strench', url }
45
+ this.once(ImageEvent.LOADED, (e) => this.image = e.image)
46
+ }
32
47
 
33
- public drawFill(): void {
48
+ super.__updateBoxBounds()
34
49
 
35
50
  }
36
51
 
package/src/Line.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { IPointData, ITwoPointBoundsData, __Number } from '@leafer/interface'
2
- import { PathCreator, PointHelper, TwoPointBoundsHelper, boundsType, dataProcessor, registerUI } from '@leafer/core'
2
+ import { PathCommandDataHelper, PointHelper, TwoPointBoundsHelper, boundsType, affectStrokeBoundsType, dataProcessor, registerUI } from '@leafer/core'
3
3
 
4
- import { ILine, ILineData, ILineInputData } from '@leafer-ui/interface'
4
+ import { ILine, ILineData, ILineInputData, IStrokeAlign } from '@leafer-ui/interface'
5
5
  import { LineData } from '@leafer-ui/data'
6
6
 
7
7
  import { UI } from './UI'
8
8
 
9
9
 
10
- const { begin, moveTo, lineTo, end } = PathCreator
10
+ const { moveTo, lineTo } = PathCommandDataHelper
11
11
  const { rotate, getAngle, getDistance, defaultPoint } = PointHelper
12
12
  const { setPoint, addPoint, toBounds } = TwoPointBoundsHelper
13
13
 
@@ -17,12 +17,17 @@ const pointBounds = {} as ITwoPointBoundsData
17
17
  @registerUI()
18
18
  export class Line extends UI implements ILine {
19
19
 
20
+ public get __tag() { return 'Line' }
21
+
20
22
  @dataProcessor(LineData)
21
23
  public __: ILineData
22
24
 
23
25
  @boundsType()
24
26
  public rotation: __Number
25
27
 
28
+ @affectStrokeBoundsType('center')
29
+ public strokeAlign: IStrokeAlign
30
+
26
31
  protected __toPoint: IPointData
27
32
 
28
33
  public get toPoint(): IPointData {
@@ -51,12 +56,11 @@ export class Line extends UI implements ILine {
51
56
 
52
57
  public __updatePath(): void {
53
58
 
54
- begin(this.__.path = [])
55
- moveTo(0, 0)
59
+ const path: number[] = this.__.path = []
60
+ moveTo(path, 0, 0)
56
61
 
57
62
  const to = this.toPoint
58
- lineTo(to.x, to.y)
59
- end()
63
+ lineTo(path, to.x, to.y)
60
64
  }
61
65
 
62
66
  public __updateBoxBounds(): void {
package/src/Path.ts CHANGED
@@ -1,21 +1,19 @@
1
- import { ITwoPointBoundsData, ILeaferCanvas, IPathCommandData, IWindingRule } from '@leafer/interface'
2
- import { PathHelper, TwoPointBoundsHelper, dataProcessor, pathType, registerUI } from '@leafer/core'
1
+ import { IPathCommandData, IWindingRule } from '@leafer/interface'
2
+ import { PathBounds, dataProcessor, pathType, affectStrokeBoundsType, registerUI, PathConvert } from '@leafer/core'
3
3
 
4
- import { IPath, IPathData, IPathInputData, IPathString } from '@leafer-ui/interface'
4
+ import { IPath, IPathData, IPathInputData, IPathString, IStrokeAlign } from '@leafer-ui/interface'
5
5
  import { PathData } from '@leafer-ui/data'
6
6
 
7
7
  import { UI } from './UI'
8
8
 
9
9
 
10
- const { toTwoPointBounds } = PathHelper
11
- const { toBounds } = TwoPointBoundsHelper
12
-
13
- const pointBounds = {} as ITwoPointBoundsData
14
-
10
+ const { toBounds } = PathBounds
15
11
 
16
12
  @registerUI()
17
13
  export class Path extends UI implements IPath {
18
14
 
15
+ public get __tag() { return 'Path' }
16
+
19
17
  @dataProcessor(PathData)
20
18
  public __: IPathData
21
19
 
@@ -25,18 +23,25 @@ export class Path extends UI implements IPath {
25
23
  @pathType()
26
24
  public windingRule: IWindingRule
27
25
 
26
+ @affectStrokeBoundsType('center')
27
+ public strokeAlign: IStrokeAlign
28
+
28
29
  constructor(data?: IPathInputData) {
29
30
  super(data)
30
31
  }
31
32
 
32
- public __drawRenderPath(canvas: ILeaferCanvas): void {
33
- canvas.beginPath()
34
- this.__drawPathByData(canvas, this.__.path)
33
+ public getCurvePath(): IPathCommandData {
34
+ return PathConvert.toCanvasData(this.__.path, true)
35
+ }
36
+
37
+ public getStringPath(curve?: boolean): IPathString {
38
+ let path = this.__.path
39
+ if (curve) path = PathConvert.toCanvasData([...path], true)
40
+ return PathConvert.stringify(path)
35
41
  }
36
42
 
37
43
  public __updateBoxBounds(): void {
38
- toTwoPointBounds(this.__.path, pointBounds)
39
- toBounds(pointBounds, this.__layout.boxBounds)
44
+ toBounds(this.__.path, this.__layout.boxBounds)
40
45
  }
41
46
 
42
47
  }
package/src/Pen.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { PathCreator, dataProcessor, registerUI, useModule } from '@leafer/core'
2
+
3
+ import { IPenData, IPenInputData, IPathInputData, IPathCommandData, IPath } from '@leafer-ui/interface'
4
+ import { PenData } from '@leafer-ui/data'
5
+
6
+ import { Group } from './Group'
7
+ import { Path } from './Path'
8
+
9
+
10
+ @useModule(PathCreator, ['beginPath'])
11
+ @registerUI()
12
+ export class Pen extends Group {
13
+
14
+ public get __tag() { return 'Pen' }
15
+
16
+ @dataProcessor(PenData)
17
+ public __: IPenData
18
+
19
+ public pathElement: IPath
20
+ public pathStyle: IPathInputData
21
+ public path: IPathCommandData
22
+
23
+ constructor(data?: IPenInputData) {
24
+ super(data)
25
+ }
26
+
27
+ public setStyle(data: IPathInputData): Pen {
28
+ const path = this.pathElement = new Path(data)
29
+ this.pathStyle = data
30
+ this.path = path.path as IPathCommandData || (path.path = [])
31
+ this.add(path)
32
+ return this
33
+ }
34
+
35
+ public beginPath(): Pen {
36
+ this.path.length = 0
37
+ this.paint()
38
+ return this
39
+ }
40
+
41
+ // svg and canvas
42
+
43
+ public moveTo(_x: number, _y: number): Pen { return this }
44
+
45
+ public lineTo(_x: number, _y: number): Pen { return this }
46
+
47
+ public bezierCurveTo(_x1: number, _y1: number, _x2: number, _y2: number, _x: number, _y: number): Pen { return this }
48
+
49
+ public quadraticCurveTo(_x1: number, _y1: number, _x: number, _y: number): Pen { return this }
50
+
51
+ public closePath(): Pen { return this }
52
+
53
+
54
+ // canvas
55
+
56
+ public rect(_x: number, _y: number, _width: number, _height: number): Pen { return this }
57
+
58
+ public roundRect(_x: number, _y: number, _width: number, _height: number, _cornerRadius: number | number[]): Pen { return this }
59
+
60
+ public ellipse(_x: number, _y: number, _radiusX: number, _radiusY: number, _rotation?: number, _startAngle?: number, _endAngle?: number, _anticlockwise?: boolean): Pen { return this }
61
+
62
+ public arc(_x: number, _y: number, _radius: number, _startAngle?: number, _endAngle?: number, _anticlockwise?: boolean): Pen { return this }
63
+
64
+ public arcTo(_x1: number, _y1: number, _x2: number, _y2: number, _radius: number): Pen { return this }
65
+
66
+
67
+ // moveTo, then draw
68
+
69
+ public moveToEllipse(_x: number, _y: number, _radiusX: number, _radiusY: number, _rotation?: number, _startAngle?: number, _endAngle?: number, _anticlockwise?: boolean): Pen { return this }
70
+
71
+ public moveToArc(_x: number, _y: number, _radius: number, _startAngle?: number, _endAngle?: number, _anticlockwise?: boolean): Pen { return this }
72
+
73
+
74
+ public paint(): void {
75
+ this.pathElement.forceUpdate('path')
76
+ }
77
+
78
+ }
package/src/Polygon.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { __Number } from '@leafer/interface'
2
- import { PathCreator, dataProcessor, pathType, registerUI } from '@leafer/core'
2
+ import { PathCommandDataHelper, dataProcessor, pathType, registerUI } from '@leafer/core'
3
3
 
4
4
  import { IPolygon, IPolygonData, IPolygonInputData } from '@leafer-ui/interface'
5
5
  import { PolygonData } from '@leafer-ui/data'
@@ -8,12 +8,14 @@ import { UI } from './UI'
8
8
 
9
9
 
10
10
  const { sin, cos, PI } = Math
11
- const { begin, moveTo, lineTo, close } = PathCreator
11
+ const { moveTo, lineTo, closePath } = PathCommandDataHelper
12
12
 
13
13
 
14
14
  @registerUI()
15
15
  export class Polygon extends UI implements IPolygon {
16
16
 
17
+ public get __tag() { return 'Polygon' }
18
+
17
19
  @dataProcessor(PolygonData)
18
20
  public __: IPolygonData
19
21
 
@@ -29,14 +31,14 @@ export class Polygon extends UI implements IPolygon {
29
31
  const { width, height, sides } = this.__
30
32
  const rx = width / 2, ry = height / 2
31
33
 
32
- begin(this.__.path = [])
33
- moveTo(rx, 0)
34
+ const path: number[] = this.__.path = []
35
+ moveTo(path, rx, 0)
34
36
 
35
37
  for (let i = 1; i < sides; i++) {
36
- lineTo(rx + rx * sin((i * 2 * PI) / sides), ry - ry * cos((i * 2 * PI) / sides))
38
+ lineTo(path, rx + rx * sin((i * 2 * PI) / sides), ry - ry * cos((i * 2 * PI) / sides))
37
39
  }
38
40
 
39
- close()
41
+ closePath(path)
40
42
  }
41
43
 
42
44
  }
package/src/Rect.ts CHANGED
@@ -12,6 +12,8 @@ import { UI } from './UI'
12
12
  @registerUI()
13
13
  export class Rect extends UI implements IRect {
14
14
 
15
+ public get __tag() { return 'Rect' }
16
+
15
17
  @dataProcessor(RectData)
16
18
  public __: IRectData
17
19
 
package/src/Star.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { __Number } from '@leafer/interface'
2
- import { PathCreator, dataProcessor, pathType, registerUI } from '@leafer/core'
2
+ import { PathCommandDataHelper, dataProcessor, pathType, registerUI } from '@leafer/core'
3
3
 
4
4
  import { IStar, IStarData, IStarInputData } from '@leafer-ui/interface'
5
5
  import { StarData } from '@leafer-ui/data'
@@ -8,19 +8,21 @@ import { UI } from './UI'
8
8
 
9
9
 
10
10
  const { sin, cos, PI } = Math
11
- const { begin, moveTo, lineTo, close } = PathCreator
11
+ const { moveTo, lineTo, closePath } = PathCommandDataHelper
12
12
 
13
13
 
14
14
  @registerUI()
15
15
  export class Star extends UI implements IStar {
16
16
 
17
+ public get __tag() { return 'Star' }
18
+
17
19
  @dataProcessor(StarData)
18
20
  public __: IStarData
19
21
 
20
22
  @pathType(5)
21
23
  public points: __Number
22
24
 
23
- @pathType(0.38)
25
+ @pathType(0.382)
24
26
  public innerRadius: __Number
25
27
 
26
28
  constructor(data?: IStarInputData) {
@@ -32,14 +34,14 @@ export class Star extends UI implements IStar {
32
34
  const { width, height, points, innerRadius } = this.__
33
35
  const rx = width / 2, ry = height / 2
34
36
 
35
- begin(this.__.path = [])
36
- moveTo(rx, 0)
37
+ const path: number[] = this.__.path = []
38
+ moveTo(path, rx, 0)
37
39
 
38
40
  for (let i = 1; i < points * 2; i++) {
39
- lineTo(rx + (i % 2 === 0 ? rx : rx * innerRadius) * sin((i * PI) / points), ry - (i % 2 === 0 ? ry : ry * innerRadius) * cos((i * PI) / points))
41
+ lineTo(path, rx + (i % 2 === 0 ? rx : rx * innerRadius) * sin((i * PI) / points), ry - (i % 2 === 0 ? ry : ry * innerRadius) * cos((i * PI) / points))
40
42
  }
41
43
 
42
- close()
44
+ closePath(path)
43
45
 
44
46
  }
45
47
 
package/src/Text.ts CHANGED
@@ -1,24 +1,42 @@
1
- import { IPathDrawer, IPathCommandData, __Boolean, __Number, __String } from '@leafer/interface'
2
- import { Platform, boundsType, dataProcessor, registerUI, useModule } from '@leafer/core'
1
+ import { ILeaferCanvas, IPathDrawer, IPathCommandData, __Boolean, __Number, __String } from '@leafer/interface'
2
+ import { BoundsHelper, boundsType, dataProcessor, registerUI, useModule, affectStrokeBoundsType } from '@leafer/core'
3
3
 
4
- import { IText, IFontWeight, ITextCase, ITextDecoration, IPercent, ITextData, ITextInputData } from '@leafer-ui/interface'
5
- import { TextData } from '@leafer-ui/data'
4
+ import { IText, IFontWeight, ITextCase, ITextDecoration, ITextData, ITextInputData, ITextAlign, IVerticalAlign, ITextDrawData, IOverflow, IUnitData, IStrokeAlign } from '@leafer-ui/interface'
5
+ import { TextData, UnitConvert } from '@leafer-ui/data'
6
6
  import { TextRender } from '@leafer-ui/display-module'
7
+ import { TextConvert } from '@leafer-ui/text'
7
8
 
8
9
  import { UI } from './UI'
9
10
 
10
11
 
12
+ const { copyAndSpread, includes, spread } = BoundsHelper
13
+
11
14
  @useModule(TextRender)
12
15
  @registerUI()
13
16
  export class Text extends UI implements IText {
14
17
 
18
+ public get __tag() { return 'Text' }
19
+
15
20
  @dataProcessor(TextData)
16
21
  public __: ITextData
17
22
 
23
+ // size
24
+ @boundsType(0)
25
+ public width: __Number
26
+
27
+ @boundsType(0)
28
+ public height: __Number
29
+
30
+ @boundsType(0)
31
+ public padding: number | number[]
32
+
33
+ @affectStrokeBoundsType('outside')
34
+ public strokeAlign: IStrokeAlign
35
+
18
36
  @boundsType('')
19
- public content: __String
37
+ public text: __String
20
38
 
21
- @boundsType('arial')
39
+ @boundsType('L')
22
40
  public fontFamily: __String
23
41
 
24
42
  @boundsType(12)
@@ -30,45 +48,104 @@ export class Text extends UI implements IText {
30
48
  @boundsType(false)
31
49
  public italic: __Boolean
32
50
 
33
- @boundsType('normal')
51
+ @boundsType('none')
34
52
  public textCase: ITextCase
35
53
 
36
- @boundsType('normal')
54
+ @boundsType('none')
37
55
  public textDecoration: ITextDecoration
38
56
 
39
- @boundsType()
40
- public letterSpacing: __Number | IPercent
57
+ @boundsType(0)
58
+ public letterSpacing: __Number | IUnitData
41
59
 
42
- @boundsType()
43
- public lineHeight: __Number | IPercent
60
+ @boundsType({ type: 'percent', value: 150 } as IUnitData)
61
+ public lineHeight: __Number | IUnitData
44
62
 
45
- @boundsType()
46
- public paragraphIndent: __Number
63
+ @boundsType(0)
64
+ public paraIndent: __Number
47
65
 
48
- @boundsType()
49
- public paragraphSpacing: __Number
66
+ @boundsType(0)
67
+ public paraSpacing: __Number
68
+
69
+ @boundsType('left')
70
+ public textAlign: ITextAlign
71
+
72
+ @boundsType('top')
73
+ public verticalAlign: IVerticalAlign
74
+
75
+ @boundsType('show')
76
+ public textOverflow: IOverflow | string
77
+
78
+ public get textDrawData(): ITextDrawData {
79
+ this.__layout.checkUpdate()
80
+ return this.__.__textDrawData
81
+ }
50
82
 
51
83
  constructor(data?: ITextInputData) {
52
84
  super(data)
53
85
  }
54
86
 
87
+ public __drawHitPath(canvas: ILeaferCanvas): void {
88
+ const { __lineHeight, __baseLine, __textDrawData: data } = this.__
89
+ canvas.beginPath()
90
+ data.rows.forEach(row => canvas.rect(row.x, row.y - __baseLine, row.width, __lineHeight))
91
+ }
92
+
55
93
  public __drawPathByData(drawer: IPathDrawer, _data: IPathCommandData): void {
56
- const { width, height } = this.__
57
- drawer.rect(0, 0, width, height)
94
+ const { x, y, width, height } = this.__layout.boxBounds
95
+ drawer.rect(x, y, width, height)
96
+ }
97
+
98
+ public __updateTextDrawData(): void {
99
+ const data = this.__
100
+ data.__textDrawData = TextConvert.getDrawData(data.text, this.__)
101
+
58
102
  }
59
103
 
60
104
  public __updateBoxBounds(): void {
61
- const { fontFamily, fontSize, fontWeight, italic, textCase, lineHeight } = this.__
62
- const b = this.__layout.boxBounds
63
105
 
64
- this.__.__font = Platform.canvas.font = `${italic ? 'italic' : ''} ${textCase === 'small-caps' ? 'small-caps' : 'normal'} ${fontWeight ? fontWeight : 'normal'} ${fontSize}px/${lineHeight ? lineHeight : fontSize}px ${fontFamily}`
65
- const width = Platform.canvas.measureText(this.content).width
66
- const height = fontSize
106
+ const data = this.__
107
+ const layout = this.__layout
108
+ const { width, height, lineHeight, letterSpacing, fontFamily, fontSize, fontWeight, italic } = data
109
+
110
+ // compute
111
+
112
+ data.__lineHeight = UnitConvert.number(lineHeight, fontSize)
113
+ data.__letterSpacing = UnitConvert.number(letterSpacing, fontSize)
114
+ data.__baseLine = data.__lineHeight - (data.__lineHeight - fontSize * 0.7) / 2
115
+ data.__font = `${italic ? 'italic ' : ''}${fontWeight !== 'normal' ? fontWeight + ' ' : ''}${fontSize}px ${fontFamily}`
116
+
117
+ this.__updateTextDrawData()
118
+
119
+ const { bounds } = data.__textDrawData
120
+ const b = layout.boxBounds
121
+
122
+ if (data.__lineHeight < fontSize) spread(bounds, fontSize / 2)
123
+
124
+ if (width && height) {
125
+ super.__updateBoxBounds()
126
+ } else {
127
+ b.x = width ? 0 : bounds.x
128
+ b.y = height ? 0 : bounds.y
129
+ b.width = width ? width : bounds.width
130
+ b.height = height ? height : bounds.height
131
+ }
132
+
133
+ const contentBounds = includes(b, bounds) ? b : bounds
134
+ if (contentBounds !== layout.contentBounds) {
135
+ layout.contentBounds = contentBounds
136
+ layout.renderBoundsChanged = true
137
+ }
138
+
139
+ }
140
+
141
+ public __updateRenderBoundsSpreadWidth(): number {
142
+ let width = super.__updateRenderBoundsSpreadWidth()
143
+ if (!width) width = this.__layout.boxBounds === this.__layout.contentBounds ? 0 : 1
144
+ return width
145
+ }
67
146
 
68
- b.x = 0
69
- b.y = -fontSize * 0.75
70
- b.width = width
71
- b.height = height
147
+ public __updateRenderBounds(): void {
148
+ copyAndSpread(this.__layout.renderBounds, this.__layout.contentBounds, this.__layout.renderBoundsSpreadWidth)
72
149
  }
73
150
 
74
151
  }
package/src/UI.ts CHANGED
@@ -1,12 +1,14 @@
1
- import { ILeaferCanvas, IPathDrawer, IPathCommandData, IMatrixData, IBoundsData, IHitType, __Number, __Boolean, __String } from '@leafer/interface'
2
- import { Leaf, PathHelper, affectEventBoundsType, surfaceType, dataType, positionType, boundsType, pathType, scaleType, rotationType, opacityType, sortType, dataProcessor, useModule, rewrite, rewriteAble } from '@leafer/core'
1
+ import { ILeaferCanvas, IPathDrawer, IPathCommandData, IHitType, __Number, __Boolean, __String } from '@leafer/interface'
2
+ import { Leaf, PathDrawer, surfaceType, dataType, positionType, boundsType, pathType, scaleType, rotationType, opacityType, sortType, maskType, dataProcessor, useModule, rewrite, rewriteAble, UICreator, PathCorner, hitType, strokeType } from '@leafer/core'
3
3
 
4
- import { IUI, IShadowEffect, IBlurEffect, IPaint, IStrokeAlign, IStrokeJoin, IStrokeCap, IBlendMode, IPaintString, IDashPatternString, IShadowString, IGrayscaleEffect, IUIData, IGroup, IStrokeWidthString, ICornerRadiusString } from '@leafer-ui/interface'
4
+ import { IUI, IShadowEffect, IBlurEffect, IPaint, IStrokeAlign, IStrokeJoin, IStrokeCap, IBlendMode, IPaintString, IDashPatternString, IShadowString, IGrayscaleEffect, IUIData, IGroup, IStrokeWidthString, ICornerRadiusString, IUITagInputData, IUIInputData } from '@leafer-ui/interface'
5
5
  import { effectType } from '@leafer-ui/decorator'
6
6
 
7
7
  import { UIData } from '@leafer-ui/data'
8
8
  import { UIBounds, UIHit, UIRender } from '@leafer-ui/display-module'
9
9
 
10
+ import { Paint } from '@leafer-ui/external'
11
+
10
12
 
11
13
  @useModule(UIBounds)
12
14
  @useModule(UIHit)
@@ -14,11 +16,9 @@ import { UIBounds, UIHit, UIRender } from '@leafer-ui/display-module'
14
16
  @rewriteAble()
15
17
  export class UI extends Leaf implements IUI {
16
18
 
17
-
18
19
  @dataProcessor(UIData)
19
20
  public __: IUIData
20
21
 
21
- public root?: IGroup
22
22
  public parent?: IGroup
23
23
 
24
24
  // ---
@@ -35,15 +35,24 @@ export class UI extends Leaf implements IUI {
35
35
 
36
36
 
37
37
  // layer
38
+ @surfaceType() // "pass-through"
39
+ public blendMode: IBlendMode
40
+
38
41
  @opacityType(1)
39
42
  public opacity: __Number
40
43
 
41
44
  @opacityType(true)
42
45
  public visible: __Boolean
43
46
 
47
+ @maskType(false)
48
+ public isMask: __Boolean
49
+
44
50
  @sortType(0)
45
51
  public zIndex: __Number
46
52
 
53
+ @dataType()
54
+ public locked: __Boolean
55
+
47
56
 
48
57
  // position
49
58
  @positionType(0)
@@ -84,28 +93,23 @@ export class UI extends Leaf implements IUI {
84
93
  public draggable: __Boolean
85
94
 
86
95
  // hit
87
- @dataType(true)
96
+ @hitType(true)
88
97
  public hittable: __Boolean
89
98
 
90
- @dataType('visible')
91
- public hitType: IHitType
92
-
93
- @dataType(true)
94
- public hitChildren: __Boolean
99
+ @hitType('visible')
100
+ public hitFill: IHitType
95
101
 
96
- // ---
102
+ @strokeType('visible')
103
+ public hitStroke: IHitType
97
104
 
105
+ @hitType(true)
106
+ public hitChildren: __Boolean
98
107
 
99
- // layer
108
+ @hitType(true)
109
+ public hitSelf: __Boolean
100
110
 
101
- @surfaceType() // "pass-through"
102
- public blendMode: IBlendMode
103
-
104
- @boundsType()
105
- public mask: __Boolean
111
+ // ---
106
112
 
107
- @dataType()
108
- public locked: __Boolean
109
113
 
110
114
  // fill
111
115
 
@@ -114,28 +118,28 @@ export class UI extends Leaf implements IUI {
114
118
 
115
119
  // stroke
116
120
 
117
- @affectEventBoundsType()
121
+ @strokeType()
118
122
  public stroke: IPaint | IPaint[] | IPaintString
119
123
 
120
- @affectEventBoundsType('center')
124
+ @strokeType('inside')
121
125
  public strokeAlign: IStrokeAlign
122
126
 
123
- @affectEventBoundsType(1)
127
+ @strokeType(1)
124
128
  public strokeWidth: number | number[] | IStrokeWidthString
125
129
 
126
- @surfaceType('none')
130
+ @strokeType('none')
127
131
  public strokeCap: IStrokeCap
128
132
 
129
- @surfaceType('miter')
133
+ @strokeType('miter')
130
134
  public strokeJoin: IStrokeJoin
131
135
 
132
- @surfaceType()
136
+ @strokeType()
133
137
  public dashPattern: __Number[] | IDashPatternString
134
138
 
135
- @surfaceType()
139
+ @strokeType()
136
140
  public dashOffset: __Number
137
141
 
138
- @surfaceType(10)
142
+ @strokeType(10)
139
143
  public miterLimit: __Number
140
144
 
141
145
 
@@ -165,23 +169,31 @@ export class UI extends Leaf implements IUI {
165
169
  public grayscale: __Number | IGrayscaleEffect
166
170
 
167
171
 
168
- // now transform
169
-
170
- public get worldTransform(): IMatrixData { return this.__layout.getTransform('world') }
171
-
172
- public get relativeTransform(): IMatrixData { return this.__layout.getTransform('relative') }
172
+ constructor(data?: IUIInputData) {
173
+ super(data)
174
+ }
173
175
 
174
- // now bounds
175
176
 
176
- public get worldBoxBounds(): IBoundsData { return this.__layout.getBounds('world', 'box') }
177
+ public set(data: IUITagInputData): void {
178
+ Object.assign(this, data)
179
+ }
177
180
 
178
- public get worldRenderBounds(): IBoundsData { return this.__layout.getBounds('world', 'render') }
181
+ public get(): IUITagInputData {
182
+ return this.__.__getInputData()
183
+ }
179
184
 
185
+ public __onUpdateSize(): void {
186
+ if (this.__.__input) {
187
+ const { fill, stroke } = this.__.__input
188
+ if (fill) Paint.computeFill(this)
189
+ if (stroke) Paint.computeStroke(this)
190
+ }
191
+ }
180
192
 
181
193
  public __updateRenderPath(): void {
182
194
  if (this.__.path) {
183
195
  const { __: data } = this
184
- data.__pathForRender = data.cornerRadius ? PathHelper.smoothCorner(data.path, data.cornerRadius, data.cornerSmoothing) : data.path
196
+ data.__pathForRender = data.cornerRadius ? PathCorner.smooth(data.path, data.cornerRadius, data.cornerSmoothing) : data.path
185
197
  }
186
198
  }
187
199
 
@@ -195,7 +207,11 @@ export class UI extends Leaf implements IUI {
195
207
  this.__drawPathByData(canvas, this.__.path)
196
208
  }
197
209
 
198
- @rewrite(PathHelper.drawData)
210
+ @rewrite(PathDrawer.drawPathByData)
199
211
  public __drawPathByData(_drawer: IPathDrawer, _data: IPathCommandData): void { }
200
212
 
213
+ static one(data: IUITagInputData, x?: number, y?: number, width?: number, height?: number): IUI {
214
+ return UICreator.get(data.tag || this.prototype.__tag, data, x, y, width, height) as IUI
215
+ }
216
+
201
217
  }
package/src/Vector.ts CHANGED
@@ -9,6 +9,8 @@ import { Group } from './Group'
9
9
  @registerUI()
10
10
  export class Vector extends Group implements IVector {
11
11
 
12
+ public get __tag() { return 'Vector' }
13
+
12
14
  @dataProcessor(VectorData)
13
15
  public __: IVectorData
14
16
 
package/src/index.ts CHANGED
@@ -1,13 +1,23 @@
1
1
  export { UI } from './UI'
2
+
2
3
  export { Group } from './Group'
4
+ export { Box } from './Box'
3
5
  export { Frame } from './Frame'
6
+
4
7
  export { Rect } from './Rect'
5
8
  export { Ellipse } from './Ellipse'
6
9
  export { Polygon } from './Polygon'
7
10
  export { Star } from './Star'
8
11
  export { Line } from './Line'
12
+
13
+ export { Image } from './Image'
14
+ export { Canvas } from './Canvas'
15
+
16
+ export { Text } from './Text'
17
+
9
18
  export { Path } from './Path'
19
+ export { Pen } from './Pen'
10
20
  export { Vector } from './Vector'
11
- export { Text } from './Text'
12
- export { Image } from './Image'
21
+
22
+
13
23