{"id":18733149,"url":"https://github.com/bioforestchain/virtual-scroll","last_synced_at":"2025-10-15T15:44:03.827Z","repository":{"id":47556369,"uuid":"399391931","full_name":"BioforestChain/virtual-scroll","owner":"BioforestChain","description":"Virtual-Scroll ❤️ WebComponent","archived":false,"fork":false,"pushed_at":"2021-09-09T03:58:41.000Z","size":245,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-21T11:50:59.802Z","etag":null,"topics":["virtual-scroll"],"latest_commit_sha":null,"homepage":"https://bioforestchain.github.io/virtual-scroll/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BioforestChain.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-24T08:33:26.000Z","updated_at":"2021-09-17T10:07:43.000Z","dependencies_parsed_at":"2022-09-10T15:50:18.708Z","dependency_job_id":null,"html_url":"https://github.com/BioforestChain/virtual-scroll","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BioforestChain%2Fvirtual-scroll","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BioforestChain%2Fvirtual-scroll/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BioforestChain%2Fvirtual-scroll/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BioforestChain%2Fvirtual-scroll/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BioforestChain","download_url":"https://codeload.github.com/BioforestChain/virtual-scroll/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248613461,"owners_count":21133520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["virtual-scroll"],"created_at":"2024-11-07T15:08:52.306Z","updated_at":"2025-10-15T15:43:58.793Z","avatar_url":"https://github.com/BioforestChain.png","language":"TypeScript","readme":"# Virtual Scroll - WebComponent\n\n[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://www.webcomponents.org/element/owner/my-element)\n\n## DEMO\n\n- [example 1](https://bioforestchain.github.io/virtual-scroll/)\n\n## Usage\n\n**Install Dep**\n\n```bash\nnpm i @bfchain/virtual-scroll --save\nyarn add @bfchain/virtual-scroll\n```\n\n**moderns web dev**\n\n\u003e For Angular/React/Vue/Preact etc.\n\n```ts\nimport \"@bfchain/virtual-scroll\";\n```\n\n**or include directly**\n\n\u003e For vanillajs\n\n```html\n\u003cscript src=\"./node_modules/@bfchain/virtual-scroll/dist/vitrual-scroll.iife.js\"\u003e\u003c/script\u003e\n```\n\n**then you can use the component in your html**\n\n\u003c!--\n```html\n\u003ccustom-element-demo height=\"640\"\u003e\n  \u003ctemplate\u003e\n    \u003cscript type=\"module\" src=\"./iife/virtual-scroll.iife.js\"\u003e\u003c/script\u003e\n    \u003cstyle\u003e\n    html,\n    body {\n        width: 100%;\n        height: 100%;\n        margin: 0;\n        background-color: #ddd;\n    }\n    body {\n        box-sizing: border-box;\n        padding: 10px 20px;\n        align-items: center;\n        justify-content: stretch;\n    }\n    scroll-viewport {\n        width: 100%;\n        height: 100%;\n        background-color: #999;\n        position: relative;\n    }\n    .block-card-item {\n        height: 200px;\n        width: 100%;\n        box-sizing: border-box;\n        --card-color: #2196f3;\n        height: 100%;\n        padding: 10px 25px;\n    }\n    .block-card-item [name=\"height\"] {\n        contain: strict;\n    }\n    .block-content {\n        height: 100%;\n        padding: 30px;\n        box-sizing: border-box;\n        background: linear-gradient(180deg, var(--card-color), #fff);\n        border-radius: 20px;\n        /* box-shadow: -4px -4px 8px rgba(255, 255, 255, 0.2),\n            4px 4px 8px rgba(0, 0, 0, 0.2); */\n    }\n    .block-card-item.first .chain-link {\n        display: none;\n    }\n    .block-card-item.hide {\n        display: none;\n    }\n    .chain-link {\n        position: absolute;\n        top: -30px;\n        left: 0;\n        z-index: 2;\n        height: 60px;\n        width: 100%;\n        display: flex;\n        flex-direction: row;\n        justify-content: space-around;\n    }\n    .chain-link::before,\n    .chain-link::after {\n        content: \" \";\n        width: 10px;\n        background: #81c784;\n        /* box-shadow: -1px -1px 2px rgba(255, 255, 255, 0.2),\n            1px 1px 2px rgba(0, 0, 0, 0.2); */\n        border-radius: 5px;\n    }\n    .top-button {\n        width: 100%;\n    }\n    .my-sliders {\n        width: 100%;\n        height: 100%;\n        padding: 10px;\n        box-sizing: border-box;\n    }\n    .my-sliders .slider-item {\n        background-color: #e91e63;\n        width: 100%;\n        height: 100%;\n        border-radius: 10px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n    }\n    \u003c/style\u003e\n    \u003cscript\u003e\n    function rangeChange(event) {\n        const { entries } = event.detail;\n        for (const { node, index, isIntersecting } of entries) {\n        const viewClass = event.target.className;\n        if (isIntersecting) {\n            const height = l.itemCount - index;\n            const heightEle = node.querySelector(\"[name=height]\");\n            if (heightEle.textContent != height) {\n            (heightEle.firstChild || heightEle).textContent = height;\n            }\n            node.contentNode.classList.toggle(\n            \"first\",\n            index === 0n || index === 3n\n            );\n            node.contentNode.classList.toggle(\"hide\", index === 2n);\n        }\n        }\n    }\n    function gotoTop() {\n        l.virtualScrollTop = 0;\n    }\n    \u003c/script\u003e\n    \u003cnext-code-block\u003e\u003c/next-code-block\u003e\n  \u003c/template\u003e\n\u003c/custom-element-demo\u003e\n```\n--\u003e\n\n```html\n\u003cscroll-viewport\u003e\n  \u003cfixed-size-virtual-list-s1\n    id=\"l\"\n    item-size=\"200\"\n    item-count=\"1000000\"\n    safe-area-inset-top=\"400\"\n    safe-area-inset-bottom=\"100\"\n    onrenderrangechange=\"rangeChange(event)\"\n  \u003e\n    \u003cdiv class=\"block-card-item\" slot=\"template\"\u003e\n      \u003cdiv class=\"block-content\"\u003e\n        \u003cdiv class=\"height\"\u003eHeight: \u003cspan name=\"height\"\u003e\u003c/span\u003e\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"chain-link\"\u003e\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cvirtual-list-custom-item item-size=\"400\"\u003e\n      \u003cdiv class=\"my-sliders\"\u003e\n        \u003cdiv class=\"slider-item\"\u003eThis is Banner!\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/virtual-list-custom-item\u003e\n    \u003cvirtual-list-custom-item position-top=\"800\" item-size=\"200\"\u003e\n      \u003cdiv class=\"my-sliders\"\u003e\n        \u003cdiv class=\"slider-item\"\u003erethink, this is grid.\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/virtual-list-custom-item\u003e\n  \u003c/fixed-size-virtual-list-s1\u003e\n\u003c/scroll-viewport\u003e\n```\n\n## Document\n\n1. `\u003cscroll-viewport\u003e`\n   \u003e viewport for virtual scroll.\n   - `@slot` - This element has a slot\n2. `\u003cfixed-size-virtual-list-s1\u003e`/`\u003cfixed-size-virtual-list-s2\u003e`\n   \u003e virtual scroll list with fixed size.\n   \u003e\n   \u003e - `s1` means has only one scrollbar to capture two direction scroll.\n   \u003e - `s2` means has two scrollbar for two direction scroll (maybe you will need it).\n   - `@slot` - for custom list item\n   - `@slot` `template` - for buildable item\n   - `@csspart` `scroll-ctrl` - (s1) the scroll controller\n   - `@csspart` `scroll-up` - (s2) the scroll up controller\n   - `@csspart` `scroll-down` - (s2) the scroll down controller\n   - `@csspart` `virtual-list-view` - the scroll item container\n   - `@fires` `renderrangechange` - when scroll, the item will need render changed\n   - `@attr` `{bigint} item-count` -\n   - `@attr` `{number} item-size` -\n   - `@attr` `{number} safe-area-inset-top` - like padding-top\n   - `@attr` `{number} safe-area-inset-bottom` - like padding-bottom\n   - `@attr` `{number} cache-render-top` - will make list-view higher, for render more item. if item content overflow, you may need change this to make it render correctly\n   - `@attr` `{number} cache-render-bottom` - will make list-view higher, for render more item. like {cache-render-top}\n   - `@method` `refresh()` - if you change the attr directly by set property. you may need call the method\n   - `@prop` `{bigint} virtualScrollTop` - change the scroll top. without animation\n3. `\u003cvirtual-list-custom-item\u003e`\n   \u003e custom item in virtual scroll list\n   - `@slot` - for custom list item\n   - `@attr` `{bigint} position-top` - the posiction in virtual scroll list\n   - `@attr` `{number} item-size` - the item height\n\n## ISSUES\n\n1. because of `scroll-ctrl` behavior base on scroll-snap, but Firefox and WebKit don't behave the same. so in firebox you need use controlbar but no mousewheel.\n\n## TODO\n\n- [x] fixed-size-virtual-list 重构成 LitElement\n- [x] 抽象出 common-fixed-size-virtual-list-builder 以扩展多种不同滚动策略\n- [ ] fixed-size-virtual-list 需要正确区分 create / visible / hidden / destroy 四种状态\n- [ ] create 与 destroy 发生的时候，如果该元素不在页面中，不应该发生滚动\n- [ ] 实现 `pushCount(newCount:bigint)`, `insertCount(newCount:bigint, refIndex = 0n)`\n- [ ] 实现 `scrollToIndex(index:bigint)`\n- [ ] 支持 `scroll-behavior: smooth`\n- [x] 滚动的平滑衰减应该基于帧时间而不是基于帧数\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbioforestchain%2Fvirtual-scroll","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbioforestchain%2Fvirtual-scroll","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbioforestchain%2Fvirtual-scroll/lists"}