当前位置:网站首页>仿网易云音乐小程序
仿网易云音乐小程序
2022-07-03 21:18:00 【*neverGiveUp*】
一、程序描述
仿网易云音乐小程序,调用网易云API真实接口,实现了音乐播放,音乐切换,歌词显示,搜索音乐,视频播放,用户登录退出等功能。以下是效果展示:
仿网易云小程序
二、代码介绍
(1)目录介绍
(2)搜索 代码讲解
实现思路:
- 先把搜索的静态页面显示出来
- 调用接口把热搜榜的数据改为动态
- 实现根据输入框的值进行搜索
- 每搜索一次,历史记录会记录一下存到本地存储,之后再小程序的生命周期onLoad去获取历史记录
代码如下:
--search.wxml
<view class="searchContainer">
<!-- 头部搜索区域 -->
<view class="header">
<view class="searchInput">
<text class="iconfont icon-search1 searchIcon"></text>
<input type="text" value="{
{searchContent}}" placeholder="{
{placeholderData}}" placeholder-class="placeholder" bindinput="handleInputChange" />
<text class="clear" bindtap="clearSearchContent" hidden="{
{!searchContent}}">X</text>
</view>
<text class="cancel">取消</text>
</view>
<block wx:if="{
{searchList.length}}">
<!-- 搜索内容展示 -->
<view class="showSearchContent">
<view class="searchContent">搜索内容:{
{
searchContent}}</view>
<view class="searchList">
<view class="searchItem" wx:for="{
{searchList}}" wx:key="id">
<text class="iconfont icon-search1"></text>
<text class="content" bindtap="toSongDeatil" id="{
{item.id}}">{
{
item.name}}</text>
</view>
</view>
</view>
</block>
<block wx:else>
<!--搜索历史记录 -->
<view class="history" wx:if="{
{historyList.length}}">
<view class="title">历史</view>
<view class="historyItem" wx:for="{
{historyList}}" wx:id="{
{item}}" bindtap="searchHistory" data-historyword="{
{item}}">{
{
item}}</view>
<!-- 删除 -->
<text class="iconfont icon-shanchu delete" bindtap="deleteSearchHistory"></text>
</view>
<!-- 热搜榜 -->
<view class="hotContainer">
<view class="title">热搜榜</view>
<!-- 热搜列表 -->
<view class="hotList">
<view class="hotItem" wx:for="{
{showList}}" wx:key="searchWord">
<text class="order">{
{
index+1}}</text>
<text bindtap="searchHotSong" data-hotwords="{
{item.searchWord}}">{
{
item.searchWord}}</text>
<image class="iconImg" wx:if="{
{item.iconUrl}}" src="{
{item.iconUrl}}"></image>
</view>
</view>
</view>
</block>
</view>
--search.js
import request from "../../utils/request"
let isSend = false
// pages/search/search.js
Page({
/** * 页面的初始数据 */
data: {
placeholderData: "", // 搜索框的默认值
showList: [],// 热搜榜的歌曲推荐
searchContent: "", //输入框的值
searchList: [],// 搜索的匹配数据
historyList: [], // 历史记录中
},
/** * 生命周期函数--监听页面加载 */
onLoad: function (options) {
// 调用 获取搜索框的默认值
this.getPlaceholderData()
// 获取历史记录
this.getHistory()
},
// 从本地获取搜索历史记录
getHistory() {
let historyList = wx.getStorageSync('searchHistory')
if (historyList) {
this.setData({
historyList
})
}
},
// 获取默认搜索框的值
async getPlaceholderData() {
let placeholderData = await request("/search/default")
let showMusic = await request("/search/hot/detail")
// 修改状态
this.setData({
placeholderData: placeholderData.data.showKeyword,
showList: showMusic.data
})
},
// 表单项内容发生改变的回调
handleInputChange(event) {
// 修改searchContent的状态数据
this.setData({
searchContent: event.detail.value.trim(),
})
if (isSend) {
return
}
isSend = true;
this.getSearchList();
// 函数节流
setTimeout(() => {
isSend = false;
}, 300)
},
// 获取搜索数据的功能函数
async getSearchList() {
// 如果搜索框没有值,我们是要显示歌曲推荐
if (!this.data.searchContent) {
this.setData({
searchList: []
})
return;
}
// 发请求获取关键字模糊匹配数据
let {
searchContent, historyList } = this.data
let searchList = await request("/search", {
keywords: searchContent, limit: 10 })
this.setData({
searchList: searchList.result.songs
})
if (historyList.indexOf(searchContent) != -1) {
// 在历史记录中以及有出现过
historyList.splice(historyList.indexOf(searchContent), 1) // 在历史记录中删除
}
// 将搜索的关键字添加到搜索历史记录中
historyList.unshift(searchContent)
this.setData({
historyList
})
wx.setStorageSync('searchHistory', historyList)
},
// 清除搜索内容
clearSearchContent() {
this.setData({
searchContent: "",
searchList: [],
})
},
// 删除搜索历史记录
deleteSearchHistory() {
wx.showModal({
content: '确认删除吗?',
success: (res) => {
if (res.confirm) {
// 点击确认删除
this.setData({
historyList: []
});
wx.removeStorageSync('searchHistory')
}
}
})
},
/* 跳转到歌曲详情页面 */
toSongDeatil(event) {
console.log(event)
wx.navigateTo({
url: '/pages/songDetail/songDetail?musicId=' + event.currentTarget.id,
})
},
/* 点击热搜索榜进行搜索 */
searchHotSong(event) {
console.log(event)
this.setData({
searchContent: event.currentTarget.dataset.hotwords
})
// 发送请求获取搜索匹配到的数据
this.getSearchList()
}
})
--search.wxss
/* pages/search/search.wxss */
.searchContainer{
padding: 0 20rpx;
}
.header {
display: flex;
height: 60rpx;
line-height: 60rpx;
padding: 10rpx 0;
}
.searchInput {
position: relative;
flex: 1;
background: rgba(237,237,237,0.3);
border-radius: 30rpx;
}
.cancel {
width: 100rpx;
text-align: center;
}
.searchIcon{
position: absolute;
left: 15rpx;
}
.searchInput input{
margin-left: 50rpx;
height: 60rpx;
}
.placeholder{
font-size: 28rpx;
}
/* 热搜榜 */
.hotContainer {
margin-top: 30rpx;
}
.hotContainer .title{
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
border-bottom: 1rpx solid #eee;
}
.hotList{
display: flex;
flex-wrap: wrap;
}
.hotItem {
width: 50%;
height: 80rpx;
line-height: 80rpx;
font-size: 26rpx;
}
.hotItem .order{
margin: 0 10rpx;
}
.hotItem .iconImg {
width: 35rpx;
height: 20rpx;
margin-left: 10rpx;
}
/* 搜索历史 */
.history {
position: relative;
display: flex;
flex-wrap: wrap;
line-height: 50rpx;
margin: 20rpx 0;
}
.history .title{
font-size: 28rpx;
height: 50rpx;
}
.history .historyItem {
height: 50rpx;
font-size: 26rpx;
background: #ededed;
margin-top: 5rpx;
margin-left: 20rpx;
padding: 0 30rpx;
border-radius: 20rpx;
}
.history .delete{
position: absolute;
bottom: 10rpx;
right: 15rpx;
font-size: 36rpx;
}
.clear{
position: absolute;
z-index: 10; /* 优先级 */
right: 30rpx;
top:0;
font-size: 30rpx;
}
/* 搜索内容展示 */
.searchContent {
color: #d43c33;
height: 80rpx;
line-height: 80rpx;
font-size: 24rpx;
border-bottom: 1rpx solid #d43c33;
}
.searchItem {
height: 80rpx;
line-height: 80rpx;
display: flex;
}
.searchItem .content {
flex: 1;
margin-left: 20rpx;
border-bottom: 1rpx solid #eee;
font-size: 26rpx;
}
(3)每日推荐 代码讲解
实现思路:
- 写好静态页面
- 头部日期改为动态
- 调用接口实现动态数据
代码如下:
--recommendSong.wxml
<view class="recommendSongContainer">
<!-- 头部 -->
<view class="header">
<image src="/static/images/recommendSong/recommendSong.jpg"></image>
<view class="date">
<text class="day">{
{
month}} / </text>
<text class="month">{
{
day}}</text>
</view>
</view>
<!-- 列表区域 -->
<view class="ListContainer">
<view class="listHeader">
<text>播放全部</text>
<text class="changeMore">多选</text>
</view>
<!-- 内容区 -->
<scroll-view scroll-y class="listScroll">
<view class="scrollItem" wx:for="{
{recommentList}}" wx:key="id" bindtap="toSongDetail" data-index="{
{index}}" data-song="{
{item}}">
<image src="{
{item.album.picUrl}}"></image>
<view class="musicInfo">
<text class="musicName">{
{
item.name}}</text>
<text class="author">{
{
item.ar[0].name}}</text>
</view>
<text class="iconfont icon-gengduo"></text>
</view>
</scroll-view>
</view>
</view>
--recommendSong.js
import Pubsub from 'pubsub-js'
import request from '../../utils/request.js'
Page({
/** * 页面的初始数据 */
data: {
day: " ", // 天
month: " ", // 月
recommentList: [], // 存储推荐歌曲
index:0,// 点击音乐下标
},
/** * 生命周期函数--监听页面加载 */
onLoad: function (options) {
// 判断用户是否登录
let userLogin = wx.getStorageSync('userInfo')
if (!userLogin) {
// 用户没有登录,出现提示信息
wx.showToast({
title: '请先登录',
icon:'none',
success: function () {
wx.reLaunch({
url: '/pages/login/login'
})
}
})
}
// 动态修改日期
this.setData({
/* date.getDay()//星期几 date.getDate()//当前是几号 */
day: new Date().getDate(),
month: new Date().getMonth() + 1
})
// 调用推荐歌曲接口
this.getRecommendList()
// 订阅来自songDetail页面发布的消息
Pubsub.subscribe("switchType",(msg,type) =>{
// console.log(msg,type)
let {
recommentList,index} = this.data
if(type==='pre'){
// 上一首
// 如果是是第一首歌的情况,也就是下标为0,会跳到最后一首歌的下标
(index==0)&&(index=recommentList.length)
index-=1
}else{
// 下一首
// 如果是最后一首歌的情况,也就是下标为长度-1,那么下一首歌会到第一首歌
(index==recommentList.length-1)&&(index=-1)
index+=1
}
// 更新下标
this.setData({
index
})
// 将musicId回传给songDetail页面
Pubsub.publish('musicId',recommentList[index].id)
})
},
async getRecommendList() {
let result = await request('/recommend/songs')
// 更新数据
this.setData({
recommentList: result.data.dailySongs
})
},
/* 点击每日推荐歌曲事件 */
toSongDetail(event){
// 获取点击歌曲的id
let {
song,index} = event.currentTarget.dataset;
// 存储点击歌曲的下标
this.setData({
index
})
// 路由跳转
wx.navigateTo({
url:"/pages/songDetail/songDetail?musicId="+song.id
})
}
--recommendSong.wxss
/* pages/recommendSong/recommendSong.wxss */
/* 头部 */
page{
height: 100%;
}
.recommendSongContainer .header {
position: relative;
width: 100%;
height: 300rpx;
}
.recommendSongContainer .header image {
width: 100%;
height: 100%;
}
.recommendSongContainer .header .date{
position: absolute;
left: 50%;
top:50%;
margin-left: -133rpx;
margin-top: -36rpx;
width: 300rpx;
height: 100rpx;
text-align: center;
line-height: 100rpx;
color: #fff;
}
.header .date .day {
font-size: 38rpx;
}
/* 列表区域 */
.ListContainer {
position: relative;
top:-20rpx;
padding: 0 20rpx;
border-radius: 30rpx;
background: #fff;
}
.listHeader {
height: 80rpx;
line-height: 80rpx;
}
.listHeader .changeMore {
float: right;
}
/* 内容区域 */
.listScroll{
height: calc(100vh - 380rpx);
}
.scrollItem{
position: relative;
display: flex;
margin-bottom: 20rpx;
}
.scrollItem image{
width: 80rpx;
height: 80rpx;
border-radius:8rpx;
}
.musicInfo {
display: flex;
flex-direction: column;
margin-left: 20rpx;
}
.musicInfo text {
height: 40rpx;
line-height: 40rpx;
font-size: 24rpx;
max-width: 500rpx;
white-space: noweap;
overflow: hidden;
text-overflow: ellipsis;
}
.scrollItem .iconfont {
position: absolute;
right: 0;
width: 80rpx;
height: 80rpx;
line-height: 80rpx;
text-align: right;
}
(4)音乐播放页 代码讲解
实现思路:
- 写好静态页面
- 从推荐歌单页或每日推荐页获取音乐id,音乐下标index,再传给音乐播放页,实现音乐的播放和切换
- 歌词显示和音乐播放进度
代码如下:
--songDetail.wxml
<view class="songDetailContainer">
<view class="author">{
{
songList.ar[0].name}}</view>
<view class="circle"></view>
<image class="needle {
{isPlay && 'needleRotate'}}" src="/static/images/song/needle.png"></image>
<view class="discContainer {
{isPlay && 'discAnimation'}}" >
<image class="disc" src="/static/images/song/disc.png"></image>
<image class="musicImg" src="{
{songList.al.picUrl}}"></image>
</view>
<!-- 歌词 -->
<view class="scrollLrc">
<text>{
{
currentLyric}}</text>
</view>
<!-- 进度条控制区域 -->
<view class="progressControl">
<text>{
{
currentTime}}</text>
<!-- 总进度 -->
<view class="barContril">
<!-- 实时进度条 -->
<view class="audio-currentime-Bar" style="width: {
{currentWidth + 'rpx'}}">
<!-- 小圆球 -->
<view class="audio-circle"></view>
</view>
</view>
<text>{
{
durationTime}}</text>
</view>
<!-- 底部控制播放区域 -->
<view class="musicControl">
<text class="iconfont icon-iconsMusicyemianbofangmoshiShuffle"></text>
<text class="iconfont icon-shangyishou" id="pre" bindtap="handleSwitch"></text>
<text class="iconfont {
{isPlay?'icon-zanting': 'icon-bofang'}} big" bindtap="handleMusicPlay"></text>
<text class="iconfont icon-next" id="next" bindtap="handleSwitch"></text>
<text class="iconfont icon-iconsMusicyemianbofangmoshiPlayList"></text>
</view>
</view>
--songDeatai.js
import Pubsub from 'pubsub-js'
import request from '../../utils/request.js'
import moment from 'moment'
// pages/songDetail/songDetail.js
Page({
/** * 页面的初始数据 */
data: {
isPlay: false, // 标记音乐是否播放
songList: {
}, // 歌曲详细信息
musicId: "",// 存储歌曲id
musicLink: "", // 存储音乐链接
currentTime: '00:00',// 实时时间
durationTime: '00:00', // 总时长
currentWidth: 0, // 实时进度条的宽度
currentLyric: "",//当前歌词对象
lyric: [], //歌词
lyricTime: 0,//歌词对应的时间
},
/** * 生命周期函数--监听页面加载 */
onLoad: function (options) {
/* options 接受的是路由传过来的值 */
let musicId = options.musicId
this.setData({
musicId,
})
// 调用 获取歌单详细信息
this.getMusicInfo(musicId)
// 调用 歌词
this.getLyric(musicId);
// 创建控制音乐播放的实例
this.backgroundAudioManager = wx.getBackgroundAudioManager();
// 监视音乐播放/暂停
this.backgroundAudioManager.onPlay(() => {
this.getIsPlay(true)
});
this.backgroundAudioManager.onPause(() => {
this.getIsPlay(false)
});
this.backgroundAudioManager.onStop(() => {
this.getIsPlay(false)
})
// 监听音乐实时播放的进度
this.backgroundAudioManager.onTimeUpdate(() => {
this.musicPlayTime()
})
// 监听音乐播放自然结束
this.backgroundAudioManager.onEnded(() => {
console.log("即将进入下一首")
// 自动切换至下一首音乐,并且自动播放
Pubsub.publish('switchType', 'next')
// 将实时进度条的长度还原成0;时间还原成0;
this.setData({
currentWidth: 0,
currentTime: '00:00',
isPlay: false,
lyric: [],
currentLyric: "",
lyricTime: 0,//歌词对应的时间
})
Pubsub.subscribe('musicId', (msg, musicId) => {
// 获取歌曲
this.getMusicInfo(musicId)
// 获取歌词
this.getLyric(musicId)
// 自动播放当前音乐
this.musicControl(true, musicId)
Pubsub.unsubscribe('musicId')
})
})
// 一进来就让音乐播放
this.musicControl(true, musicId)
},
// 观察音乐播放进度
musicPlayTime() {
// 获取歌词对应时间
let lyricTime = Math.ceil(this.backgroundAudioManager.currentTime)
// 格式化实时的播放时间
let currentTime = moment(this.backgroundAudioManager.currentTime * 1000).format("mm:ss")
let currentWidth = (this.backgroundAudioManager.currentTime / this.backgroundAudioManager.duration) * 450;
this.setData({
currentTime,
lyricTime,
currentWidth
})
// 获取当前歌词
this.getCurrentLyric()
},
// 修改isPlay的状态
getIsPlay(isPlay) {
this.setData({
isPlay
})
},
// 获取歌曲详情信息
async getMusicInfo(musicId) {
let result = await request("/song/detail", {
ids: musicId })
let durationTime = moment(result.songs[0].dt).format("mm:ss")
this.setData({
songList: result.songs[0],
durationTime
})
// 动态修改窗口标题
wx.setNavigationBarTitle({
title: this.data.songList.name
})
},
// 点击播放 / 暂停的回调
handleMusicPlay() {
let isPlay = !this.data.isPlay
// 修改数据状态
this.setData({
isPlay
})
let {
musicId, musicLink } = this.data;
this.musicControl(isPlay, musicId, musicLink)
},
// 控制音乐播放/暂停的功能函数
async musicControl(isPlay, musicId, musicLink) {
if (isPlay) {
// 音乐播放
if (!musicLink) {
// 第一次发请求
let musicLinkData = await request('/song/url', {
id: musicId })
// 获取音乐链接
musicLink = musicLinkData.data[0].url;
// 更新音乐链接
this.setData({
musicLink
})
}
this.backgroundAudioManager.src = musicLink; // 音频的数据源
this.backgroundAudioManager.title = this.data.songList.name; // 音频标题,必填
} else {
// 音乐暂停
this.backgroundAudioManager.pause(); // 暂停音乐
}
},
// 点击切歌的回调
handleSwitch(event) {
// 获取切歌的类型
let type = event.currentTarget.id;
console.log(type)
// 关闭当前播放的音乐
this.backgroundAudioManager.stop();
// 发布消息数据给recommendSong页面
Pubsub.publish("switchType", type)
Pubsub.subscribe('musicId', (msg, musicId) => {
console.log(musicId)
// 获取音乐详情信息
this.getMusicInfo(musicId);
// 获取音乐的歌词
this.getLyric(musicId)
// 自动播放当前音乐
this.musicControl(true, musicId);
// 取消订阅
Pubsub.unsubscribe('musicId')
})
},
//获取歌词
async getLyric(musicId) {
let lyricData = await request("/lyric", {
id: musicId });
let lyric = this.formatLyric(lyricData.lrc.lyric);
},
//传入初始歌词文本text
formatLyric(text) {
let result = [];
let arr = text.split("\n"); //原歌词文本已经换好行了方便很多,我们直接通过换行符“\n”进行切割
let row = arr.length; //获取歌词行数
for (let i = 0; i < row; i++) {
let temp_row = arr[i]; //现在每一行格式大概就是这样"[00:04.302][02:10.00]hello world";
let temp_arr = temp_row.split("]");//我们可以通过“]”对时间和文本进行分离
let text = temp_arr.pop(); //把歌词文本从数组中剔除出来,获取到歌词文本了!
//再对剩下的歌词时间进行处理
temp_arr.forEach(element => {
let obj = {
};
let time_arr = element.substr(1, element.length - 1).split(":");//先把多余的“[”去掉,再分离出分、秒
let s = parseInt(time_arr[0]) * 60 + Math.ceil(time_arr[1]); //把时间转换成与currentTime相同的类型,方便待会实现滚动效果
obj.time = s;
obj.text = text;
result.push(obj); //每一行歌词对象存到组件的lyric歌词属性里
});
}
result.sort(this.sortRule) //由于不同时间的相同歌词我们给排到一起了,所以这里要以时间顺序重新排列一下
this.setData({
lyric: result
})
},
//控制歌词播放
getCurrentLyric() {
let j;
for (j = 0; j < this.data.lyric.length - 1; j++) {
if (this.data.lyricTime == this.data.lyric[j].time) {
this.setData({
currentLyric: this.data.lyric[j].text
})
}
}
}
})
--songDeatil.wxss
page {
height: 100%;
}
.songDetailContainer{
height: 100%;
/* background: rgba(0,0,0,0.5); */
background-color: #696969;
display: flex;
flex-direction: column;
align-items: center;
}
/* 底座 */
.circle{
position: relative;
z-index: 100;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: #fff;
margin: 10rpx 0;
}
/* 摇杆*/
.needle {
position: relative;
z-index: 99;
top: -40rpx;
left: 60rpx;
width: 192rpx;
height: 274rpx;
transform-origin: 40rpx 0; /* 旋转中心点 ,x轴 y轴 */
transform: rotate(-20deg); /* 旋转 */
transition: transform 1s; /* 过渡 */
}
.needleRotate {
transform: rotate(0deg);
}
/* 磁盘 */
.discContainer {
position: relative;
top: -170rpx;
width: 598rpx;
height: 598rpx;
}
.discAnimation {
animation: disc 4s linear infinite;/* infinite 无限循环 */
animation-delay: 1s; /* 动画延迟1s */
}
/* @keyframes:设置动画帧 1) from to -使用简单的动画,只有起始帧和结束帧 2) 百分比 -多用于复杂的动画,动画不止两帧 */
@keyframes disc {
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
.disc {
width: 598rpx;
height: 598rpx;
}
.musicImg{
position: absolute;
top:0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 370rpx;
height: 370rpx;
border-radius: 50%;
}
/* 歌词显示 */
.scrollLrc {
position: absolute;
bottom: 280rpx;
width: 640rpx;
height: 120rpx;
line-height: 120rpx;
text-align: center;
}
/* 底部控制区域 */
.musicControl {
position: absolute;
bottom: 40rpx;
left: 0;
border-top: 1rpx solid #fff;
width: 100%;
display: flex;
}
.musicControl text {
width: 20%;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: #fff;
font-size: 50rpx;
}
.musicControl text.big{
font-size: 80rpx;
}
/* 进度条控制区域 */
.progressControl{
position: absolute;
bottom: 200rpx;
width: 640rpx;
height: 80rpx;
line-height: 80rpx;
display: flex;
}
.barContril{
position: relative;
width: 450rpx;
height: 4rpx;
background: rgba(0,0,0,0.4);
margin: auto;
}
.audio-currentime-Bar{
position: absolute;
top:0;
left: 0;
width: 100rpx;
z-index: 1;
height: 4rpx;
background: red;
}
/* 小圆球 */
.audio-circle{
position: absolute;
right: -12rpx;
top:-4rpx;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: #fff;
}
(5)pubsub-js消息发布订阅
pubsub-js适用于任意组件间的通信
npm install pubsub-js // 安装
import Pubsub from ‘pubsub-js’ // 文件中引用Pubsub.subscribe ( “XXX” , (msg,type) => { } ) // 订阅
Pubsub.unsubscribe(“XXX”)// 取消订阅
Pubsub.publish(“XXX”,值)// 发布
每日推荐页与音乐播放页所用到的pubsub-js
三、相关资料
https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=%e6%8e%a5%e5%8f%a3%e6%96%87%e6%a1%a3
https://download.csdn.net/download/qq_48701993/85877882
边栏推荐
- Pengcheng cup Web_ WP
- Scientific research document management Zotero
- Volley source code analysis
- Advanced collaboration: coroutinecontext
- Xai+ network security? Brandon University and others' latest "interpretable artificial intelligence in network security applications" overview, 33 page PDF describes its current situation, challenges,
- 《ActBERT》百度&悉尼科技大学提出ActBERT,学习全局局部视频文本表示,在五个视频-文本任务中有效!...
- University of Electronic Science and technology | playback of clustering experience effectively used in reinforcement learning
- Qualcomm platform WiFi update disconnect end open event
- MySQL——规范数据库设计
- CesiumJS 2022^ 源码解读[7] - 3DTiles 的请求、加载处理流程解析
猜你喜欢
Link aggregation based on team mechanism
Design e-commerce seckill system
How to choose cache read / write strategies in different business scenarios?
强化学习-学习笔记1 | 基础概念
Haven't expressed the artifact yet? Valentine's Day is coming. Please send her a special gift~
Borui data and Sina Finance released the 2021 credit card industry development report
leetcode-540. A single element in an ordered array
19、 MySQL -- SQL statements and queries
Monkey/ auto traverse test, integrate screen recording requirements
QT6 QML book/qt quick 3d/ Basics
随机推荐
Read the root directory of the folder, write txt and generate random samples
[gd32l233c-start] 5. FLASH read / write - use internal flash to store data
Reinforcement learning - learning notes 1 | basic concepts
[Tang Laoshi] C -- encapsulation: member variables and access modifiers
Software testing skills, JMeter stress testing tutorial, obtaining post request data in x-www-form-urlencoded format (24)
Qt6 QML Book/Qt Quick 3D/基础知识
Apprentissage intensif - notes d'apprentissage 1 | concepts de base
Qualcomm platform WiFi -- P2P issue
Memory analyzer (MAT)
强化學習-學習筆記1 | 基礎概念
Thread, thread stack, method stack, the difference of creating thread
Set, weakset, map, weakmap in ES6
请教大家一个问题,用人用过flink sql的异步io关联MySQL中的维表吗?我按照官网设置了各种
Is it OK for fresh students to change careers to do software testing? The senior answered with his own experience
强化学习-学习笔记1 | 基础概念
Gee calculated area
leetcode-540. A single element in an ordered array
Brief analysis of ref nerf
[Yugong series] go teaching course 002 go language environment installation in July 2022
阻塞非阻塞和同步异步的区分 参考一些书籍