Cocos Creator移动端适配组件-Ts版

序言

平时在使用Cocos开发移动端的项目时,总会遇到适配类的问题,索性写了一个适配的组件,最近在整理之前的项目和经验,把组件优化了一下,提供给大家使用。

使用方法

  1. 挂载到所需适配的节点上
  2. 往规则列表添加适配规则

适配规则

该适配组件可自适配移动端屏幕,提供多种适配规则:

  1. 吸附顶部、底部、左边、右边,可设置上下或左右扣除的值(为1以下小数时为当前屏幕宽度、高度乘于该值,为1以上的值时为当前屏幕宽度、高度减去该值)
    吸附顶部、底部、左边、右边的示例图片
  2. 适配宽度、高度,即铺满当前屏幕的宽度、高度,可设置上下或左右扣除的值(为1以下小数时为当前屏幕宽度、高度乘于该值,为1以上的值时为当前屏幕宽度、高度减去该值)
    适配宽度高度的示例图片
  3. 根据范围,自适应吸附顶部、底部、左边、右边。在屏幕尺寸大于等于最大范围下,吸附值为最小值,在屏幕尺寸小于等于最小范围下,吸附值为最大值。
    自适应根据范围吸附顶部、底部、左边、右边的示例图片
    如果还是不太清楚,给你们放一下示例的图片:
    自适应适配规则示例图片
    我们来根据这张图片来分析一下,在最大尺寸1624下,取值为100,在小尺寸下,取值则在100-200之间浮动。
    这样在大屏幕下,不会出现主要元素都处于安全区,出现其他区域很空的状况。显示效果更好
    小屏幕下,也不会出现主要元素被裁剪的情况。
  4. 根据范围显示或不显示滚动条,在屏幕尺寸小于范围下会显示滚动条,防止元素被截取不可操作。在屏幕尺寸大于范围的情况,则不会显示滚动条。
    添加这个规则,是防止出现极端尺寸下(列如:iframe嵌套),主要元素和按钮等被遮挡不可操作。

这个是github地址:https://github.com/kenvschen/ListView-for-CocosCreator

为了方便不使用github的同学,我这边也把代码copy出来,如有疑问和错误,欢迎大家指出问题所在。

const { ccclass, property } = cc._decorator;

const config = {
    gameWidth: 0,
    gameHeight: 0
};

const adaptationType = cc.Enum({
    // 吸附顶部,距离为 value,判断基准固定为高度
    吸附顶部: 0,
    // 吸附底部,距离为 value,判断基准固定为高度
    吸附底部: 1,
    // 吸附左边,距离为 value,判断基准固定为宽度
    吸附左边: 2,
    // 吸附右边,距离为 value,判断基准固定为宽度
    吸附右边: 3,    
    // 适配高度,可设置上下扣除高度或扣除百分比高度
    适配高度: 4,
    // 适配宽度,可设置左右扣除宽度或扣除百分比宽度
    适配宽度: 5,
    // 根据最大范围和最小范围来自适应吸附顶部,数值必须是固定值
    根据范围自适应吸附顶部: 6,
    // 根据最大范围和最小范围来自适应吸附底部,数值必须是固定值
    根据范围自适应吸附底部: 7,
    // 根据最大范围和最小范围来自适应吸附左边,数值必须是固定值
    根据范围自适应吸附左边: 8,
    // 根据最大范围和最小范围来自适应吸附右边,数值必须是固定值
    根据范围自适应吸附右边: 9,
    // 适配是否在范围内,如果小于范围则显示滚动条,适配极端尺寸
    自适应显示滚动条: 10,
});

const szieType = cc.Enum({
    /**
     * 根据宽度适配
     */
    adaptationWidth: 0,
    /**
     * 根据高度适配
     */
    adaptationHeight: 1
});

@ccclass('adaptationListClass')
class adaptationListClass {
    // 类型
    @property({
        type: adaptationType,
        displayName: 'adaptationType',
        tooltip: '适配规则类型'
    })
    type = adaptationType.吸附顶部;

    @property({
        type: szieType,
        displayName: 'adaptationFromType',
        tooltip: '适配规则判断标准'
    })
    szieType = szieType.adaptationWidth;

    // 数值
    @property({
        visible() {
            return (this.type != adaptationType.适配宽度 && this.type != adaptationType.适配高度 && this.type != adaptationType.根据范围自适应吸附顶部 && this.type != adaptationType.根据范围自适应吸附底部 && this.type != adaptationType.根据范围自适应吸附左边 && this.type != adaptationType.根据范围自适应吸附右边 && this.type != adaptationType.自适应显示滚动条);
        },
        displayName: 'value',
        tooltip: '根据规则扣除的数值,1以下小数为百分比,其他为固定扣除'
    })
    value: number = 0;

    // 固定顶部数值
    @property({
        visible() {
            return (this.type == adaptationType.适配高度);
        },
        displayName: 'costTopValue',
        tooltip: '距离顶边扣除数值, 1以下小数为百分比,其他为固定扣除'
    })
    costTopValue: number = 0;

    // 固定底部数值
    @property({
        visible() {
            return (this.type == adaptationType.适配高度);
        },
        displayName: 'costBottomValue',
        tooltip: '距离底边扣除数值, 1以下小数为百分比,其他为固定扣除'
    })
    costBottomValue: number = 0;

    // 固定左边数值
    @property({
        visible() {
            return (this.type == adaptationType.适配宽度);
        },
        displayName: 'costLeftValue',
        tooltip: '距离左边扣除数值, 1以下小数为百分比,其他为固定扣除'
    })
    costLeftValue: number = 0;

    // 右边数值(1以下小数为百分比,其他为固定扣除)
    @property({
        visible() {
            return (this.type == adaptationType.适配宽度);
        },
        displayName: 'costRightValue',
        tooltip: '距离右边扣除数值, 1以下小数为百分比,其他为固定扣除'
    })
    costRightValue: number = 0;

    // 最大范围的情况下,适配的数值
    @property({
        visible() {
            return (this.type == adaptationType.根据范围自适应吸附顶部 || this.type == adaptationType.根据范围自适应吸附底部 || this.type == adaptationType.根据范围自适应吸附左边 || this.type == adaptationType.根据范围自适应吸附右边);
        },
        displayName: 'minValue',
        tooltip: '最大范围下的数值,如正常尺寸下的适配数值'
    })
    minValue: number = 0;

    // 高度最小范围的情况下,距离顶部的数值
    @property({
        visible() {
            return (this.type == adaptationType.根据范围自适应吸附顶部 || this.type == adaptationType.根据范围自适应吸附底部 || this.type == adaptationType.根据范围自适应吸附左边 || this.type == adaptationType.根据范围自适应吸附右边);
        },
        displayName: 'maxValue',
        tooltip: '最小范围下的数值,如安全区下的适配数值'
    })
    maxValue: number = 0;

    // 最小范围
    @property({
        visible() {
            return (this.type == adaptationType.根据范围自适应吸附顶部 || this.type == adaptationType.根据范围自适应吸附底部 || this.type == adaptationType.根据范围自适应吸附左边 || this.type == adaptationType.根据范围自适应吸附右边 || this.type == adaptationType.自适应显示滚动条);
        },
        displayName: 'minRange',
        tooltip: '最小范围数值,如设计稿最小安全区尺寸'
    })
    minRange: number = 0;

    // 最大范围
    @property({
        visible() {
            return (this.type == adaptationType.根据范围自适应吸附顶部 || this.type == adaptationType.根据范围自适应吸附底部 || this.type == adaptationType.根据范围自适应吸附左边 || this.type == adaptationType.根据范围自适应吸附右边);
        },
        displayName: 'maxRange',
        tooltip: '最大范围数值,如设计稿最大尺寸'
    })
    maxRange: number = 0;
}

@ccclass
export default class adaptation extends cc.Component {

    // 设计稿尺寸
    @property({
        displayName: 'configSize',
        tooltip: '设计稿尺寸'
    })
    configSize: cc.Size = cc.size(0, 0);
    
    @property({
        type: [adaptationListClass],
        displayName: 'adaptationList',
        tooltip: '适配规则列表,从头到底一条条执行'
    })
    adaptationList: adaptationListClass[] = [];

    // LIFE-CYCLE CALLBACKS:
    private nowSize: cc.Size = null;
    private firstAdaptation: boolean = true;

    onLoad() {
        this.nowSize = cc.size(cc.winSize.width, cc.winSize.height);
        // cc.log(cc.winSize);
        this.setWidget();

        this.firstAdaptation = false;
    }

    /**
     * 开始适配规则
     */
    setWidget() {
        // 系统尺寸
        let winSize = cc.size(cc.winSize.width, cc.winSize.height);
        // 设计尺寸
        let configSize = this.returnConfigSize();
        // 对齐组件
        let widget: cc.Widget = null;

        let _top: number, _bottom: number, _left: number, _right: number;
        let _height: number, _width: number;

        cc.log('winSize', winSize);
        cc.log('configSize', configSize);

        if (this.adaptationList.length > 0) {
            widget = this.node.getComponent(cc.Widget) ? this.node.getComponent(cc.Widget) : this.node.addComponent(cc.Widget);
        } else {
            // this.node.removeComponent(cc.Widget);
            this.node.getComponent(cc.Widget) ? this.node.getComponent(cc.Widget).enabled = false : void(0);
        }

        this.adaptationList.map(value => {
            value.value = Math.abs(value.value);
            value.costBottomValue = Math.abs(value.costBottomValue);
            value.costTopValue = Math.abs(value.costTopValue);
            value.costLeftValue = Math.abs(value.costLeftValue);
            value.costRightValue = Math.abs(value.costRightValue);
            switch (value.type) {
                // 吸附顶部
                case adaptationType.吸附顶部:
                    _top = (configSize.height - winSize.height) / 2;
                    cc.log('吸附顶部', _top);
                    widget.enabled = true;
                    widget.top = (value.value < 1 && value.value != 0) ? _top + winSize.height * value.value : _top + value.value;
                    widget.isAlignTop = true;
                    widget.updateAlignment();
                    break;
                // 吸附底部
                case adaptationType.吸附底部:
                    let _bottom = (configSize.height - winSize.height) / 2;
                    cc.log('吸附底部', _bottom);
                    widget.enabled = true;
                    widget.bottom = (value.value < 1 && value.value != 0) ? _bottom + winSize.height * value.value : _bottom + value.value;
                    widget.isAlignBottom = true;
                    widget.updateAlignment();
                    break;
                // 吸附左边
                case adaptationType.吸附左边:
                    _left = (configSize.width - winSize.width) / 2;
                    cc.log('吸附左边', _left);
                    widget.enabled = true;
                    widget.left = (value.value < 1 && value.value != 0) ? _left + winSize.width * value.value : _left + value.value;
                    widget.isAlignLeft = true;
                    widget.updateAlignment();
                    break;
                // 吸附右边
                case adaptationType.吸附右边:
                    _right = (configSize.width - winSize.width) / 2;
                    cc.log('吸附右边', _right);
                    widget.enabled = true;
                    widget.right = (value.value < 1 && value.value != 0) ? _right + winSize.width * value.value : _right + value.value;
                    widget.isAlignRight = true;
                    widget.updateAlignment();
                    break;
                // 适配高度
                case adaptationType.适配高度:
                    _top = value.costTopValue < 1 && value.costTopValue != 0 ? value.costTopValue * winSize.height : value.costTopValue;
                    _bottom = value.costBottomValue < 1 && value.costBottomValue != 0 ? value.costBottomValue * winSize.height : value.costBottomValue;
                    if (_top + _bottom > winSize.height) {
                        _top = winSize.height / 2;
                        _bottom = winSize.height / 2;
                    }
                    _height = winSize.height - _top - _bottom;
                    cc.log('适配高度', _height);
                    this.node.height = _height;
                    break;
                // 适配宽度
                case adaptationType.适配宽度:
                    _left = value.costLeftValue < 1 && value.costLeftValue != 0 ? value.costLeftValue * winSize.width : value.costLeftValue;
                    _right = value.costRightValue < 1 && value.costRightValue != 0 ? value.costRightValue * winSize.width : value.costRightValue;
                    if (_left + _right > winSize.width) {
                        _left = winSize.width / 2;
                        _right = winSize.width / 2;
                    }
                    _width = winSize.width - _left - _right;
                    cc.log('适配宽度', _width);
                    this.node.width = _width;
                    break;
                // 根据范围自适应吸附顶部,在最大范围时,适配取值为最小值,最小范围下,适配取值为最大值
                case adaptationType.根据范围自适应吸附顶部:
                    if (value.minRange > (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _top = value.maxValue;
                    } else if (value.maxRange < (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _top = value.minValue;
                    } else {
                        _top = ((1 - ((value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height) - value.minRange) / (value.maxRange - value.minRange)) * (value.maxValue - value.minValue)) + value.minValue;
                    }
                    cc.log('根据范围自适应吸附顶部', _top);

                    widget.enabled = true;
                    widget.top = _top;
                    widget.isAlignTop = true;
                    widget.updateAlignment();
                    break;
                // 根据范围自适应吸附底部,在最大范围时,适配取值为最小值,最小范围下,适配取值为最大值
                case adaptationType.根据范围自适应吸附底部:
                    // cc.log('minRange: ' + value.minRange + ' winSize.width:' + winSize.width);
                    if (value.minRange > (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _bottom = value.maxValue;
                        // cc.log('高度小于最小值', _bottom);
                    } else if (value.maxRange < (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _bottom = value.minValue;
                        // cc.log('高度大于最大值', _bottom);
                    } else {
                        _bottom = ((1 - ((value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height) - value.minRange) / (value.maxRange - value.minRange)) * (value.maxValue - value.minValue)) + value.minValue;
                        // cc.log('范围', _bottom);
                    }
                    
                    widget.enabled = true;
                    widget.bottom = _bottom;
                    widget.isAlignBottom = true;
                    widget.updateAlignment();
                    break;
                // 根据范围自适应吸附左边,在最大范围时,适配取值为最小值,最小范围下,适配取值为最大值
                case adaptationType.根据范围自适应吸附左边:
                    // let _left = null;
                    if (value.minRange > (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _left = value.maxValue;
                    } else if (value.maxRange < (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _left = value.minValue;
                    } else {
                        _left = ((1 - ((value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height) - value.minRange) / (value.maxRange - value.minRange)) * (value.maxValue - value.minValue)) + value.minValue;
                    }
                    
                    widget.enabled = true;
                    widget.left = _left;
                    widget.isAlignLeft = true;
                    widget.updateAlignment();
                    break;
                // 根据范围自适应吸附右边,在最大范围时,适配取值为最小值,最小范围下,适配取值为最大值
                case adaptationType.根据范围自适应吸附右边:
                    // let _right = null;
                    if (value.minRange > (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _right = value.maxValue;
                    } else if (value.maxRange < (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        _right = value.minValue;
                    } else {
                        _right = ((1 - ((value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height) - value.minRange) / (value.maxRange - value.minRange)) * (value.maxValue - value.minValue)) + value.minValue;
                    }
                    
                    widget.enabled = true;
                    widget.right = _right;
                    widget.isAlignRight = true;
                    widget.updateAlignment();
                    break;
                // 适配是否在范围内,如果小于范围则显示滚动条
                case adaptationType.自适应显示滚动条:
                    let scrollView = this.node.getComponent(cc.ScrollView);
                    if (!scrollView) {
                        scrollView = this.node.addComponent(cc.ScrollView);
                        scrollView.vertical = false;
                        scrollView.horizontal = false;
                        // 生成遮罩节点
                        let viewNode = new cc.Node('view');
                        viewNode.setContentSize(winSize);
                        // 添加mask组件
                        let mask = viewNode.addComponent(cc.Mask);
                        mask.type = cc.Mask.Type.RECT;
                        mask.enabled = true;
                        // 生成content节点
                        let contentNode = new cc.Node('content');
                        contentNode.setContentSize(this.node.getContentSize());

                        let childrens = this.node.children.concat([]);
                        this.node.removeAllChildren(false);
                        childrens.map(item => {
                            // cc.log('item', item, item.getComponent(cc.Camera));
                            if (!item.getComponent(cc.Camera)) {
                                // item.removeFromParent(false);
                                contentNode.addChild(item, item.zIndex);
                            }
                        });

                        contentNode.parent = viewNode;
                        viewNode.parent = this.node;
                        scrollView.content = contentNode;
                    } else {
                        // 设置遮罩节点尺寸
                        scrollView.content.parent.setContentSize(winSize);
                        // 设置content尺寸
                        scrollView.content.setContentSize(this.node.getContentSize());
                    }
                    if (value.minRange > (value.szieType == szieType.adaptationWidth ? winSize.width : winSize.height)) {
                        cc.log('屏幕尺寸不足开启滚动, 滚动至居中');
                        scrollView.enabled = true;
                        if (value.szieType == szieType.adaptationWidth) {
                            scrollView.horizontal = true;
                            scrollView.horizontalScrollBar ? scrollView.horizontalScrollBar.node.active = true : void (0);
                        } else {
                            scrollView.vertical = true;
                            scrollView.verticalScrollBar ? scrollView.verticalScrollBar.node.active = true : void (0);
                        }

                        cc.director.once(cc.Director.EVENT_AFTER_DRAW, () => {
                            scrollView.scrollToOffset(cc.v2(value.szieType == szieType.adaptationWidth ? (configSize.width - winSize.width) / 2 : 0, value.szieType == szieType.adaptationWidth ? 0 : (configSize.height - winSize.height) / 2));
                        });
                    } else {
                        cc.log('屏幕尺寸足够不开启滚动');
                        scrollView.enabled = false;
                        scrollView.verticalScrollBar ? scrollView.verticalScrollBar.node.active = false : void (0);
                        scrollView.horizontalScrollBar ? scrollView.horizontalScrollBar.node.active = false : void (0);
                    }
                    break;
            }
        });
    }

    /**
     * 返回屏幕尺寸
     * 如设置了config里的宽高不为0,则使用config里的数据作为基准
     * 否则使用configSize里的数据为基准
     */
    returnConfigSize (): cc.Size {
        if (config.gameWidth !== 0 && config.gameHeight !== 0) {
            return cc.size(config.gameWidth, config.gameHeight);
        } else {
            return this.configSize;
        }
    }

    // start () {}

    update (dt: number) {
        if (!this.firstAdaptation && (cc.winSize.width != this.nowSize.width || cc.winSize.height != this.nowSize.height)) {
            this.nowSize = cc.size(cc.winSize.width, cc.winSize.height);

            this.setWidget();
        }
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值