You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
9.6 KiB
290 lines
9.6 KiB
|
1 week ago
|
|
||
|
|
//*********************
|
||
|
|
// create by 流云
|
||
|
|
// time: Thu Nov 13 2025
|
||
|
|
// desc:
|
||
|
|
//*********************
|
||
|
|
import { _decorator, Node, EventTouch, UITransform, tween, Mask, Graphics, Color } from 'cc';
|
||
|
|
import { Auto_NewGuideMask } from './Auto_NewGuideMask';
|
||
|
|
import MTools from 'db://assets/mx/tools/MTools';
|
||
|
|
const { ccclass, property } = _decorator;
|
||
|
|
|
||
|
|
@ccclass('UINewGuideMask')
|
||
|
|
export class UINewGuideMask extends Auto_NewGuideMask {
|
||
|
|
|
||
|
|
|
||
|
|
registNode = null
|
||
|
|
graphics = null
|
||
|
|
finishCallBack = null
|
||
|
|
nodeArray: Node[] = [];
|
||
|
|
labArray: string[] = [];
|
||
|
|
isPlayAnim = true
|
||
|
|
blackOpacity = 255
|
||
|
|
//不需要点击拦截
|
||
|
|
hideSpineshouzhi = true
|
||
|
|
|
||
|
|
/** 镂空圆角半径(像素),不超过短边一半 */
|
||
|
|
holeCornerRadius = 18
|
||
|
|
/** 边缘虚化描边最大宽度 */
|
||
|
|
holeFeatherMaxLine = 5
|
||
|
|
/** 虚化层数 */
|
||
|
|
holeFeatherLayers = 8
|
||
|
|
|
||
|
|
_holeBorderGraphics: Graphics = null
|
||
|
|
|
||
|
|
onLoad(): void {
|
||
|
|
super.onLoad();
|
||
|
|
}
|
||
|
|
|
||
|
|
onEnable(): void {
|
||
|
|
super.onEnable();
|
||
|
|
}
|
||
|
|
|
||
|
|
onDisable(): void {
|
||
|
|
super.onDisable();
|
||
|
|
}
|
||
|
|
|
||
|
|
bindNode() {
|
||
|
|
|
||
|
|
this.node.on(Node.EventType.TOUCH_START, (event:EventTouch) => {
|
||
|
|
//目标节点不存在,拦截
|
||
|
|
if (!this.registNode || this.registNode.isValid == false) {
|
||
|
|
// this.node['_touchListener'].setSwallowTouches(true);
|
||
|
|
event.preventSwallow = true;
|
||
|
|
event.propagationStopped = true;
|
||
|
|
if(this.registNode && this.registNode.isValid == false){
|
||
|
|
this.close()
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
//目标区域存在,击中放行
|
||
|
|
let rect = this.registNode.getComponent(UITransform).getBoundingBoxToWorld();
|
||
|
|
if ( rect.contains(event.getUILocation()) ) {
|
||
|
|
// this.node['_touchListener'].setSwallowTouches(false);
|
||
|
|
// 命中镂空区域:不拦截,让触摸继续传给底层按钮
|
||
|
|
event.preventSwallow = true;
|
||
|
|
event.propagationStopped = false;
|
||
|
|
|
||
|
|
} else {
|
||
|
|
// this.node['_touchListener'].setSwallowTouches(true);
|
||
|
|
// 命中非目标区域:拦截,避免误触底层
|
||
|
|
event.preventSwallow = true;
|
||
|
|
event.propagationStopped = true;
|
||
|
|
|
||
|
|
}
|
||
|
|
}, this);
|
||
|
|
|
||
|
|
|
||
|
|
//监听事件
|
||
|
|
this.node.on(Node.EventType.TOUCH_END, (event) => {
|
||
|
|
//目标节点不存在,拦截
|
||
|
|
if (!this.registNode || this.registNode.isValid == false) {
|
||
|
|
// this.node['_touchListener'].setSwallowTouches(true);
|
||
|
|
event.preventSwallow = true;
|
||
|
|
event.propagationStopped = true;
|
||
|
|
if(this.registNode && this.registNode.isValid == false){
|
||
|
|
this.close()
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
//目标区域存在,击中放行
|
||
|
|
let rect = this.registNode.getComponent(UITransform).getBoundingBoxToWorld();
|
||
|
|
if ( rect.contains(event.getUILocation()) ) {
|
||
|
|
// this.node['_touchListener'].setSwallowTouches(false);
|
||
|
|
// 命中镂空区域:不拦截,让触摸继续传给底层按钮
|
||
|
|
event.preventSwallow = true;
|
||
|
|
event.propagationStopped = false;
|
||
|
|
|
||
|
|
} else {
|
||
|
|
// this.node['_touchListener'].setSwallowTouches(true);
|
||
|
|
// 命中非目标区域:拦截,避免误触底层
|
||
|
|
event.preventSwallow = true;
|
||
|
|
event.propagationStopped = true;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
//目标区域存在,击中放行
|
||
|
|
|
||
|
|
if ( rect.contains(event.getUILocation())) {
|
||
|
|
|
||
|
|
|
||
|
|
// 先让底层按钮/节点吃到本次 TOUCH_END,再执行引导回调与切换/关闭
|
||
|
|
this.scheduleOnce(() => {
|
||
|
|
this.registNode = null
|
||
|
|
if (this.finishCallBack) {
|
||
|
|
this.finishCallBack()
|
||
|
|
}
|
||
|
|
if (this.nodeArray.length == 0) {
|
||
|
|
this.close()
|
||
|
|
//EventMgr.emitEvent_custom(ryw_Event.swallowToucheFinishEvent);
|
||
|
|
} else {
|
||
|
|
let nextNode = this.nodeArray.shift()
|
||
|
|
this.nextNode(nextNode)
|
||
|
|
}
|
||
|
|
}, 0);
|
||
|
|
//EventMgr.emitEvent_custom(ryw_Event.swallowToucheEvent);
|
||
|
|
} else {
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
}, this);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
nextNode(registNode){
|
||
|
|
this.registNode = registNode
|
||
|
|
let str = this.labArray.shift()
|
||
|
|
|
||
|
|
|
||
|
|
let posi = MTools.getNodeToTargetPos(this.shouzhiNode, registNode)
|
||
|
|
if(this.isPlayAnim && this.shouzhiNode.active) {
|
||
|
|
// this.shouzhiNode.setPosition(0, 0)
|
||
|
|
|
||
|
|
tween(this.shouzhiNode)
|
||
|
|
.to(0.6, {x:posi.x, y:posi.y-5})
|
||
|
|
.call(()=>{
|
||
|
|
this._focusToNode(registNode)
|
||
|
|
if(str && str != ''){
|
||
|
|
this.guideNode.active = true
|
||
|
|
this.guideDesc.string = str
|
||
|
|
}else{
|
||
|
|
this.guideNode.active = false
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.start()
|
||
|
|
}else{
|
||
|
|
this.shouzhiNode.setPosition(posi.x+50, posi.y)
|
||
|
|
this._focusToNode(registNode)
|
||
|
|
if(str && str != ''){
|
||
|
|
this.guideNode.active = true
|
||
|
|
this.guideDesc.string = str
|
||
|
|
}else{
|
||
|
|
this.guideNode.active = false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
_ensureHoleBorderGraphics(): Graphics {
|
||
|
|
if (this._holeBorderGraphics && this._holeBorderGraphics.isValid) {
|
||
|
|
return this._holeBorderGraphics
|
||
|
|
}
|
||
|
|
let borderNode = this.maskNode.node.getChildByName('holeBorder')
|
||
|
|
if (!borderNode) {
|
||
|
|
borderNode = new Node('holeBorder')
|
||
|
|
const ut = borderNode.addComponent(UITransform)
|
||
|
|
ut.setAnchorPoint(0.5, 0.5)
|
||
|
|
ut.setContentSize(1, 1)
|
||
|
|
borderNode.setParent(this.maskNode.node)
|
||
|
|
borderNode.setSiblingIndex(0)
|
||
|
|
}
|
||
|
|
this._holeBorderGraphics = borderNode.getComponent(Graphics) || borderNode.addComponent(Graphics)
|
||
|
|
return this._holeBorderGraphics
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 与引擎 Mask GRAPHICS_RECT 一致的局部坐标原点(考虑锚点) */
|
||
|
|
_maskLocalRectOrigin(w: number, h: number): { x: number, y: number } {
|
||
|
|
const ap = this.maskNode.node.getComponent(UITransform).anchorPoint
|
||
|
|
return { x: -w * ap.x, y: -h * ap.y }
|
||
|
|
}
|
||
|
|
|
||
|
|
_redrawHoleStencil(w: number, h: number, radius: number) {
|
||
|
|
const g = this.graphics as Graphics
|
||
|
|
if (!g) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const { x, y } = this._maskLocalRectOrigin(w, h)
|
||
|
|
const r = Math.min(radius, Math.min(w, h) * 0.5 - 0.5)
|
||
|
|
g.clear()
|
||
|
|
g.fillColor = new Color(255, 255, 255, 0)
|
||
|
|
g.roundRect(x, y, w, h, Math.max(0, r))
|
||
|
|
g.fill()
|
||
|
|
}
|
||
|
|
|
||
|
|
_drawHoleBorderFeather(w: number, h: number, radius: number) {
|
||
|
|
const g = this._ensureHoleBorderGraphics()
|
||
|
|
const { x, y } = this._maskLocalRectOrigin(w, h)
|
||
|
|
const r = Math.min(radius, Math.min(w, h) * 0.5 - 0.5)
|
||
|
|
g.clear()
|
||
|
|
g.lineJoin = Graphics.LineJoin.ROUND
|
||
|
|
g.lineCap = Graphics.LineCap.ROUND
|
||
|
|
const layers = Math.max(1, this.holeFeatherLayers)
|
||
|
|
const maxLw = Math.max(1, this.holeFeatherMaxLine)
|
||
|
|
for (let i = layers; i >= 1; i--) {
|
||
|
|
const t = i / layers
|
||
|
|
const lw = 1 + (maxLw - 1) * t
|
||
|
|
const a = Math.floor(40 * (1 - t * 0.88))
|
||
|
|
g.lineWidth = lw
|
||
|
|
g.strokeColor = new Color(200, 245, 255, a)
|
||
|
|
g.roundRect(x, y, w, h, Math.max(0, r))
|
||
|
|
g.stroke()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_focusToNode(node) {
|
||
|
|
this.scheduleOnce(() => {
|
||
|
|
|
||
|
|
let rect = node.getComponent(UITransform).getBoundingBoxToWorld();
|
||
|
|
this.maskNode.node.getComponent(UITransform).setContentSize(0,0)
|
||
|
|
let p = this.node.getComponent(UITransform).convertToNodeSpaceAR(rect.origin);
|
||
|
|
rect.x = p.x;
|
||
|
|
rect.y = p.y;
|
||
|
|
|
||
|
|
MTools.setNodeToTargetPos(this.maskNode.node, node)
|
||
|
|
|
||
|
|
const pad = 20
|
||
|
|
const w = rect.width + pad
|
||
|
|
const h = rect.height + pad
|
||
|
|
this.maskNode.node.getComponent(UITransform).setContentSize(w, h)
|
||
|
|
|
||
|
|
const rr = Math.min(this.holeCornerRadius, Math.min(w, h) * 0.12)
|
||
|
|
this._redrawHoleStencil(w, h, rr)
|
||
|
|
this._drawHoleBorderFeather(w, h, rr)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
onInit(): void {
|
||
|
|
|
||
|
|
// finishCallBack = null
|
||
|
|
// nodeArray: Node[] = [];
|
||
|
|
// labArray: string[] = [];
|
||
|
|
// isPlayAnim = true
|
||
|
|
// blackOpacity = 255
|
||
|
|
this.maskNode.type = Mask.Type.GRAPHICS_STENCIL
|
||
|
|
this.graphics = this.maskNode.node.getComponent(Graphics)
|
||
|
|
|
||
|
|
this.nodeArray = this.prema[0]
|
||
|
|
this.labArray = this.prema[1]
|
||
|
|
this.finishCallBack = this.prema[2]
|
||
|
|
this.isPlayAnim = this.prema[3] || true
|
||
|
|
this.blackOpacity = this.prema[4] || 255
|
||
|
|
|
||
|
|
if(this.prema[5] === false){
|
||
|
|
this.hideSpineshouzhi = this.prema[5]
|
||
|
|
}else{
|
||
|
|
this.hideSpineshouzhi = true
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!this.hideSpineshouzhi){
|
||
|
|
this.node.getChildByName('dianji').active = false
|
||
|
|
}
|
||
|
|
|
||
|
|
this.maskBlackNode.opacity = this.blackOpacity
|
||
|
|
|
||
|
|
this.bindNode()
|
||
|
|
if (this.nodeArray.length == 0) {
|
||
|
|
this.close()
|
||
|
|
//EventMgr.emitEvent_custom(ryw_Event.swallowToucheFinishEvent);
|
||
|
|
|
||
|
|
} else {
|
||
|
|
let nextNode = this.nodeArray.shift()
|
||
|
|
this.nextNode(nextNode)
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|