基于 Gulp 自动化构建工具仿 APP 界面云音乐播放器

基于 Gulp 自动化构建工具仿 APP 界面云音乐播放器


话不多说,先瞄一眼效果图?!(当然只是针对移动端界面布局的,没考虑适配 pc 端)

在这里插入图片描述

外观上相差不太大吧(可能有些位置,图标什么的有点区别吧),当然啦,主要功能还是没全部实现,实现的部分功能如下:

  1. 播放器嘛,播放/暂停 是基本,还有 上 / 下一曲 ,右下角点击歌单拉出歌单列表(点击歌单某一条来切歌当然也要有);
  2. 数据也是本地的模拟数据, json 文件存储资源信息(通过 $.ajax() 获取), MP3文件 / 图片存放在根目录下的 source 文件夹下;
  3. 图片旋转功能( setInterval() 实现);
  4. 随机播放按钮点击仅仅完成切换图标功能,实际切换歌曲播放模式没有去实现;
  5. 播放 状态进度条移动( requestAnimationFrame() 实现),图片旋转;暂停 进度条停止,图片停止转动;
  6. 歌曲播放结束,自动播放下一曲 / 暂停或播放状态切歌(进度条归零,图片角度归零);
  7. 进度条拖拽实现拖到相应进度继续播放(暂停播放状态只是更改进度时间,不开始播放,需要手动触发播放)。

gulp 插件依赖问题

  1. 压缩图片插件 gulp-imagemin 出现问题(导致压缩后输出到 dist 目录无法完成) → 因而更改使用 gulp-imagemin-coding-net-vendor 插件完成图片压缩( √ )。
  2. gulp-live-server 插件未能正常使用,故换用 gulp-connect 插件。
  3. 较之 gulp 3.9.x 及之前版本,4.0.2 版本 在语法使用上有些区别(以下是用到的 gulpfile.js 任务配置):
    • 每个 task 任务函数需要接收一个回调函数参数,并在任务函数结束之际调用回调函数(用以指明任务函数的结束时刻):
      // 示例
      gulp.task("fn_test", cb => {
      	// other task code...
      	cb(); // 任务函数结束的标志
      });
      
    • gulp.task("default", [其他任务参数])(4 版本之前的语法),更改为,第二个参数(任务数组)作为 gulp.series() 的参数,并将此函数整体作为 default 任务的第二个参数传入:
      // 如此例
      gulp.task("default", gulp.series(["fn_htmlClean", "fn_less", "fn_uglify"]));
      
    • 在开启监听时, gulp.watch() 函数的第二个参数与 default 任务一致,即变更为第二个参数是 gulp.series([]) 的形式。
  4. gulp-uglify 插件无法完成 js 代码压缩功能 (详细情况写在在本文下方 压缩代码出现的问题 中)。

工具环境

开发依赖如下图所示 :
zepto —— 轻量级移动端的 JS 库(与jQuery用法大致一致 —— 当然,我没有细致对比过,此项目中用到的 API 没发现什么区别);
此处是直接下载放到 dist/js 文件夹目录下(已是压缩后的 zepto.min.js 文件,若放到 src 工作目录下,经过下面 babel 及代码压缩处理后,运行项目时生成的 zepto.min.js 文件会报错)。

package.json 文件依赖

压缩代码出现的问题

gulp-uglify 插件无法如预期一样正常压缩 js 代码

// 压缩 js 代码的任务
gulp.task("fn_uglify", cb => {
	// folder 定义的变量,用于存储 src / dist 目录路径
    let js = gulp.src(folder.src + "js/*.js")
        .pipe(connect.reload()); // 监听重加载
    // devMode 是记录( process.env.NODE_ENV === "development" )的Boolean值,用于决定是否压缩代码
    // 即非开发环境下开启代码压缩 - 通过命令行 export NODE_ENV=development 设置为开发环境
    if (!devMode) {
        js.pipe(uglify());
    }
    js.pipe(gulp.dest(folder.dist + "js/"));
    cb();
});

uglify plugin error
如上图所示的错误在使用 gulp-uglify 插件压缩 js 代码无法完成压缩,本人猜测可能是 ES6 新语法(如 class 类等)造成使用 gulp-uglify 插件压缩操作无法正常进行,进而导致后续输出无法完成的问题(因为一个单纯的 ES5 规范的 js 文件可以正常压缩输出)。

本人尝试使用 babel 转换 js 代码:

  1. 下载 @babel/core@babel/preset-envgulp-babel,上面 package.json 文件截图有记载依赖模块及版本。(可能需要 babel-cli ,我之前是全局安装了的,如果能正常使用,这句不用管)。
  2. gulpfile.js 文件配置
// ... 其他工具模块此处就省略了哈
const babel = require('gulp-babel');
// js  →  uglify
gulp.task("fn_uglify", cb => {
    let js = gulp.src(folder.src + "js/*.js")
        .pipe(connect.reload())
        .pipe(babel({
			presets: ['@babel/env']
		}));
    if (!devMode) {
        js.pipe(uglify());
    }
    js.pipe(gulp.dest(folder.dist + "js/"));
    cb();
});
  1. 创建 .babelrc 文件,并配置如下:
{  
    "presets": ["@babel/preset-env"] 	// 他人封装好的 babel 配置预设
} 

配好上面三点之后,搓搓手敲敲 gulp 命令试试(鸡冻!!!)。运行结果如下:
gulp
uglify_js
哈哈,成了!!!这个问题纠结了好久啊…… (╥╯^╰╥)

部分代码

  1. gulpfile.js 配置:

    const gulp = require('gulp'),
        htmlClean = require('gulp-htmlclean'),
        less = require('gulp-less'),
        cleanCss = require('gulp-clean-css'),
        postCss = require("gulp-postcss"),
        autoprofixer = require('autoprefixer'),
        imageMin = require("gulp-imagemin-coding-net-vendor"),
        uglify = require('gulp-uglify'),
        babel = require('gulp-babel'),
        connect = require('gulp-connect');
    const folder = {
        src: "src/",
        dist: "dist/"
    };
    
    const devMode = process.env.NODE_ENV === "development";
    
    // html
    gulp.task('fn_htmlClean', cb => {
        let page = gulp.src(folder.src + "html/")
            .pipe(connect.reload());
        if (!devMode) {
            page.pipe(htmlClean());
        }
        page.pipe(gulp.dest(folder.dist + "html/"));
        cb();
    });
    
    // css
    gulp.task('fn_cleanCss', cb => {
        let css = gulp.src(folder.src + "css/")
            .pipe(connect.reload())
            .pipe(less())
            .pipe(postCss([autoprofixer()]));
        if (!devMode) {
            css.pipe(cleanCss());
        }
        css.pipe(gulp.dest(folder.dist + "css/"));
        cb();
    });
    
    // image
    gulp.task('fn_imageMin', cb => {
        let image = gulp.src(folder.src + "images/");
        if (!devMode) {
            image.pipe(imageMin());
        }
        image.pipe(gulp.dest(folder.dist + "images/"));
        cb();
    });
    
    // js
    gulp.task("fn_uglify", cb => {
        let js = gulp.src(folder.src + "js/*.js")
            .pipe(connect.reload())
            .pipe(babel({
                presets: ['@babel/env']
            }));
        if (!devMode) {
            js.pipe(uglify());
        }
        js.pipe(gulp.dest(folder.dist + "js/"));
        cb();
    });
    
    // server
    gulp.task("fn_server", cb => {
        connect.server({
            port: 9999,
            livereload: true
        });
        cb();
    });
    
    gulp.task('watch', cb => {
        gulp.watch(folder.src + 'html/', gulp.series(["fn_htmlClean"]));
        gulp.watch(folder.src + 'css/', gulp.series(["fn_cleanCss"]));
        gulp.watch(folder.src + 'js/', gulp.series(["fn_uglify"]));
        cb();
    })
    
    gulp.task("default", gulp.series(['fn_htmlClean', 'fn_cleanCss', 'fn_imageMin', 'fn_uglify', 'watch', 'fn_server']));
    
  2. 其他相关模块均是立即执行函数,通过暴露到自定义的 window.player 对象上进行统一管理;进度条部分:

    (($, root) => {
        let duration,
            frameId,
            startTime = 0,
            lastPer = 0;
        function renderTotalTime(data) {
            duration = data.duration;
            const time = formatTime(duration);
            $('.progress .total-time').text(time);
        }
    
        function formatTime(time) {
            time = Math.round(time);
            let minutes = Math.floor(time / 60),
                seconds = time % 60;
            minutes = minutes > 10 ? minutes : ('0' + minutes);
            seconds = seconds > 10 ? seconds : ('0' + seconds);
            return (minutes + ':' + seconds);
        }
    
        function start(pTime) {
            // 决定是否从头开始
            lastPer = pTime === undefined ? lastPer : pTime;
            cancelAnimationFrame(frameId);
            startTime = new Date().getTime();
    
            function frame() {
                const curTime = new Date().getTime();
                let per = lastPer + (curTime - startTime) / (duration * 1000); // 百分比
                if (per <= 1) {
                    upDate(per);
                } else {
                    cancelAnimationFrame(frameId);
                }
                frameId = requestAnimationFrame(frame);
            }
            frame();
        }
    
        // 更新进度条
        function upDate(per) {
            // update time
            let time = formatTime(per * duration);
            $('.cur-titme').text(time);
            // update progress bar
            let persent = per * 100 + '%';
            $('.real-pro').css('width', persent);
        }
    
        // 停止进度条更新
        function stop() {
            let stopTime = new Date().getTime();
            cancelAnimationFrame(frameId);
            // 依次加上前面的记录时间
            lastPer += (stopTime - startTime) / (duration * 1000);
        }
    
        root.progress = {
            renderTotalTime,
            start,
            upDate,
            stop
        }
    })(window.Zepto, window.player || (window.player = {}));
    
  3. ajax 请求的模拟 json 数据 (后面使用时, des 没去用来渲染,这可以删了):

    [{
            "image": "/source/imgs/img_1.png",
            "audio": "/source/songs/song_1.mp3",
            "song": "世间美好与你环环相扣",
            "des": "世间美好与你环环相扣(薛之谦粉丝版)",
            "singer": "小力同学",
            "duration": 181,
            "isLike": true
        },
        {
            "image": "/source/imgs/img_2.jpg",
            "audio": "/source/songs/song_2.mp3",
            "song": "与我无关",
            "des": "与我无关(女生完整版)",
            "singer": "Emily酱",
            "duration": 240,
            "isLike": false
        }
    	// others
    ]
    

总结

这个小项目虽然只是简单地仿了云音乐 APP,最大的收获还是在踩 gulp-uglify 插件压缩 js 代码的小插曲上,来来回回整了好多次才正常配置好 babel,而且在解决压缩后,zepto 转成 ES5 语法时又报错(这个就没去找问题了,然后可以选择用 cdn 引入,此处是直接放到 dist/js 目录下的);也了解并使用了 gulp 自动化构建工具去 “流式” 地处理任务,完成项目的构建。

然后,源码附上 github 地址咯! cloudMusic

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值