为页面实现真·自定义形状的滚动条

webkit支持对页面滑轮和滑道的自定义,但是那种自定义是有限的。比如:如果你想让滚轮滑道与页面的背景图片融合是不可能的,也不能让滑块有自行义的形状。

所以咱前一阵子强迫症爆发,在自己的应用中重新写了个滑道,案例的话请参考:https://github.com/DFLS/DAdolfans

自备less编译器,源码里的less浏览器nw是读不了的。

之后说一下我做的设计视觉效果大概是怎么样的:

鼠标滑到页面右边缘的时候会出现滑块,拖动滑块页面就会滚。

注:再次强调此方法仅在webkit内核浏览器下生效,拿来实现个node-webkit的应用是可行的,不过别想着在页面里做……FF用户和IE用户会疯掉。

注2:本页面只讲纵向滚轮实现,一般页面也不会有奇葩的横向滚动需求,真有这方面需求自己看着代码改就行了。

在开始写自定义形状的滚轮前咱要提醒乃个事情,node-webkit的window API和所有window有关的DOM API执行都卡的一笔,想去实现个动画只有卡到不行一条路,所以我们在这里选择给页面做个warp,height和width都是100%,overflow设置成scroll-y:

overflow-y: scroll;

之后把这个warp的原生滚动条K掉:

#balabalabala::-webkit-scrollbar {
    width: 0px;
}

ヾ(:3ノシヾ)ノシ 我是好好起名字会死星人。

接下来让我们做个热区,当鼠标滑动到热区内的时候显示滑块。按照我之前提的需求构建下滑块的HTML代码:

<div id="navigator_hotarea">
    <div id="navigator_arror"></div>
</div>

其中#navigator_hotarea是热区的名字,鼠标滑到热区上的时候就会显示指针,#navigator_arror就是那个指针。
看下CSS代码:

#navigator_hotarea{
    top:50px;
    right:0;
    width:34px;
    height:-webkit-calc(100% - 50px);
    position:fixed;
    z-index:7;

    #navigator_arror{
        top:50px;
        right:0;
        width:34px;
        height:28px;
        margin-top:-14px;
        background:url(../image/arrow.png);
        position:fixed;
        opacity: 0;
        transition-property:opacity,right;
        transition-duration:0.2s;
        transition-timing-function:ease-in-out;
    }
}

指针的图参见附件。

解释下这里面的margin-top:-14px是干啥,老思路,让y方向的容器原点变成指针的中心,省得一会做js的时候再算了。

别的都没啥难的,看看就好。

下一步开始写jS,先讲思路:

当鼠标滑入热区的时候要显示指针,同时指针要准确的指出页面的位置,滑道的高度是一定的,页面的高度是会变的。所以我们需要计算出来一个百分比,算出页面滚动了百分之多少。

算法如下:

$("#main_area").scrollTop() / ($("#main_area")[0].scrollHeight - $("#main_area").height())

这里是一道数学题,咱高考数学92,擦边过,所以……恩……,数学计算部分就不讲了……

这里用到了一个比较神棍的东西:JS和JQ的混用:

scrollHeight是JS的方法,直接在jQ选择器上用会报错的,所以要JS和JQ混用要在JQ的选择器后面加个[0]。

页面滚动百分比拿到之后去算指针的位置:

(($("#navigator_hotarea").height() - 50) * mainAreaScrollPositionPercent) + 50;

第一个减50像素是我想让滑轮距离页面底部有50px的位置,第二个50滑道距离页面顶部有50px的距离。

然后这部分的完整代码就出来了,是这样的:

$("#navigator_hotarea").on("mouseover", function() {
var mainAreaScrollPositionPercent = $("#main_area").scrollTop() / ($("#main_area")[0].scrollHeight - $(document).height());
var arrorPosition = (($("#navigator_hotarea").height() - 50) * mainAreaScrollPositionPercent) + 50;
$("#navigator_arror").css({"top": arrorPosition + "px", "opacity": "1", "right": "5px"});
//(σ′▽‵)′▽‵)σ我是长变量名星人
});

回调函数里最后一行是控制位置,顺便显示个动画,聪明的你一定看得懂ヽ(✿゚▽゚)ノ。

然后当鼠标从热区离开的时候我们的指针需要消失,没啥技术含量,上代码:

$("#navigator_hotarea").on("mouseleave", function() {
    $("#navigator_arror").css({"opacity": "0", "right": "0px"});
});

接下来是第二部分:拖拽滚轮的时候页面滚动:

先来做一个能拖动的指针:

$("#navigator_arror").on("mousedown", function() {
    $(document).on("mousemove", function(evt) {
        screenPosition.y = evt.pageY;
        if (screenPosition.y < 50)
            arrowY = 50;
        else if (screenPosition.y > $("#navigator_hotarea").height())
            arrorY = $("#navigator_hotarea").height();
        else
            arrowY = screenPosition.y;
        $("#navigator_arror").css("top", arrowY + "px");

        //  FLAG

    });
});

也没啥解释的,当按下指针的时候监听鼠标移动的事件。

鼠标移动的事件里有个回调,pageY,就是你的鼠标相对页面左上角的坐标,为了不让你的指针跑出滑道用了两个if进行判断。当Y小于50的时候把指针限制在50,大于滑道宽的时候指针会被限制在滑道底部。

接下来计算你的指针位置,之后让页面也跟着跑。

FLAG位置插入下面的代码:

var arrorPositionPercent = ($("#navigator_arror").position().top - 50) / ($("#navigator_hotarea").height() - 50);
var arrorScrollPosition = ($("#main_area")[0].scrollHeight - $(document).height()) * arrorPositionPercent;
$("#main_area").scrollTop(arrorScrollPosition);

这样页面就会跟着滚了。

然后当鼠标松开的时候我们不能再让指针跟着我们跑了对吧,用这个:

$(document).on("mouseup", function() {
    $(document).off("mousemove");
    $("#navigator_arror").css({"opacity": "0", "right": "0"});
});

写到这里你会发现个问题,当你拖动滑块的时候鼠标移出了热区小箭头就会消失,这是个bug,怎么解决呢?

增加一个arrorDraging的变量就好了,用于判断我是不是在拖动这个指针是否在拖动,如果是拖动的,鼠标移出热区就不隐藏这个指针。

完整代码是这样的:

//绑定滑入热区事件
$("#navigator_hotarea").on("mouseover", function() {
    var mainAreaScrollPositionPercent = $("#main_area").scrollTop() / ($("#main_area")[0].scrollHeight - $(document).height());
    var arrorPosition = (($("#navigator_hotarea").height() - 50) * mainAreaScrollPositionPercent) + 50;
    $("#navigator_arror").css({"top": arrorPosition + "px", "opacity": "1", "right": "5px"});
});

$("#navigator_hotarea").on("mouseleave", function() {
    if (!arrorDraging)
        $("#navigator_arror").css({"opacity": "0", "right": "0px"});
});

//这俩是滚针事件
$("#navigator_arror").on("mousedown", function() {
    arrorDraging = true;
    $(document).on("mousemove", function(evt) {
        screenPosition.y = evt.pageY;
        if (screenPosition.y < 50)
            arrowY = 50;
        else if (screenPosition.y > $("#navigator_hotarea").height())
            arrorY = $("#navigator_hotarea").height();
        else
            arrowY = screenPosition.y;
        var arrorPositionPercent = ($("#navigator_arror").position().top - 50) / ($("#navigator_hotarea").height() - 50);
        var arrorScrollPosition = ($("#main_area")[0].scrollHeight - $(document).height()) * arrorPositionPercent;
        $("#navigator_arror").css("top", arrowY + "px");
        $("#main_area").scrollTop(arrorScrollPosition);
    });
});

$(document).on("mouseup", function() {
    arrorDraging = false;
    $(document).off("mousemove");
    $("#navigator_arror").css({"opacity": "0", "right": "0"});

});

怎么样,很简单吧(∫・ω・)∫