做项目的时候要用到手机联系人列表的功能,没弄过,找了几仿的效果也并不是很好,没有原版手机联系人效果不能滑动不能顶部顶上去,想想用别人的代码,还不如自己写着痛快。想着就从QQ音乐的数据抓去歌手数据排行。我们常见的手机联系人信息是按字母顺序排列的列表,通过点击右侧的字母,会迅速定位检索到首字母对应的联系人,还可以根据右侧字母滑动检测到左边的首字母联系人,在向上滑动时可以明显观察到左侧字母向上顶的效果。综上那么多,这次就是来做这个列表吧


<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="common.css">
    <link rel="stylesheet" href="reset.css">
    <title>Document</title>
    <style>
        .listView{
            position: relative;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background: #222
        }
        .listView .list-group{
            padding-bottom: 30px
        }
        .listView .list-group .list-group-title{
            height: 30px;
            line-height: 30px;
            padding-left: 20px;
            font-size: 12px;
            color: rgba(255, 255, 255, 0.5);
            background: #333;
        }
        .listView .list-group .list-group-item{
            display: flex;
            align-items: center;
            padding: 20px 0 0 30px;
        }
        .listView .list-group .list-group-item .avatar{
            width: 50px;
            height: 50px;
            border-radius: 50%;
        }
        .listView .list-group .list-group-item .name{
            margin-left: 20px;
            color: rgba(255, 255, 255, 0.5);
            font-size: 14px;
        }

        .listView .list-shortcut{
            position: absolute;
            z-index: 30;
            right: 0;
            top: 50%;
            transform: translateY(-50%);
            width: 20px;
            padding: 20px 0;
            border-radius: 10px;
            text-align: center;
            background: rgba(0, 0, 0, 0.3);
            font-family: Helvetica;
        }
        .listView .list-shortcut .item{
            padding: 3px;
            line-height: 1;
            color: rgba(255, 255, 255, 0.5);
            font-size: 12px;
        }
        .listView .list-shortcut .item.current{
            color: #ffcd32
        }

        .listView .list-fixed{
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
        }
        .listView .list-fixed .fixed-title{
            height: 30px;
            line-height: 30px;
            padding-left: 20px;
            font-size: 12px;
            color: rgba(255, 255, 255, 0.5);
            background: #333;
        }

    </style>

</head>
<body>
    <div class="listView" id="listView">
        <ul id="listWrapper"></ul>
        <script id="listWrapperBox" type="text/doTtemplate">
            {{for(var i in it) {}}
                <li class="list-group">
                    <h2 class="list-group-title">{{=it[i].title}}</h2>
                    <ul>
                        {{for(var j in it[i].items) {}}
                            <li class="list-group-item">
                                <img src="{{=it[i].items[j].avatar}}" class="avatar">
                                <span class="name">{{=it[i].items[j].name}}</span>
                            </li>
                        {{ } }}
                    </ul>
                </li>
            {{ } }}
        </script>
        <div class="list-shortcut" id="list-shortcut">
            <ul id="shortcutWrapper"></ul>
            <script id="shortcutWrapperBox" type="text/doTtemplate">
                {{for(var i in it) {}}
                    <li class="item" data-index="{{=[i]}}">{{=it[i].title.substr(0, 1)}}</li>
                {{ } }}
            </script>
        </div>
        <div class="list-fixed" id="list-fixed">
            <h1 class="fixed-title" id="fixed-title"></h1>
        </div>
    </div>
<script src="bscroll.js"></script>
<script src="jquery-3.1.1.min.js"></script>
<script src="doT.min.js"></script>
<script>
    // 定义一个常量
    var HOT_NAME = '热门';
    var HOT_LEN = 10;
    // 定义全局 touch 来共享 touchstart 和 touchmove 的 pageY 值和 anchorIndex
    var touch = {};
    // 定义每个 shortcut 的高度
    var ANCHOR_HEIGHT= 18;
    // 滚动的高度和单个区间距离顶部的适口的高度差
    var diff = -1;
    // list-fixed 高度
    var TITLE_HEIGHT = 30;

    // 文档初始化
    $( document ).ready( readyHandler );

    // 请求列表数据
    function readyHandler() {
        $.post( "data.json", null, loadHandler, "json" );
    }

    /**
     * 1. 数据列表加载完成
     * @param data
     *
     * 2. mock QQ音乐的数据,抓过来的数据不是想要的数据,没有改,只能手动更改自己想要的数据结构
     *    最后拿到用的数据是 dataList
     *    可以打印看下 dataList 内的数据格式和最初的数据 data 比较
     */
    function loadHandler( data ) {

        //console.log( data );
        var list = data.list;
        var map = {
            hot: {
                title: HOT_NAME,
                items: []
            }
        };

        data.list.forEach(function (item, index) {
            // 判断是否索引是否小于 10条,就添加到热门数据内
            if (index < HOT_LEN) {
                map.hot.items.push({
                    id: item.Fsinger_mid,
                    name: item.Fsinger_name,
                    avatar : 'https://y.gtimg.cn/music/photo_new/T001R150x150M000'+ item.Fsinger_mid +'.jpg?max_age=2592000'
                })
            }
            // 根据Findex 做一个聚类,判断map 数组内是否有key,没有就赋值
            var key = item.Findex;
            if( !map[key] ){
                map[key] = {
                    title: key,
                    items: []
                }
            }
            map[key].items.push({
                id: item.Fsinger_mid,
                name: item.Fsinger_name,
                avatar : 'https://y.gtimg.cn/music/photo_new/T001R150x150M000'+ item.Fsinger_mid +'.jpg?max_age=2592000'
            })
        });

        var hot = [];
        var ret = [];
        for(var key in map){
            var val = map[key];
            // 正则校验 检测从字母 a 到 z,逐个添加到 ret 数组内
            if(val.title.match(/[a-zA-Z]/)){
                ret.push(val)
            // 如果title 是 热门,就添加到 hot 数组内
            }else if(val.title === HOT_NAME){
                hot.push(val)
            }
        }
        // 数组 ret 的排序,如果相减大于 0 就为 true,这样就可以拿到 a到z 的顺序
        ret.sort(function(a, b) {
            return a.title.charCodeAt(0) - b.title.charCodeAt(0)
        });
        // 合并的数组就是有序排列数据,可以用作循环遍历
        var dataList =  hot.concat(ret);
        setDataList(dataList)
    }

    function setDataList(dataList) {
        console.log(dataList);
        /*
        * 1. 先填充数据,然后再判断 listWrapper 是否有内容,有内容就实例 BScroll
        * 2. 监听右侧 list-short 的touchstart 事件,点击跳到左侧对应的列表页
        * 3. 通过全局存储的 y2 和 y1相减,来确定 touchmove 在 list-shortcut 的移动的距离
        * 4. 通过BScroll 提供的方法 scrollToElement 滚动到相应的位置
        * 5. 滚动监测右侧shortcut 联动效果
        * 6. 设置滚动文字顶上,通过控制 dom 的偏移来做效果
        */
        var listWrapperBoxTpl,shortcutWrapperBoxTpl,listView;

        listWrapperBoxTpl = doT.template(document.getElementById('listWrapperBox').innerHTML);
        document.getElementById("listWrapper").innerHTML = listWrapperBoxTpl(dataList);

        shortcutWrapperBoxTpl = doT.template(document.getElementById('shortcutWrapperBox').innerHTML);
        document.getElementById("shortcutWrapper").innerHTML = shortcutWrapperBoxTpl(dataList);

        if($("#listWrapper").html()) {
            listView = new BScroll(document.getElementById('listView'), {
                probeType: 3
            });
            $("#fixed-title").html('热门');
        }

        document.getElementById("list-shortcut").addEventListener('touchstart', function (e) {
            //console.log(e);
            $(e.target).addClass("current").siblings().removeClass("current");

            var anchorIndex = e.target.attributes[1].value;

            // 给touch 添加一个 y1 属性
            var firstTouch = e.touches[0];
            touch.y1 = firstTouch.pageY;
            touch.anchorIndex = anchorIndex;

            listView.scrollToElement(document.getElementsByClassName("list-group")[anchorIndex], 0)
        });

        document.getElementById("list-shortcut").addEventListener('touchmove', function (e) {
            // 阻止浏览器的默认行为
            e.preventDefault;

            // 给touch 添加一个 y2 属性
            var firstTouch = e.touches[0];
            touch.y2 = firstTouch.pageY;
            // 除以 18 再向下取整
            var delta = (touch.y2 - touch.y1) / ANCHOR_HEIGHT | 0;
            // 拿到上面的 anchorIndex 是个字符串
            var anchorIndex = parseInt(touch.anchorIndex) + delta;

            listView.scrollToElement(document.getElementsByClassName("list-group")[anchorIndex], 0);

            $(document.getElementsByClassName("item")[anchorIndex]).addClass("current").siblings().removeClass("current");
        });

        var listGroup = document.getElementsByClassName("list-group");
        var height = 0;
        var listHeight = [];
        listHeight.push(height);

        for (var i = 0; i < listGroup.length; i++) {
            var item = listGroup[i];
            height += item.clientHeight;

            listHeight.push(height);
        }

        listView.on('scroll', function (pos) {
            // 获取滚动的高度,默认的向下滚动是 负值
            var upScrollY = Math.round(pos.y);
            // 获取滚动的高度,加绝对值
            var scrollY = Math.abs(Math.round(pos.y));

            for(var i = 0; i < listHeight.length; i++){
                var height1 = listHeight[i];
                var height2 = listHeight[i + 1];
                if ((scrollY >= height1 && scrollY < height2)) {
                    // 高度差
                    diff = height2 - scrollY;

                    $($(".item")[i]).addClass("current").siblings().removeClass("current");

                    // 判断如果是向下拖拽滚动,就隐藏 list-fixed
                    if(upScrollY < 0){
                        // 通过滚动添加文字
                        $("#fixed-title").html(dataList[i].title);
                        $("#list-fixed").css("display", "block")
                    }else {
                        $("#list-fixed").css("display", "none")
                    }
                }
            }

            // console.log(diff);
            /*
            *  设置 list-fixed 的属性改变位置来做顶部向上偏移
            */
            var fixedTop = (diff > 0 && diff < TITLE_HEIGHT) ? diff - TITLE_HEIGHT : 0;
            document.getElementById('list-fixed').style.transform = 'translate3d(0,'+ fixedTop +'px,0)'
        });
        // 给第一个添加默认点击
        $("#shortcutWrapper").children().first().addClass("current")
    }

</script>
</body>
</html>

猛戳这里:快速预览