第一天:
-
音乐门户网站数据采集
搜索列表数据采集
歌曲MP3地址的获取 -
node环境安装
node环境的安装 — node安装包下载
https://nodejs.org/en/ -
visual studio code 编辑器的安装
官网地址下载并直接安装即可
https://code.visualstudio.com/ -
node服务器搭建
桌面上新建一个文件夹 node_server_demo
终端 cd 到 新建的文件夹中
通过 npm init 指令, 将 普通文件夹 node_server_demo,
做成 项目文件夹, 此时 会在文件夹中生成一个
package.json 文件
将 改文件夹 拖拽到 vs code中, 打开, 开始写代码
在文件夹 node_server_demo 中新建 index.js 文件
在 index.js 文件中 , 开始写 服务器代码 -
题外话: cnpm 的安装
题外话: npm 市场, 镜像 是国外, 所有有点慢
我们需要另外一个指令: cnpm
进行 是 过内容
cnpm安装办法:
npm install -g cnpm --registry=https://registry.npm.taobao.org
- koa服务器模块安装及使用
安装 服务器模块 koa
cnpm install koa --save
–save: 安装模块的同时, 是否将模块的依赖关系保存到
package.json文件中
用 koa 来搭建服务器
在 node_modules 模块文件夹中, 找到 koa
查看 koa 模块的 readme 文件, 里面有 代码说明
代码 写完之后 , 运行项目:
node index.js --------完事, 服务器成功了
停止服务器的快捷键: ctr + C
尽量在服务器代码中, 增加一个打印, 告诉用户, 服务器跑起来了。。
代码示例
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
- 让服务器具有 路由功能
安装 koa-router 路由模块
cnpm install koa-router --save
代码示例 不在 readme 中, 在 koa-router / lib / router.js 文件中 对
抄过来的代码进行简单的修改 重要的: const Router = require('@koa/router');
需要修改成下面的引入方式 const Router = require('koa-router'); 不同路由接口的示例代码:
const Koa = require('koa');
const app = new Koa();
// response
// 注释掉, 不要了
// app.use(ctx => {
// ctx.body = 'Hello Koa';
// });
const Router = require('koa-router');
const router = new Router();
router.get('/abc', (ctx, next) => {
ctx.body = "服务器 的abc界面"
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
停止服务器 , 重新运行
- 让服务器具有 静态资源访问能力
安装 koa-static 模块
cnpm install koa-static --save
readme 抄代码
const serve = require('koa-static');
// dirname: 整个项目的根路径
app.use(serve(dirname + '/static'));
停止服务器 , 重新运行
示例代码
const serve = require('koa-static');
// __dirname: 整个项目的根路径
app.use(serve(__dirname + '/static'));
- 让服务器 具有 文件上传能力
安装 koa-body 模块
cnpm install koa-body --save
readme 抄代码
const koaBody = require('koa-body');
app.use(koaBody({
multipart: true,
formidable: {
maxFieldsSize: 1024 * 1024 * 10
}
}));
准备一个网页界面, 写出 文件上传的功能
‘
未选择任何文件
’
服务器 提供 文件上传的接口 /upload
const fs = require("fs")
router.post('/upload', (ctx, next) => {
var xxx = ctx.request.files
var file_path = xxx.sx.path
var r = fs.createReadStream(file_path)
var w = fs.createWriteStream("./static/diao.jpg")
r.pipe(w)
ctx.body = "9999999"
});
服务器 完成接收到的上传的文件, 并执行 保存功能
停止服务器 , 重新运行
完整示例代码
const koaBody = require('koa-body');
app.use(koaBody({
multipart: true,
formidable: {
maxFieldsSize: 1024 * 1024 * 10
}
}));
router.post('/upload', (ctx, next) => {
var xxx = ctx.request.files
var file_path = xxx.sx.path
var r = fs.createReadStream(file_path)
var w = fs.createWriteStream("./static/1111.jpg")
r.pipe(w)
ctx.body = "9999999"
});
- 前端 实现 ajax 网络请求, 请求服务器的某一个接口
var req = new XMLHttpRequest()
req.open("GET" , "http://localhost:3000/xiaomi" , true)
req.send()
req.onreadystatechange = function () {
if (req.readyState == 4 && req.status == 200) {
console.log(req.responseText)
}
}
}
- 在服务器中完成 网络请求
安装 网络请求模块 request
cnpm install request --save
使用 request 模块 对, 酷我 进行网络请求
将 请求 得到的酷我的数据 放到 ctx.body=数据 进行返回
示例代码
// 根据关键字 搜索 音乐 --- 接口
router.get('/shuju', async (ctx, next) => {
var key_word = ctx.request.query.sx
key_word = encodeURIComponent(key_word)
var kuwu_url = "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=" + key_word + "&pn=1&rn=30&httpsStatus=1&reqId=b7648390-e052-11eb-849b-edb625ef25be"
var tou = {
"Cookie": "_ga=GA1.2.2054600232.1625791052; _gid=GA1.2.1090010168.1625791052; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1625791052,1625822255; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1625822272; kw_token=7O0VWI6F9WT",
"csrf": "7O0VWI6F9WT",
"Host": "www.kuwo.cn",
"Referer": "http://www.kuwo.cn/search/list?key=%E5%91%A8%E6%9D%B0%E4%BC%A6",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"
}
var a = new Promise(function (aaa , bbb) {
request.get({
url: kuwu_url,
headers: tou
} , function (err , res , body) {
if (err) {
bbb(err)
} else {
aaa(body)
}
})
})
// var res = 酷我的 接口 返回的 数据
ctx.body = await a
});
第二天
- 前后端交互, 获取音乐列表
根据搜索音乐列表的接口, 通过代理服务器,实现音乐搜索功能的数据采集
前端ajax示例代码:
function search_fn() {
var req = new XMLHttpRequest()
req.open("GET" , "http://localhost:3000/shuju?sx=" + key_input.value , true)
req.send()
req.onreadystatechange = function () {
if (req.readyState == 4 && req.status == 200) {
music_list = JSON.parse(req.responseText)
music_list = music_list.data.list
console.log(music_list)
var big_str = ""
for (var i = 0; i < music_list.length; i++) {
var a = music_list[i].name
var b = music_list[i].artist
var img_url = music_list[i].pic120
big_str = big_str + '<li><img src="' + img_url + '" />' + a + ' ' + b + ' <button onclick="play_fn(' + i + ')">播放</button></li>'
}
music_wrap.innerHTML = big_str
}
}
}
- 列表歌曲播放功能
得到音乐列表数据之后, 将数据放到 li 中进行界面展示, 当点解界面上播放按钮时,
将当前所点击的播放按钮的音乐id后得到, 并传递给后台服务器,
播放按钮的示例代码:
<button onclick="play_fn(' + i + ')">播放</button>
function play_fn(music_index){
play_index = music_index
play_by_index(music_index)
}
// 根据 播放坐标 实现具体歌曲的 播放功能
function play_by_index(music_index) {
cur_play_data = music_list[music_index]
var req = new XMLHttpRequest()
req.open("GET" , "http://localhost:3000/get_mp3_by_id?rid=" + cur_play_data.rid , true)
req.send()
req.onreadystatechange = function () {
if (req.readyState == 4 && req.status == 200) {
var music_data = JSON.parse(req.responseText)
var music_url = music_data.url
my_audio.src = music_url
my_audio.play()
}
}
}
让后台服务器 通过 该 id 号来获取该 歌曲的 mp3 播放地址
并返回给 前端网页
服务器获取 mp3 播放地址并返回的示例代码:
router.get("/get_mp3_by_id" , async (ctx , next) => {
var rid = ctx.request.query.rid
var format_mp3_url = "http://www.kuwo.cn/url?format=mp3&rid=" + rid + "&response=url&type=convert_url3&br=128kmp3&from=web&t=1625884737728&httpsStatus=1&reqId=f9e28c01-e127-11eb-bc58-479da32112ce"
var a = new Promise(function (aaa , bbb) {
request.get(format_mp3_url , function (err , res , body) {
if (err) {
bbb(err)
} else {
aaa(body)
}
})
})
ctx.body = await a
})
前端网页通过从后台得到的 mp3 播放地址, 进行歌曲的播放:
if (req.readyState == 4 && req.status == 200) {
var music_data = JSON.parse(req.responseText)
var music_url = music_data.url
my_audio.src = music_url
my_audio.play()
}
第三天
- 播放暂停功能
通过对按钮描述信息的判断, 实现不同的功能 播放 / 暂停
示例代码:
// 播放 / 暂停的方法
play_or_pause.onclick = function () {
// 获取元素身上的属性
if (this.getAttribute("zhuangtai") == "playing") {
my_audio.pause()
this.innerHTML = "播放"
this.setAttribute("zhuangtai" , "pausing")
play_img.className = "play_img"
} else {
my_audio.play()
this.innerHTML = "暂停"
this.setAttribute("zhuangtai" , "playing")
play_img.className = "play_img zhuan"
}
}
-
上一曲 / 下一曲功能
通过改变当前所播放的音乐下标来完成上一曲 或 下一曲功能 -
随机播放
通过随机函数, 生成随机下标, 并完成随机播放功能
// 随机函数
function my_random(min , max) {
return parseInt(Math.random() * (max - min) + min)
}
- 单曲循环 / 列表循环
枚举不同的 歌曲的播放模式, 根据不同的枚举值进行不同的处理操作
// 0:顺序播放 1:随机播放 2:单曲循环
var order_type = 0
// 切换播放顺序 按钮 的 点击事件
order_btn.onclick = function () {
// 顺序播放
// 随机播放
// 单曲循环
order_type++
order_type = order_type % 3
if (order_type == 0) {
this.innerHTML = "顺序播放"
} else if (order_type == 1) {
this.innerHTML = "随机播放"
} else if (order_type == 2) {
this.innerHTML = "单曲循环"
}
}
- 上一曲 、 下一曲 、 单曲循环 、 列表循环 、 随机播放 示例代码
// 上一曲 按钮 的 点击事件
prev_btn.onclick = function () {
if (order_type == 0) {
if (play_index == 0) {
} else {
play_index = play_index - 1
play_by_index(play_index)
}
} else if (order_type == 1){
play_index = 随机数
play_by_index(随机数)
} else if (order_type == 2) {
play_by_index(play_index)
}
}
// 下一曲 按钮 的 点击事件
next_btn.onclick = function () {
if (order_type == 0) {
play_index = play_index + 1
play_index = play_index % music_list.length
play_by_index(play_index)
} else if (order_type == 1){
var suiji_num = my_random(0 , music_list.length)
play_index = suiji_num
play_by_index(suiji_num)
} else if (order_type == 2) {
play_by_index(play_index)
}
}
- 监听播放进度, 并通过时间和进度条来展示
在界面上准备进度条, 并做好css样式
<div class="progress"></div>
/* 进度条 */
.progress{
width: 90%;
height: 20px;
background: linear-gradient(to right , red 70% , blue 0%);
margin: 0 auto;
}
准备好监听函数, 监听当前播放器的状态
// 播放器监听函数
function listen_audio() {
var cur_time = my_audio.currentTime
var all_time = my_audio.duration
if (cur_time && all_time) {
var percent = cur_time / all_time * 100
// 将监听到的播放的百分比 绘制到 进度条上
progress.style.background = "linear-gradient(to right , red " + percent + "% , blue 0%)"
// 将监听到的时间, 同时时间函数的转换之后, 再绘制到界面上
cur_time_wrap.innerHTML = format_time(cur_time)
all_time_wrap.innerHTML = format_time(all_time)
}
requestAnimationFrame(listen_audio)
}
listen_audio()
// 时间格式的转换函数
function format_time(pass_time){
// 123
// 02:03
var fen = parseInt(pass_time / 60)
if (fen < 10) {
fen = "0" + fen
}
var miao = parseInt(pass_time % 60)
if (miao < 10) {
miao = "0" + miao
}
return fen + ":" + miao
}
- 实现快进 、 快退功能
给进度条增加点击事件函数, 并实现歌曲的 点击快进 、 快退的播放功能
示例代码:
// 进度条的点击事件
progress.onclick = function () {
var percent = event.offsetX / this.offsetWidth
my_audio.currentTime = my_audio.duration * percent
}
第四天
- CSS布局 ---- 定位布局
定位:position
四种定位方式:
1. static 静态定位 没有开定位
2. fixed 固定定位 开定位了 固定到窗口上
参照物: 窗口
3. relative: 相对定位 开定位了 相对自己原有的位置进行定位
参照物: 自己 原有的位置
4. absolute: 绝对定位 开定位了 在某一定的定位范围之内进行定位
参照物: 已开定位的范围
元素必须开定位:
上下左右(top , bottom , left , right) 才管用
使用技巧
父元素 开 定位 relative
给子元素 开 absolute
left > right
top > bottom
通过定位让元素居中的问题: - 让元素懵
- 给元素开 margin:auto;
- CSS布局 ---- 浮动布局
浮动 ----- 飘:
1.float: 只有 两个方向 left 和 right
2. 浮动的元素 必然 会 影响自己的兄弟(兄弟可以和净化: clear:both;)
3. 一个元素 要想浮动, 先看自己的哥哥
如果 自己的哥哥浮动了, 那么跟着自己的哥哥浮动
如果 自己的哥哥没有浮动, 那么在自己哥哥的下一行进行浮动
浮动的元素 永远不能超出父视图的 横向范围 - CSS布局 ---- 弹性布局
弹性布局:- 给父元素 开启 弹性盒子模式
- 子元素 按照弹性比例值, 进行区域划分就可以
- 弹性布局的方式, 默认是横向布局方式
- 比例值的运算规则: 按照父元素的剩余空间, 进行比例的划分
- 有两个用来控制 布局位置的 单词
justify-content: 主轴
align-item: 次轴
弹性布局的方向 ==== 主轴
另外一个方向 ==== 次轴
- CSS ---- 弹性正方形
.test{
width: 10%;
height: 0px;
border: 1px solid black;
padding-top: 10%;
background-color: lightblue;
}
- CSS ---- 三角形
.test{
width: 0px;
height: 0px;
/* border: 60px solid black; */
/* background-color: lightblue; */
border-left: 60px solid yellow;
border-right: 60px solid transparent;
border-top: 60px solid yellow;
border-bottom: 60px solid yellow;
border-radius: 50%;
}
- 上午完善该项目, 下午项目答辩