import { _decorator, Component, Mask, Graphics, UITransform } from 'cc'; const { ccclass, property, executeInEditMode, requireComponent } = _decorator; @ccclass('CustomMask') @requireComponent(Mask) @executeInEditMode // 允许在编辑器内预览绘制效果 export class CustomMask extends Component { @property({ tooltip: '左上角圆角半径(像素)' }) leftTopRadius = 0; @property({ tooltip: '右上角圆角半径(像素)' }) rightTopRadius = 20; @property({ tooltip: '右下角圆角半径(像素)' }) rightBottomRadius = 20; @property({ tooltip: '左下角圆角半径(像素)' }) leftBottomRadius = 0; private _mask: Mask | null = null; onEnable() { this.updateMask(); } // 当在编辑器中修改属性时自动刷新 onValidate() { this.updateMask(); } /** 绘制四个圆角可独立设置的遮罩路径。 */ public updateMask() { this._mask = this.getComponent(Mask); if (!this._mask) return; // 自定义模板遮罩,避免矩形模式覆盖自定义路径。 this._mask.type = Mask.Type.GRAPHICS_STENCIL; // 获取或添加 Graphics 组件 let g = this.getComponent(Graphics); if (!g) { g = this.addComponent(Graphics); } const uiTrans = this.getComponent(UITransform); if (!uiTrans) return; const width = uiTrans.width; const height = uiTrans.height; const anchorX = uiTrans.anchorX; const anchorY = uiTrans.anchorY; // 根据锚点计算左下角起始坐标 (x, y) const x = -width * anchorX; const y = -height * anchorY; const leftTop = this.getRadius(this.leftTopRadius, width, height); const rightTop = this.getRadius(this.rightTopRadius, width, height); const rightBottom = this.getRadius(this.rightBottomRadius, width, height); const leftBottom = this.getRadius(this.leftBottomRadius, width, height); // 清除旧路径 g.clear(); // 从底边开始逆时针绘制,所有圆角保持在同一条闭合路径中。 g.moveTo(x + leftBottom, y); g.lineTo(x + width - rightBottom, y); this.drawArc(g, x + width - rightBottom, y + rightBottom, rightBottom, -Math.PI / 2, 0); g.lineTo(x + width, y + height - rightTop); this.drawArc(g, x + width - rightTop, y + height - rightTop, rightTop, 0, Math.PI / 2); g.lineTo(x + leftTop, y + height); this.drawArc(g, x + leftTop, y + height - leftTop, leftTop, Math.PI / 2, Math.PI); g.lineTo(x, y + leftBottom); this.drawArc(g, x + leftBottom, y + leftBottom, leftBottom, Math.PI, Math.PI * 1.5); g.close(); g.fill(); } private getRadius(radius: number, width: number, height: number): number { if (!Number.isFinite(radius)) return 0; return Math.max(0, Math.min(radius, width / 2, height / 2)); } private drawArc( graphics: Graphics, centerX: number, centerY: number, radius: number, startAngle: number, endAngle: number, ): void { if (radius > 0) { // Graphics.arc 内部会 moveTo,不能用于连续的圆角轮廓。 // 使用三次贝塞尔近似四分之一圆,控制点沿端点切线排列。 const tangent = radius * 4 / 3 * Math.tan((endAngle - startAngle) / 4); const startX = centerX + radius * Math.cos(startAngle); const startY = centerY + radius * Math.sin(startAngle); const endX = centerX + radius * Math.cos(endAngle); const endY = centerY + radius * Math.sin(endAngle); graphics.bezierCurveTo( startX - tangent * Math.sin(startAngle), startY + tangent * Math.cos(startAngle), endX + tangent * Math.sin(endAngle), endY - tangent * Math.cos(endAngle), endX, endY, ); } } }