亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

商城“智能”導(dǎo)航欄實踐

nifhlheimr / 2968人閱讀

摘要:細心的用戶可能會發(fā)現(xiàn),在或者等大型網(wǎng)站中,當鼠標在一級導(dǎo)航欄中垂直移動時,二級菜單可以無延遲的響應(yīng)展示。很顯然,用戶希望在選擇某一級菜單下的子菜單時,想要以斜向最短路徑移動鼠標,而其他掠過的一級菜單也并不會激活。

需求與目標

在電商的大屏主頁上,一般都會有一個顯眼的品類導(dǎo)航欄,作為整個商城的重要分流入口,客戶體驗就必須要做到自然、極致。細心的用戶可能會發(fā)現(xiàn),在jd.com或者tmall.com等大型網(wǎng)站中,當鼠標在一級導(dǎo)航欄中垂直移動時,二級菜單可以無延遲的響應(yīng)展示。神奇的是,當用戶將鼠標懸浮在某一級菜單,想去點擊對應(yīng)的二級菜單區(qū)域時,即使這時鼠標掠過其他一級菜單,也并沒有切換到其他二級菜單,似乎這樣的菜單欄很懂你,可以準確預(yù)測到你的行為,高大上的叫法是基于用戶行為預(yù)測的切換技術(shù),我稱之為“智能”導(dǎo)航欄,效果如下。

在動手實踐之前,我們再來明確一下目標效果:

鼠標正常切換一級菜單時,二級菜單無延遲響應(yīng);

鼠標快速移動到二級子菜單時,要求一級菜單無冗余切換;

知識準備

先來把需要用到的知識點劃出來。如果完成這樣一個小的需求,還能把輻射出的知識點都搞清楚,做到查漏補缺,再把相同的技術(shù)衍生到其他的場景,舉一反三,那么這樣的實踐才是充分的、有價值的。

事件代理與事件委托;

mouseenter和mouseover的區(qū)別;

debounce(防抖)和throttle(節(jié)流);

用向量叉乘判斷點在三角形內(nèi);(本實踐中選擇算法4,用叉乘符號相同判斷)

如何高效判斷兩個數(shù)字符號異同;

h5語義化標簽--dl dt dd標簽元素的語法結(jié)構(gòu)與使用;

對于以上我梳理出來的的知識點,其中第2、第5、第6點比較簡單,幾句話就可以說清楚,其余三點拿出一條就可以端端正正的寫出一篇文章,所以我已把我私藏的優(yōu)質(zhì)鏈接附上,如果你對于某些點比較模糊,請點擊跳轉(zhuǎn)學(xué)習(xí)。

實踐講解

我會采用漸進增強的方式來進行講解,完整的示例代碼請進codepen。

基礎(chǔ)實現(xiàn)

首先對于文檔結(jié)構(gòu),遵循語義化的原則,左側(cè)的一級菜單用ul li組合.

  • 一級導(dǎo)航1
  • 一級導(dǎo)航2
  • ···

右側(cè)的子菜單,用dl dt dd標簽來表達,因為他們最常用在一個標題下有若干對應(yīng)列表項的菜單場景。如需進一步了解請點擊。

接下來,添加js交互。通過鼠標在左側(cè)不同li的懸浮,來激活顯示右側(cè)不同的.sub_content塊,其中通過一級菜單的data-id屬性與其id值作為鉤子來進行聯(lián)動。

這里我們遇到選擇綁定mouseenter還是mouseover事件,其二者的區(qū)別可概括為:


使用mouseover/mouseout時,在鼠標指針經(jīng)過綁定元素或者經(jīng)過任何其子元素時,都會觸發(fā) mouseover 事件。如果鼠標移動到其子元素上,而沒有離開綁定元素,也會觸發(fā)綁定元素的mouseout事件;

使用mouseenter/mouseleave時,只有在鼠標指針經(jīng)過綁定元素時(不包括鼠標指針經(jīng)過任何子元素),才會觸發(fā)

mouseenter 事件。如果鼠標沒有離開綁定元素,在其子元素上任意移動,也不會觸發(fā)mouseleave事件;

為了助于理解,我做了一個示例,請參考mouseenter/mouseover。

通過比較,顯然我們只需要給各li綁定mouseenter/mouseout事件即可。

var sub = $("#sub"); // 子級菜單包裹層
var activeRow, //  已激活的一級菜單
    activeMenu; //  已激活的子級菜單
    
$("#wrap").on("mouseenter", function() {
    // 顯示子菜單
    sub.removeClass("none");
})
.on("mouseleave", function() {
    // 隱藏子菜單
    sub.addClass("none");
    // 重置兩個已激活變量
    if (activeRow) {
        activeRow.removeClass("active");
        activeRow = null;
    }
    if (activeMenu) {
        activeMenu.addClass("none");
        activeMenu = null;
    }
})
.on("mouseenter", "li", function(e) {
    if (!activeRow) {
        activeRow = $(e.target).addClass("active");
    activeMenu = $("#" + activeRow.data("id"));
        activeMenu.removeClass("none");
        return;
    }
    // 若有已激活菜單,先還原之
    activeRow.removeClass("active");
    activeMenu.addClass("none");

    activeRow = $(e.target);
    activeRow.addClass("active");
    activeMenu = $("#" + activeRow.data("id"));
    activeMenu.removeClass("none");
});

以上便實現(xiàn)了基本效果,需要注意的是,在知識準備一節(jié)中所提到的事件代理的運用,是優(yōu)化DOM性能的一種很好的實踐,同時寫法又不失優(yōu)雅。

然而這個版本在體驗上是有問題的,用戶為了選擇子菜單,必須要謹慎的讓鼠標在當前所選一級菜單的范圍內(nèi),以折線路徑移動到子菜單,才可以進一步選擇,如下圖。

很顯然,用戶希望在選擇某一級菜單下的子菜單時,想要以斜向最短路徑移動鼠標,而其他掠過的一級菜單也并不會激活。下面我們來對此做出改進。

解決斜向移動問題

當鼠標移動時,頻繁的觸發(fā)每一個一級菜單所綁定的mouseenter事件是問題的關(guān)鍵。因此我們很自然的想到延時觸發(fā),又為避免頻繁觸發(fā),引入防抖/節(jié)流。每次觸發(fā)一級菜單時,并不讓他立即執(zhí)行展示子菜單的邏輯,而是延后300ms,直到最后一次觸發(fā)后300ms,判斷鼠標的位置是否在子菜單區(qū)域內(nèi),如果在,便可直接return不做任何切換菜單操作,如下。

.on("mouseenter", "li", function(e) {
    if (!activeRow) {
        active(e.target);// 一個激活對應(yīng)子菜單的函數(shù)
        return;
    }
    if (timer) {
        clearTimeout(timer);
    }

    timer = setTimeout(function() {
        if (mouseInSub) {
            return;
        }
        activeRow.removeClass("active");
        activeMenu.addClass("none");
        active(e.target);

        timer = null;
    }, 300);
});

由此,因為每一次切換一級菜單,都會有一個延遲300ms觸發(fā)的效果,所以當用戶在一級菜單區(qū)域中上下移動時,或者真的想去快速切換菜單時,這樣粗糙的延時處理在解決了斜向移動的問題后,又引入了新的問題,如下圖。

那如何做到當用戶真的想要快速切換一級菜單時,子級菜單快速響應(yīng),而只有當用戶想去選擇子級菜單時,才會去運用延時觸發(fā),進而可以斜向移動。至此,如果你的知識領(lǐng)域只局限于編程或者計算機科學(xué),那么要解決這個問題著實困難。這里我們需要些跨學(xué)科的啟發(fā)式思維,根據(jù)用戶行為抽象出一個數(shù)學(xué)模型,進而實現(xiàn)對于用戶切換菜單的預(yù)測。

進一步改善

事實上,我們可以根據(jù)用戶鼠標的移動軌跡抽象出這樣一個三角形(如下圖),構(gòu)成它的三個點分別是,子級菜單容器的左上頂點(top),及其左下頂點(bottom),另外一個是用戶鼠標剛剛移動經(jīng)過的點(pre)。處在三角形內(nèi)的cur點代表用戶鼠標當前的位置。其中pre和cur之間的距離取決于鼠標移動每次觸發(fā)mousemove事件的粒度,通常會很短很短,這里圖例為了方便觀察,做了合理放大。

這樣的一個三角形有何意義呢?在通常的用戶行為中,我們是否可以認為當鼠標在三角形內(nèi)時,便可以判定用戶有選擇子級菜單的傾向,當鼠標在三角形外時,此時用戶更傾向于快速切換一級菜單。這樣在用戶不斷的移動鼠標時,也同時會不斷的形成多個這樣的三角形,此時,解決問題的突破口就轉(zhuǎn)化成,不斷監(jiān)聽鼠標位置,并判斷當前點是否在剛剛經(jīng)過的點和子級菜單左側(cè)上下兩頂點所形成的三角形中。

不斷監(jiān)聽鼠標位置,我們可以通過mousemove輕松解決,只需要注意綁定和解綁的時機,讓其只在菜單范圍內(nèi)觸發(fā),因為持續(xù)的監(jiān)聽與觸發(fā)對于瀏覽器來講開銷不小。而判斷一個點是否在一個三角形內(nèi),這個問題需要用到知識準備一節(jié)中的第四點,我們選擇用向量叉乘符號相同來判斷一個點在一個三角形中。至于數(shù)學(xué)上的證明,不在本文討論范圍內(nèi),此處我們只需要知道該結(jié)論是嚴密的即可。

接下來我們用代碼來模擬實現(xiàn)向量及其叉乘:

// 向量是終點坐標減去起點坐標
function vector(a, b) {
    return {
        x: b.x - a.x,
        y: b.y - a.y
    }
}

// 向量的叉乘
function vectorPro(v1, v2) {
    return v1.x * v2.y - v1.y * v2.x;
}

然后我們利用上邊的兩個輔助函數(shù)來判斷一個點是否在某個三角形內(nèi),函數(shù)的入?yún)⑹撬膫€已知的點,最終返回的結(jié)果是,所形成的三個向量叉乘后是否兩兩符號相同,相同即點在三角形內(nèi),反之亦反。

// 判斷點是否在三角形內(nèi)
function isPointInTranjgle(p, a, b, c) {
    var pa = vector(p, a);
    var pb = vector(p, b);
    var pc = vector(p, c);

    var t1 = vectorPro(pa, pb);
    var t2 = vectorPro(pb, pc);
    var t3 = vectorPro(pc, pa);

    return sameSign(t1, t2) && sameSign(t2, t3);
}

// 用位運算高效判斷符號相同
function sameSign(a, b) {
    return (a ^ b) >= 0;
}

這里需要留意sameSign這個用于判斷兩個值的符號是否相同的輔助函數(shù),判斷符號相同的方法有很多,但此處巧妙的利用了計算機二進制的最高位--符號位。將兩個值按位異或,符號位不同取1,相同取0,所以如果最終符號位為1,即結(jié)果值整體小于0,則代表兩值符號不同,反之亦反。位運算的執(zhí)行效率是要比我們直接操作非二進制數(shù)的執(zhí)行效率高,所以應(yīng)用于此處大量頻繁地判斷符號異同的場景,對于性能優(yōu)化是很有幫助的。

最終,我們利用上邊準備好的輔助函數(shù),通過跟蹤鼠標的位置信息,判斷當前是否需要啟用延時器,選擇性的實施上一節(jié)的優(yōu)化方案,這樣便實現(xiàn)了最終需求。(完整示例代碼codepen)

// 是否需要延遲
function needDelay(ele, curMouse, prevMouse) {
    if (!curMouse || !prevMouse) {
        return;
    }
    var offset = ele.offset();// offset() 方法返回或設(shè)置匹配元素相對于文檔的偏移(位置)

    // 左上點
    var topleft = {
        x: offset.left,
        y: offset.top
    };
    // 左下點
    var leftbottom = {
        x: offset.left,
        y: offset.top + ele.height()
    };

    return isPointInTranjgle(curMouse, prevMouse, topleft, leftbottom);
}
啟發(fā)

通過本例實踐,給我最深刻的體會便是,高數(shù)為提高生產(chǎn)力所帶來的價值,哈哈···

恕敝人淺薄,第一次看到這個實例時的那種激動現(xiàn)在依然猶存,再加之前些天翻看了幾頁深度學(xué)習(xí)領(lǐng)域的一本經(jīng)典教材,有大半的篇幅講所用到的數(shù)學(xué)知識,不禁感嘆數(shù)學(xué)原來是這么玩兒的,可惜了···

以碾壓式的高度和視野去看待問題,可以讓無解變有解,唯一解變多解,這才是我心目中的高手。

如果這篇文章可以讓你在coding本身、或者向量(數(shù)學(xué))對于其他類似場景(點線面)的應(yīng)用有所啟發(fā),甚至有對于教育引導(dǎo)方面的外延思考,我覺得我寫這篇文章的目的便達到了。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/90493.html

相關(guān)文章

  • 你們要的HTML布局技巧:如何規(guī)范搭建網(wǎng)頁架構(gòu)?

    摘要:主體內(nèi)容區(qū)域小米首頁下小米商城的主題內(nèi)容區(qū)域,也是整體網(wǎng)頁面積最廣的區(qū)塊實在不知道定主體內(nèi)容區(qū)塊時也可以根據(jù)面積比重來劃分,最大的那塊一定是主題中心,布局的重復(fù)性很高。 單就深入了解布局規(guī)范都足夠說上一個月的,今天我就不論大范圍,挑選小米網(wǎng)站首頁的部分區(qū)塊布局來講解吧! 下面是小米官網(wǎng)的首頁,很多人一看到這樣的網(wǎng)頁就傻眼,不知道咋弄,要么就隨性布局,要么就干看著,其實遇到問題首先一點就...

    yvonne 評論0 收藏0
  • 你們要的HTML布局技巧:如何規(guī)范搭建網(wǎng)頁架構(gòu)?

    摘要:主體內(nèi)容區(qū)域小米首頁下小米商城的主題內(nèi)容區(qū)域,也是整體網(wǎng)頁面積最廣的區(qū)塊實在不知道定主體內(nèi)容區(qū)塊時也可以根據(jù)面積比重來劃分,最大的那塊一定是主題中心,布局的重復(fù)性很高。 單就深入了解布局規(guī)范都足夠說上一個月的,今天我就不論大范圍,挑選小米網(wǎng)站首頁的部分區(qū)塊布局來講解吧! 下面是小米官網(wǎng)的首頁,很多人一看到這樣的網(wǎng)頁就傻眼,不知道咋弄,要么就隨性布局,要么就干看著,其實遇到問題首先一點就...

    KaltZK 評論0 收藏0
  • 網(wǎng)頁導(dǎo)航 html + css的代碼實現(xiàn)

    摘要:一般來講,我們的網(wǎng)頁導(dǎo)航欄是這么個模式來構(gòu)建在結(jié)構(gòu)上首先我們需要給導(dǎo)航欄的給個類名一般為然后就是一個無序表格由于導(dǎo)航欄的文字一般都是鏈接用來跳轉(zhuǎn)頁面要在里面包含一個首頁云云商城智慧門店營銷平臺媒體聯(lián)盟關(guān)于云道在樣式上目前我見過的分為兩種導(dǎo)航一般來講,我們的網(wǎng)頁導(dǎo)航欄是這么個模式來構(gòu)建在結(jié)構(gòu)上:1.首先我們需要給導(dǎo)航欄的div 給個類名 一般為nav2.然后就是一個無序表格?3.由于導(dǎo)航欄的文...

    keke 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<