微信小程序中将图片与音乐制作成MV

article/2025/10/31 7:40:50

最近一直在开发一个类似于小年糕的微信小程序,在开发制作MV功能时 ,花费了一些心思,其间主要遇到了以下一些问题点:

1. 上传图片的动画效果如何像播放视频一样实现播放与暂停?

2.  用户上传的图片数量不确定,在音乐没有播完之前,上传图片太多或太少将如何处理?

3.  如何让展现的歌词与当前播放的那一句保持同步,即唱哪一句就显示哪一句?

4. 当前音乐的播放时间如何与自定义进度条的进度保持一致?

针对以上问题,首先我们来看一下实现的效果,

下面我们来一一解答以上提出的问题点:

第一个问题,动画如何暂停与播放,我采用了animation-play-state 这个属性来控制动作的播放与暂停,当它的值为 play 时,动画会播放,如果值为 paused 时,动画会暂停。

第二个问题,当音乐还在播放时,用户上传的图片如果太少,将图片进行了循环展示,直到音乐播放完毕,由于整个MV的时长取决于所选择音乐的时长,如果上传的图片太多,当音乐播放完毕时,我将后面的图片进行了省略处理。

第三个问题,为了让歌词展示与音乐播放保持同步,我对音乐的歌词格式进行了处理,将每一句歌词与该歌词的播放时间分别组成一个对象,然后将多个对象组成一个数组,将数组循环展示在页面上,其中时间格式为是整型的秒数。在播放时,将每一句歌词的播放时间与音乐当前播放的时间进行了对比,如果歌词的播放时间大于等于当前音乐播放时间,并且小于一下句歌词的播放时间就显示该歌词,否则不显示。

第四个问题,为了让播放时间与进度条的进度同步,我利用了 onTimeUpdate 这个API,即监听音乐播放时间更新的函数,在这个API的回调函数里,获取当前音乐的动态播放时间,将播放时间与音乐的总时间相除,即可得出播放时间的百分比,然后将这个比值乘以100进行向上取整,最后将该结果赋给进度条 slider 的 value 值,这样就可以实现音乐在播的同时进度条同步更新的效果。
 

以上就是问题的解决方案,下面是实际的代码,在实际的代码中,我还加入了拖拽进度条快进或快退、页面显示、页面隐藏、页面销毁时对音乐播放器的一些处理。

WXML代码:

<view style="height:calc(100vh - {{height}}rpx - 202rpx)" class="mv-box"><!-- MV动画 --><block><view class="mv" bindtap="showPlayControl"><image class="mv-background" src="{{mvImageSrc[mvImgIndex]}}" style="animation:{{animationArray[mvImgIndex%4]}} 5s linear 1 both;animation-play-state:{{mvStatus}};" wx:if="{{mvImageSrc.length}}"></image></view><view class="mv" bindtap="showPlayControl"><image class="mv-image" wx:for="{{mvImageSrc}}" wx:key="index" src="{{item}}" style="animation:{{animationArray[index%4]}} 5s linear 1 both;animation-play-state:{{mvStatus}};" bindanimationend='singleMvOver' wx:if="{{mvImageSrc.length && mvImgIndex==index?true:false}}"mode="widthFix"></image></view></block><!-- MV歌词 --><view class="lyric" wx:for="{{mvMusicInfo.lrcText}}" wx:key="index" wx:if="{{mvMusicInfo.lrcText.length && index < mvMusicInfo.lrcText.length &&  mvMusicInfo.lrcText[index][0]<= sliderStartTimeNum  && sliderStartTimeNum < mvMusicInfo.lrcText[index + 1][0]}}">{{item[1]}}</view><!-- 控制条 --><view class="play-control" hidden="{{!showPlayControl}}"><view class="play-control-left" bindtap="changeMvStatus"><image src="/images/play-white-40.png" wx:if="{{mvIsPlay}}"></image><image src="/images/pause-white-40.png" wx:if="{{!mvIsPlay}}"></image></view><view class="play-control-right" bindtap="clickSlider"><text class="start-time">{{sliderStartTime}}</text><slider block-size="16" value="{{sliderValue}}" backgroundColor="#838383" activeColor='#ffffff' bindchanging="dragingEvent" bindchange="dragEndEvent"></slider><text class="end-time">{{sliderEndTime}}</text></view></view></view>

WXSS代码

/* 外层容器 */.mv-box {width: 100%;background-color: #fff;display: flex;position: relative;z-index: 25;
}/* MV动画 */.mv {width: 100%;height: 100%;position: absolute;left: 0;top: 0;display: flex;flex-direction: column;justify-content: space-around;align-items: center;
}.mv-background {filter: blur(10px);height: 100%;
}.mv-image {position: absolute;
}/* 放大动画 */@keyframes enlarge {0% {width: 750rpx;transform: scale(1);}100% {width: 750rpx;transform: scale(1.2);opacity: 0.3;}
}/* 缩小动画 */@keyframes shrink {0% {width: 750rpx;transform: scale(1.2);}100% {width: 750rpx;transform: scale(1);opacity: 0.3;}
}/* 左移动画 */@keyframes moveLeft {0% {width: 850rpx;left: 0rpx;}100% {width: 850rpx;left: -100rpx;opacity: 0.3;}
}/* 右移动画 */@keyframes moveRight {0% {width: 850rpx;left: -100rpx;}100% {width: 850rpx;left: 0;opacity: 0.3;}
}/* MV歌词 */.lyric {position: absolute;bottom: 80rpx;left: 0;width: calc(100% - 60rpx);padding: 0 30rpx;line-height: 80rpx;z-index: 30;text-align: center;font-size: 40rpx;font-weight: 600;color: #ff2066;
}/* 控制条 */.play-control {position: absolute;bottom: 0;left: 0;width: 100%;height: 80rpx;display: flex;flex-direction: row;justify-content: space-between;align-items: center;background-color: #030303;z-index: 30;
}.play-control-left {width: 80rpx;height: 80rpx;
}.play-control-left image {width: 40rpx;height: 40rpx;padding: 20rpx;
}.play-control-right {width: 670rpx;height: 80rpx;display: flex;flex-direction: row;justify-content: space-between;align-items: center;
}.play-control-right .start-time {display: inline-block;height: 40rpx;line-height: 40rpx;width: 70rpx;color: #fff;font-size: 22rpx;text-align: center;
}.play-control-right slider {width: 490rpx;
}.play-control-right .end-time {display: inline-block;height: 40rpx;line-height: 40rpx;width: 90rpx;color: #fff;font-size: 22rpx;text-align: left;
}

JS代码

Page({data: {// 系统状态栏高度// 实际动态获取height: 40,// MV轮播的图片mvImageSrc: [],// 当前轮播图片的下标mvImgIndex: 0,// MV轮播的动画效果animationArray: ['enlarge', 'shrink', 'moveLeft', 'moveRight'],// MV动画的播放状态mvStatus: 'play',// 音乐信息mvMusicInfo: {},// 显示音频播放进度条showPlayControl: true,// 控制条暂停与播放的图标mvIsPlay: true,// 音乐正在播放时间sliderStartTime: '00:00',// 音乐正在播放时间秒数sliderStartTimeNum: 0,// 当前控制条播放进度sliderValue: 0,// 音乐播放总时长sliderEndTime: '00:00',// 音乐播放总秒数sliderEndTimeNum: 0,},// 页面加载时onLoad: function(options) {// 创建音乐播放器var audioCtx = wx.createInnerAudioContext();this.setData({audioCtx: audioCtx}, function() {this.getData();})},// 获取图片和音乐信息getData: function() {// 模拟请求获取MV相关信息// MV的图片(图片地址已作处理,非真实有效)var mvImageSrc = ["https://qnybd.xxxxx.cn/44b76201910210912393964.jpg","https://qnybd.xxxxx.cn/7e33520191030155441391.png","https://qnybd.xxxxx.cn/43c96201910301553019375.png"];// 音乐信息var mvMusicInfo = {// 音乐地址(音乐地址已作处理,非真实有效)sourcePath: 'https://qnybd.xxxxx.cn/d0d6a8eacd9ec492ba0b5424335bfb4e.mp3',// 音乐歌词(由于歌词太多,此处仅做部分示例)lrcText: "[00:00.860]作词:杨丽丽\n[00: 02.580] 混音:周佳佳\n[00:04.480] 编曲:王悦悦",// 音乐时长musicTime: '04:21'};// 添加音乐地址var audioCtx = this.data.audioCtx;audioCtx.src = mvMusicInfo.sourcePath;audioCtx.play();// 播放状态,绑定播放进度更新事件,控制进度条和时间显示// onTimeUpdate在audioCtx.onTimeUpdate(this.timeUpdate);// 处理音乐歌词的格式mvMusicInfo.lrcText = this.parseLyric(mvMusicInfo.lrcText);// 播放器控件的最大时长var sliderEndTime = mvMusicInfo.musicTime;var sliderEndTimeNum = this.formatTimeToNum(sliderEndTime);// 更新页面信息this.setData({mvImageSrc: mvImageSrc,mvMusicInfo: mvMusicInfo,sliderEndTime: sliderEndTime,sliderEndTimeNum: sliderEndTimeNum,audioCtx: audioCtx})},// 滑块拖动过程中的事件dragingEvent: function(e) {var that = this;// 暂停播放that.data.audioCtx.stop();// 取消监听播放进度that.data.audioCtx.offTimeUpdate();that.setData({mvIsPlay: false,mvStatus: 'paused',});},// 拖动完成后dragEndEvent: function(e) {var that = this;// 总时间var sliderEndTimeNum = that.data.sliderEndTimeNum;// 拖动百分比var percent = e.detail.value / 100;// 计算拖拽时的播放时间var sliderStartTimeNum = Math.ceil(sliderEndTimeNum * percent);var sliderStartTime = that.formatTimeToStr(sliderStartTimeNum);that.setData({mvIsPlay: true,mvStatus: 'play',sliderStartTimeNum: sliderStartTimeNum,sliderStartTime: sliderStartTime,sliderValue: e.detail.value},function(){// 跳转时间that.data.audioCtx.seek(that.data.sliderStartTimeNum);// 播放音乐that.data.audioCtx.play();// 监听进度that.data.audioCtx.onTimeUpdate(that.timeUpdate);});},// 处理歌词格式parseLyric: function(text) {// 按换行切割var text = text.replace(/\\n/g,'\n');text = text.replace(/\n/g, '===');var lines = text.split('===');// 筛选歌词,修正时间let list = [];for (let b = 0; b < lines.length; b++) {let item = lines[b];// 拆分与切割item = item.replace(/\[/g, '');item = item.split(']');// 去掉有时间的空歌词if (item[item.length - 1]) {for (let j = 0; j < item.length - 1; j++) {// 去掉时间的毫秒数let index = item[j].indexOf('.');// 截取分和秒并替换分后面的空格item[j] = item[j].substring(0, index).replace(/\s+/g, '');}list.push(item);};};// 处理歌词多个时间共享的问题let arr = [];for (let k = 0; k < list.length; k++) {if (list[k].length == 2) {list[k][0] = App.formatTimeToNum(list[k][0]);arr.push(list[k]);} else {for (let n = 0; n < (list[k].length - 1); n++) {let obj = [// 时间App.formatTimeToNum(list[k][n]),// 共用的歌词list[k][list[k].length - 1],];arr.push(obj);}}};// 对歌词按时间排序arr.sort(function (a, b) {return a[0] - b[0];});return arr;},// 页面影藏onHide: function() {this.setData({mvIsPlay: false,mvStatus: 'paused'});this.data.audioCtx.pause();},// 页面显示onShow: function() {this.setData({mvIsPlay: true,mvStatus: 'play'});if (this.data.audioCtx) {this.data.audioCtx.play();};},// 页面卸载onUnload: function() {this.data.audioCtx.destroy();},// 播放的时候,更新进度条和时间显示timeUpdate: function() {let that = this;// 当前播放的秒数let sliderStartTimeNum = Math.ceil(that.data.audioCtx.currentTime);let sliderValue = Math.round(sliderStartTimeNum / that.data.audioCtx.duration * 100);if (sliderValue == 100) {that.data.audioCtx.pause();that.setData({// 切换为暂停按钮mvIsPlay: false,// 停止动画mvStatus: 'paused',// 图片换为第一张mvImgIndex: 0,// 音乐播放时间归0sliderStartTime: '00:00',// 音乐播放时间归0sliderStartTimeNum: 0,// 当前播放进度sliderValue: 0,});} else {that.setData({sliderStartTime: that.formatTimeToStr(sliderStartTimeNum),sliderStartTimeNum: sliderStartTimeNum,sliderValue: sliderValue})}},// 将时间秒数转为字符串formatTimeToStr: function(num) {var a = Math.floor(num / 600);var b = Math.floor(num % 600 / 60);var c = Math.floor((num - a * 600 - b * 60) / 10);var d = parseInt((num - a * 600 - b * 60) % 10);var timeStr = '' + a + b + ':' + c + d;return timeStr},// 将时间格式转为秒数formatTimeToNum: function(str) {var first = str.substring(0, 2);var end = str.substring(3, 5);var allTimeStr = first + end;var allTimeArry = allTimeStr.split('');var allTimeNum = 0;for (var i = 0; i < allTimeArry.length; i++) {if (i == 0) {allTimeNum += parseInt(allTimeArry[i] * 600);} else if (i == 1) {allTimeNum += parseInt(allTimeArry[i] * 60);} else if (i == 2) {allTimeNum += parseInt(allTimeArry[i] * 10);} else if (i == 3) {allTimeNum += parseInt(allTimeArry[i]);}};return allTimeNum},// 显示播放进度条showPlayControl: function(e) {this.setData({showPlayControl: true})},// 单个图片播放结束时singleMvOver: function() {var that = this;var mvImgIndex = that.data.mvImgIndex + 1;if (mvImgIndex == that.data.mvImageSrc.length) {that.setData({mvImgIndex: 0});} else {that.setData({mvImgIndex: mvImgIndex})}},// 暂停或播放MVchangeMvStatus: function() {var that = this;// 获取当前正在播放的状态var isPlay = that.data.mvIsPlay;if (isPlay) {// 暂停音乐that.data.audioCtx.pause();this.setData({mvIsPlay: false,mvStatus: 'paused',});} else {// 播放that.data.audioCtx.play();this.setData({mvIsPlay: true,mvStatus: 'play',});};},// 点击滑动滑块clickSlider: function(e) {this.setData({showPlayControl: false})},})


http://chatgpt.dhexx.cn/article/PbZEoo1s.shtml

相关文章

FL Studio中文版21最新免费音乐编曲软件制作工具

FL Studio较为适合专业的音乐制作者&#xff0c;操作难度较大&#xff0c;学习门槛也较高&#xff1b;Studio One则主打一站式的音乐制作&#xff0c;从编曲到录音到后期的专辑制作都可以在其中实现&#xff0c;同时操作难度不大&#xff0c;对初学者和业余爱好者都较为友好。 …

mv

mv 移动文件或改名 mv 命令&#xff08;move 的缩写&#xff09;&#xff0c;既可以在不同的目录之间移动文件或目录&#xff0c;也可以对文件和目录进行重命名。 该命令的基本格式如下&#xff1a; [rootlocalhost ~]# mv 【选项】 源文件 目标文件“mv” 默认执行命令(mv -…

怎样做音乐相册怎样制作?手把手教你制作

大家平时出门游玩的时候&#xff0c;会拍摄一些好看的照片吗&#xff1f;那你们会将这些照片分享在社交平台上吗&#xff1f;普通的照片分享&#xff0c;有时会显得比较枯燥单调&#xff0c;其实我们可以将这些照片制作成音乐相册&#xff0c;这样就可以丰富照片的内容&#xf…

Audacity(电脑音频剪辑软件)官方中文版V3.0.2 | 完全免费的音乐制作软件audacity下载

Audacity 是一款专业易于使用且完全免费的音乐制作软件&#xff0c;高度可定制的界面能实时显示预览&#xff0c;具备多轨音频剪辑和音频录制功能&#xff0c;提供了数十种可修改或自己创建的插件以及无限的撤消和重做能力&#xff0c;能将磁带和唱片转换成数字唱片或 CD&#…

手把手教你批量制作MV连播视频

一、点击下载CR MVMixer解压到D盘 找到图标双击打开CR MVMixer 本软件授权注册码可以登陆酷软街获取 右下角点击注册&#xff0c;填入注册码激活。 软件操作页面&#xff0c;左侧是加载音乐MP3跟LRC歌词的 右侧可以添加视频素材&#xff0c;设置封面。 二、加载素材&#xf…

MV制作器UI版来噜

一、项目介绍 MV制作器UI版来噜 在之前的制作MV的项目中&#xff0c;我们使用代码来调用文心大模型API&#xff0c;批量的获取图片&#xff0c;然后我们需要使用专业德视频剪辑工具譬如pr或剪映等完成后续的步骤&#xff0c;但并不是所有人都会使用剪辑软件… 那有没有什么可…

轻音——基于Swing与JavaFx的音乐播放器

简介 轻音是一款我个人开发的支持本地和在线音乐的播放器&#xff0c;我制作这款软件的初衷也是为了消除不同平台之间版权的差异&#xff0c;让听歌不再“跨平台化”&#xff0c;实现听歌自由使用的技术&#xff1a;100% Java 编程语言&#xff0c;98% Swing 开发GUI&#xff…

LaTeX会议论文添加版权信息

1. 在导言区增加 \usepackage{fancyhdr} \renewcommand{\headrulewidth}{0pt} \renewcommand{\footrulewidth}{0pt} 2. maketitle后增加 \thispagestyle{fancy} \fancyhead{} \lhead{} \lfoot{\copyright~2014 IEEE} \cfoot{} \rfoot{}&#xff0c; 效果如下

CCF论文会议 IEEE 如何查询某个会议期刊的所有文章

1. 下载CCF英文列表 CCF英文会议期刊 列表目录 下载 2. 找到要查询的会议&#xff0c;点击后面的网址 3. 就可以看到该会议期刊列年的整理好的记录&#xff0c;随便点击一个进入 4. 可以看到该年收录的所有文章 5. 可以点击这里查看文章详情 微程序学堂

会议论文扩展摘要写作指南 conference extended abstract

简单的讲就是一个迷你版的论文&#xff0c;除了篇幅非常短只有2-4页之外&#xff0c;论文该有的部分都要有&#xff08;e.g. Introduction, Methods, Discussion …&#xff09;。扩展摘要可能需要包含一个abstract&#xff0c;也可能直接从introduction开始&#xff0c;视不同…

计算机发顶级会议论文难,发会议论文不如发期刊?被反驳的无言以对……

顶级会议和顶级期刊哪个牛?是不是发会议不如发期刊呢? 本期关键词:SCI期刊,顶级会议,计算机 有网友在小木虫开帖称自己投稿中了机器人和自动化领域top会议——IROS2012,引发网友关于“投会议论文还是投期刊”的大讨论。 “真心恭喜楼主呀!不过国内不太认可会议呀,有这水…

撰写全英文EI会议论文值得注意的要点!

首先&#xff0c;EI会议论文有用吗&#xff1f;当然是有用的&#xff0c;EI是国际上三大检索系统之一&#xff0c;所以EI论文肯定是有用的&#xff0c;是受认可的&#xff0c;但EI检索的不单单是学术期刊&#xff0c;还有学术会议&#xff0c;期刊论文和会议论文是不同的&#…

ISTP会议论文检索

目录 1. ISTP会议论文的概念 2. ISTP会议论文的特点 3. ISTP会议论文检索 4. ISTP会议论文检索基本检索&#xff0c;检索步骤 1. ISTP会议论文的概念 ISTP会议论文是指在学术会议上宣读和交流的论文、报告及其他有关资料&#xff0c;包括会议前参加会议者预先提交的论文文…

参考文献是会议论文应该什么格式?

“既然你诚心诚意的发问了&#xff0c;那我就大发慈悲的告诉你!”(请自行带入火箭队三人组语气&#xff0c;谢谢&#xff01;) 注意啦&#xff01;参考文献会议论文的必须要素在此&#xff01; [序号]作者名题名[C]//&#xff08;出版地&#xff09;出版者出版年&#xff08;…

会议论文和期刊论文在写作上有什么区别?有什么侧重点?

作为一名科研工作者&#xff0c;无论你是初入圈内的“小萌新”还是久经沙场的“老将”&#xff0c;写论文都是我们的必经之路&#xff0c;而要想论文投中&#xff0c;就需要我们的积极努力啦&#xff01;其中&#xff0c;杂志社以及很多大型学术会议的征稿就是很好的一个途径哦…

科普文章:会议论文VS期刊论文,两者有何区别?学界的认可度两者一致吗?

文章目录 一、会议论文和期刊论文的概念1.1 会议论文1.2 期刊论文1.3 总结 二、不同之处2.1 内容的不同2.2 审稿周期的不同2.3 认可度的区别 三、投稿的一些建议四、自己的一点小想法 计算机深度学习方向研一学生一枚&#xff0c;论文才有想法&#xff0c;下一步打算开始写了&a…

Petri网学习(五):Petri网子类的性质分析

一、标识S-图 S-图定义&#xff1a; 定义&#xff1a;网N(P,T;F)是S-图或状态机当且仅当&#xff1a;&#xff0c;而带有标识的S-图称为标识S-图。将S-图中的变迁撤掉可以简化为有向图SG(P,E) 我对S-图的理解&#xff1a;所有的变迁都是单进单出。 S-图的性质&#xff1a; 1、…

软件工程----有穷状态机和Petri网

有穷状态机 概念 有穷状态机的作用是描述对象在它的生命周期内所经历状态序列&#xff0c;以及如何响应来自外界的事件。有穷状态机首先包含一个有限状态的集合&#xff0c;还包含了从一个状态到另外一个状态的转换。 有穷自动机看上去就像是一个有向图&#xff0c;其中状态…

很久以前某位大仙对petri网的总结

2006年03月14日 计算模型的统一分析 计算模型的统一分析 人类所有的计算模型都包括如下四个要素&#xff1a; 1&#xff09;输入集合或者输入变量&#xff08;I&#xff09;&#xff1b; 2&#xff09;输出集合或者输出变量&#xff08;O&#xff09;&#x…

Petri网-2、有向网

petri网 2、有向网 三元组 N ( S , T ; F ) N(S,T;F) N(S,T;F) 称为有向网&#xff0c;如果 表达式含义 S ⋃ T ≠ ∅ S \bigcup T \neq \emptyset S⋃T∅非空 ⋀ S ⋂ T ≠ ∅ \bigwedge S \bigcap T \neq \emptyset ⋀S⋂T∅两类元素 ⋀ F ⊆ S T ⋃ T S \bigwedg…