分享一种适配方案

ccc2.x 适配方案

效果

iphone7


ipad

iphoneX

说明

方案初衷是想给已稳定的项目添加一个适配各种分辨率的整体方案,要求对场景、预制、代码逻辑做最小的改动;最好不必修改图片等资源。

适配要求

1、ui不可变形,只能做整体拉伸

2、不能有黑边

3、ui上下左右对应自动靠边

4、异形屏旋转后自适应

具体方案

1、适配规则

    export enum AdaptType {

        None = 0,

        ScaleX = 1,         //拉伸宽度

        ScaleY = 1 << 1,    //拉伸高度

        Left = 1 << 2,      //靠左

        Right = 1 << 3,     //靠右

        Top = 1 << 4,       //靠上

        Bottom = 1 << 5,    //靠下

        ScaleAll = 1 << 6,  //整体缩放

        ScaleBg = 1 << 7,   //整体缩放,铺满全屏,根据图片(节点)尺寸确定缩放比例

        Fit = 1 << 8,       //拉伸宽或高,铺满全屏

    }

2、配置文件

录入每个需要适配的场景、预制中每个节点的适配规则

例如:

    /** 各个场景、预制的适配规则 */

    private static _nodeDatas = {

        //scene

        'Main': {

            'bg': AdaptType.ScaleBg,

            'btns/1': AdaptType.Right|AdaptType.Top,

            'btns/2': AdaptType.Right|AdaptType.Top,

            'btns/3': AdaptType.Left,

            'btns/4': AdaptType.Left,

            'btns/5': AdaptType.Bottom,

            'btns/6': AdaptType.Bottom,

            'btns/7': AdaptType.Bottom,

            'btns/8': AdaptType.Right,

            'btns/9': AdaptType.Right,

            'label': AdaptType.Left|AdaptType.Top,

        },

        //Prefab

        'Dialog_Test': {

            'mask': AdaptType.ScaleBg,

            'close': AdaptType.Right|AdaptType.Top,

        },

    }

3、获取适配需要的参数

    private static _params:number[] = null;

    /** 适配参数 */

    private static get params():number[] {

        if(!this._params) {

            let w = cc.view.getFrameSize().width; //实际宽度

            let h = cc.view.getFrameSize().height; //实际高度

            let ratio = w/h; //实际宽高比

            let desRatio = 1332/740; //设计宽高比

            let isNotch = cc.sys.isNative && ratio>1432/740; //是否异型屏(简化,实际如果需要,可以在通过原生方法中获取)

            let lsLeft = ratio > desRatio && -((ratio-desRatio)*740/2)+(isNotch?50:0); //左侧修正(简化,默认异形屏尺寸位置50)

            let lsRight = ratio > desRatio && ((ratio-desRatio)*740/2)+(isNotch?-50:0); //右侧修正

            let lsTop = ratio < desRatio ? ((1/ratio-1/desRatio)*1332/2) : 0; //顶部修正

            let lsBottom = ratio < desRatio ? -((1/ratio-1/desRatio)*1332/2) : 0; //底部修正

            this._params = [ratio, desRatio, lsLeft, lsRight, lsTop, lsBottom];

        }

        return this._params;

    }

4、适配节点

    /** 适配单个节点 */

    static dealNode(node:cc.Node, type:AdaptType) {

        if(!this.isValid) {

            return;

        }

        let p = this.params;

        let ratio = p[0], desRatio = p[1], lsLeft = p[2], lsRight = p[3], lsTop = p[4], lsBottom = p[5];

        if(!node) {

            return;

        }

        let pos = node.position;

        if(type & AdaptType.ScaleAll) {

            node.scale = ratio > desRatio ? ratio/desRatio : desRatio/ratio;

        }

        else if(type & AdaptType.ScaleBg) {

            let bgRatio = node.width/node.height;

            node.scale = ratio > desRatio ? (bgRatio >= ratio ? 1 : ratio/bgRatio) : (bgRatio >= ratio ? bgRatio/ratio : 1);

        }

        else if(type & AdaptType.Fit) {

            ratio > desRatio ? node.scaleX = ratio/desRatio : node.scaleY = desRatio/ratio;

        }

        if(type & AdaptType.Left) {

            node.x = pos.x + lsLeft;

        }

        if(type & AdaptType.Right) {

            node.x = pos.x + lsRight;

        }

        if(type & AdaptType.Top) {

            node.y = pos.y + lsTop;

        }

        if(type & AdaptType.Bottom) {

            node.y = pos.y + lsBottom;

        }

    }

    /** 初始化适配,手动适配全部节点 */

    refresh() {}

5、初始化方式

方式1、挂在root节点

方式2、代码添加

    onLoad() {

        let adapt = cc.find('Canvas').addComponent(Adaptation);

        adapt.sceneName = 'Main';

        adapt.refresh();

    }

方式3、如果场景或者预制中需要适配节点不多,也可不录入配置文件,直接在预制的脚本中使用代码控制

    onLoad() {

        Adaptation.dealNode(this.node.getChildByName('bg'), AdaptType.ScaleBg);

    }

6、注意事项

使用此方案,务必设置Canvas的适配规则:

    onLoad() {

        cc.find('Canvas').getComponent(cc.Canvas).fitHeight = true;

        cc.find('Canvas').getComponent(cc.Canvas).fitWidth = true;

    }

预制件和场景用法基本一致,可参考项目链接:https://github.com/fh7315/Adaptation

总结

幸好我们的项目中,场景和预制都有一个基类,我只需要在基类的onload中添加适配组件,基本不需要修改额外代码和场景预制文件。

3赞

好麻烦。不干了 搬砖去

1赞

牛逼啊!学习一下

:grinning:好厉害

光看截图的话感觉 widget 也能实现同样的效果?

1赞

是呢,对比widget有啥优势?

没啥优势,不同的方案罢了

其他的都好说- -,你可以考虑一下把配置那个地方优化掉,稍微大点的项目写配置的话很难受。

项目大反而是优势,不需要所有的都写配置,预制的基类写个统一调用就可以了,比如我的项目中弹框的基类中都有一个黑色mask,只需要在基类的onload中给mask加个缩放就行了