Vue introduction and training cases - Music Player (search music, listen to songs, watch comments, watch mv, etc.) (continuously updated)

Posted by venradio on Sun, 20 Feb 2022 07:14:22 +0100

Vue overview

  1. It is a javascript framework
  2. DOM operations can be simplified
  3. Responsive data driven

el: mount point

  1. Scope of vue instance: vue will manage the elements hit by el option and its internal descendant elements.
  2. Other selectors can be used, but id selectors are recommended
  3. Other double tags can be used, but HTML and BODY cannot be used. div tag is generally recommended
  4. Function: set the element of vue instance mount (Management)

Data: data object

  1. The data used in vue is defined in data
  2. Data can write complex types of data, such as objects, arrays, etc
  3. When rendering data of complex data types, just follow the syntax of js. The array is indexed with subscripts and the object is indexed with attributes

Local application

  • v-test: set the text value of the label; The default writing method will replace all contents. No matter what the contents are, they will only be parsed into text. If partial replacement is carried out, two braces {}} (difference expression) will be used. For example: < H2 > prompt: {{message + "!"}}</ h2>

  • v-html: innerHtml for label setting; HTML structure in the content will be automatically parsed into tags

  • V-on: bind event for element v-on:click = "method name"; The event name does not need to be written on; The method of binding events is to write method object methods: {method name: function() {/ / detailed logic}} in the script tag

    Short form: @ click = "method name"

    v-on can also pass custom parameters, event modifiers, and pass parameters: function (p1,p2)

    The method of event binding is written in the form of function call, and user-defined parameters can be passed in

    When defining a method, you need to define formal parameters to receive the incoming arguments

    Keep up with events Modifiers can limit events

    . enter can limit the triggered key to enter

  • Use this Get DOM element from data name

  • v-show: switch the display and hiding of elements according to the true or false expression. The contents behind the instruction will eventually be parsed into Boolean values. In order to facilitate manipulation, this instruction is generally defined as data. Supports the writing of expressions. Frequently switch elements with this

    Essence: modify the display attribute of the element to realize display and hiding

  • v-if: switch the display and hiding of elements according to the authenticity of the expression. This is used for infrequent switching.

    Essence: manipulate the removal and addition of dom elements

  • v-bind: set the attribute of the element. v-bind: attribute name = expression attribute name can be omitted. The class attribute needs to be added / deleted dynamically. It is recommended to use the object method

  • v-for: generate list structure based on data; Format: v-for="item in data (usually array name)" / / this will only generate li tag, but there is no content in it, so {item}} should be used to add content. In addition, the array should use the acquisition method of array index number (item, index), and the object should use the object's attribute acquisition method {item. Attribute name}};

    Arrays are often used in conjunction with v-for

    item and index can be used together with other instructions

    The update of array length will be synchronized to the page, which is responsive

  • v-model: easily obtain and set the value of form elements (two-way data binding); The bound data is associated with the value of the form element

network application

axios: network request library, which develops applications in combination with Vue network data

axios

axios. Get (address is the interface address provided by the document) then( function(response) {}, function(err) {})

axios. Get (address? Key = value & key2 = Value2) then( function(response) {}, function(err) {})

axios. Post (address, parameter object) then(function(response) {}, function(err) {})

axios. Post (address, {key: value, key2: Value2}) then(function(response) {}, function(err) {})

be careful:

  • axios must be imported before it can be used
  • Use the get or post method to send the corresponding request
  • The callback function in the then method is triggered when the request succeeds or fails
  • The response content or error information can be obtained through the formal parameters of the callback function
  • When used in combination with vue, this in the axios callback function cannot be changed and the data in data cannot be accessed. Therefore, it is necessary to save this and directly use the saved this in the callback function

Application case:

  1. When the data in the server is complex, pay attention to the hierarchical structure of the data
  2. Set the class name through the object. Whether the class name takes effect depends on the authenticity of the following values
  3. There are play and pause events in the audio
  4. When the page structure is complex, the related elements can be quickly located by reviewing the elements

music player

Implementation interface

HTML code
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>music player </title>
		<link type="text/css" href="css/music.css" rel="stylesheet" />
	</head>
	<body>
		<div class="wrap">
			<div class="play_wrap" id="player">
				<div class="search_bar">
					<div class="title">Pleasant listening</div>					
					<div class="search">
						<input v-model="music" @keyup.enter="search(music)" type="text" class="content" />
						<img class="loupe" src="img/mirror.png"  @click="search(music)"/>
					</div>
				</div>

				<div class="center_con">
					<div class="music_list">
						<ul class="music">
							<li class="music_li" v-for="item in musicList">
								<a href="javascript:;" @click="playMusic(item.id)">
									<img class="music_btn" src="img/play.png" />
								</a>
								<span class="music_name"> {{ item.name }} </span>
								<span class="mv_btn" @click="mvPlay(item.mvid)">
									<img class="mvtable" v-if="item.mvid!=0" src="img/mv.png" />
								</span>
							</li>
						</ul>
					</div>
					<div>
						<img class="left_line" src="img/line.png" />
					</div>
					<div class="pic" :class="{playing: isPlaying}" >
						<div class="top">
							<img class="player" src="img/player_bar.png" />
						</div>
						<div class="center">
							<img class="cover" :src="picUrl" />
							<img class="disc" src="img/disc.png" />
						</div>
					</div>
					<div>
						<img class="right_line" src="img/line.png" />
					</div>
					<div class="comment_list_outer">
						<b class="comm">Popular message</b><br />
						<div class="comment_list">
							<ul class="comment">
								<li class="comment_li" v-for="item in hotComments">
									<img class="user_pic" :src="item.user.avatarUrl"/>
									<div class="comm_right">
										<b class="user_nickname">{{ item.user.nickname }}</b><br />
										<span class="user_comm">{{ item.content }}</span>
									</div>
								</li>
							</ul>
						</div>
					</div>

				</div>

				<div class="audio_con">
					<audio :src="musicURL" @play="play" @pause="pause" class="audio" autoplay controls loop></audio>
				</div>

				<div class="video_con"  v-show="isShow">
					<video ref="video" v-if="{hide: pause}" v-bind:src="mvUrl" v-show="isShow" autoplay controls loop></video>
					<div class="mask" @click="hide">
						<div class="map">
							<div class="city">
								<div class="dotted"></div>
								<div class="pulse1"></div>
								<div class="pulse2"></div>
								<div class="pulse3"></div>
								You can order outside!
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
		<script src="js/vue.js" type="text/javascript" charset="UTF-8"></script>
		<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
		<script src="js/music.js"></script>
		
	</body>
</html>

CSS code
* {
	margin: 0;
	padding: 0;
	list-style: none;
}

.wrap {
	width: 100vw;
	height: 100vh;
	overflow: hidden;
	background: url(../img/bg.jpg) no-repeat;
	background-size: 100% 100%;
}

.play_wrap {
	width: 800px;
	height: 520px;
	margin: 77px auto;
}

.search_bar {
	position: relative;
	z-index: 4;
	border-radius: 5px 5px 0 0;
	height: 60px;
	display: flex;
	background-color: dodgerblue;
}

.title {
	flex: 1;
	padding-top: 10px;
	padding-left: 15px;
	color: white;
	line-height: 40px;
	font-size: 0;
	background: url(../img/player_title.png) no-repeat;
	background-position: 20px;
}

.search {
	position: relative;
	flex: 1;
	line-height: 60px;
	padding-left: 240px;
}

.loupe {
	position: absolute;
	top: 16px;
	left: 456px;
	width: 30px;
	height: 29px;
	cursor: pointer;
}

.content {
	overflow: hidden;
	position: absolute;
	top: 16px;
	left: 241px;
	border: none;
	outline: none;
	width: 250px;
	height: 30px;
	padding-left: 10px;
	border-radius: 15px;
	background-color: #fcfcfc;
}

.center_con {
	height: 445px;
	display: flex;
	background-color: rgb(255, 255, 255, .5);
}

.music_list {
	flex: 6;
	overflow-y: auto;
	overflow-x: hidden;
}

.music_list::-webkit-scrollbar,
.comment_list::-webkit-scrollbar {
	display: none;
}

.music {
	padding: 5px;
}

.music_li:nth-of-type(odd) {
	display: flex;
	padding: 3px;
	line-height: 37px;
	font-size: 12px;
}

.music_li:nth-of-type(even) {
	display: flex;
	padding: 3px;
	line-height: 37px;
	font-size: 12px;
	background-color: rgba(240, 240, 240, 0.3);
}

.comment_li {
	display: flex;
	padding: 10px;
	line-height: 20px;
	font-size: 15px;
}

.music_btn {
	width: 17px;
	height: 17px;
	padding-top: 10px;
	flex: 2;
}

.music_name {
	flex: 5;
	margin-left: 12px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.mv_btn {
	flex: 2;
	cursor: pointer;
}

.mvtable {
	padding-top: 10px;
	margin-left: 15px;
	width: 18px;
	height: 18px;
}

.left_line,
.right_line {
	width: 1px;
	height: 440px;
}

.pic {
	flex: 12;
}

.player {
	position: absolute;
	top: 125px;
	left: 745px;
	z-index: 3;
	transform: rotate(-30deg);
	transform-origin: 12px 12px;
	transition: 1s;
}

.playing .player {
	transform: rotate(0);
}

.disc {
	position: absolute;
	width: 265px;
	height: 265px;
	top: 200px;
	left: 629px;
	z-index: 2;
}

.cover {
	position: absolute;
	width: 160px;
	height: 160px;
	top: 250px;
	left: 682px;
	z-index: 1;

}

.comment_list_outer {
	flex: 6;
	padding: 8px;
}

.comment_list {
	height: 415px;
	overflow-y: auto;
	overflow-x: hidden;
}

.user_pic {
	width: 40px;
	height: 40px;
	border-radius: 50%;
}

.comm_right {
	margin: 7px;
}

.user_comm {
	font-size: 12px;
	color: #666;
}

.audio_con {
	height: 35px;

}

.audio {
	outline: none;
	width: 800px;
	height: 45px;
	border-radius: 0 0 5px 5px;
	background-color: rgb(241, 243, 244);
}

.mask {
	position: fixed;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
	z-index: 980;
	background-color: rgb(0, 0, 0, .4);
}

video {
	position: fixed;
	left: 368px;
	top: 96px;
	width: 800px;
	height: 531px;
	z-index: 990;
	/* border: 0; */
	outline: none;
}

@keyframes rotation {
	from {
		transform: rotateZ(0deg);
	}

	to {
		transform: rotateZ(360deg);
	}
}

.cover,
.disc {
	-webkit-transform: rotate(360deg);
	animation-name: rotation;
	animation-iteration-count: infinite;
	animation-play-state: paused;
	animation-timing-function: linear;
	animation-duration: 4s;
}

.pic.playing .disc,
.pic.playing .cover {
    animation-play-state: running;
}

.map {
	position: relative;
}

.city {
	position: absolute;
	top: 227px;
	right: 193px;
	color: #FFFFFF;
}

.city div[class^="pulse"] {
	/* Ensure that the small ripples are centered horizontally and vertically in the parent box */
	position: absolute;
	top: 11%;
	left: 3%;
	transform: translate(-50%, -50%);
	width: 8px;
	height: 8px;
	box-shadow: 0 0 12px #009DFB;
	border-radius: 50%;
	animation: pulse 1.6s linear infinite;
}

.city div.pulse2 {
	animation-delay: 0.8s;
}

.city div.pulse3 {
	animation-delay: 1.6s;
}

@keyframes pulse {
	10% {
		width: 10px;
		height: 10px;
		opacity: 1;
	}

	40% {
		width: 30px;
		height: 30px;
		opacity: 0.7;
	}

	70% {
		width: 50px;
		height: 50px;
		opacity: 0.4;
	}

	100% {
		width: 70px;
		height: 70px;
		opacity: 0;
	}
}

.dotted {
	width: 8px;
	height: 8px;
	border-radius: 4px;
	background-color: #009dfb;
}

js code
// Song search
// 1. Press enter (v-on. Enter)
// 2. Query data (axios interface v-model)
// 3. Render data (v-for array that)
// Request address: https://autumnfish.cn/search
// Request parameter: keywords

// Song playing
// 1. Click play (v-on)
// 2. Song address acquisition (interface song id)
// 3. Song address setting (v-bind)
// Request address: https://autumnfish.cn/song/url
// Request parameter: id (song id)

// Song cover
// 1. Click play (add logic)
// 2. Song cover acquisition (interface song id)
// 3. Song cover setting (v-bind)
// Request address: HTTPS: autumnfish cn/song/detail
// Request parameters: ids

// Song review
// 1. Click play (add logic)
// 2. Song comment acquisition (interface song id)
// 3. Song comment rendering (v-for)
// Request address: https://autumnfish.cn/comment/hot?type=0
// Request parameter: id

// Play animation
// 1. Monitor music play (v-on play)
// 2. v-on pause
// 3. Manipulate the class name (v-bind object, so you need to add a boolean value to the data)

// mv Play
// 1. Icon display (v-if)
// 2. Address acquisition (interface mvid)
// 3. Mask layer (v-show v-on)
// 4. Address setting (v-bind)
// Request address: https://autumnfish.cn/mv/url
// Request parameter: id (mvid, 0 means no mv)

var app = new Vue({
	el: "#player",
	data: {
		music: "",
		musicList: [],
		musicURL: "",
		picUrl: "",
		hotComments: [],
		// Display and hide of animation
		isPlaying: false,
		mvId: "",
		mvUrl: "",
		// Display and hide of mask layer
		isShow: false		
	},
	methods: {
		// Song search
		search:function(music) {
			var that = this;
			axios.get("https://autumnfish.cn/search?keywords=" + music)
			.then(function(response) {
				console.log(response);
				that.musicList = response.data.result.songs;
			}, function(err) {
				console.log(err);
			})
		},
		// Song playing
		playMusic: function(musicId) {
			var that = this;
			axios.get("https://autumnfish.cn/song/url?id="+musicId)
			.then(function(response) {
				that.musicURL = response.data.data[0].url;
			}, function(err) {
				console.log(err);
			});
			// Song cover
			axios.get("https:autumnfish.cn/song/detail?ids="+musicId)
			.then(function(response){
				console.log(1);
				// console.log(response);
				that.picUrl = response.data.songs[0].al.picUrl;
			},function(err){
				console.log(err);
			});
			// Song review
			axios.get("https://autumnfish.cn/comment/hot?type=0&id=" + musicId)
			.then(function(response) {
				console.log(2);
				// console.log(response);
				// console.log(response.data.hotComments);
				that.hotComments = response.data.hotComments;
			}, function(err) {
				console.log(err);
			})
		},
		// Play animation
		play: function() {
			// console.log("play");
			this.isPlaying = true;
		},
		pause: function() {
			// console.log("pause");
			this.isPlaying = false;
		},
		// Play mv
		mvPlay: function(mvId) {
			var that = this;
			axios.get("https://autumnfish.cn/mv/url?id=" + mvId)
			.then(function(response) {
				console.log(3);
				that.isShow = true;
				that.mvUrl = response.data.data.url;
			}, function(err) {
				console.log(err);
			})
		},
		hide: function() {
			this.isShow = false;
		}
		

	}
})

Last words

Some functions have not been realized yet, such as searching the historical list of songs, lyrics page, previous song, next song and other functions, which will be continuously updated in the later stage. If there are problems in the above code, please communicate.

Topics: Javascript Front-end css3 html5 Vue