@leafer-ui/hit 1.0.0-rc.8 → 1.0.0

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/hit",
3
- "version": "1.0.0-rc.8",
3
+ "version": "1.0.0",
4
4
  "description": "@leafer-ui/hit",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -21,8 +21,12 @@
21
21
  "leafer-ui",
22
22
  "leaferjs"
23
23
  ],
24
+ "dependencies": {
25
+ "@leafer/core": "1.0.0",
26
+ "@leafer-ui/draw": "1.0.0"
27
+ },
24
28
  "devDependencies": {
25
- "@leafer/interface": "1.0.0-rc.8",
26
- "@leafer-ui/interface": "1.0.0-rc.8"
29
+ "@leafer/interface": "1.0.0",
30
+ "@leafer-ui/interface": "1.0.0"
27
31
  }
28
32
  }
@@ -0,0 +1,53 @@
1
+ import { IHitCanvasManager, ILeaf, IHitCanvas, ILeafList, ILeaferCanvasConfig } from '@leafer/interface'
2
+ import { CanvasManager, LeafList, Creator } from '@leafer/core'
3
+
4
+
5
+ export class HitCanvasManager extends CanvasManager implements IHitCanvasManager {
6
+
7
+ public maxTotal = 1000 // 最多缓存多少张画布
8
+
9
+ protected pathList: ILeafList = new LeafList()
10
+ protected pixelList: ILeafList = new LeafList()
11
+
12
+ public getPixelType(leaf: ILeaf, config?: ILeaferCanvasConfig): IHitCanvas {
13
+ this.__autoClear()
14
+ this.pixelList.add(leaf)
15
+ return Creator.hitCanvas(config)
16
+ }
17
+
18
+ public getPathType(leaf: ILeaf): IHitCanvas {
19
+ this.__autoClear()
20
+ this.pathList.add(leaf)
21
+ return Creator.hitCanvas()
22
+ }
23
+
24
+ public clearImageType(): void {
25
+ this.__clearLeafList(this.pixelList)
26
+ }
27
+
28
+ public clearPathType(): void {
29
+ this.__clearLeafList(this.pathList)
30
+ }
31
+
32
+ protected __clearLeafList(leafList: ILeafList): void {
33
+ if (leafList.length) {
34
+ leafList.forEach(leaf => {
35
+ if (leaf.__hitCanvas) {
36
+ leaf.__hitCanvas.destroy()
37
+ leaf.__hitCanvas = null
38
+ }
39
+ })
40
+ leafList.reset()
41
+ }
42
+ }
43
+
44
+ protected __autoClear(): void {
45
+ if (this.pathList.length + this.pixelList.length > this.maxTotal) this.clear()
46
+ }
47
+
48
+ public clear(): void {
49
+ this.clearPathType()
50
+ this.clearImageType()
51
+ }
52
+
53
+ }
package/src/LeafHit.ts ADDED
@@ -0,0 +1,42 @@
1
+ import { IRadiusPointData, ILeaferCanvas } from '@leafer/interface'
2
+ import { Leaf, PointHelper, BoundsHelper } from '@leafer/core'
3
+
4
+
5
+ const { toInnerRadiusPointOf, copy, setRadius } = PointHelper
6
+ const inner = {} as IRadiusPointData
7
+
8
+ const leaf = Leaf.prototype
9
+
10
+ leaf.__hitWorld = function (point: IRadiusPointData): boolean {
11
+ if (!this.__.hitSelf) return false
12
+
13
+ if (this.__.hitRadius) {
14
+ copy(inner, point), point = inner
15
+ setRadius(point, this.__.hitRadius)
16
+ }
17
+
18
+ toInnerRadiusPointOf(point, this.__world, inner)
19
+
20
+ const { width, height } = this.__world
21
+ const isSmall = width < 10 && height < 10
22
+
23
+ if (this.__.hitBox || isSmall) {
24
+ if (BoundsHelper.hitRadiusPoint(this.__layout.boxBounds, inner)) return true
25
+ if (isSmall) return false
26
+ }
27
+
28
+ if (this.__layout.hitCanvasChanged || !this.__hitCanvas) {
29
+ this.__updateHitCanvas()
30
+ if (!this.__layout.boundsChanged) this.__layout.hitCanvasChanged = false
31
+ }
32
+
33
+ return this.__hit(inner)
34
+ }
35
+
36
+ leaf.__hitFill = function (inner: IRadiusPointData): boolean { return this.__hitCanvas?.hitFill(inner, this.__.windingRule) }
37
+
38
+ leaf.__hitStroke = function (inner: IRadiusPointData, strokeWidth: number): boolean { return this.__hitCanvas?.hitStroke(inner, strokeWidth) }
39
+
40
+ leaf.__hitPixel = function (inner: IRadiusPointData): boolean { return this.__hitCanvas?.hitPixel(inner, this.__layout.renderBounds, this.__hitCanvas.hitScale) }
41
+
42
+ leaf.__drawHitPath = function (canvas: ILeaferCanvas): void { if (canvas) this.__drawRenderPath(canvas) }
package/src/RectHit.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { IRadiusPointData } from '@leafer/interface'
2
+ import { BoundsHelper } from '@leafer/core'
3
+ import { Rect, UI } from '@leafer-ui/draw'
4
+
5
+
6
+ const ui = new UI()
7
+ const rect = Rect.prototype
8
+
9
+ // hit
10
+
11
+ rect.__updateHitCanvas = function () {
12
+ if (this.stroke || this.cornerRadius || ((this.fill || this.__.__isCanvas) && this.hitFill === 'pixel') || this.hitStroke === 'all') ui.__updateHitCanvas.call(this)
13
+ else if (this.__hitCanvas) this.__hitCanvas = null
14
+ }
15
+
16
+ rect.__hitFill = function (inner: IRadiusPointData): boolean {
17
+ return this.__hitCanvas ? ui.__hitFill.call(this, inner) : BoundsHelper.hitRadiusPoint(this.__layout.boxBounds, inner)
18
+ }
package/src/UIHit.ts CHANGED
@@ -1,54 +1,85 @@
1
1
  import { IRadiusPointData } from '@leafer/interface'
2
+ import { Platform, Matrix, tempBounds } from '@leafer/core'
3
+ import { UI, ImageManager } from '@leafer-ui/draw'
2
4
 
3
- import { IUIHitModule } from '@leafer-ui/interface'
4
- import { Platform } from '@leafer/core'
5
5
 
6
+ const matrix = new Matrix()
7
+ const ui = UI.prototype
6
8
 
7
- export const UIHit: IUIHitModule = {
9
+ ui.__updateHitCanvas = function (): void {
10
+ const data = this.__, { hitCanvasManager } = this.leafer
8
11
 
9
- __updateHitCanvas(): void {
10
- if (!this.__hitCanvas) this.__hitCanvas = this.leafer.hitCanvasManager.getPathType(this)
11
- const h = this.__hitCanvas
12
- this.__drawHitPath(h)
13
- h.setStrokeOptions(this.__)
14
- },
12
+ const isHitPixelFill = (data.__pixelFill || data.__isCanvas) && data.hitFill === 'pixel'
13
+ const isHitPixelStroke = data.__pixelStroke && data.hitStroke === 'pixel'
14
+ const isHitPixel = isHitPixelFill || isHitPixelStroke
15
15
 
16
- __hit(inner: IRadiusPointData): boolean {
17
- const { __hitCanvas: h } = this
18
- if (Platform.name === 'miniapp') this.__drawHitPath(h) // fix: 小程序需要实时更新
16
+ if (!this.__hitCanvas) this.__hitCanvas = isHitPixel ? hitCanvasManager.getPixelType(this, { contextSettings: { willReadFrequently: true } }) : hitCanvasManager.getPathType(this)
19
17
 
20
- const { fill, hitFill, windingRule } = this.__
21
- const needHitFill = (fill && hitFill === 'path') || hitFill === 'all'
22
- const isHitFill = h.hitFill(inner, windingRule)
23
- if (needHitFill && isHitFill) return true
18
+ const h = this.__hitCanvas
24
19
 
25
- const { stroke, hitStroke, strokeWidth, strokeAlign } = this.__
26
- const needHitStroke = (stroke && hitStroke === 'path') || hitStroke === 'all'
27
- const radiusWidth = inner.radiusX * 2
20
+ if (isHitPixel) {
21
+ const { renderBounds } = this.__layout
22
+ const size = Platform.image.hitCanvasSize
23
+ const scale = h.hitScale = tempBounds.set(0, 0, size, size).getFitMatrix(renderBounds, 0.5).a
24
+ const { x, y, width, height } = tempBounds.set(renderBounds).scale(scale)
25
+ h.resize({ width, height, pixelRatio: 1 })
26
+ h.clear()
28
27
 
29
- let hitWidth = radiusWidth
28
+ ImageManager.patternLocked = true
29
+ this.__renderShape(h, { matrix: matrix.setWith(this.__world).scaleWith(1 / scale).invertWith().translate(-x, -y) }, !isHitPixelFill, !isHitPixelStroke) // 矩阵
30
+ ImageManager.patternLocked = false
31
+ h.resetTransform()
30
32
 
31
- if (needHitStroke) {
32
- switch (strokeAlign) {
33
- case 'inside':
34
- hitWidth += strokeWidth * 2
35
- if (!needHitFill && (isHitFill && h.hitStroke(inner, hitWidth))) return true
33
+ data.__isHitPixel = true
34
+ } else {
35
+ data.__isHitPixel && (data.__isHitPixel = false)
36
+ }
37
+
38
+ this.__drawHitPath(h)
39
+ h.setStrokeOptions(data)
40
+
41
+ }
42
+
43
+ ui.__hit = function (inner: IRadiusPointData): boolean {
44
+ if (Platform.name === 'miniapp') this.__drawHitPath(this.__hitCanvas) // fix: 小程序需要实时更新
45
+
46
+ // hit pixel
47
+
48
+ const data = this.__
49
+ if (data.__isHitPixel && this.__hitPixel(inner)) return true
50
+
51
+ // hit path
52
+
53
+ const { hitFill } = data
54
+ const needHitFillPath = ((data.fill || data.__isCanvas) && (hitFill === 'path' || (hitFill === 'pixel' && !(data.__pixelFill || data.__isCanvas)))) || hitFill === 'all'
55
+ if (needHitFillPath && this.__hitFill(inner)) return true
56
+
57
+ const { hitStroke, __strokeWidth } = data
58
+ const needHitStrokePath = (data.stroke && (hitStroke === 'path' || (hitStroke === 'pixel' && !data.__pixelStroke))) || hitStroke === 'all'
59
+ if (!needHitFillPath && !needHitStrokePath) return false
60
+
61
+ const radiusWidth = inner.radiusX * 2
62
+ let hitWidth = radiusWidth
63
+
64
+ if (needHitStrokePath) {
65
+ switch (data.strokeAlign) {
66
+ case 'inside':
67
+ hitWidth += __strokeWidth * 2
68
+ if (!needHitFillPath && this.__hitFill(inner) && this.__hitStroke(inner, hitWidth)) return true
69
+ hitWidth = radiusWidth
70
+ break
71
+ case 'center':
72
+ hitWidth += __strokeWidth
73
+ break
74
+ case 'outside':
75
+ hitWidth += __strokeWidth * 2
76
+ if (!needHitFillPath) {
77
+ if (!this.__hitFill(inner) && this.__hitStroke(inner, hitWidth)) return true
36
78
  hitWidth = radiusWidth
37
- break
38
- case 'center':
39
- hitWidth += strokeWidth
40
- break
41
- case 'outside':
42
- hitWidth += strokeWidth * 2
43
- if (!needHitFill) {
44
- if (!isHitFill && h.hitStroke(inner, hitWidth)) return true
45
- hitWidth = radiusWidth
46
- }
47
- break
48
- }
79
+ }
80
+ break
49
81
  }
50
-
51
- return hitWidth ? h.hitStroke(inner, hitWidth) : false
52
82
  }
53
83
 
84
+ return hitWidth ? this.__hitStroke(inner, hitWidth) : false
54
85
  }
package/src/canvas.ts ADDED
@@ -0,0 +1,24 @@
1
+
2
+ import { IPointData, IRadiusPointData, IWindingRule } from '@leafer/interface'
3
+ import { LeaferCanvasBase, tempBounds } from '@leafer/core'
4
+
5
+
6
+ const canvas = LeaferCanvasBase.prototype
7
+
8
+ canvas.hitFill = function (point: IPointData, fillRule?: IWindingRule): boolean {
9
+ return fillRule ? this.context.isPointInPath(point.x, point.y, fillRule) : this.context.isPointInPath(point.x, point.y)
10
+ }
11
+
12
+ canvas.hitStroke = function (point: IPointData, strokeWidth?: number): boolean {
13
+ this.strokeWidth = strokeWidth
14
+ return this.context.isPointInStroke(point.x, point.y)
15
+ }
16
+
17
+ canvas.hitPixel = function (radiusPoint: IRadiusPointData, offset?: IPointData, scale = 1): boolean { // 画布必须有alpha通道
18
+ let { x, y, radiusX, radiusY } = radiusPoint
19
+ if (offset) x -= offset.x, y -= offset.y
20
+ tempBounds.set(x - radiusX, y - radiusY, radiusX * 2, radiusY * 2).scale(scale).ceil()
21
+ const { data } = this.context.getImageData(tempBounds.x, tempBounds.y, tempBounds.width || 1, tempBounds.height || 1)
22
+ for (let i = 0, len = data.length; i < len; i += 4) { if (data[i + 3] > 0) return true }
23
+ return data[3] > 0
24
+ }
package/src/find.ts ADDED
@@ -0,0 +1,21 @@
1
+
2
+ import { IFindMethod, IPointData, IPickOptions, IPickResult } from '@leafer/interface'
3
+ import { IFindUIMethod, IUI } from '@leafer-ui/interface'
4
+ import { UI, Group } from '@leafer-ui/draw'
5
+
6
+
7
+ const ui = UI.prototype, group = Group.prototype
8
+
9
+ ui.find = function (condition: number | string | IFindUIMethod, options?: any): IUI[] {
10
+ return this.leafer ? this.leafer.selector.getBy(condition as IFindMethod, this, false, options) as IUI[] : []
11
+ }
12
+
13
+ ui.findOne = function (condition: number | string | IFindUIMethod, options?: any): IUI | undefined {
14
+ return this.leafer ? this.leafer.selector.getBy(condition as IFindMethod, this, true, options) as IUI : null
15
+ }
16
+
17
+ group.pick = function (hitPoint: IPointData, options?: IPickOptions): IPickResult {
18
+ this.__layout.update()
19
+ if (!options) options = {}
20
+ return this.leafer ? this.leafer.selector.getByPoint(hitPoint, options.hitRadius || 0, { ...options, target: this }) : null
21
+ }
package/src/index.ts CHANGED
@@ -1 +1,8 @@
1
- export { UIHit } from "./UIHit"
1
+ export { HitCanvasManager } from './HitCanvasManager'
2
+
3
+ export * from './LeafHit'
4
+ export * from './UIHit'
5
+ export * from './RectHit'
6
+
7
+ export * from './find'
8
+ export * from './canvas'
package/types/index.d.ts CHANGED
@@ -1,5 +1,17 @@
1
- import { IUIHitModule } from '@leafer-ui/interface';
1
+ import { IHitCanvasManager, ILeafList, ILeaf, ILeaferCanvasConfig, IHitCanvas } from '@leafer/interface';
2
+ import { CanvasManager } from '@leafer/core';
2
3
 
3
- declare const UIHit: IUIHitModule;
4
+ declare class HitCanvasManager extends CanvasManager implements IHitCanvasManager {
5
+ maxTotal: number;
6
+ protected pathList: ILeafList;
7
+ protected pixelList: ILeafList;
8
+ getPixelType(leaf: ILeaf, config?: ILeaferCanvasConfig): IHitCanvas;
9
+ getPathType(leaf: ILeaf): IHitCanvas;
10
+ clearImageType(): void;
11
+ clearPathType(): void;
12
+ protected __clearLeafList(leafList: ILeafList): void;
13
+ protected __autoClear(): void;
14
+ clear(): void;
15
+ }
4
16
 
5
- export { UIHit };
17
+ export { HitCanvasManager };