【VUE】【IOS】【APP】IOS Music APP播放器开发

前言

周末闲来无事,学习了下移动端的一些知识。了解到移动端的一些实现方式,先从最简单的开始。本人没有IOS swift 和Android的开发经验。抱着学习态度从简单的入手,经过了解,本人之前自己用vue的写着玩了几个小项目。看到可以用VUE直接生成IOS IPA。所以先简单基于vue写前端页面(有一点点点点的VUE经验)然后基于Cordova来打包ios项目。最终通过Xcode来发布和生成IOS安装包到手机。

在学习的过程中也了解到,目前Vue对移动端有一个项目VUE Native Script. Native Script在设备的API之上,提供一系列API可以在VUE中调用。性能各方面看官网上说很接近于原生app,后面也会去尝试这种方式去开发小程序。有兴趣的同学可以自行学习 Introduction - NativeScript-Vue。

技术栈

基于VUE, Nginx, Springboot,Cordova, XCode 开发Music APP。

  • 前端:VUE
  • 网关:Nginx
  • 音乐后台: Springboot

 Module Overview Diagram

Music VUE 前端

使用vue开发页面,安装相关组件

node

# installs nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
# download and install Node.js (you may need to restart the terminal)
nvm install 20
# verifies the right Node.js version is in the environment
node -v # should print `v20.18.0`
# verifies the right npm version is in the environment
npm -v # should print `10.8.2`

Reference  Node.js — Download Node.js®

vue-cli生成项目

npm install -g vue-cli
vue init webpack my-project
cd my-project
npm install
npm run dev

这个过程中可能会有一些存在一些包无法下载或者问题,可以更改可以自行google,baidu.

这边给一些代码更改npm的register地址作为参考

* npm --registry https://registry.npm.taobao.org/  install @types/lodash
* npm config set registry <仓库地址>官方仓库 - https://registry.npmjs.org/官方镜像仓库 - https://skimdb.npmjs.com/registry/淘宝镜像仓库(旧域名) - https://registry.npm.taobao.org/淘宝镜像仓库(新域名) - https://registry.npmmirror.com/腾讯仓库 - https://mirrors.cloud.tencent.com/npm/cnpm仓库 - https://r.cnpmjs.org/yarn仓库 - https://registry.yarnpkg.com/* 通过cnpm来安装npm intsall -g cnpm -- registry=https://registry.npm.taobao.org/cnpm -v

music vue 页面开发

  • music play 页面效果

绘制播放器,包括music封面,music名称,music作者。

  • Template 代码如下:
<template><main class="audioPlayer"><a class="nav-icon" @click="togglePlaylist" :class="{ 'isActive': isPlaylistActive }" title="Music List"><span></span><span></span><span></span></a><div class="audioPlayerList" :class="{'isActive': isPlaylistActive}"><div class="item" v-for="(item,index) in musicPlaylist" v-bind:class="{ 'isActive':isCurrentSong(index) }"v-on:click="changeSong(index), isPlaylistActive=!isPlaylistActive"><p class="title">{{ item.title }}</p><p class="artist">{{ item.artist }}</p></div><p class="debugToggle" @click="toggleDebug">debug: {{ debug }}</p></div><div class="audioPlayerUI" :class="{ 'isDisabled': isPlaylistActive }"><!-- Audio Player UI --><div class="albumImage"><transition name="ballmove" enter-active-class="animated zoomIn" leave-active-class="animated fadeOutDown" mode="out-in"><img @load="onImageLoaded" :src="currentSongImage" :key="currentSong" id="playerAlbumArt"/></transition><div class="loader" v-if="!imgLoaded">Loading...</div></div><div class="albumDetails"><p class="title" :key="currentSong">{{ currentSongTitle }}</p><p class="artist" :key="currentSong">{{ currentSongArtist }}</p></div><div class="playerButtons"><a class="button" :class="{ 'isDisabled': currentSong === 0 }" @click="prevSong" title="Previous Song"><i class="fas fa-step-backward"></i></a><a class="button play" @click="playAudio" title="Play/Pause Song"><transition name="slide-fade" mode="out-in"><i class="fas" :class="playPauseIcon" :key="1"></i></transition></a><a class="button" :class="{ 'isDisabled': currentSong === musicPlaylist.length - 1 }" @click="nextSong" title="Next Song"><i class="fas fa-step-forward"></i></a></div><div class="currentTimeContainer" style="text-align:center"><span class="currentTime">{{ Math.round(this.audio.currentTime) | fancyTimeFormat }}</span><span class="totalTime">{{ trackDuration | fancyTimeFormat }}</span></div><div class="currentProgressBar"><div class="currentProgress" :style="{ width: currentProgressBar + '%' }"></div></div></div></main>
</template>
  • script
<script>
export default {name: 'MusicPlayer',data() {return {audio: "",imgLoaded: false,currentlyPlaying: false,currentTime: 0,trackDuration: 0,currentProgressBar: 0,isPlaylistActive: false,currentSong: 0,debug: false,musicPlaylist: []};},created() {this.musicData();},computed: {currentSongImage() {return this.musicPlaylist[this.currentSong].image;},currentSongTitle() {return this.musicPlaylist[this.currentSong].title;},currentSongArtist() {return this.musicPlaylist[this.currentSong].artist;},playPauseIcon() {return this.currentlyPlaying ? 'fa-pause-circle' : 'fa-play-circle'; // Update icon names}},mounted() {this.changeSong();this.audio.loop = false;},filters: {fancyTimeFormat(s) {const minutes = Math.floor(s / 60);const seconds = s % 60;return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;}},methods: {musicData: function () {this.$axios({method: "post",data:{pageSize: 10,pageNum: 1},url: "<your url>",baseURL: "<https://your host>"}).then(response => {this.musicPlaylist = response.data.list;this.changeSong();}).catch(error => this.$message.error( error + 'Request failed'));},togglePlaylist() {this.isPlaylistActive = !this.isPlaylistActive;},nextSong() {if (this.currentSong < this.musicPlaylist.length - 1) {this.changeSong(this.currentSong + 1);}},prevSong() {if (this.currentSong > 0) {this.changeSong(this.currentSong - 1);}},changeSong(index) {const wasPlaying = this.currentlyPlaying;this.imgLoaded = false;if (index !== undefined) {this.stopAudio();this.currentSong = index;}this.audio = new Audio(this.musicPlaylist[this.currentSong].url);this.audio.addEventListener("loadedmetadata", () => {this.trackDuration = Math.round(this.audio.duration);});this.audio.addEventListener("ended", this.handleEnded);if (wasPlaying) {this.playAudio();}},isCurrentSong(index) {return this.currentSong === index;},playAudio() {if (!this.currentlyPlaying) {this.currentTime = 0; // Reset current timethis.audio.play();this.currentlyPlaying = true;this.updateProgressBar();} else {this.stopAudio();}},stopAudio() {this.audio.pause();this.currentlyPlaying = false;clearTimeout(this.updateProgressBar); // Clear the interval},handleEnded() {this.nextSong();this.playAudio();},onImageLoaded() {this.imgLoaded = true;},updateProgressBar() {this.currentProgressBar = (this.audio.currentTime / this.trackDuration) * 100;if (this.currentlyPlaying) {requestAnimationFrame(this.updateProgressBar);}},toggleDebug() {this.debug = !this.debug;document.body.classList.toggle('debug');}},beforeDestroy() {this.audio.removeEventListener("ended", this.handleEnded);this.audio.removeEventListener("loadedmetadata", this.handleEnded);this.stopAudio();}
};
</script>

Springboot 后台

API 数据结构

  • 接口Url定义
api/v1/xyxMusic/page
  •  Method POST
  • Request Body
{"pageSize": 10,"pageNum": 1
}

Response Body

{"code":200,"msg":"success","data":{"total":5,"list":[{"id":1,"title":"Remember Our Summer","artist":"Frog Monster","url":"https://xyxmusic.com/RememberOurSummer.mp3","image":"https://xyxmusic.com/RememberOurSummer.jpg","uploadDate":"2024-10-17 08:00:00"},{"id":5,"title":"Kids and Climate Change","artist":"6 Minute English","url":"https://xyxmusic.com/240815_6_minute_english_kids_and_climate_change_download.mp3","image":"https://xyxmusic.com/kids_and_climate_change.jpg","uploadDate":"2024-10-17 08:00:00"},{"id":2,"title":"How Bubble tea got its Bubbles","artist":"6 Minute English","url":"https://xyxmusic.com/240523_6_minute_english_how_bubble_tea_got_its_bubbles_download.mp3","image":"https://xyxmusic.com/bubbles.jpg","uploadDate":"2024-10-17 08:00:00"},{"id":4,"title":"Did Taylor Swift fans Cause an Earthquake","artist":"6 Minute English","url":"https://xyxmusic.com/241010_6_minute_english_did_taylor_swift_fans_cause_an_earthquake_download.mp3","image":"https://xyxmusic.com/earthquake.jpg","uploadDate":"2024-10-17 08:00:00"},{"id":3,"title":"Can you keep a Secret","artist":"6 Minute English","url":"https://xyxmusic.com/240516_6_minute_english_can_you_keep_a_secret_download.mp3","image":"https://xyxmusic.com/serects.jpg","uploadDate":"2024-10-17 08:00:00"}],"pageNum":1,"pageSize":10,"size":5,"startRow":1,"endRow":5,"pages":1,"prePage":0,"nextPage":0,"isFirstPage":true,"isLastPage":true,"hasPreviousPage":false,"hasNextPage":false,"navigatePages":8,"navigatepageNums":[1],"navigateFirstPage":1,"navigateLastPage":1}
}

 API JAVA代码

MusicController

@PostMapping(value = "/page")public RestResponse<Page<XyxMusic>> page(@Schema(example = "{\"pageSize\":10,\"pageNum\":1}")@RequestBody XyxMusicModel xyxMusicModel) {XyxMusicExample xyxMusicExample = new XyxMusicExample();xyxMusicExample.setOrderByClause("title desc");PageParam pageParam = new PageParam();pageParam.setPageNum(xyxMusicModel.getPageNum());pageParam.setPageSize(xyxMusicModel.getPageSize());return RestResponse.ok(iXyxMusicService.page(xyxMusicExample, pageParam));}
  •  Cross-Domain 配置

这边要说一下后台需要配置跨域, 因为app在Cordova中使用web浏览器访问,在手机端的用的localhost,对于外部请求会跨域。当然也有一些方式可以绕过。这边不做讨论。

package top.xyx0123.wx.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** This configuration for APP*/
@Configuration
@Slf4j
public class WebConfig {@Beanpublic WebMvcConfigurer corsConfigurer() {return new WebMvcConfigurer() {@Overridepublic void addCorsMappings(CorsRegistry registry) {log.info("coming cross domain");registry.addMapping("/**") // Allow all paths.allowedOrigins("*") // Allow all origins, replace with specific domains if needed.allowedMethods("*").allowedHeaders("*");}};}
}

我这边请求的数据库的数据,你可以直接在controller写死music list的返回。结果参考API数据结构的Response Body

Ngnix配置

因为使用nginx做反代,这边也需要做一些跨域的配置如下

location / {# Handle preflight OPTIONS requestsif ($request_method = 'OPTIONS') {add_header 'Access-Control-Allow-Origin' *;  # Reflect the requesting originadd_header 'Access-Control-Allow-Headers' '*';  # Add any additional headers you are sendingreturn 204;  # No content response}proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;proxy_pass http://host:ip/path
}

安装Xcode

接下来你需要XCode。XCode将安装在macOS 10.15.7 Catalina或更高版本上。安装需要50G左右的磁盘空间。打开AppStore,搜索XCode并安装它。

一旦安装完成(这可能需要一段时间-冲泡咖啡并享受一点休息),打开XCode,如果它提示你安装命令行工具,请确保选择Yes。

打开XCode›Preferences›Locations并确保设置了命令行工具

Cordova

  • 使用 Cordova

Apache Cordova is another option for building mobile apps from web applications. 

  • 安装Cordova 
npm install -g cordova
  • 添加Cordova到Vue 

在的vue 根目录运行如下命令, 提示命名cordova目录为src-cordova

vue add cordova
  • 初始化 Cordova with iOS platform
cd src-cordova
cordova platform add ios

生成目录如下:

 

Vue打包 && XCode部署到手机

  • vue打包项目
npm run build
  • 拷贝dist中的内容到src-cordova/www/, 确保你的当前路径是在vue根目录。运行如下拷贝命令。
cp -r dist/* src-cordova/www/
  • cordova build ios 运行命令,目录切换到src-cordova目录
cd src-cordova
cordova build ios

结果出现success

  • 打开XCode 运行如下命令
open platforms/ios/YourApp.xcworkspace

打开如下图

config.xml 配置一些app信息,注意下cross domain配置。可以参考如下

<?xml version='1.0' encoding='utf-8'?>
<widget id="top.xyx0123.xyxmusic" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>XyxMusicApp</name><description>Xyx Music App</description><author email="xuzhichengxyx@gmail.com" href="https://xyx0123.top">Zhicheng Xu</author><!-- Dev Server Hook (for Vue hot-reloading during development) --><hook type="after_prepare" src="../node_modules/vue-cli-plugin-cordova/serve-config-hook.js" /><!-- The main entry point of the app --><content src="index.html" /><!-- Allow intents for external browsing --><allow-intent href="http://*/*" /><allow-intent href="https://*/*" /><!-- Preferences for using HTTPS scheme --><preference name="scheme" value="https" /><!-- Allow navigation to external URLs (especially for API requests) --><allow-navigation href="*" /><!-- Access control for external resources --><access origin="*" /> <!-- Or specify specific origins if needed --><!-- Plugins --><plugin name="cordova-plugin-whitelist" spec="1" />
</widget>
  • 用数据线连上你的手机,模拟器选择你自己的手机,点击 左上角 运行即可。XCode会在你手机安装并运行。
    注意安装到手机你需要apple developer account。这个很简单邮箱手机都可以注册。参考此处:Become a member - Apple Developer Program
  • 手机APP效果

结语

有一点需要强调下,这种直接用vue打包成APP安装包的方式。在性能上肯定比不上原生语言开发。但是由于这种方式省去了原生语言的学习成本,让有js基础同学也可以参与到app的开发中来。这也是为何前些年混合app雨后春笋的流行起来。好了,整个过程中也遇到很多问题,强烈建议官方英文文档。基本可以解决遇到的问题,若有疑问也欢迎留言。

本人打算下一步学习下VUE Native Script的相关知识,这种集成了直接调用原生设备的api。性能上肯定要比现在的这种方式强。后面也会带来demo,欢迎点赞关注支持!


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/882512.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《使用Gin框架构建分布式应用》阅读笔记:p101-p107

《用Gin框架构建分布式应用》学习第7天&#xff0c;p101-p107总结&#xff0c;总计7页。 一、技术总结 1.StatusBadRequest vs StatusInternalServerError 写代码的时候有一个问题&#xff0c;什么时候使用 StatusBadRequest(400错误)&#xff0c;什么时候使用 StatusIntern…

1.2电子商务安全内涵

目录 1 电子商务安全的层次 2 计算机网络安全 3电子商务安全的特点 只有在你生命美丽的时候&#xff0c;世界才是美丽的。 —— 顾城 《顾城哲思录》 1 电子商务安全的层次 安全:主体没有危险的客观状态 电子商务安全是一个广泛的概念&#xff0c;它涉及到电子商务的各个方…

现今 CSS3 最强二维布局系统 Grid 网格布局

深入学习 CSS3 目前最强大的布局系统 Grid 网格布局 Grid 网格布局的基本认识 Grid 网格布局: Grid 布局是一个基于网格的二位布局系统&#xff0c;是目前 CSS 最强的布局系统&#xff0c;它可以同时对列和行进行处理&#xff08;它将网页划分成一个个网格&#xff0c;可以任…

PHP函数$_FILES详解

PHP函数$_FILES详解 在PHP中上传一个文件建一个表单要比ASP中灵活得多。具体的看代码。 <form enctype"multipart/form-data" action"upload.php" method"post"> <input type"hidden" name"MAX_FILE_SIZE" value…

嵌入式入门学习——8基于Protues仿真Arduino+SSD1306液晶显示数字时钟

0 系列文章入口 嵌入式入门学习——0快速入门&#xff0c;Let‘s Do It&#xff01; SSD1306 1 Protues查找SSD1306器件并放置在画布&#xff0c;画好电气连接&#xff08;这里VCC和GND画反了&#xff0c;后面仿真出错我才看见&#xff0c;要是现实硬件估计就烧毁了&#xf…

【时时三省】(C语言基础)函数介绍strncat

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 strncat 打印结果是hello wor 跟strcat不同的是他后面可以加一个参数 这个参数就是它可以根据后面的数字 来追加多少个字符 这个如果后面的参数改成10的话 就是打印hello world 不会跟strn…

Appium环境搭建、Appium连接真机

文章目录 一、安装Android SDK二、安装Appium-desktop三、安装Appium Inspector 一、安装Android SDK 首先需要安装jdk&#xff0c;这里就不演示安装jdk的过程了 SDK下载地址&#xff1a;Android SDK 下载 1、点击 Android SDK 下载 -> SKD Tools 2、选择对应的版本进行下…

诊断知识:NRC78(Response Pending)的回复时刻

文章目录 前言NRC78的使用场景客户需求解读Autosar Dcm中的定义工具链中的配置总结 前言 在项目开发过程中&#xff0c;客户变更需求&#xff0c;是关于NRC78的回复时间点的&#xff0c;该需求在Autosar Dem中也有对应的参数&#xff0c;DcmTimStrP2ServerAdjust&#xff08;针…

Cortex-A7:如何切换ARM和Thumb状态

0 参考资料 ARM Cortex-A(armV7)编程手册V4.0.pdf1 Cortex-A7&#xff1a;如何切换ARM和Thumb状态 1.1 Cortex-A7支持的指令集 Cortex-A7支持的指令集包括ARM指令集和Thumb-2&#xff08;ARM官方一般用Thumb表示&#xff09;指令集。 ARM指令集指令大小都是32位&#xff0c;…

CLion和Qt 联合开发环境配置教程(Windows和Linux版)

需要安装的工具CLion 和Qt CLion下载链接 :https://www.jetbrains.com.cn/clion/ 这个软件属于直接默认安装就行&#xff0c;很简单&#xff0c;不多做介绍了 Qt:https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/ window 直接点exe Linux 先c…

【一种比较万能的方法删除磁盘里删除不了的文件】

一种比较万能的方法删除磁盘里删除不了的文件 只需要以下三步&#xff1a; 1、运行命令提示符&#xff08;以管理员身份打开&#xff09; 2、修复磁盘文件&#xff0c;运行命令 chkdsk 文件所在目录 /f 3、del 文件所在目录

手写Spring IOC-简易版

目录 项目结构entitydaoIUserDaoUserDaoImpl serviceIUserServiceUserServiceImpl ApplicationContext 配置文件初始化 IOC 容器RunApplication 注解初始化 IOC 容器BeanAutowired Reference 项目结构 entity User Data NoArgsConstructor AllArgsConstructor Accessors(chai…

计算DOTA文件的IOU

背景 在目标检测任务中&#xff0c;评估不同对象之间的重叠情况是至关重要的&#xff0c;而IOU&#xff08;Intersection Over Union&#xff09;是衡量这种重叠程度的重要指标。本文将介绍如何编写一个Python脚本&#xff0c;通过并行化处理DOTA格式的标注文件&#xff0c;统…

JDK17下,使用SHA1算法报Certificates do not conform to algorithm constraints错误

JDK17从17.0.5开始&#xff0c;默认不再允许使用SHA1算法&#xff0c;如果引用的jar包或代码里使用了SHA1算法&#xff0c;会报以下错误。 Caused by: javax.net.ssl.SSLHandshakeException: Certificates do not conform to algorithm constraintsat java.base/sun.security.…

演示:基于WPF的DrawingVisual开发的高刷新率示波器

一、目的&#xff1a;分享一个基于WPF的DrawingVisual开发的高刷新率示波器 二、效果演示 特此说明&#xff1a;由于Gif录制工具帧率不够&#xff0c;渲染60帧用了4.6秒&#xff0c;平均帧率在12Hz左右&#xff0c;所以展示效果不好&#xff0c;想要看好些的效果可以看文章下面…

python中堆的用法

Python 堆&#xff08;Headp&#xff09; Python中堆是一种基于二叉树存储的数据结构。 主要应用场景&#xff1a; 对一个序列数据的操作基于排序的操作场景&#xff0c;例如序列数据基于最大值最小值进行的操作。 堆的数据结构&#xff1a; Python 中堆是一颗平衡二叉树&am…

每日OJ题_牛客_集合_排序_C++_Java

目录 牛客_集合_排序 题目解析 C代码 Java代码 牛客_集合_排序 集合_牛客题霸_牛客网 (nowcoder.com) 题目解析 笔试题可直接用set排序&#xff0c;面试可询问是否要手写排序函数&#xff0c;如果要手写排序&#xff0c;推荐写快排。 C代码 #include <iostream> …

Redis中String类型数据扩容原理分析

大家好&#xff0c;我是 V 哥。在 Java 中&#xff0c;我们有动态数组ArrayList&#xff0c;当插入新元素空间不足时&#xff0c;会进行扩容&#xff0c;好奇 Redis 中的 String 类型&#xff0c;C 语言又是怎样的实现策略&#xff0c;带着疑问&#xff0c;咱们来了解一下。 最…

SOD-YOLOv8 - 增强YOLOv8以在交通场景中检测小目标

原文链接:中英文对照阅读 摘要 计算机视觉中的目标检测对于交通管理,紧急响应,自动驾驶车辆和智能城市至关重要。 尽管在目标检测上有重大进步,但在远程摄像头获取的图像中检测小目标仍具有挑战性,这主要是由于它们的大小、与摄像头的距离、形状的多样性和杂乱的背景所造…

集合框架07:LinkedList使用

1.视频链接&#xff1a;13.14 LinkedList使用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p142.LinkedList集合的增删改查操作 package com.yundait.Demo01;im…