CocosCreator无尽循环列表,ScrollView优化

 之前在u3d里写过,现在用CocosCreator又需要用到,就再写了一个。

实现了垂直和水平方向的无限循环功能。

用typescript写的,两百行不到的代码,请大家看看。

demo在可看链接demo

/*
 * @Author: HuangLang 
 * @Date: 2018-07-13 15:17:37  
 * @Description :无限循环列表
 * @Last Modified by: HuangLang
 * @Last Modified time: 2018-07-17 14:41:19
 */
/**
 * 滑动方向枚举
 */
export enum ScrollDirEnum {
    Vertical,
    Horizon
}
const { ccclass, property } = cc._decorator;

@ccclass
export default class ScrollHelper extends cc.Component {

    //赋值用的obj
    @property(cc.Node)
    item: cc.Node = null;
    @property(cc.ScrollView)
    Scroll: cc.ScrollView = null;
    //显示用的content
    @property(cc.Node)
    content: cc.Node = null;
    //附加mask组件的对象
    @property(cc.Node)
    maskNode: cc.Node = null;
    //方向,0是垂直方向,1是水平方向
    @property(Number)
    dir: Number = 0;

    //滑动的方向
    public scrollDir: ScrollDirEnum = ScrollDirEnum.Vertical;
    public get ScrollDir() { return this.scrollDir; }
    public set ScrollDir(val) { this.scrollDir = val; }
    private num: number;//需要显示的item数量
    public get Num() { return this.num; }
    public set Num(val) { this.num = val; }
    private itemNum: number;//实际生成item数量
    public get ItemNum() { return this.itemNum; }
    public set ItemNum(val) { this.itemNum = val; }
    public distance = 100;//item之间的间距
    public needSize = 0;//需求要求的高度/宽度
    public visibleHeight = 0;//显示范围高度
    public itemsHeight = 0;//物品高度
    public OnScrollFun = null;//滑动回调
    //可见范围
    private minY: number = 0;
    private maxY: number = 0;
    //可以显示的范围,可见范围 超过 可以显示的范围,就刷新列表(把缓存的item放到下面或上面)
    private minVisibleY: number = 0;
    private maxVisibleY: number = 0;
    public initX = this.distance / 2;
    public initY = -this.distance / 2;

    start() {
        this.num = 7;
        this.itemNum = 6;
        this.scrollDir = this.dir as ScrollDirEnum;
        this.Init(this.itemNum);
    }

    Init(num: number) {
        var eventHandler = new cc.Component.EventHandler();
        eventHandler.target = this.node;
        eventHandler.component = "ScrollHelper";
        eventHandler.handler = "OnScroll";
        this.Scroll.scrollEvents.push(eventHandler);
        this.needSize = this.num * this.distance;
        if (this.scrollDir == ScrollDirEnum.Horizon) {
            this.initX = this.distance / 2;
            this.initY = 0;
            this.content.setContentSize(new cc.Size(this.needSize, this.content.getContentSize().height));

        } else {
            this.initX = 0;
            this.initY = -this.distance / 2;
            this.content.setContentSize(new cc.Size(this.content.getContentSize().width, this.needSize));
        }
        this.visibleHeight = this.maskNode.getContentSize().height;
        this.InitObjs();
    }

    //初始化可见的item
    private itemsList = new Array();
    InitObjs() {
        let curX = 0;
        let curY = 0;
        for (let i = 0; i < this.itemNum; i++) {
            let obj = cc.instantiate(this.item);
            obj.parent = this.content;
            obj.active = true;
            if (this.scrollDir == ScrollDirEnum.Horizon) {
                curX = this.initX + this.distance * i;
                // console.error("curX:" + curX);
            }
            else {
                curY = this.initY - this.distance * i;
                // console.error("curY:" + curY);
            }

            obj.setPositionX(curX);
            obj.setPositionY(curY);
            this.onRefresh(obj, i.toString(), i);
            this.itemsList.push(obj);
        }
    }

    //计算边界,超过边界则刷新列表
    //offest是左上角原点滑动的偏移量
    private countBorder(offest) {
        let height = this.visibleHeight;//可见高度
        this.minY = offest;//获得相对于左上角原点的最小y值
        this.maxY = offest + height;//获得相对于左上角原点的最大y值
    }

    private miniIdx = 0;
    private maxIdx = 0;
    private curOffset = 0;
    OnScroll() {
        //获取滚动视图相对于左上角原点的当前滚动偏移
        let scrollOffset: cc.Vec2 = this.Scroll.getScrollOffset();
        let offest = 0;
        if (this.scrollDir == ScrollDirEnum.Horizon)
            //水平的offset是负数,为什么会有这么sb的设计,将它取反和垂直方向的统一一下
            offest = -scrollOffset.x;
        else
            offest = scrollOffset.y;
        this.curOffset = offest;
        this.refresh();
    }

    //强行刷新
    public refresh() {
        let offest = this.curOffset;

        //最大高度,超过该高度,不刷新
        let maxY = this.needSize;
        if (offest < 0 || offest + this.visibleHeight >= maxY)
            return;
        
        let idx: number = 0;//从0开始
        this.countBorder(offest);
        this.miniIdx = Math.floor(offest / this.distance);
        // console.error("this.miniIdx:" + this.miniIdx);

        this.minVisibleY = this.miniIdx * this.distance;
        this.maxVisibleY = this.maxIdx * this.distance;
        //miniIdx到maxIdx都会刷新
        for (let i = 0; i < this.itemNum; i++) {
            let obj = this.itemsList[i];
            idx = this.miniIdx + i;
            this.refreshItem(idx, i, obj);
        }
        this.maxIdx = this.miniIdx + this.itemNum;
    }

    //idx是UI该刷新的第几个元素
    private refreshItem(idx, objIdx, obj) {
        if (idx < 0 || idx >= this.num)
            return;

        if (obj == null) {
            console.error("obj为空!");
            return;
        }
        let curX = 0;
        let curY = 0;
        if (this.scrollDir == ScrollDirEnum.Horizon)
            curX = this.initX + this.distance * idx;
        else
            curY = this.initY - this.distance * idx;
        // console.error("idx:" + idx + ",curX:" + curX + ",curY:" + curY);
        obj.setPositionX(curX);
        obj.setPositionY(curY);
        this.onRefresh(obj, idx, objIdx);
    }

    /**
     * 刷新回调
     * @param obj 
     * @param idx 需求显示的索引
     * @param objIdx 实际的item索引
     */
    private onRefresh(obj, idx: string, objIdx) {
        //测试用
        let label = cc.find("text", obj).getComponent(cc.Label);
        label.string = idx;
        if (this.OnScrollFun)
            this.OnScrollFun(obj, idx, objIdx);
    }
}

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值