【ccc3.8】虚拟列表

news2024/10/3 8:28:09

一个简单的虚拟列表,没有任何其他东西。

原理就是向上滚动时,将下面离开屏幕的那一个item塞到上侧来:

主代码仅有两个:ScrollList对应的滚动容器,ScrollListItem对应单项的预制体

当前支持两种:竖向滚动、横线滚动。不支持那种横竖排版的。有需要自己添加

使用案例:https://github.com/EddyLwei/ccc3.8_VirtualList.git

ScrollList:



/**横向排布拖动*/
export const SCROLL_HORIZONTAL: number = 1;
/**竖向排布拖动*/
export const SCROLL_VERTICAL: number = 2;

import { _decorator, Node, Prefab, instantiate, ScrollView, UITransform, Vec3, log } from 'cc';
import { ScrollListItem } from './ScrollListItem';

const { ccclass, property } = _decorator;

@ccclass('ScrollList')
export class ScrollList extends ScrollView {


    /**item子节点预制体*/
    @property({ type: Prefab, tooltip: "item子节点预制体" })
    itemPrefab: Prefab = null;

    /**单条记录高度*/
    private _itemSize: number;

    /**需要多少个记录组件 在可视范围内+2条*/
    private _numItem: number = 0;

    private _itemArr: Array<Node> = [];

    /**开始端下标*/
    private _itemIndex: number = 0;
    /**结束端下标*/
    private _dataIndex: number = 0;

    /**数据源*/
    private _dataArr: any[];

    /**滚动方向*/
    private _direction: number = 0;

    /**间隙 0=开始边框,1=结束边框,2=间隙*/
    private _gapNum: number[];

    /**子节点刷新绑定事件,或者使用item继承的模式*/
    public onItemRender: Function;

    start() {
        super.start();
        this.node.on('scrolling', this.scrollCheck, this);
    }

    onDestroy() {
        super.onDestroy();
        if (this.node) {
            this.node.off('scrolling', this.scrollCheck, this);
        }
    }


    /**设置数据
     * @param dataArr : 数据源
     * @param direction : 滚动方向,默认上下
     * @param gap : [开始边框距离,结束边框距离,每个之间空隙]
    */
    public setDataList(dataArr: any[], direction: number = SCROLL_VERTICAL, gap?: number[]) {
        this._dataArr = dataArr;
        this._direction = direction;
        this._gapNum = gap;
        this.createItem();
    }


    /**获得数据后开始创建*/
    private createItem() {

        let _showSize = this.node.getComponent(UITransform).height;
        //获得预制体的高度
        if (!this._itemSize) {
            let pNode = instantiate(this.itemPrefab);
            if (this._direction == SCROLL_HORIZONTAL) {
                this._itemSize = pNode.getComponent(UITransform).contentSize.width;
                _showSize = this.node.getComponent(UITransform).width;
            }
            else {
                this._itemSize = pNode.getComponent(UITransform).contentSize.height;
            }
            pNode.destroy();
            // log("---_itemSize--", this._itemSize);
        }

        //可视范围,对应可以创建多少个实体单例item
        this._numItem = Math.floor(_showSize / this._itemSize) + 2;
        log(_showSize, "初始化获得数量:", this._numItem)
        if (this._dataArr.length < this._numItem) {
            this._numItem = this._dataArr.length;
        }

        this._itemArr.length = 0;
        for (let index = 0; index < this._numItem; index++) {
            let pNode = instantiate(this.itemPrefab);
            pNode.parent = this.content;
            this._itemArr.push(pNode);
            this.itemRender(pNode, index);
        }

        //设置容器大小
        let contentSize = this._itemSize * this._dataArr.length;
        //前面距离边框
        if (this._gapNum && this._gapNum[0]) {
            contentSize += this._gapNum[0];
        }
        //后面距离边框
        if (this._gapNum && this._gapNum[1]) {
            contentSize += this._gapNum[1];
        }
        //间隙距离
        if (this._gapNum && this._gapNum[2]) {
            contentSize += this._gapNum[2] * (this._dataArr.length - 1);
        }

        if (this._direction == SCROLL_HORIZONTAL) {
            this.content.getComponent(UITransform).width = contentSize;
        }
        else {
            this.content.getComponent(UITransform).height = contentSize;
        }

        this._itemIndex = this._dataIndex = this._itemArr.length - 1;
        log("初始化结束:", this._dataIndex, this._itemArr.length)
    }



    private scrollCheck() {
        let nowPos = this.getScrollOffset().y;
        let topPos = (this._dataIndex + 1 - this._numItem) * this._itemSize;//当前屏幕中靠近最开始的坐标

        //前面边框
        if (this._gapNum && this._gapNum[0]) {
            topPos += this._gapNum[0];
        }
        //间隙距离
        if (this._gapNum && this._gapNum[2]) {
            topPos += this._gapNum[2] * (this._dataIndex + 1 - this._numItem);
        }

        // let topPos = this.countPosByIndex(this._dataIndex + 1 - this._numItem);
        let size = this._itemSize;
        if (this._direction == SCROLL_HORIZONTAL) {
            nowPos = this.getScrollOffset().x;
            topPos = -topPos;
            size = -this._itemSize;
        }

        //判断向结束端滚动,滚动点和初始点对比
        if ((this._direction == SCROLL_VERTICAL && nowPos > size + topPos) ||
            (this._direction == SCROLL_HORIZONTAL && nowPos < size + topPos)) {
            let newIndex = this._dataIndex + 1;
            // Log.log(this._dataIndex, "-判断向结束端滚动 1 --将头部item转移到最后---", nowPos, topPos);
            if (newIndex >= this._dataArr.length) {
                return; //如果滚动到底部最后一条数据,不再进行写入
            }

            this._dataIndex = newIndex;

            let topItemIndex = this._itemIndex + 1;
            if (topItemIndex >= this._numItem) {
                topItemIndex = 0;
            }

            let item = this._itemArr[topItemIndex];
            if (item) {
                this.itemRender(item, newIndex);
                // Log.error(topItemIndex, "转移到最后", item.node.position);
            }

            this._itemIndex = topItemIndex;
        }

        //判断向开始端滚动
        else if ((this._direction == SCROLL_VERTICAL && nowPos < topPos) ||
            (this._direction == SCROLL_HORIZONTAL && nowPos > topPos)) {

            let newIndex = this._dataIndex + 1 - this._numItem - 1;
            // Log.log(this._dataIndex, "-判断向上滚动 2 -将最后item转移到头部----", newIndex);
            if (newIndex < 0) {
                // Log.warn("如果滚动到第一条数据,不再进行写入", newIndex)
                return; //如果滚动到第一条数据,不再进行写入
            }
            this._dataIndex--;
            // Log.error(this._itemIndex, "将最后item转移到头部", this._dataIndex, newIndex, newIndex * -this._itemSize);
            let item = this._itemArr[this._itemIndex];
            if (item) {
                this.itemRender(item, newIndex);
                // Log.error(this._itemIndex, "转移头部", item.node.position);
            }

            this._itemIndex--;
            if (this._itemIndex < 0) {
                this._itemIndex = this._numItem - 1;
            }
        }
    }


    /**刷新单项*/
    private itemRender(node: Node, newIndex: number) {
        //设置有全局得刷新事件
        if (this.onItemRender) {
            this.onItemRender(node, newIndex);
        }
        //没有全局,使用继承的item
        else {
            const item = node.getComponent(ScrollListItem)
            if (item) {
                item.onItemRender(this._dataArr[newIndex]);
            }
        }
        this.setPos(node, newIndex);

    }

    /**设置坐标*/
    private setPos(node: Node, index: number) {
        let pos = this.countPosByIndex(index);
        if (this._direction == SCROLL_HORIZONTAL) {
            node.setPosition(new Vec3(pos, 0));
        }
        else {
            node.setPosition(new Vec3(0, -pos));
        }
    }

    /**根据下标计算坐标*/
    private countPosByIndex(index: number): number {
        let pos = (1 / 2 + index) * this._itemSize;
        //前面距离边框
        if (this._gapNum && this._gapNum[0]) {
            pos += this._gapNum[0];
        }
        //间隙距离
        if (this._gapNum && this._gapNum[2]) {
            pos += this._gapNum[2] * index;
        }
        return pos;
    }

}

ScrollListItem:(其实仅用一个接口的作用)


import { _decorator, Component, } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('ScrollListItem')
export class ScrollListItem extends Component {

    /**滚动列表数据变更*/
    onItemRender(data, ...param: any[]) { }

}

使用方式很简单:

import { _decorator, Component, Node } from 'cc';
import { SCROLL_HORIZONTAL, SCROLL_VERTICAL, ScrollList } from './ScrollList';
const { ccclass, property } = _decorator;

@ccclass('TestScene')
export class TestScene extends Component {


    @property({ type: ScrollList, tooltip: "竖行滚动容器" })
    private vScroll: ScrollList;


    @property({ type: ScrollList, tooltip: "横向滚动容器" })
    private hScroll: ScrollList;


    start() {
        const dataArr = [];
        for (let index = 0; index < 50; index++) {
            dataArr.push(index)
        }

        this.hScroll.setDataList(dataArr, SCROLL_HORIZONTAL, [50, 50, 20]);
        this.vScroll.setDataList(dataArr, SCROLL_VERTICAL, [50, 50, 20]);
    }

}

组件的设置注意:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1128483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# out参数out多个参数

文章目录 C# out参数out多个参数背景说明作用方法定义调用方法测试结果注意 C# out参数out多个参数 背景说明 一个方法返回多个相同数据类型的变量&#xff0c;可以采用数据的方式&#xff1b; 我需要返回多个不同数据类型的方法&#xff0c;在这里采用out多个参数的方式。 …

js中循环判断找到满足条件的单项后结束循环

当选择的所有项中&#xff0c;如果有一项不满足条件则返回false&#xff0c;让业务逻辑停止&#xff0c;一般都是使用forEach循环&#xff0c;但是forEach循环有个弊端就是return不能跳出循环&#xff0c;所以这时候就需要使用for循环中的break来跳出循环。 下面是代码示例&am…

【Maven】VSCode Java+Maven 环境配置

0x00 前言 没写过 Java&#xff0c;得配个带 Maven 的编码环境&#xff0c;不太明白&#xff0c;试试看顺便记录一下 0x01 配置过程 安装 jdk1.8 后&#xff0c;找到安装位置&#xff1a; (base) dianCD-Ali doraemon % /usr/libexec/java_home -V Matching Java Virtual Ma…

数据结构:选择题+编程题(每日一练)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;单值二叉树 思路一&#xff1a; 题二&#xff1a;二叉树的最大深度 思路一&#xff1a; 本人实力有限可能对…

缺少win32spl.dll文件? 教你快速修复win32spl.dll

缺少win32spl.dll文件&#xff1f;不要怕&#xff0c;其实这个问题还是比较好解决的&#xff0c;我们今天会给大家介绍多种的解决方法&#xff0c;让你花式去解决缺少win32spl.dll的问题&#xff0c;好了&#xff0c;废话不多少&#xff0c;我们一起进入正题吧。 一.介绍win32s…

在docker环境下从头搭建openvslam/orb_slam3的流程记录以及问题总结

文章目录 0. 前言1. MobaXterm软件2. docker操作2.1. 拉一个ubuntu镜像2.2. 修改名字&#xff08;可选&#xff09;2.3. 删除之前的docker镜像&#xff08;可选&#xff09; 3. openvslam搭建流程3.1. 起容器3.2. 前置包的安装3.3. 安装Eigen3.4. 安装opencv3.5. 安装DBoW23.6.…

uCOSIII实时操作系统 十一 消息传递

目录 消息队列&#xff1a; 消息列队相关的API函数 创建消息队列&#xff1a; 等待消息列队&#xff1a; 向消息列队发送消息&#xff1a; 消息队列实验 任务内嵌消息队列&#xff1a; 任务内建消息队列的API函数 等待任务内建消息&#xff1a; 发送任务内建消息&…

Python-pptx教程之一从零开始生成PPT文件

简介 python-pptx是一个用于创建、读取和更新PowerPoint&#xff08;.pptx&#xff09;文件的python库。 典型的用途是根据动态内容&#xff08;如数据库查询、分析数据等&#xff09;&#xff0c;将这些内容自动化生成PowerPoint演示文稿&#xff0c;将数据可视化&#xff0c…

ES6初步了解生成器

生成器函数是ES6提供的一种异步编程解决方案&#xff0c;语法行为与传统函数完全不同 语法&#xff1a; function * fun(){ } function * gen(){console.log("hello generator");}let iterator gen()console.log(iterator)打印&#xff1a; 我们发现没有打印”hello…

计算机组成原理(一目了然的顶级总纲)(持续更新!)

文章目录 886冯诺依曼计算机计算机的五大部件&#xff08;又称五大字系统&#xff09;细化的计算机组成框图存储器 886 计算机系统由“硬件”和“软件”两大部分组成。 计算机的软件通常又可以分为两大类&#xff1a;系统软件和应用软件。 冯诺依曼计算机 数学家冯诺依曼&am…

MySQL数据库增删改查

删除表 drop table 表名&#xff1b; drop table if exists 表名&#xff1b;修改表 修改表名 alter table 表名 rename to 新表名&#xff1b;添加列 alter table 表名 add 列名 数据类型&#xff1b;删除列 alter table 表名 drop 列名&#xff1b;修改数据类型 alter …

WorkPlus专注私有化部署,为企业安全打造超级沟通协作APP

在如今全球化竞争和高速发展的商业环境中&#xff0c;企业内部的沟通和协作至关重要。面对众多的通讯和协作平台&#xff0c;WorkPlus独辟蹊径&#xff0c;专注私有化部署&#xff0c;致力于为企业打造安全专属、自主可控的超级沟通协作APP。正是这一专注与创新&#xff0c;让W…

发表《Nature》!哈佛大学团队成功研发自纠错量子计算机

&#xff08;图片来源&#xff1a;网络&#xff09; 量子计算机能达到当今最快的超级计算机也无法达到的速度和效率。然而&#xff0c;该技术尚未大规模推广和商业化&#xff0c;很大程度上是因为它无法自我纠错。与经典计算机不同&#xff0c;量子计算机无法通过一遍又一遍地…

双赢!企业咨询行业和低代码工具的破局之路

对于传统咨询企业来说&#xff0c;主要专注于流程和功能方面的咨询&#xff0c;在信息化时代中&#xff0c;以流程和业务驱动的模式为基础进行战略咨询、管理咨询和业务咨询&#xff0c;作为传统企业的外脑&#xff0c;在大数据时代&#xff0c;咨询行业在数智化时代如何应对自…

发表《数学》期刊!西班牙研究人员成功应用量子计算模型来预测多种疾病

Jos Luis Salmern 将量子计算应用于医疗保健领域&#xff08;图片来源&#xff1a;网络&#xff09; 谷歌量子人工智能&#xff08;AI&#xff09;研究小组的Sergio Boixo表示&#xff0c;量子计算还处于起步阶段&#xff0c;虽然很难预测其未来&#xff0c;但该技术已取得了一…

lvs+keepalived: 高可用集群

lvskeepalived: 高可用集群 keepalived为lvs应运而生的高可用服务。lvs的调度器无法做高可用&#xff0c;于是keepalived软件。实现的是调度器的高可用。 但是&#xff1a;keepalived不是专门为集群服务的&#xff0c;也可以做其他服务器的高可用。 lvs的高可用集群&#xf…

第九章 无线网络和移动网络 | 计算机网络(谢希仁 第八版)

文章目录 第九章 无线网络和移动网络9.1 无线局域网WLAN9.1.1 无线局域网的组成9.1.2 802.11局域网的物理层9.1.3 802.11局域网的MAC层协议9.1.4 802.11局域网的MAC帧 9.2 无线个人区域网WPAN9.3 无线城域网WMAN9.4 蜂窝移动通信网9.4.1 蜂窝无线通信技术简介9.4.2 移动IP9.4.3…

1. 概述

1.概述 1.1 信息时代的计算机网络 1.1.1 计算机网络的各类应用 1.1.2 计算机网络带来的负面问题 1.2 因特网概述 1.2.1 网络、互联网与因特网的区别与关系 若干个节点&#xff08;Node&#xff09;和链路&#xff08;Link&#xff09;互连形成了网络&#xff08;Network&…

Flask后端开发(一)-基础知识和前期准备

目录 1.背景介绍1.1. 项目背景1.2. 项目难点1.3. 项目环境 2. flask后端开发实现的功能3. flask部署和前后端对接3.1. flask运行配置和服务器部署3.2. flask前后端传参 4. 后端测试工具4.1. 工具介绍4.2. 工具使用 后记 1.背景介绍 1.1. 项目背景 就是前几个月临时接手了一个…

Linux基础命令4——Linux快捷键与帮助命令

目录 Linux快捷键 Linux常用帮助命令 help命令——只适用于于内置命令 --help命令——只适用于外置命令 内外部都可使用的帮助命令 man命令 info命令 Linux快捷键 ctrlshift加号 放大屏幕 ctrl减号 缩小屏幕 ctrl l …