You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现无限滚动列表的半圆滚动视觉效果?

问题描述

我有一个列表,初始显示5个项,每次滚动时加载5个新项直到全部内容加载完成。想要实现圆形左半部分的滚动视觉效果,但因为无法控制最终列表项总数,所以打算模拟半圆滚动动画。目前代码存在两个问题:

  1. 滚动时应该显示更多列表项
  2. 上下半部分列表项使用相同计算逻辑,但上半部分位移距离比下半部分大,原因不明

核心需求:滚动时列表项的移动效果模拟处于圆形左半部分的状态。


原代码

HTML

<div class="items-selector">
<div class="list">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>

<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>

CSS

.items-selector {
  overflow-x:hidden;
  overflow-y:auto;
  height:300px;
  width:350px;
}

.list {
  display:flex;
  flex-direction:column;
  gap: 48px;
  align-items:flex-end;
}

.item {
  width:50px;
  height:50px;
  background:#cecece;
  display:flex;
  align-items:center;
  justify-content:center;
  border-radius:100%;
}

JavaScript

const container = document.querySelector(".items-selector");
const yMax = container.clientHeight/2;
const xMax = container.clientWidth-25;

container.addEventListener("scroll", function(e) {

    //must get containers on scroll to retreive current position
    var items = container.querySelectorAll(".item");
    

    for(var i = items.length-1; i >= 0; i--) {
      
        var item = items[i];
          
        var itemTop = item.getBoundingClientRect().top + item.clientHeight/2;
        var yScrollPercentage = (itemTop - yMax)/itemTop;
        var xAxisPercentage = (xMax*yScrollPercentage)/100;
        var xAmoutToRemove = (xMax-(xMax*xAxisPercentage));
        var xMove;
        

        if(itemTop >= yMax ) {
        //When item if on the bottom half of the container
           
            xMove = -1*xAmoutToRemove;  
        }
      
        if(itemTop < yMax) {
        //When item if on the top half of the container

            xMove = -1*xMax + ((-1*xMax) + xAmoutToRemove);  
        } 

        item.style.transform = "translate("+xMove+"px, 0)"
  }

});

问题修复与优化

1. 滚动时显示更多列表项

当前.list的gap设为48px,加上item自身50px高度,单个item占据98px垂直空间,300px容器只能显示3个左右的item。减小gap值(比如改为20px),单个item占70px,容器可显示4-5个item,滚动时也能看到更多内容。

2. 上下半部分位移不一致问题

原计算逻辑中yScrollPercentage的公式(itemTop - yMax)/itemTop存在范围不对称的问题,导致x轴计算偏差。正确做法是基于item中心相对于容器中心的偏移比例,用圆形左半部分的坐标公式计算位移:x = -sqrt(r² - y²)(r为圆半径,即容器半高;y为item中心相对容器中心的垂直偏移)。


修改后的完整代码

HTML(不变)

<div class="items-selector">
<div class="list">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>

<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>

CSS(调整间距并优化过渡)

.items-selector {
  overflow-x:hidden;
  overflow-y:auto;
  height:300px;
  width:350px;
  position: relative;
}

.list {
  display:flex;
  flex-direction:column;
  gap: 20px; /* 减小间距,显示更多项 */
  align-items:flex-end;
  padding: 10px 0; /* 避免item贴边 */
}

.item {
  width:50px;
  height:50px;
  background:#cecece;
  display:flex;
  align-items:center;
  justify-content:center;
  border-radius:100%;
  position: relative;
  transition: transform 0.1s ease; /* 增加平滑滚动过渡 */
}

JavaScript(修正位移计算+滚动加载)

const container = document.querySelector(".items-selector");
const containerRect = container.getBoundingClientRect();
const yCenter = containerRect.top + container.clientHeight / 2; // 容器中心垂直坐标
const radius = container.clientHeight / 2; // 圆半径(容器半高)
const maxXOffset = container.clientWidth - 50; // 最大x偏移量(容器宽度- item宽度)

// 初始化时先计算一次位置
updateItemPositions();

container.addEventListener("scroll", function() {
    updateItemPositions();
    // 滚动加载逻辑
    if (container.scrollTop + container.clientHeight >= container.scrollHeight - 20) {
        const list = document.querySelector(".list");
        // 每次加载5个新项
        for (let i = 0; i < 5; i++) {
            const newItem = document.createElement("div");
            newItem.className = "item";
            list.appendChild(newItem);
        }
        updateItemPositions();
    }
});

function updateItemPositions() {
    const items = container.querySelectorAll(".item");
    
    items.forEach(item => {
        const itemRect = item.getBoundingClientRect();
        const itemCenterY = itemRect.top + item.clientHeight / 2;
        // 计算item中心相对容器中心的垂直偏移
        const yOffset = itemCenterY - yCenter;
        
        // 圆形左半部分x坐标计算,映射到实际偏移范围
        const normalizedX = -Math.sqrt(Math.pow(radius, 2) - Math.pow(yOffset, 2));
        const xMove = (normalizedX / radius) * maxXOffset;
        
        item.style.transform = `translate(${xMove}px, 0)`;
    });
}

内容的提问来源于stack exchange,提问作者Mario Andrade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 22:17:50