Project Address:Github.com/ermu5922752…

Demo address: ermu592275254. Making. IO/MiniMusicPl… (Song link no longer works)

Pre-development concept

interface

Make music player, the interface must be cool. It’s too low to feel the music. Itself is to use at work, so made a similar netease cloud music interface, the size is appropriate. Not compatible with mobile phones.

Use CSS for ICONS

The ICONS can be SVG, URL or CSS. SVG and CSS are better than urls. In order to cultivate, the final choice of CSS. Taking advantage of after and before can reduce dom nesting.

 .next {
    position: relative;
    display: inline-block;
    height: 36px;
    width: 36px;
    border: 2px solid #fff;
    border-radius: 20px;
    -webkit-border-radius: 20px;
    -moz-border-radius: 20px;
}
        
.next:before {
    content: ' ';
    height: 0;
    width: 0;
    display: block;
    border: 10px transparent solid;
    border-right-width: 0;
    border-left-color: #fff;
    position: absolute;
    top: 8px;
    left: 10px;
}

.next:after {
    content: ' ';
    height: 20px;
    width: 4px;
    display: block;
    background: #fff;
    position: absolute;
    top: 8px;
    left: 22px;
}

Copy the code

Draw a record

Netease cloud’s record is very beautiful, I also want to make a record! With box-shadow, an element can be made into a nice album.

.disc {
    position: relative;
    margin-top: 10%;
    margin-left: 10%;
    width: 300px;
    height: 300px;
    border-radius: 300px;
    transform: rotate(45deg);
    background-image: radial-gradient(5em 30em ellipse, #fff, #000);
    border: 2px solid # 131313;
    box-shadow: 0 0 0 10px # 343935;Opacity: 0.7; }Copy the code

Make progress bar with range

The style of audio itself is ugly, and it behaves differently in different browsers. Of course you can change the style of Audio. The traditional way is to hide Audio through controls and use div instead. Now is the age of HTML5, of course, use new elements that are more suitable for the scene ————range.

 input[type=range] {
    -webkit-appearance: none;
    width: 80%;
    height: 8px;
    border-radius: 10px;
    background-color: #fff;
}
input[type=range]::-webkit-slider-thumb{
    -webkit-appearance: none;
} 
input[type=range]::-webkit-slider-runnable-track {
    height: 8px;
    border-radius: 20px;
}
input[type=range]:focus {
    outline: none;
}

input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    margin-top: -3px;
    height: 14px;
    width: 14px;
    background: #eb7470;
    border-radius: 50%;
    border: solid 3px #fff;Box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.5); }Copy the code

The background filter is blurred

It feels great to have the image set as the background, which provides half of the player’s appearance level. Setup is also very simple, using cSS3 filters.

.bg-blur {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
    height: 100%;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    filter: blur(20px);
    z-index: -1;
}
Copy the code

The background image is controlled by JS.

 <div class="bg-blur" :style="`background-image:url(${currentSong.album_logo}) `"></div>
Copy the code

Song resources

Climb down interface

Go directly to xiami’s official website to open the network, copy the URL into Postman to make a request. By modifying headers, the Referer is verified. This means that only domain names allowed by xiami can access this interface. http://api.xiami.com/web?v=2.0&app_key=1&key=aliez&page=1&limit=5&callback=jsonp154&r=search/songs

Resolve cross-domain problems

Because the interface supports JSONP. Start by trying to set chrome to cross domains and then make a JSONP request via $.ajax. It can be accessed normally.

Then all of a sudden it’s not working. Is it restricted by shrimp?

Instead, Node started a service to forge the referer to initiate a request and forward the request to the page. Accidentally wrote an agent.

.case '/song':
    let songOptions = {
        url: 'http://api.xiami.com/web?'+ urlArr[1],
        headers: {
            'Referer': 'http://m.xiami.com/'}};function callback1(error, response, body) {
        if(! error && response.statusCode == 200) { res.end(body); } } request(songOptions, callback1);break; .Copy the code

The lyrics scroll

As a forced lattice relatively high player, lyrics rolling is a must.

The principle of

Save each lyric as an object with a corresponding time. When the current playing time of the song is greater than or equal to the time of the song and less than the time of the next line of the song, the song is scrolled into the viewable area. And change the font color.

Format lyrics

Interface to return the lyrics of a face meng force, carefully study, found that there is a rule.

[ti:aLIEz] [AR :SawanoHiroyuki[nZk]: Mizuki] [LY: Hiroyuki] [MA :] [PU :] [TOTAL :268512] [offset:0] [00:00.000]<195>aLIEz <199>- <451>SawanoHiroyuki[nZk]: Mizuki [X-trans [00:01onsaturday (UK time). 095] < 201 > < 200 > < 250 > word: < 201 > jersey < 200 > wild yihong < 300 > < 199 > [x - trans] [00:02. 846] < 200 > < 150 > < 150 > music: < 200 > jersey < 200 > wild yihong < 349 > < 351 > [x - trans] [00:20. 828] should < 250 > < 200 > め < 200 > つ < 201 > け < 149 > ば < 201 > か < 349 > り [x - trans] has been assertive authoritarian [00:23. 279] < 200 > < 200 > since her < 200 > れ < 200 > を < 200 > and < 400 > た [x - trans] always try to be brave conceit [00:24. 979] < 200 > チ < 200 > ー < 200 > プ < 450 > な < 550 > hokori < 350 > で [x-trans] Obviously just a worthless pride...... refactoringLyrics(lyric){let text = lyric.split('[offset:0]') [1];let textArr = text.split('\n');
    let lyricsArr = [], translate = [];
    textArr.forEach((item, index) => {
        let time = 0, text = ' ';
        if (item.indexOf('[x-trans]') > -1) {
            translate.push(item.split('[x-trans]')[1])
        } else if(item.trim() ! =' ') {
            time = item.slice(1, 6).split(':');
            time = parseInt(time[0]) * 60 + parseInt(time[1]);
            text = item.slice(11);
            let arr = text.split('>');
            let str = arr.reduce((a, b) => {
                return a.split('<')[0] + b.split('<')[0]
            });
            letobj = { time: time, text: str }; lyricsArr.push(obj); }});for (let i in translate) {
        lyricsArr[i].text = lyricsArr[i].text + '\n' + translate[i];
    }
    this.currentLyrics = lyricsArr;
},
Copy the code

Search bar implementation

Mount the same file subcomponent

To follow modular development, it was decided to write the search bar as a child component. Write child components on the same page and mount them to the corresponding template. This template cannot be contained by the parent component’s mount element, otherwise the parent component will report undefined because it cannot render data in the child component.

<div id="app" class="main">... </div> <template id="search-box">... </template> var searchBox = { template:'#search-box',
        props: {
            isShow: Boolean,
            openFun: Function
        },
        data() {return {
                resultList: [],
                searchValue: ' ',
            }
        },
        methods: {
        }
    };
 new Vue({
    el: '#app',
    components: {
        'com-tip': tip,
        'search-box': searchBox
    },
    ...
})
Copy the code

EventBus handles data transfer

To request through the json data, you need to set up a callback function, the callback as a global function, if not to write, but rather through searchBox. The methods. The form of the callback, this point will be for the methods. You cannot directly assign a value to the searchBox’s data. This is handled through eventBus, which is more maintainable.

var EventBus = new Vue();
var callBack = function(result) {
    console.log(result);
    EventBus.$emit('callBack', result); }; .mounted() {let self = this;
    EventBus.$on('callBack'.function(res) {
        if(res && res.data) { self.resultList = res.data.songs; }})}...Copy the code

LocalStrong stores song information

The next time you open it, the playlist should retain the previous data, which you can implement directly with localstrong

Technology supplement

Filter fuzzy http://www.zhangxinxu.com/wordpress/2013/11/css-svg-image-blur/

CSS ICONSwww.uiplayground.in/css3-icons/

Flex compatible writing:www.cnblogs.com/iriszhang/p…

Flex Layout Tutorialwww.ruanyifeng.com/blog/2015/0…

Slider styles are customBlog.csdn.net/u013347241/…

Hide scrollbar compatibilitySegmentfault.com/q/101000000…

Audio propertiesDeveloper.mozilla.org/zh-CN/docs/…

Audio eventsDeveloper.mozilla.org/zh-CN/docs/…

On the pit

Prop transfer data

Using CDN, VUE prop supports only the midline format, not the hump format

Ps: It is ok to use humps in webpack-packed projects, which will be processed during the packaging process.

<search-box :is-show="showSearch" :open-fun="openSearch" @push-song="pushNewSong"
                @play-song="playSong"></search-box> // error <search-box :isShow="showSearch" :openFun="openSearch" @pushSong="pushNewSong"
                @playSong="playSong"></search-box>
Copy the code

To optimize

Manually modify the schedule, sometimes not take effect.

Search does not currently support paging

Playlists are not supported

The background color is similar to the progress bar color. Change the progress bar color

Play mode selection – single loop – random play is not supported