久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      那些年,微信小程序仿網(wǎng)易云音樂有關(guān)實(shí)時(shí)搜索功能

      那些年,微信小程序仿網(wǎng)易云音樂有關(guān)實(shí)時(shí)搜索功能

      相關(guān)學(xué)習(xí)推薦:微信小程序教程

      前言

      前段時(shí)間我的小伙伴已經(jīng)將網(wǎng)易云音樂小程序的音樂播放功能詳細(xì)的介紹出來了,作為前端小白學(xué)習(xí)了一段時(shí)間,最近也比較忙,沒有及時(shí)將實(shí)時(shí)搜索這塊內(nèi)容及時(shí)寫出來跟大家分享(其實(shí)代碼和功能之前就寫的差不多了),那么今天就給大家講一講個(gè)人在里面的一些細(xì)節(jié)和優(yōu)化吧。

      搜索功能很常見,很多地方都能用到,希望能夠給大家分享到有用的東西,同時(shí),有不足的地方,也希望各位大佬指出,給出一些修改的意見,小白在此感謝了!

      實(shí)時(shí)搜索功能里面我們也需要用到API接口,從input框輸入值到搜索建議,再到搜索結(jié)果,最后到跳轉(zhuǎn)歌曲播放,不再只是接那么簡(jiǎn)單,傳值很關(guān)鍵,同時(shí)不同功能下不同容器框的隱藏與顯示,還有一些搜索當(dāng)中涉及的細(xì)節(jié)內(nèi)容和優(yōu)化。讓我們一起來看看吧!

      界面預(yù)覽

      那些年,微信小程序仿網(wǎng)易云音樂有關(guān)實(shí)時(shí)搜索功能

      界面分析

      頭部搜索欄中:左邊返回箭頭,中間輸入框,右邊歌手排行榜頁(yè)面跳轉(zhuǎn);至于清除按鈕呢我們隱藏了起來,只有在輸入輸入值之后才會(huì)出現(xiàn)。

      往下時(shí)歷史記錄,像每個(gè)搜搜的記錄值這里都是一小塊一小塊等隔距離分布,搜索值有多長(zhǎng),這小塊就有多長(zhǎng),這里用到的是display: flex;flex-wrap: wrap;,對(duì)這個(gè)界面樣式感興趣的小伙伴可以待會(huì)看看全部代碼。

      接下來是熱搜榜,這里沒有太多講究,就是發(fā)起接口請(qǐng)求數(shù)據(jù),把數(shù)據(jù)埋進(jìn)去顯示出來就行了。

      搜索建議會(huì)在輸入結(jié)束后才會(huì)出現(xiàn),并且是很立體的一塊覆蓋在整個(gè)頁(yè)面上,用box-shadow: 1px 1px 5px #888888達(dá)到立體效果,z-index起到覆蓋的效果。

      搜索結(jié)果會(huì)在點(diǎn)擊搜索建議中的某一條或者點(diǎn)擊搜索歷史或者熱搜才出現(xiàn),同時(shí)界面上其他所有的容器快都會(huì)隱藏起來,這里其實(shí)就是一個(gè)容器框的隱藏與出現(xiàn)的小細(xì)節(jié)了,待會(huì)在功能中我們會(huì)詳細(xì)講到。這里我們先講一下組件(容器)如何進(jìn)行隱藏與顯示,以免下面的功能中看到這幾項(xiàng)內(nèi)容蒙圈

      幾個(gè)容器的頭部展示

      <!-- 點(diǎn)擊×可以清空正在輸入 --> <image class="{{showClean ? 'header_view_hide' : 'clean-pic'}}" src="../../image/search_delete.png" bindtap="clearInput" />復(fù)制代碼
      <!-- 搜索建議 --> <view class="{{showSongResult ? 'search_suggest' : 'header_view_hide'}}">復(fù)制代碼
      <!-- 搜索結(jié)果 --> <view class="{{showSearchResult ? 'header_view_hide' : 'search_result_songs'}}">復(fù)制代碼
      <!-- 搜索歷史 --> <view class="{{showView?'option':'header_view_hide'}}">復(fù)制代碼
      <!-- 熱搜榜 --> <view class="{{showView?'option':'header_view_hide'}}">復(fù)制代碼

      解析:這里只放了這幾塊容器的頭部的內(nèi)容,在data數(shù)據(jù)源中分別放了showClean,showSongResult,showSearchResult,showView, 為true 則這幾塊容器默認(rèn)為:(冒號(hào))前面的樣式,為false則默認(rèn)為:(冒號(hào))后面的樣式;header_view_hide樣式設(shè)置為display: none;,即隱藏不顯示;所以當(dāng)在某一個(gè)方法中可以去改變showClean,showSongResult,showSearchResult,showViewtrue還是false可以讓這幾塊容器分別為顯示或是隱藏。

      接口封裝

      接口封裝在上一篇我的小伙伴已經(jīng)講的十分清晰了,我們這里不再多去講解了,同樣現(xiàn)在用到的功能也不只是光調(diào)接口請(qǐng)求數(shù)據(jù)那么簡(jiǎn)單了,我們需要傳值給接口,讓接口收到值后再給我們返回相應(yīng)的數(shù)據(jù);在搜索界面我們用到的是搜索建議以及搜索結(jié)果的接口。熱搜榜我們暫時(shí)只使用最基礎(chǔ)的wx.request直接獲取數(shù)據(jù)

      api.js

      const API = {     getSearchSuggest: (data) => request(GET, `/search/suggest`, data),  // 搜索建議接口     getSearchResult: (data) => request(GET, `/search`, data),  // 搜索結(jié)果接口 }復(fù)制代碼

      實(shí)時(shí)搜索功能:

      1.數(shù)據(jù)源分析

      一個(gè)搜索功能我們?cè)O(shè)計(jì)到的數(shù)據(jù)會(huì)有很多,可以細(xì)列一下:輸入的值inputValue,在輸入時(shí)獲??;熱搜榜數(shù)據(jù)hots,熱搜接口獲??;搜索關(guān)鍵詞searchKey,本身就是輸入框的值,用來傳遞給搜索建議作為搜索關(guān)鍵詞;searchSuggest,搜索建議接口拿到搜索關(guān)鍵詞后返回的的數(shù)據(jù)(搜索建議);搜索結(jié)果searchResult,當(dāng)點(diǎn)擊搜索建議中的某一條,該值將填入搜索框,此時(shí)搜索關(guān)鍵詞searchKey將變?yōu)樵撝涤謧鬟f給搜索結(jié)果接口,并返回?cái)?shù)據(jù)放入searchResult;最后是搜索歷史history,每當(dāng)進(jìn)行一次搜索,將原本輸入框的值放到history數(shù)據(jù)源中。關(guān)于其他數(shù)據(jù)源涉及到組件隱藏與展示,即每一塊的容器框在何種情況下隱藏,何種情況下顯示。

      數(shù)據(jù)源展示

      data: {     inputValue: null,//輸入框輸入的值     history: [], //搜索歷史存放數(shù)組     searchSuggest: [], //搜索建議     showView: true,//組件的顯示與隱藏     showSongResult: true,     searchResult: [],//搜索結(jié)果     searchKey: [],     showSearchResult: true,     showClean: true,     hots: [] //熱門搜索  }復(fù)制代碼

      2.獲取熱搜榜

      這里我們直接在頁(yè)面的初始數(shù)據(jù)中調(diào)用接口,直接獲取到數(shù)據(jù)使用

      onLoad: function (options) {     wx.request({       url: 'http://neteasecloudmusicapi.zhaoboy.com/search/hot/detail',       header: { "Content-Type": "application/json" },       success: (res) => {  // console.log(res)         this.setData({           hots: res.data.result.hots })       }     })   },復(fù)制代碼

      3.獲取input文本

      前面已將講過,搜索建議和結(jié)果的接口并沒有直接的獲取方式,需要我們進(jìn)行傳值,所以首先我們需要獲取到輸入框的值

      input框內(nèi)容分析

      <input focus='true' type="text" class="weui-search-bar__input" placeholder="大家都在搜 " placeholder-style="color:#eaeaea" value='{{inputValue}}' bindinput="getSearchKey" bindblur="routeSearchResPage" bindconfirm="searchOver" />復(fù)制代碼

      小程序中關(guān)于input輸入框的相關(guān)屬性大家可以去詳細(xì)了解一下;placeholder為輸入框?yàn)榭諘r(shí)占位符,即還沒輸入前輸入框顯示的內(nèi)容,placeholder-style可以去設(shè)置placeholder的樣式;value是輸入框的初始內(nèi)容,即自己在輸入框輸入的內(nèi)容,我們?cè)谶@里直接將輸入的內(nèi)容value直接作為了data數(shù)據(jù)源中inputValue的內(nèi)容;bindinput是在鍵盤輸入時(shí)觸發(fā),即我們一進(jìn)行打字,就能觸發(fā)我們的自定義事件getSearchKey,并且會(huì)返還相應(yīng)數(shù)據(jù);bindblur在輸入框失去焦點(diǎn)時(shí)觸發(fā),進(jìn)行搜索功能時(shí),需要在搜索框輸值,此時(shí)焦點(diǎn)一直在輸入框,當(dāng)點(diǎn)擊輸入框以外的地方即輸入框失去焦點(diǎn),同時(shí)觸發(fā)routeSearchResPage事件,還會(huì)返回相應(yīng)的數(shù)據(jù),在下面功能中會(huì)講到;bindconfirm在點(diǎn)擊完成按鈕時(shí)觸發(fā),這里綁定一個(gè)searchOver,用來隱藏組件(容器塊),再次觸發(fā)搜索功能,在下面的功能中也會(huì)講到。

      獲取input文本

      getSearchKey: function (e) {     // console.log(e.detail) //打印出輸入框的值     if (e.detail.cursor != this.data.cursor) { //實(shí)時(shí)獲取輸入框的值       this.setData({         showSongResult: true,         searchKey: e.detail.value })       this.searchSuggest();     }     if (e.detail.value) { // 當(dāng)input框有值時(shí),才顯示清除按鈕'x'       this.setData({         showClean: false  // 出現(xiàn)清除按鈕 })     }     if(e.detail.cursor === 0){       this.setData({  // 當(dāng)輸入框沒有值時(shí),即沒有輸入時(shí),隱藏搜索建議界面,返回到最開始的狀態(tài)         showSongResult: false })       return     }   }復(fù)制代碼

      bindinput本身是會(huì)返回?cái)?shù)據(jù),在代碼運(yùn)行時(shí),可以打印出來先看看; e.detail.value即為輸入框的值,將它賦值給searchKey; 查看打印數(shù)據(jù)e:

      那些年,微信小程序仿網(wǎng)易云音樂有關(guān)實(shí)時(shí)搜索功能

      解析:

      疑惑的小伙伴可以將代碼運(yùn)行,打印出以上設(shè)計(jì)的幾個(gè)數(shù)據(jù)進(jìn)行分析

      ①當(dāng)此時(shí)輸入框的值和bindinput返回的輸入框的值時(shí)一樣的,就將輸入框的值賦給搜索關(guān)鍵詞searchKey,此時(shí)顯示搜索建議欄(showSongResult寫在wxml當(dāng)中,用來控制該容器是否展示,可以看到最后面發(fā)的整個(gè)界面的wxml中的詳情);同時(shí)searchSuggest事件(方法)生效。

      ②當(dāng)輸入框沒值時(shí),清除按鈕x是不會(huì)顯示的,只有當(dāng)輸入框有值是才會(huì)出現(xiàn)清除按鈕x

      ③當(dāng)輸入框沒有值時(shí),隱藏搜索建議欄,其實(shí)本身我們最開始進(jìn)入這個(gè)頁(yè)面時(shí),輸入框是沒值的,搜索建議欄也是不展示的,為沒進(jìn)行輸入就沒有數(shù)據(jù);但是當(dāng)我們輸入內(nèi)容后,出現(xiàn)搜索建議,此時(shí)我們點(diǎn)擊清除按鈕,輸入框的內(nèi)容沒了,但是搜索建議還停留在之前的狀態(tài),所以這里我們優(yōu)化一下,讓showSongResultfalse,即一清空輸入框內(nèi)容,隱藏掉搜索建議欄。另外我們?yōu)槭裁匆?code>return呢?這里還有一個(gè)bug,當(dāng)清除輸入框內(nèi)容后,再輸入發(fā)現(xiàn)已經(jīng)不再具備搜索功能了,所以需要return回到初始的狀態(tài),就能重新進(jìn)行輸入并且搜索。同時(shí)當(dāng)輸入框?yàn)榭諘r(shí)進(jìn)行搜索功能還會(huì)報(bào)錯(cuò),這也是一個(gè)bug,所以有了return即使空值搜索也會(huì)立馬回到初始狀態(tài),解決了空值搜索報(bào)錯(cuò)的bug。

      4.搜索框其他功能

      • 清空輸入框內(nèi)容

         clearInput: function (e) {     // console.log(e)       this.setData({       inputValue: '',  // 將輸入框的值為空       showSongResult: false,  // 隱藏搜索建議欄       showClean: true // 隱藏清除按鈕 (不加此項(xiàng)會(huì)出現(xiàn)清除輸入框內(nèi)容后清除按鈕不消失的bug)     })   },復(fù)制代碼

        點(diǎn)擊清除按鈕,就讓inputValue值為空,即輸入框的內(nèi)容為空,達(dá)到清除文本的效果;在獲取輸入框文本那里我們也提到了清除按鈕,也提到輸入框文本清空時(shí),之前的搜索建議欄還會(huì)留下,所以這里我們讓showSongResultfalse,使得搜索建議欄隱藏。清除文本的同時(shí)再隱藏掉清除按鈕。

      • 取消搜索返回上頁(yè)

        back: function () {     wx: wx.navigateBack({  // 關(guān)閉當(dāng)前頁(yè)面,返回上一頁(yè)面或多級(jí)頁(yè)面       delta: 0   // 返回的頁(yè)面數(shù),如果 delta 大于現(xiàn)有頁(yè)面數(shù),則返回到首頁(yè)      });   }復(fù)制代碼

        這里用到的小程序自帶的返回頁(yè)面的功能,當(dāng)給delta值為0即回到上一個(gè)頁(yè)面。(可去文檔查看詳情)

      • 跳轉(zhuǎn)歌手排行榜

        singerPage: function () {     wx.navigateTo({  // 保留當(dāng)前頁(yè)面,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁(yè)面。但是不能跳到 tabbar 頁(yè)面       url: `../singer/singer` // 要跳轉(zhuǎn)去的界面     })   },復(fù)制代碼

        在微信官方文檔可以查看到navigateTo的功能及其屬性,這里不多講。

      5.搜索建議

       searchSuggest() {     $api. getSearchSuggest({ keywords: this.data.searchKey, type: 'mobile' }).then(res => {       //請(qǐng)求成功        // console.log(res);  // 打印出返回?cái)?shù)據(jù)進(jìn)行查看       if(res.statusCode === 200){         this.setData({           searchSuggest: res.data.result.allMatch  // 將返回?cái)?shù)據(jù)里的歌名傳給搜索建議         })        }     })     .catch(err => {  // 請(qǐng)求失敗       console.log('錯(cuò)誤')   })   }復(fù)制代碼

      解析:開始我們將接口進(jìn)行了封裝,在上一篇講播放的文章中我的小伙伴已經(jīng)把接口跟封裝講的很仔細(xì)了,這里我們就不在講這個(gè)了,就分析我們的接口。searchKey作為搜索關(guān)鍵詞需要傳遞給接口,在前面的getSearchKey方法中,我們已經(jīng)講輸入框的內(nèi)容傳給了searchKey作為它的值;所以此時(shí)我們拿到有值的searchKey傳遞給接口,讓接口返回相關(guān)數(shù)據(jù),返回的數(shù)據(jù)中的res.data.result.allMatch就是從搜索關(guān)鍵詞返回的搜索建議里的所有歌名,在將這些數(shù)據(jù)放到searchSuggest數(shù)據(jù)源中,這樣在wxml埋好的空就能拿到數(shù)據(jù),將搜索建議欄顯示出。

      6.搜索結(jié)果

      • 搜索建議內(nèi)的歌曲點(diǎn)擊事件
        // 看看 wxml中的點(diǎn)擊事件展示 // <view wx:for="{{searchSuggest}}" wx:key="index" class='search_result' data-value='{{item.keyword}} ' bindtap='fill_value'> // js如下: fill_value: function (e) {   // 點(diǎn)擊搜索建議,熱門搜索值或搜索歷史,填入搜索框     // console.log(e.currentTarget.dataset.value)  // 打印`e`中的數(shù)據(jù)->點(diǎn)擊的值     this.setData({       searchKey: e.currentTarget.dataset.value, // 點(diǎn)擊時(shí)把值給searchKey進(jìn)行搜索       inputValue: e.currentTarget.dataset.value, // 在輸入框顯示內(nèi)容       showSongResult: false, // 給false值,隱藏搜索建議頁(yè)面       showClean: false // 顯示清除按鈕 (不加此項(xiàng),會(huì)出現(xiàn)點(diǎn)擊后輸入框有值但不顯示清除按鈕的bug)     })     this.searchResult();  // 執(zhí)行搜索功能   },復(fù)制代碼

        解析:首先點(diǎn)擊事件可以攜帶額外信息,如 id, dataset, touches;返回參數(shù)eventevent本身會(huì)有一個(gè)currentTarget屬性;這里解釋一下data-value='{{item.keyword}}=>data就是dataset;item.keyword是搜索建議完成之后返回的數(shù)據(jù)賦值給searchSuggest里面的某個(gè)數(shù)據(jù);當(dāng)一點(diǎn)擊搜索建議里面的某一個(gè)歌名時(shí),此歌名即為此時(shí)的item.keyword,并將該值存入點(diǎn)擊事件的參數(shù)event內(nèi)的dataset。大家也可操作一波打印出來看看結(jié)果,currentTarget.dataset.value就是我們點(diǎn)擊的那個(gè)歌曲名字。所以一點(diǎn)擊搜索建議中的某個(gè)歌名或者搜索歷史以及熱搜榜單中的某個(gè)歌名時(shí),點(diǎn)擊事件生效,返回這樣該歌曲名稱,并將該值給到此時(shí)的searchKeyinputValue,此時(shí)輸入框的值會(huì)變成該值,搜索結(jié)果的關(guān)鍵詞的值也會(huì)變成該值;同時(shí)this.searchResult()可讓此時(shí)執(zhí)行搜索結(jié)果功能。showSongResult: false這里還將搜索建議欄給隱藏了。增加showClean: false是為了解決點(diǎn)擊后輸入框有值但不顯示清除按鈕的bug。 查看打印數(shù)據(jù)e:

        那些年,微信小程序仿網(wǎng)易云音樂有關(guān)實(shí)時(shí)搜索功能
      • 返回搜索結(jié)果
        searchResult: function () {     // console.log(this.data.searchKey)  // 打印此時(shí)的搜索關(guān)鍵詞     $api.getSearchResult({ keywords: this.data.searchKey, type: 1, limit: 100, offset: 2 }).then(res => {       // 請(qǐng)求成功       if (res.statusCode === 200) {         // console.log(res)  // 打印返回?cái)?shù)據(jù)         this.setData({           searchResult: res.data.result.songs, // 將搜索出的歌曲名稱給到搜索結(jié)果           showSearchResult: false, // 顯示搜索結(jié)果欄           showView: false,  // 隱藏搜索歷史欄和熱搜榜單欄         });       }     })     .catch(ree => {       //請(qǐng)求失敗     })   },復(fù)制代碼

        解析:上面的歌曲名稱點(diǎn)擊同時(shí)觸發(fā)了搜索結(jié)果的功能,將點(diǎn)擊后的新的keywords傳遞給了搜索結(jié)果的接口,接口請(qǐng)求后返回給我們數(shù)據(jù),數(shù)據(jù)中的res.data.result.songs為搜索到的歌曲,此時(shí)將它賦值給到searchResult,這樣搜索結(jié)果欄中會(huì)拿到數(shù)據(jù),并且showSearchResult: false讓搜索結(jié)果欄顯示出來;這里還做了搜索歷史欄和熱搜欄的隱藏功能注:搜索結(jié)果和搜索建議都需要將搜索關(guān)鍵詞傳遞給接口,不清楚的小伙伴可以去查看接口文檔研究一下:https://binaryify.github.io/NeteaseCloudMusicApi/#/

      • 搜索完成后的優(yōu)化
          searchOver: function () { // 搜索結(jié)果完成后(再次點(diǎn)擊輸入框)    this.setData({      showSongResult: false  // 搜索建議這塊容器消失    })    this.searchResult();  // 執(zhí)行搜索結(jié)果  },復(fù)制代碼

        解析:前面我們講到過, searchOver是綁定在input框中的bindconfirm事件,即點(diǎn)擊完成按鈕時(shí)觸發(fā)。當(dāng)我們搜索完成之后,界面上還有搜索欄以及搜索結(jié)果的顯示,此時(shí)我們?cè)俅吸c(diǎn)擊輸入框,可以進(jìn)行清除文本,同時(shí)我們還需要增加一個(gè)功能,即在此種情況下,我們還可以進(jìn)行再次輸入并且返回搜索建議以及點(diǎn)擊搜索建議中的歌曲時(shí)再次執(zhí)行搜索結(jié)果功能。

      7.搜索歷史

      • input失去焦點(diǎn)
        routeSearchResPage: function (e) {       // console.log(this.data.searchKey)  // 打印此時(shí)的搜索關(guān)鍵詞     // console.log(this.data.searchKey.length)       if (this.data.searchKey.length > 0) {  // 當(dāng)搜索框有值的情況下才把搜索值存放到歷史中,避免將空值存入歷史記錄       let history = wx.getStorageSync("history") || [];  // 從本地緩存中同步獲取指定 key 對(duì)應(yīng)的內(nèi)容,key指定為history       // console.log(history);       history = history.filter(item => item !== this.data.searchKey)  // 歷史去重       history.unshift(this.data.searchKey)  // 排序傳入       wx.setStorageSync("history", history);     }   }復(fù)制代碼

        解析:之前講過routeSearchResPage事件時(shí)放在input框中的,輸入框失去焦點(diǎn)時(shí)觸發(fā),即不在輸入框內(nèi)進(jìn)行輸入,點(diǎn)擊輸入框以外的內(nèi)容時(shí)觸發(fā)。當(dāng)輸入完成時(shí)會(huì)出現(xiàn)搜索建議,此時(shí)焦點(diǎn)還在輸入框,當(dāng)我們點(diǎn)擊搜索建議中的某一天時(shí),輸入框即失去焦點(diǎn),此時(shí)該事件觸發(fā)。失去焦點(diǎn)函數(shù)是在搜索建議事件后發(fā)生,此時(shí)的搜索關(guān)鍵詞為搜索建議的搜索關(guān)鍵詞,前面也講到過,這個(gè)搜索關(guān)鍵詞就是我們?cè)谳斎肟蜉斎氲奈谋緝?nèi)容,所以將此時(shí)的搜索關(guān)鍵詞賦值給搜索歷史history。注:關(guān)于搜索歷史,我們這里增加了一個(gè)判斷,即當(dāng)搜索關(guān)鍵詞不為空時(shí),才會(huì)拿到搜索關(guān)鍵詞給到搜索歷史里面,否則,每一次不輸入值也去點(diǎn)擊輸入框以外,會(huì)將一個(gè)空值傳給搜索歷史,導(dǎo)致搜索歷史中會(huì)有空值得顯示,這也是一個(gè)`bug得解決。同時(shí)還進(jìn)一步將代碼進(jìn)行優(yōu)化,用到filter達(dá)到歷史去重得效果,即判斷新拿到得搜索關(guān)鍵詞是否與已有得搜索歷史中的搜索關(guān)鍵詞相同,同則過濾掉先前的那個(gè),并使用到unshift向數(shù)組開頭增加這個(gè)作為新的歷史記錄。

      • 歷史緩存
        onShow: function () {  //每次顯示變動(dòng)就去獲取緩存,給history,并for出來。   // console.log('a')   this.setData({     history: wx.getStorageSync("history") || []   }) }復(fù)制代碼

        解析:雖然上一步將拿到的搜索記錄存入到了搜索歷史,但是還不能顯示出來,讓數(shù)據(jù)源拿到數(shù)據(jù),這里要做一個(gè)歷史緩存的操作。onShow為監(jiān)聽頁(yè)面顯示,每次在搜素建議功能后進(jìn)行點(diǎn)擊歌名出現(xiàn)搜索結(jié)果欄時(shí)觸發(fā),此時(shí)將上一步拿到的historygetStorageSync進(jìn)行本地緩存,使得在刷新或者跳轉(zhuǎn)時(shí),不會(huì)講搜索歷史丟失,一直保存下來。

      • 刪除歷史
        clearHistory: function () {  // 清空page對(duì)象data的history數(shù)組 重置緩存為[](空)     const that = this;     wx.showModal({       content: '確認(rèn)清空全部歷史記錄',       cancelColor: '#DE655C',       confirmColor: '#DE655C',       success(res) {         if (res.confirm) { // 點(diǎn)擊確認(rèn)           that.setData({             history: []           })           wx.setStorageSync("history", []) //把空數(shù)組給history,即清空歷史記錄         } else if (res.cancel) {         }       }     })   }復(fù)制代碼

        解析:showModal() 方法用于顯示對(duì)話窗,當(dāng)點(diǎn)擊刪除按鈕時(shí)觸發(fā),顯示出確認(rèn)清空全部歷史記錄的窗口,并有兩個(gè)點(diǎn)擊按鈕:確認(rèn)取消;當(dāng)點(diǎn)擊確認(rèn)時(shí),將history數(shù)組中的內(nèi)容重置為空,即達(dá)到清空搜索歷史中的數(shù)據(jù)的功能;同時(shí)也需要將此時(shí)沒有數(shù)據(jù)的的搜索歷史進(jìn)行緩存。點(diǎn)擊取消,提示窗消失,界面不會(huì)發(fā)生任何變化。

      8.歌曲跳轉(zhuǎn)播放播放

      • 傳值跳轉(zhuǎn)播放界面
        // 先來看看handlePlayAudio綁定的地方 // <view wx:for="{{searchResult}}" wx:key="index" class='search_result_song_item' data-id="{{item.id}}" bindtap='handlePlayAudio'> // 以下為js: handlePlayAudio: function (e) { //event 對(duì)象,自帶,點(diǎn)擊事件后觸發(fā),event有type,target,timeStamp,currentTarget屬性   // console.log(e)   // 打印出返回參數(shù)內(nèi)容   const musicId = e.currentTarget.dataset.id; //獲取到event里面的歌曲id賦值給musicId   wx.navigateTo({                       //獲取到musicId帶著完整url后跳轉(zhuǎn)到play頁(yè)面     url: `../play/play?musicId=${musicId}`  // 跳轉(zhuǎn)到已經(jīng)傳值完成的歌曲播放界面   }) }復(fù)制代碼

        解析:handlePlayAudio綁定在每天搜索結(jié)果上,即點(diǎn)擊搜索建議后完成搜索結(jié)果功能顯示出搜索結(jié)果欄,點(diǎn)擊每一天搜索結(jié)果都可以觸發(fā)handlePlayAudio。前面也講到過bindtap是帶有參數(shù)返回,攜帶額外信息dataset,event本身會(huì)有一個(gè)currentTarget屬性,data-id="{{item.id}}"的作用跟上面的搜索建議內(nèi)的歌曲點(diǎn)擊事件是同樣的效果,item.id為執(zhí)行搜索結(jié)果時(shí)接口返回給searchResult的數(shù)據(jù),也就是搜索結(jié)果中每首歌曲各自對(duì)應(yīng)的id。當(dāng)點(diǎn)擊搜索結(jié)果內(nèi)的某一首歌,即將這首歌的id傳給event中的dataset,數(shù)據(jù)名為dataset里的id。此時(shí)我們定義一個(gè)musicId,將event里面的歌曲id賦值給musicId,用 wx.navigateTo跳轉(zhuǎn)到播放界面,同時(shí)將musicId作為播放請(qǐng)求接口需要的傳入數(shù)據(jù)。 查看打印數(shù)據(jù)e:

        那些年,微信小程序仿網(wǎng)易云音樂有關(guān)實(shí)時(shí)搜索功能

      9.search功能源碼分享

      wxml

      <nav-bar></nav-bar> <view class="wrapper">     <!-- 上部整個(gè)搜索框 -->     <view class="weui-search-bar">         <!-- 返回箭頭按鈕 -->         <view class="weui-search-bar__cancel-btn" bindtap="back">             <image class="return-pic" src="../../image/search_return.png" bindtap="cancel" />         </view>         <!-- 搜索欄 -->         <view class="weui-search-bar__form">             <view class="weui-search-bar__box">                 <input focus='true' type="text" class="weui-search-bar__input" placeholder="大家都在搜 " placeholder-style="color:#eaeaea" value='{{inputValue}}' bindinput="getSearchKey" bindblur="routeSearchResPage" bindconfirm="searchOver" />             </view>             <!-- 點(diǎn)擊×可以清空正在輸入 -->             <view class="clean-bar">                 <image class="{{showClean ? 'header_view_hide' : 'clean-pic'}}" src="../../image/search_delete.png" bindtap="clearInput" />             </view>         </view>         <!-- 跳轉(zhuǎn)歌手分類界面 -->         <view class="songer">             <image class="songer-pic" src="../../image/search_songner.png" bindtap="singerPage" />         </view>     </view>     <!-- 搜索建議 -->     <view class="{{showSongResult ? 'search_suggest' : 'header_view_hide'}}">         <view wx:for="{{searchSuggest}}" wx:key="index" class='search_result' data-value='{{item.keyword}} ' bindtap='fill_value'>             <image class="search-pic" src="../../image/search_search.png"></image>             <view class="search_suggest_name">{{item.keyword}}</view>         </view>     </view>     <!-- 搜索結(jié)果 -->     <view class="{{showSearchResult ? 'header_view_hide' : 'search_result_songs'}}">         <view class="search-title">             <text class="songTitle">單曲</text>             <view class="openBox">                 <image class="openTap" src="../../image/search_openTap.png" />                 <text class="openDes">播放全部</text>             </view>         </view>         <view wx:for="{{searchResult}}" wx:key="index" class='search_result_song_item' data-id="{{item.id}}" bindtap='handlePlayAudio'>             <view class='search_result_song_song_name'>{{item.name}}</view>             <view class='search_result_song_song_art-album'>                 {{item.artists[0].name}} - {{item.album.name}}             </view>             <image class="broadcast" src="../../image/search_nav-open.png" />             <image class="navigation" src="../../image/mine_lan.png" />         </view>     </view>     <!-- 搜索歷史 -->     <view class="{{showView?'option':'header_view_hide'}}">         <view class="history">             <view class="history-wrapper">                 <text class="history-name">歷史記錄</text>                 <image bindtap="clearHistory" class="history-delete" src="../../image/search_del.png" />             </view>             <view class="allhistory">                 <view class="allhistorybox" wx:for="{{history}}" wx:key="index" data-value='{{item}}' data-index="{{index}}" bindtap="fill_value">                     <text class="historyname">{{item}}</text>                 </view>             </view>         </view>     </view>     <!-- 熱搜榜 -->     <view class="{{showView?'option':'header_view_hide'}}">         <view class="ranking">             <text class="ranking-name">熱搜榜</text>         </view>         <view class="rankingList">             <view class="rankingList-box" wx:for="{{hots}}" wx:key="index">                 <view wx:if="{{index <= 2}}">                     <text class="rankingList-num" style="color:red">{{index+1}}</text>                     <view class="song">                         <text class="rankigList-songname" style="color:black;font-weight:600" data-value="{{item.first}}" bindtap='fill_value'>                             {{item.first}}                         </text>                         <block wx:for="{{detail}}" wx:key="index">                             <text class="rankigList-hotsong" style="color:red">{{item.hot}}</text>                         </block>                     </view>                 </view>                 <view wx:if="{{index > 2}}">                     <text class="rankingList-num">{{index+1}}</text>                     <view class="othersong">                         <text class="rankigList-songname" data-value="{{item.first}}" bindtap='fill_value'>                             {{item.first}}                         </text>                     </view>                 </view>             </view>         </view>     </view> </view>復(fù)制代碼

      wxss

        /* pages/search/search.wxss */ .weui-search-bar{     position:relative;     /* padding:8px; */     display:flex;     box-sizing:border-box;     /* background-color:#EDEDED; */     -webkit-text-size-adjust:100%;     align-items:center } .weui-icon-search{     margin-right:8px;font-size:14px;vertical-align:top;margin-top:.64em;     height:1em;line-height:1em } .weui-icon-search_in-box{     position:absolute;left:12px;top:50%;margin-top:-8px } .weui-search-bar__text{     display:inline-block;font-size:14px;vertical-align:top } .weui-search-bar__form{     position:relative;     /* flex:auto;     border-radius:4px;     background:#FFFFFF */     border-bottom: 1px solid #000;     margin-left: 30rpx;     width: 400rpx;     padding-right: 80rpx; } .weui-search-bar__box{     position:relative;     padding-right: 80rpx;     box-sizing:border-box;     z-index:1; } .weui-search-bar__input{     height:32px;line-height:32px;font-size:14px;caret-color:#07C160 } .weui-icon-clear{     position:absolute;top:0;right:0;bottom:0;padding:0 12px;font-size:0 } .weui-icon-clear:after{     content:"";height:100%;vertical-align:middle;display:inline-block;width:0;overflow:hidden } .weui-search-bar__label{     position:absolute;top:0;right:0;bottom:0;left:0;z-index:2;border-radius:4px;     text-align:center;color:rgba(0,0,0,0.5);background:#FFFFFF;line-height:32px } .weui-search-bar__cancel-btn{     margin-left:8px;line-height:32px;color:#576B95;white-space:nowrap } .clean-bar {     /* width: 20rpx;     height: 20rpx; */ } .clean-pic {     width: 20rpx;     height: 20rpx;     float: right;     position: absolute;      margin-top: -30rpx;       margin-left: 450rpx; } .return-pic {     width: 60rpx;     height: 60rpx;     margin-left: 20rpx; } .songer-pic{     width: 60rpx;     height: 60rpx;     margin-left: 40rpx; } .wrapper {     width: 100%;     height: 100%;     position: relative;     z-index: 1; } .poster {     width: 670rpx;     height: 100rpx;     margin-top: 40rpx;     margin-left: 40rpx; } .postername {     font-size: 15rpx;     position: absolute;     margin-top: 10rpx;     margin-left: 10rpx; } .poster-outside {     border-radius: 10rpx;     background-color: slategrey; } .poster-pic0 {     width: 80rpx;     height: 80rpx;     margin-top: 10rpx; } .test-title {     position: absolute;     font-size: 30rpx;     line-height: 100rpx;     margin-left: 20rpx;     color: red; } .test-age {     position: absolute;     font-size: 30rpx;     line-height: 100rpx;     margin-left: 80rpx; } .test-red {     position: absolute;     font-size: 30rpx;     line-height: 100rpx;     margin-left: 270rpx;     color: red; } .test-black {     position: absolute;     font-size: 30rpx;     line-height: 100rpx;     margin-left: 400rpx; } .poster-pic1 {     width: 80rpx;     height: 80rpx;     margin-left: 510rpx; } .history {     margin: 50rpx 0 0 40rpx; } .history-name {     font-size: 28rpx;     font-weight: 550; } .history-delete {     width: 50rpx;     height: 50rpx;     position: absolute;     margin-left: 510rpx; } .allhistory {     display: flex;     flex-wrap: wrap; } .allhistorybox {     margin: 30rpx 20rpx 0 0;     background-color: dimgray;     border-radius: 10rpx; } .historyname {     font-size: 28rpx;     margin: 20rpx 20rpx 20rpx 20rpx; } .ranking {     margin-left: 40rpx;     margin-top: 100rpx; } .ranking-name {     font-size: 28rpx;     color: black;     font-weight: 550; } .rankingList {     margin-left: 50rpx;     margin-top: 30rpx; } .rankingList-box {     width: 100%;     height: 80rpx;     margin: 0 0 30rpx 0; } .rankingList-num {     line-height: 80rpx;     align-content: center; } .song {     margin: -100rpx 0 0 30rpx;     display: flex;     flex-wrap: wrap; } .othersong {     margin-top: -100rpx;     margin-left: 70rpx; } .rankigList-songname {     font-size: 30rpx;     margin-left: 40rpx; } .rankigList-hotsong {     font-size: 25rpx;     font-weight: 550;     margin-top: 45rpx;     margin-left: 20rpx; } .rankigList-hotnum {     float: right;     position: absolute;     line-height: 80rpx;     margin-left: 600rpx;     font-size: 20rpx;     color: darkgrey; } .rankingList-songdes {     font-size: 22rpx;     color: darkgrey;     position: absolute;     margin-left: 60rpx;     margin-top: -30rpx; } .search_suggest{     width:570rpx;     margin-left: 40rpx;     position: absolute;     z-index: 2;     background: #fff;     box-shadow: 1px 1px 5px #888888;     margin-top: 20rpx; } .header_view_hide{     display: none;   } .search-pic {       width: 50rpx;       height: 50rpx;      margin-top: 25rpx;      margin-left: 20rpx; } .search-title {     color: #000;     margin-left: 15rpx;     margin-bottom: 30rpx; } .songTitle {     font-size: 30rpx;     font-weight: 700; } .openBox {     float: right;     border-radius: 30rpx;     margin-right: 30rpx;     border-radius: 30rpx;     border-bottom: 1px solid #eaeaea; } .openTap {     width: 30rpx;     height: 30rpx;     position: absolute;     margin: 6rpx 10rpx 0rpx 20rpx; } .openDes {     font-size: 25rpx;     color: rgba(0,0,0,0.5);     margin-right: 20rpx;     margin-left: 58rpx; } .broadcast {     width: 20px;     height: 20px;     display: inline-block;     overflow: hidden;     float: right;     margin-top: -70rpx;     margin-left: -120rpx;     margin-right: 80rpx; } .navigation {     width: 20px;     height: 20px;     display: inline-block;     overflow: hidden;     float: right;     margin-top: -70rpx;     margin-right: 20rpx; }   .search_result{     /* display: block;     font-size: 14px;     color: #000000;     padding: 15rpx;     margin: 15rpx; */     /* border-bottom: 1px solid #eaeaea; */     /* float: right; */     /* margin-left: -450rpx; */     width: 570rpx;         height: 100rpx;     border-bottom: 1px solid #eaeaea;   }   .search_suggest_name {     display: block;     float: right;     position: absolute;     margin-left: 85rpx;     margin-top: -46rpx;     font-size: 14px;     color: darkgrey;     /* padding: 15rpx;     margin: 15rpx; */   }   .search_result_songs{     margin-top: 10rpx;     width: 100%;     height: 100%;     margin-left: 15rpx;   }   .search_result_song_item{      display: block;      margin: 15rpx;      border-bottom: 1px solid #EDEEF0;   }   .search_result_song_song_name{     font-size: 15px;     color: #000000;     margin-bottom: 15rpx;   }   .search_result_song_song_art-album{     font-size: 11px;     color: #000000;     font-weight:lighter;     margin-bottom: 5rpx;   }復(fù)制代碼

      js

      // pages/search/search.js // const API = require('../../utils/req') const $api = require('../../utils/api.js').API; const app = getApp(); Page({   data: {     inputValue: null,//輸入框輸入的值     history: [], //搜索歷史存放數(shù)組     searchSuggest: [], //搜索建議     showView: true,//組件的顯示與隱藏     showSongResult: true,     searchResult: [],//搜索結(jié)果     searchKey: [],     showSearchResult: true,     showClean: true,     hots: [], //熱門搜索     detail: [       {         hot: 'HOT'       }     ],   },   onLoad: function (options) {     wx.request({       url: 'http://neteasecloudmusicapi.zhaoboy.com/search/hot/detail',       data: {       },       header: {         "Content-Type": "application/json"       },       success: (res) => {         // console.log(res)         this.setData({           hots: res.data.result.hots         })       }     })   },   // 點(diǎn)x將輸入框的內(nèi)容清空   clearInput: function (e) {     // console.log(e)     this.setData({       inputValue: '',       showSongResult: false,       showClean: true // 隱藏清除按鈕     })   },   //實(shí)現(xiàn)直接返回返回上一頁(yè)的功能,退出搜索界面   back: function () {     wx: wx.navigateBack({       delta: 0     });   },   // 跳轉(zhuǎn)到歌手排行界面   singerPage: function () {     // console.log('a')     wx.navigateTo({       url: `../singer/singer`     })   },   //獲取input文本并且實(shí)時(shí)搜索   getSearchKey: function (e) {     if(e.detail.cursor === 0){       this.setData({         showSongResult: false       })       return     }     // console.log(e.detail) //打印出輸入框的值     if (e.detail.cursor != this.data.cursor) { //實(shí)時(shí)獲取輸入框的值       this.setData({         showSongResult: true,         searchKey: e.detail.value       })       this.searchSuggest();     }     if (e.detail.value) { // 當(dāng)input框有值時(shí),才顯示清除按鈕'x'       this.setData({         showClean: false  // 出現(xiàn)清除按鈕       })     }   },   // 搜索建議   searchSuggest() {     $api. getSearchSuggest({ keywords: this.data.searchKey, type: 'mobile' }).then(res => {       //請(qǐng)求成功        // console.log(res);       if(res.statusCode === 200){         this.setData({           searchSuggest: res.data.result.allMatch          })        }     })     .catch(err => {       //請(qǐng)求失敗       console.log('錯(cuò)誤')     })   },   // 搜索結(jié)果   searchResult: function () {     // console.log(this.data.searchKey)     $api.getSearchResult({ keywords: this.data.searchKey, type: 1, limit: 100, offset: 2 }).then(res => {       // 請(qǐng)求成功       if (res.statusCode === 200) {         // console.log(res)         this.setData({           searchResult: res.data.result.songs,           showSearchResult: false,           showView: false,         });       }     })     .catch(ree => {       //請(qǐng)求失敗     })   },   handlePlayAudio: function (e) { //event 對(duì)象,自帶,點(diǎn)擊事件后觸發(fā),event有type,target,timeStamp,currentTarget屬性     // console.log(e)     const musicId = e.currentTarget.dataset.id; //獲取到event里面的歌曲id賦值給musicId     wx.navigateTo({                                 //獲取到musicId帶著完整url后跳轉(zhuǎn)到play頁(yè)面       url: `../play/play?musicId=${musicId}`     })   },   // input失去焦點(diǎn)函數(shù)   routeSearchResPage: function (e) {     // console.log(e)     // console.log(e.detail.value)     // console.log(this.data.searchKey)     // console.log(this.data.searchKey.length)       if (this.data.searchKey.length > 0) {  // 當(dāng)搜索框有值的情況下才把搜索值存放到歷史中,避免將空值存入歷史記錄       let history = wx.getStorageSync("history") || [];       // console.log(history);       history = history.filter(item => item !== this.data.searchKey)  // 歷史去重       history.unshift(this.data.searchKey)       wx.setStorageSync("history", history);     }     },   // 清空page對(duì)象data的history數(shù)組 重置緩存為[](空)   clearHistory: function () {     const that = this;     wx.showModal({       content: '確認(rèn)清空全部歷史記錄',       cancelColor: '#DE655C',       confirmColor: '#DE655C',       success(res) {         if (res.confirm) {           that.setData({             history: []           })           wx.setStorageSync("history", []) //把空數(shù)組給history,即清空歷史記錄         } else if (res.cancel) {         }       }     })   },     // 搜索結(jié)果完成后(再次點(diǎn)擊輸入框)   searchOver: function () {     this.searchSuggest();  // 執(zhí)行搜索結(jié)果     this.searchResult()   },   // 點(diǎn)擊熱門搜索值或搜索歷史,填入搜索框   fill_value: function (e) {     console.log(e)     // console.log(this.data.history)     // console.log(e.currentTarget.dataset.value)     this.setData({       searchKey: e.currentTarget.dataset.value,//點(diǎn)擊=把值給searchKey,讓他去搜索       inputValue: e.currentTarget.dataset.value,//在輸入框顯示內(nèi)容       showSongResult: false, //給false值,隱藏搜索建議頁(yè)面       showClean: false // 顯示 清除按鈕     })     this.searchResult(); //執(zhí)行搜索功能   },   /**    * 生命周期函數(shù)--監(jiān)聽頁(yè)面顯示    */   //每次顯示變動(dòng)就去獲取緩存,給history,并for出來。   onShow: function () {     // console.log('a')     this.setData({       history: wx.getStorageSync("history") || []     })   }, })復(fù)制代碼

      api.js

      const app = getApp(); // method(HTTP 請(qǐng)求方法),網(wǎng)易云API提供get和post兩種請(qǐng)求方式 const GET = 'GET'; const POST = 'POST'; // 定義全局常量baseUrl用來存儲(chǔ)前綴 const baseURL = 'http://neteasecloudmusicapi.zhaoboy.com'; function request(method, url, data) {   return new Promise(function (resolve, reject) {     let header = {       'content-type': 'application/json',       'cookie': app.globalData.login_token     };     wx.request({       url: baseURL + url,       method: method,       data: method === POST ? JSON.stringify(data) : data,       header: header,       success(res) {         //請(qǐng)求成功         //判斷狀態(tài)碼---errCode狀態(tài)根據(jù)后端定義來判斷         if (res.data.code == 200) {  //請(qǐng)求成功           resolve(res);         } else {           //其他異常           reject('運(yùn)行時(shí)錯(cuò)誤,請(qǐng)稍后再試');         }       },       fail(err) {         //請(qǐng)求失敗         reject(err)       }     })   }) } const API = {   getSearchSuggest: (data) => request(GET, `/search/suggest`, data),  // 搜索建議接口   getSearchResult: (data) => request(GET, `/search`, data),  // 搜索結(jié)果接口 }; module.exports = {   API: API }復(fù)制代碼

      總結(jié)

      其實(shí)一點(diǎn)一點(diǎn)的捋清楚會(huì)發(fā)現(xiàn)也不是很難操作,首先思路要清晰,知道每一個(gè)功能是什么作用,同時(shí)在調(diào)試是時(shí)候去發(fā)現(xiàn)一些bug,再去對(duì)代碼進(jìn)行優(yōu)化。關(guān)于搜索這個(gè)功能用處廣泛,希望本次的分享能給大家?guī)硪稽c(diǎn)用處。

      相關(guān)學(xué)習(xí)推薦:微信公眾號(hào)開發(fā)教程,javascript視頻教程

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)