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

宝可梦图鉴3D轮播半透明显示效果优化技术求助

宝可梦图鉴3D轮播显示优化问题

我正在开发一个宝可梦图鉴项目,现有的3D轮播功能可以正常运行,但需要优化显示效果:希望页面仅展示中间的主元素、上方1个50%透明度的元素、下方2个50%透明度的元素,其余元素全部隐藏。目前我只能实现隐藏除主元素外的所有内容,求解决方案。

CSS

* { box-sizing: border-box; }

body {
    margin: 0;
    padding: 0;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    background-image: url("../.img/pokedex/pokedex.png");
    background-repeat: no-repeat;
    background-position: 50%;
    background-size: 775px;
}

a, a:visited, a:active {
    color: inherit;
}

.sprite {
    display: block;
    margin-right: 310px;
    position: absolute;
}

.pkmname {
    display: block;
    margin-left: 225px;
    position: absolute;
}

.carousel {
    display: flex;
    align-items: center;
    position: relative;
    padding: 130px 0;
    margin-left: 340px;
    margin-top: 248px;
}

.carousel__scene {
    position: relative;
    width: 510px;
    height: 460px;
    max-width: 95vw;
    padding-top: 60px;
    perspective: 100px;
    /*overflow: hidden;*/
}

.carousel__controls {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 10px;
    position: absolute;
    right: 15px;
    margin-bottom: 190px;
    margin-right: 20px;
}

.previous-button, .next-button {
    margin: 5px 0;
    width: 50px;
    height: 50px;
    font-family: monospace;
    font-weight: bold;
    font-size: 2rem;
    border: 3px solid goldenrod;
    border-radius: 10px;
    background-color: #444;
}

.previous-button:focus, .next-button:focus {
    outline: none;
    box-shadow: 0 0 6px 3px goldenrod;
}

#prev, #next {
    margin-right: 50px;
}

.carousel__list {
    width: 100%;
    height: 150px;
    margin: 0;
    padding: 0;
    position: absolute;
    transform: translateZ(-288px);
    transform-style: preserve-3d;
    transition: transform 1s;
    list-style: none;
    counter-reset: list-counter;
}

.carousel__cell {
    position: absolute;
    width: 400px;
    height: 140px;
    max-width: calc(95vw - 20px);
    left: 10px;
    top: 10px;
    font-size: 20px;
    font-weight: bold;
    color: white;
    text-align: center;
    transition: transform 1s, opacity 1s;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    counter-increment: list-counter;
    background-image: url("../.img/pokedex/unknown.png");
    background-repeat: no-repeat;
    background-position: center;
}

.carousel__cell:before {
    content: counter(list-counter);
    color: white;
    text-shadow: 2px 2px black;
    width: 40px;
    height: 40px;
    line-height: 40px;
}

.carousel__cell.selected {
    color: goldenrod;
    border-color: goldenrod;
    border-width: 5px;
    /*filter: none;*/
}

.carousel__cell.before {
    opacity: 100%;
}

.carousel__cell.after {
    opacity: 50%;
}

.carousel__cell.hide {
    opacity: 0%;
}

.carousel__cell.selected::before {
    color: goldenrod;
}

.carousel__cell:nth-child(0) { transform: rotateX(0deg) translateZ(288px); }
.carousel__cell:nth-child(1) { transform: rotateX(40deg) translateZ(288px); }
.carousel__cell:nth-child(2) { transform: rotateX(80deg) translateZ(288px); }
.carousel__cell:nth-child(3) { transform: rotateX(120deg) translateZ(288px); }
.carousel__cell:nth-child(4) { transform: rotateX(160deg) translateZ(288px); }
.carousel__cell:nth-child(5) { transform: rotateX(200deg) translateZ(288px); }
.carousel__cell:nth-child(6) { transform: rotateX(240deg) translateZ(288px); }
.carousel__cell:nth-child(7) { transform: rotateX(280deg) translateZ(288px); }
.carousel__cell:nth-child(8) { transform: rotateX(320deg) translateZ(288px); }

HTML

<div class="carousel">
            <div class="carousel__scene">
                <ol class="carousel__list">
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0001-bulbasaur.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_001.png" alt="Bulbasaur"/><span class="pkmname">Bulbasaur</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0002-ivysaur.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_002.png" alt="Ivysaur"/><span class="pkmname">Ivysaur</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0003-venusaur.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_003.png" alt="Venusaur"/><span class="pkmname">Venusaur</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0004-charmander.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_004.png" alt="Charmander"/><span class="pkmname">Charmander</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0005-charmeleon.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_005.png" alt="Charmeleon"/><span class="pkmname">Charmeleon</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0006-charizard.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_006.png" alt="Charizard"/><span class="pkmname">Charizard</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0007-squirtle.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_007.png" alt="Squirtle"/><span class="pkmname">Squirtle</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0008-wartorle.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_008.png" alt="Wartortle"/><span class="pkmname">Wartortle</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0009-blastoise.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_009.png" alt="Blastoise"/><span class="pkmname">Blastoise</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0010-caterpie.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_010.png" alt="Caterpie"/><span class="pkmname">Caterpie</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0011-metapod.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_011.png" alt="Metapod"/><span class="pkmname">Metapod</span></li>
                    <li class="carousel__cell" onClick="location.href='../../dexentries/0012-butterfree.html'"><img class="sprite" src="../../../.img/game-sprites/gen1/spr_rb-gb_012.png" alt="Butterfree"/><span class="pkmname">Butterfree</span></li>
                </ol>
            </div>
            <div class="carousel__controls">
                <button class="previous-button" style="background: url('../../../.img/pokedex/prev.png'); background-repeat: no-repeat; background-position: center;" onClick="plusSlides(-1)"></button>
                <button class="next-button" style="background: url('../../../.img/pokedex/next.png'); background-repeat: no-repeat; background-position: center;" onClick="plusSlides(1)"></button>
            </div>
            <script type="text/javascript" src="../../../.js/pokedex.js"></script>
</div>

JavaScript

// JavaScript Document

const carousel = document.querySelector('.carousel__list');
const cells = carousel.querySelectorAll('.carousel__cell');

const cellWidth = carousel.offsetWidth;
const cellHeight = carousel.offsetHeight;
const cellSize = cellHeight;
const cellCount = 151;

const radius = Math.round((cellSize / 2) / Math.tan(Math.PI / cellCount));
const theta = 360 / cellCount;

var selectedIndex = 0;

function rotateCarousel() {
    const angle = theta * selectedIndex * -1;
    carousel.style.transform = 'translateZ(' + -radius + 'px) ' + 'rotateX(' + -angle + 'deg)';
    
    const cellIndex = selectedIndex < 0 ? (cellCount - ((selectedIndex * -1) % cellCount)) : (selectedIndex % cellCount);
    
    const cells = document.querySelectorAll('.carousel__cell');
    cells.forEach((cell, index) => {
        if(cellIndex === index) {
            if(!cell.classList.contains('selected'))
                cell.classList.add('selected');
        }
        else {
            if(cell.classList.contains('selected')) {
                cell.classList.remove('selected');
            }
        }
    });
}

function selectPrev() {
    selectedIndex--;
    rotateCarousel();    
}

function selectNext() {
    selectedIndex++;
    rotateCarousel();    
}

var prevButton = document.querySelector('.previous-button');
prevButton.addEventListener('click', selectPrev);

var nextButton = document.querySelector('.next-button');
nextButton.addEventListener('click', selectNext);

function initCarousel() {    
    for(let i = 0; i < cells.length; i++) {
        const cell = cells[i];
        const cellAngle = theta * i;
        cell.style.transform = 'rotateX(' + -cellAngle + 'deg) translateZ(' + radius + 'px)';
    }
    rotateCarousel();
}

initCarousel();

解决方案

核心是根据每个元素与当前选中元素的索引位置关系,动态控制透明度:

  1. 调整CSS规则
    移除全局设置非选中元素透明度的代码,避免干扰动态逻辑:
/* 删除这条规则 */
.carousel__cell:not(.selected) {
    opacity: 50%;
}
  1. 重写rotateCarousel函数
    更新JavaScript中的rotateCarousel函数,添加位置判断逻辑:
function rotateCarousel() {
    const angle = theta * selectedIndex * -1;
    carousel.style.transform = 'translateZ(' + -radius + 'px) ' + 'rotateX(' + -angle + 'deg)';
    
    const cellIndex = selectedIndex < 0 ? (cellCount - ((selectedIndex * -1) % cellCount)) : (selectedIndex % cellCount);
    const totalCells = cells.length;
    
    cells.forEach((cell, index) => {
        // 计算相对位置,处理循环轮播的边界情况
        const relativePos = (index - cellIndex + totalCells) % totalCells;
        
        // 重置所有状态类
        cell.classList.remove('selected', 'show-half', 'hide');
        
        if (relativePos === 0) {
            // 中间主元素:100%透明度
            cell.classList.add('selected');
            cell.style.opacity = '1';
        } else if (relativePos === 1 || relativePos === 2) {
            // 下方2个元素:50%透明度
            cell.classList.add('show-half');
            cell.style.opacity = '0.5';
        } else if (relativePos === totalCells - 1) {
            // 上方1个元素:50%透明度
            cell.classList.add('show-half');
            cell.style.opacity = '0.5';
        } else {
            // 其余元素:完全隐藏
            cell.classList.add('hide');
            cell.style.opacity = '0';
        }
    });
}
  1. 确保过渡效果平滑
    确认.carousel__cell的过渡属性包含opacity,保证透明度变化有动画:
.carousel__cell {
    transition: transform 1s, opacity 1s;
}

修改后,轮播时只会显示选中的主元素、上方1个半透明元素、下方2个半透明元素,其余元素完全隐藏,符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 06:48:19