@leafer-ui/hit 1.0.0-rc.9 → 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 +7 -3
- package/src/HitCanvasManager.ts +53 -0
- package/src/LeafHit.ts +42 -0
- package/src/RectHit.ts +18 -0
- package/src/UIHit.ts +70 -39
- package/src/canvas.ts +24 -0
- package/src/find.ts +21 -0
- package/src/index.ts +8 -1
- package/types/index.d.ts +15 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leafer-ui/hit",
|
|
3
|
-
"version": "1.0.0
|
|
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
|
|
26
|
-
"@leafer-ui/interface": "1.0.0
|
|
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
|
-
|
|
9
|
+
ui.__updateHitCanvas = function (): void {
|
|
10
|
+
const data = this.__, { hitCanvasManager } = this.leafer
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
package/types/index.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IHitCanvasManager, ILeafList, ILeaf, ILeaferCanvasConfig, IHitCanvas } from '@leafer/interface';
|
|
2
|
+
import { CanvasManager } from '@leafer/core';
|
|
2
3
|
|
|
3
|
-
declare
|
|
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 {
|
|
17
|
+
export { HitCanvasManager };
|