jQuery - mousemove 在元素外不起作用

jQuery - mousemove does not work outside of element

我用 mousedownmousemove 为我的 <audio> 元素制作了一个 slider/seeker。我的问题是,一旦用户在按住鼠标按钮的同时离开该元素,它就不会注册 mousemove。这是我的代码:

/** Variables **/

isPlaying = false;
isBuffering = false;
isScrubbing = false;
isScrubberHolding = false;
tempProgress = 0;
time = playerE.currentTime;
dur = playerE.duration;

/** Binds and Properties **/

player.bind("timeupdate", timeUpdate);
scrubber.bind("mousemove", scrubberGrab);
scrubber.bind("mousedown", scrubberClick);

/** Progress and Buffer **/

function progressWidth(progress) {
    var calcProgress = ((progress * 100) + "%");
    $(".progress").width(calcProgress);
}


/** Time events **/

function timeUpdate(e) {

    /** Update Variables **/

    time = playerE.currentTime;
    dur = playerE.duration;

    /** Update Progress and Buffer **/

    if (isScrubbing === false) {
        var progress = time / dur;
    }
    var buffered = playerE.buffered.end(0) / dur;
    timeConvert(time);
    progressWidth(progress);
}

function setPlayerTime(timeset) {
    playerE.currentTime = timeset * dur;
}

function timeConvert(s) {
    var h = Math.floor(s / 3600);
    s -= h * 3600;
    var m = Math.floor(s / 60);
    s -= m * 60;
    var resultSubstring = ((m < 10 ? '0' + m : m) + ":" + (s < 10 ? '0' + s : s)).substring(0, 5);
    $('#playerTime').text(resultSubstring);
}

/** Scrubber **/

$(".player-small").mouseenter(function () {
    knob.stop().fadeIn(200);
});
setTimeout(function () {
    $(".player-small").mouseleave(function () {
        knob.stop().fadeOut(200);
    });
}, 3000);

function scrubberClick(e) {
    isScrubberHolding = true;
    isScrubbing = true;
    player.trigger('pause');
    var $this = $(this);
    var x = e.pageX - $this.offset().left;
    var percent = x / $this.width();
    progressWidth(percent);
    tempProgress = percent;
}

$(document).mouseup(function () {
    if (isScrubberHolding === true) {
        isScrubberHolding = false;
        isScrubbing = false;
        setPlayerTime(tempProgress)
        player.trigger('play');
    } else {
        isScrubberHolding = false;
    }
})

function scrubberGrab(e) {
    if (isScrubberHolding === true) {
        var $this = $(this);
        var x = e.pageX - $this.offset().left;
        var percent = x / $this.width();
        tempProgress = percent;
        progressWidth(percent);
        setPlayerTime(percent)
    } else {}
}

查看实际效果:

var player = $('audio');
var playerE = $('audio')[0];
var playerE = $('audio').get(0);

var canvasviz = $('canvas');

var playbutton = $("#playButton");
var buffering = $("#buffering");
var scrubber = $(".scrubber-con");
var progress = $(".progress");
var buffered = $(".buffered");
var knob = $(".knob");
var analyser = $("#analyzer");
var currentAlbum = "";
var countElement = $('#playlistCount');
var titleElement = $('#trackTitle');

/** Variables **/

isPlaying = false;
isBuffering = false;
isScrubbing = false;
isScrubberHolding = false;
tempProgress = 0;
time = playerE.currentTime;
dur = playerE.duration;

/** Binds and Properties **/

player.bind("timeupdate", timeUpdate);
scrubber.bind("mousemove", scrubberGrab);
scrubber.bind("mousedown", scrubberClick);

/** Progress and Buffer **/

function progressWidth(progress) {
    var calcProgress = ((progress * 100) + "%");
    $(".progress").width(calcProgress);
}


/** Time events **/

function timeUpdate(e) {

    /** Update Variables **/

    time = playerE.currentTime;
    dur = playerE.duration;

    /** Update Progress and Buffer **/

    if (isScrubbing === false) {
        var progress = time / dur;
    }
    var buffered = playerE.buffered.end(0) / dur;
    timeConvert(time);
    progressWidth(progress);
}

function setPlayerTime(timeset) {
    playerE.currentTime = timeset * dur;
}

function timeConvert(s) {
    var h = Math.floor(s / 3600);
    s -= h * 3600;
    var m = Math.floor(s / 60);
    s -= m * 60;
    var resultSubstring = ((m < 10 ? '0' + m : m) + ":" + (s < 10 ? '0' + s : s)).substring(0, 5);
    $('#playerTime').text(resultSubstring);
}

/** Scrubber **/

$(".player-small").mouseenter(function () {
    knob.stop().fadeIn(200);
});
setTimeout(function () {
    $(".player-small").mouseleave(function () {
        knob.stop().fadeOut(200);
    });
}, 3000);

function scrubberClick(e) {
    isScrubberHolding = true;
    isScrubbing = true;
    player.trigger('pause');
    var $this = $(this);
    var x = e.pageX - $this.offset().left;
    var percent = x / $this.width();
    progressWidth(percent);
    tempProgress = percent;
}

$(document).mouseup(function () {
    if (isScrubberHolding === true) {
        isScrubberHolding = false;
        isScrubbing = false;
        setPlayerTime(tempProgress)
        player.trigger('play');
    } else {
        isScrubberHolding = false;
    }
})

function scrubberGrab(e) {
    if (isScrubberHolding === true) {
        var $this = $(this);
        var x = e.pageX - $this.offset().left;
        var percent = x / $this.width();
        tempProgress = percent;
        progressWidth(percent);
        setPlayerTime(percent)
    } else {}
}
.player-small {
    height: 55px;
    width: 100%;
    background: #ff4081;
}

.player-height-anim {}

.player-small .left {
    height: 55px;
    float: left;
    width: 56%;
    overflow: hidden;
}

.player-small .right {
    height: 40px;
    position: relative;
    top: 8px;
    float: right;
    width: calc(44% - 2px);
    overflow: hidden;
    border-left: solid 2px rgba(0, 0, 0, .05);
}

.transport {
    overflow: auto;
}

.play-button-con {
    height: 55px;
    width: 55px;
    float: left;
    overflow: hidden;
}

#buffering {
    height: 55px;
    width: 55px;
    animation: rotating 900ms ease infinite;
    background-image: url(img/player-buffering.svg);
    background-size: contain;
    display: none;
}

@-webkit-keyframes rotating {
    from {
        -ms-transform: rotate(0deg);
        -moz-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    to {
        -ms-transform: rotate(360deg);
        -moz-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@keyframes rotating {
    from {
        -ms-transform: rotate(0deg);
        -moz-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    to {
        -ms-transform: rotate(360deg);
        -moz-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

.rotating {
    -webkit-animation: rotating 2s linear infinite;
    -moz-animation: rotating 2s linear infinite;
    -ms-animation: rotating 2s linear infinite;
    -o-animation: rotating 2s linear infinite;
    animation: rotating 2s linear infinite;
}

#playButton {
    width: 55px;
    height: 55px;
    font-size: 18px;
    text-align: center;
    background-image: url(img/player-play.svg);
    background-size: contain;
    image-rendering: crisp-edges;
    -webkit-image-rendering: crisp-edges;
}

.playFailed {
    pointer-events: none;
}

.next-button-con {
    height: 55px;
    width: 55px;
    float: left;
}

#nextButton {
    width: 55px;
    height: 55px;
    text-align: center;
    font-size: 11px;
    background-image: url(img/player-next.svg);
    background-size: contain;
}

.scrubber-con {
    margin: auto;
    margin-top: 12px;
    height: 30px;
    width: calc(100% - 40px);
    overflow: visible;
    cursor: pointer;
}

.scrubber-container {
    float: left;
    height: 55px;
    width: calc(100% - 154px);
    overflow: hidden;
}

.scrubber {
    margin: auto;
    height: 5px;
    background: rgba(0, 0, 0, .04);
    position: relative;
    top: 13px;
}

.scrubber .knob {
    float: right;
    height: 13px;
    width: 13px;
    position: relative;
    bottom: 4px;
    left: 5px;
    background: white;
    border-radius: 50px;
    display: none;
}

.scrubber .knob:hover {
    cursor: grab;
}

.scrubber .knob:active {
    cursor: grabbing;
}

.scrubber .progress {
    height: 100%;
    float: left;
    background: white;
    width: 0%;
    position: relative;
    z-index: 1;
}

.scrubber .buffered {
    height: 5px;
    position: relative;
    width: 0%;
    background: rgba(0, 0, 0, .2);
    transition: ease 1000ms;
}

.time-con {
    float: left;
    width: 30px;
    height: 55px;
}

.time {
    position: relative;
    top: 20px;
    color: white;
    font-size: 13px;
}

.player-small .button {
    color: white;
    float: left;
    cursor: pointer;
}

.player-small .button:hover {
    background: rgba(0, 0, 0, .12);
}

.analyzer-con {
    float: left;
    position: relative;
    margin-left: 235px;
    width: calc(100% - 650px);
    height: 60px;
}

#analyzer {
    width: 100%;
    height: 45px;
    margin-top: 8px;
    display: none;
}

audio {
    display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="player-small">
                <div class="w-ctrl">
                    <div class="controls">
                        <div class="left">
                            <div class="transport">
                                <div class="play-button-con">
                                    <div class="button playFailed" id="playButton" onclick="togglePlay()">
                                    </div>
                                    <div id="buffering">
                                    </div>
                                </div>
                                <div class="next-button-con">
                                    <div class="button" id="nextButton" onclick="next()"></div>
                                </div>
                                <div class="scrubber-container" nmousedown="event.preventDefault ? event.preventDefault() : event.returnValue = false">
                                    <div class="scrubber-con" nmousedown="event.preventDefault ? event.preventDefault() : event.returnValue = false">
                                        <div class="scrubber" draggable="false" nmousedown="event.preventDefault ? event.preventDefault() : event.returnValue = false">
                                            <div class="progress" draggable="false" onmousedown="event.preventDefault ? event.preventDefault() : event.returnValue = false">
                                                <div class="knob" draggable="false" onmousedown="event.preventDefault ? event.preventDefault() : event.returnValue = false"></div>
                                            </div>
                                            <div class="buffered"></div>
                                        </div>
                                    </div>
                                </div>
                                <div class="time-con">
                                    <div class="time" id="playerTime">0:00</div>
                                </div>
                            </div>
                        </div>
                        <div class="right">
                            <audio id="player" src="your track here" controls="controls" preload="none"></audio>
                            <div class="info">
                                <div class="count" id="playlistCount">0/0</div>
                                <div class="title" id="trackTitle">Track title</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

抓住我的自定义导引头(左)并将鼠标移出粉红色区域。现在对音频元素(右)做同样的事情,你需要一个轨道来播放,以便能够移动它的导引头。看看即使鼠标不在其中也可以拖动它?

那么我怎样才能让我的自定义导引头有这种行为呢?

由于您将 mousemove 绑定到 scrubber,当鼠标悬停在 scrubber 元素上时,scrubberGrab() 只会 运行。

改变

scrubber.bind("mousemove", scrubberGrab);

$(document).bind("mousemove", scrubberGrab);

function scrubberGrab(e) {
    if (isScrubberHolding === true) {
        var x = e.pageX - scrubber.offset().left;
        var percent = Math.min(Math.max(x / scrubber.width(), 0), 1.0);
        tempProgress = percent;
        progressWidth(percent);
        setPlayerTime(percent);
    } else {}
}