[求助] 图片Mask动态创建/动态修改参数...各种问题...

  • Creator 版本: 3.3.2
  • 目标平台: Web: (Win10: Chrome, 手机: 小米浏览器)

需求:
在图片上涂鸦…可选彩色…不要越边…图片可更换…尺寸不固定…
(折腾了不少方案…暂时选择了最简单的Component组合…先实现再寻求更优方案…)


方案一:
让三个组件形成一个节点树: Sprite/Mask/Graphics
让触摸Sprite时计算坐标…在Graphics相同位置上绘制笔触…
Mask使用和Sprite相同的spf…过滤透明部分…
更换图片时…同时更换Sprite和Mask的spf…

结果:
估计Mask会在初始化时获取了宽高等信息…
更换spf后会有变形…修改UITsf的宽高也无法解决…
调用几个updateXXX之类的api也没效果…
查了官方手册也没什么资料…先放弃…


方案二:
场景只预备一个空白的Sprite…
Mask及Graphics均由代码创建…
其中Mask根据不同的spf各自创建一个节点…
Graphics再挂载到对应的Mask上…

结果:
首个创建的Mask有效…
其余时间创建的Mask会出现不正常的情况(呈现放大的状态…后面有图片)…
如果首个Mask延迟创建…亦会出现不正常的情况…
查了官方手册也没什么资料…
只能上论坛问了…


image
正常的情况(期望的效果)…


image
非首帧创建的Mask均被放大…


    start() {
        this._init();
        // setTimeout(() => this._init(), 1000);
    }

只要延迟就会放大…


Mask部分的代码:

    public setSpf(spf: SpriteFrame) {
        console.log('setSpf');
        this._changeMask(spf);
    }

    private _mask: Mask;
    private _masks: Map<SpriteFrame, Mask> = new Map();
    private _changeMask(spf: SpriteFrame) {
        console.log('changeMask');
        let newMask = this._masks.get(spf);
        if (!newMask) newMask = this._createMask(spf);
        else newMask.node.active = true;
        this.graphics.node.parent = newMask.node;
        if (this._mask) this._mask.node.active = false;
        this._mask = newMask;
        this.status;
    }
    private _createMask(spf: SpriteFrame) {
        console.log('createMask');
        const node = this.node;
        const nMask = new Node('Mask-' + spf.name);
        nMask.parent = node;
        nMask.layer = node.layer;
        nMask.position = Vec3.ZERO;

        const tsfMask = nMask.addComponent(UITransform);
        tsfMask.width = spf.width;
        tsfMask.height = spf.height;

        const mask = nMask.addComponent(Mask);
        mask.spriteFrame = spf;
        mask.type = Mask.Type.IMAGE_STENCIL;

        this._masks.set(spf, mask);

        return mask;
    }

Demo:
Test.rar (48.5 KB)


项目设置里勾选 CLEANUP_IMAGE_CACHE,不启用动态合图,你这边传的大小有问题,或者换个实现,不通过 spriteFrame 来传。
image

我这边业务变化都是基于一张图…所以最终都是从图里获取宽高的…
Sprite设置成TRIMMED…就可以自适应宽高(无论是否开启动态合图)…
那普通开发者有办法获取合适的宽高吗…?

我从Sprite节点的UITsf获取到宽高…然后传到里面…数值是对的…但是大小也是有偏差的…

export class Test extends Component {
    // ...
    private _spTsf: UITransform;
    private _init() {
        this._spTsf = this.printBoardSp.getComponent(UITransform);
        // ...
    }
    // ...
    private _onSpSelect(sp: Sprite) {
        // ...
        this._pt.setSpf(sp.spriteFrame, this._spTsf.width, this._spTsf.height);
    }
}
class Printer {
    // ...
    public setSpf(spf: SpriteFrame, w, h) {
        this._changeMask(spf, w, h);
    }
    private _changeMask(spf: SpriteFrame, w, h) {
        // ...
        if (!newMask) newMask = this._createMask(spf, w, h);
        // ...
    }
    private _createMask(spf: SpriteFrame, w, h) {
        // ...
        const tsfMask = nMask.addComponent(UITransform);
        // tsfMask.width = spf.width;
        // tsfMask.height = spf.height;
        tsfMask.width = w;
        tsfMask.height = h;
        // ...
        
        setTimeout(() => {
            console.log('spf.name', spf.name);
            console.log('wh', w, h);
            console.log('maskTsf.wh', tsfMask.width, tsfMask.height);
            const tsf = this.node.getComponent(UITransform);
            console.log('tsf.wh', tsf.width, tsf.height);
            console.log('spf.wh', spf.width, spf.height);
            const spSpf = this.node.getComponent(Sprite).spriteFrame;
            console.log('spSpf.wh', spSpf.width, spSpf.height);
            console.log('spSpf.name', spSpf.name);
        }, 1000);
    }
    // ...
}

image image image
image
image
image

我查看了Sprite的源码…里面是使用 spf.getRect() 获取裁剪后宽高的…

...
else if (SizeMode.TRIMMED === this._sizeMode) {
    var rect = this._spriteFrame.getRect();
    this.node._uiProps.uiTransformComp.setContentSize(rect.width, rect.height);
}
...

于是我就试着使用了…

class Printer {
    // ...
    private _createMask(spf: SpriteFrame, w, h) {
        // ...
        const tsfMask = nMask.addComponent(UITransform);
        // tsfMask.width = spf.width;
        // tsfMask.height = spf.height;
        // tsfMask.width = w;
        // tsfMask.height = h;
        const rect = spf.getRect();
        tsfMask.width = rect.width;
        tsfMask.height = rect.height;
        // ...
    }
    // ...
}

使用之后和上一楼的情况差不多…宽高的数据都是准的…但是蒙版还是歪的…

好了解决了…非常感谢…
原来是刚好做demo的网络图片带了透明边缘…被编辑器裁剪过…
使用 spf.originalSize 就可以创建包括透明边缘的尺寸…
这样蒙版就合适了…

sprite 上的 trim 选项去掉勾选也是可以,只是表现可能会与你预期的有点不同

稍微关注了一下Graphics的实现…
发现内部的Impl是由矢量(顶点数)来记录笔迹的…
将笔宽调成100…迭代5次模拟柔滑…
单纯来回几下把画板涂成纯黑…顶点数就已经炸裂 :joy:

再涂一次白的…再涂一次黑的…百万顶点达成 :joy: :joy: :joy:
image
image

该主题在最后一个回复创建后14天后自动关闭。不再允许新的回复。