SOURCE

console 命令行工具 X clear

                    
>
console

const list = $('#lst'),
    rows = $('>li', list),    
    rowH = 36,
    bdH = rowH * 5,
    len = rows.length;
let current = 0;

var rad2deg = $.rad2deg = function (rad) {
    return rad / (Math.PI / 180);
};

var deg2rad = $.deg2rad = function (deg) {
    return deg * (Math.PI / 180);
};

const r = Number.parseFloat(bdH/2 - 10),
    d = r*2;

console.log('r',r);

function calcAngle(c) {    
    var a = b = r;
    //直径的整倍数部分直接乘以 180
    c = Math.abs(c); //只算角度不关心正否值
    var intDeg = parseInt(c / d) * 180;
    c = c % d;
    //余弦
    var cosC = (a * a + b * b - c * c) / (2 * a * b);
    var angleC = intDeg + rad2deg(Math.acos(cosC));
    return angleC;
}

const angle =  Number.parseInt(calcAngle(rowH * 0.8));
console.log('angle',angle);
function hilight(index) {
    const row = rows.eq(index);
    list.css({
        'transform': 'perspective(1000px) rotateX(' + (index * angle) + 'deg)',
        'transition': 'all 0.1s ease-out 0s'
    });

    row.addClass('on').siblings('.on').removeClass('on');
    rows.removeClass('vis');

    for (let i = 0; i < 3; i++) {
        const m = i + 1,
            n = index + m,
            p = index - m;

        if (p > -1) {
            rows.eq(index - m).addClass('vis');
        }

        if (n < len) {
            rows.eq(index + m).addClass('vis');
        }
    }
}

function selected(index) {
    current = index;
    hilight(index);
}

rows.each(function () {
    const row = $(this),
        index = row.index();

       

    row.css({
        'transform-origin': 'center center -'+r+'px',
        'transform': 'translateZ('+r+'px) rotateX(-' + (index * angle) + 'deg)'
    });

    //选中
    row.click(function () {
        selected(index);
    });
});
selected(0);

$('#bd').on('mousewheel DOMMouseScroll', function (e) {
    e.preventDefault();
    var og = e.originalEvent,
        sn = og.wheelDelta ? og.wheelDelta / 120 : (og.detail ? -og.detail / 3 : 0);

    let index = current + sn;
    if (index < 0) {
        index = 0;
    }
    if (index > len - 1) {
        index = len - 1;
    }
    selected(index);
});

let moveing = false,
    startY = 0,
    startTime = 0,
    startIndex = 0,
    lastIndex = 0;

list.on('mousedown', function (e) {
    startY = e.originalEvent.pageY;
    startIndex = current;
    startTime = new Date().getTime();
    moveing = true;
}).on('mousemove', function (e) {
    if (moveing) {
        e.preventDefault();
        const d = e.originalEvent.pageY - startY;
        const ct = Math.round(d / rowH);
        let index = 0;

        if (ct < 0) {
            index = startIndex + -1 * ct;
        } else {
            index = startIndex - ct;
        }
        if (index < 0) {
            index = 0;
        }
        if (index > len - 1) {
            index = len - 1;
        }
        lastIndex = index;
        hilight(index);
    }
}).on('mouseup', function (e) {
    const time = new Date().getTime() - startTime;
    if (time > 100) {
        selected(lastIndex);
    } else {
        movestop(e.originalEvent.pageY, time);
    }
    moveing = false;
}).on('mouseleave', function (e) {
    const time = new Date().getTime() - startTime;
    movestop(e.originalEvent.pageY, time);
    moveing = false;
})

function movestop(pageY, time) {
    const d = pageY - startY,
        v = (d / time) * (rowH * 3),
        ct = Math.round(d / rowH);

    let index = 0;

    if (ct < 0) {
        index = startIndex + -1 * ct;
    } else {
        index = startIndex - ct;
    }
    if (index < 0) {
        index = 0;
    }
    if (index > len - 1) {
        index = len - 1;
    }
    selected(index);
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8"> 
</head>
<body>    
    <div id="bd">
        <span></span>        
        <ol id="lst">
            <li><span>这是是第1条</span></li>
            <li><span>第这2条</span></li>
            <li><span>第这是3条</span></li>
            <li><span>第这是4条</span></li>
            <li><span>第这是5条</span></li>
            <li><span>第这是6条</span></li>
            <li><span>第7条</span></li>
            <li><span>第这是8条</span></li>
            <li><span>第这是9条</span></li>
            <li><span>第这是10条</span></li>
            <li><span>第是11条</span></li>
            <li><span>第这是12条</span></li>
            <li><span>第13条</span></li>
            <li><span>第这是14条</span></li>
            <li><span>第这是15条</span></li>
            <li><span>第这是16条</span></li>
            <li><span>第这17条</span></li>
            <li><span>第这是18条</span></li>
            <li><span>第这是19条</span></li>
            <li><span>第这是20条</span></li>
        </ol>
    </div>
</body> 
</html>
*{
    margin: 0;
    padding: 0
}
body{
    font-size: 14px;
    background-color: #fff;
}
#lst{
    user-select: none;
    box-sizing:border-box;
    position: absolute;
    left: 0;
    width: 100%;
    top:50%;
    margin-top: -18px;
    list-style-type: none;
    /* border:solid 1px #eee; */
   transform-style: preserve-3d;
   height: 36px;
   line-height: 36px;
}
#lst>li{
    position: absolute;
    width: 100%;
    height: 100%;
    vertical-align: middle;
    visibility: hidden;    
    text-align: center
}
#lst>li>span{
    color: #898989;    
}
#lst>li.vis{
    visibility:visible;
}
#lst>li.on{
    visibility:visible;
    /* font-weight: bold; */
}
#lst>li.on>span{
    color: #000;
    transition: all .25s ease-in 0s;
    transform: scale(1.5)
}

#bd{
    position: relative;
    float: left;
    width: 150px;
    height: 180px;
    margin-left: 30px;
    border:solid 1px #eee;
    background-color: #fff
}
#bd:after{
   content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        top: 0;
        pointer-events: none;
        background: linear-gradient(#fff, rgba(255, 255, 255, 0) 48%, rgba(255, 255, 255, 0) 52%, #fff);
}
#bd>span{
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    margin-top: -18px;
    height:36px;
    border-top: solid 1px #eee;
    border-bottom: solid 1px #eee;
}

本项目引用的自定义外部资源