我比较喜欢听音乐,特别是周末的时候,电脑开着百度随心听fm,随机播放歌曲,躺在床上享受。但碰到了一个烦人的事情,想切掉不喜欢的曲子,还得起床去操作电脑换歌。于是思考能不能用手机控制电脑切换歌曲,经过一段事件的思考,绝对采用html5+socket.io来实现这个功能。首先我把该功能的实现拆分为以下几个步骤:

  1. 移动端前端页面+脚本逻辑实现
  2. PC端前端页面+脚本逻辑实现
  3. 后台逻辑实现
  4. 加入socket.io实现长连接
  5. 实现移动端控制PC端逻辑

下文中的代码有不全的地方,大家可以查看我的Github项目源码https://github.com/zhu495472831/shake-music

1、移动端页面脚本的实现

html页面编写

仿造微信摇一摇的页面,实现一个类似的界面,如下所示:



当我们摇手机的时候,会做一个动画,中间的图案一分为二,上半部向上运动然后回来,下半部亦同理,如下所示:



html代码(shake.html):

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
<title>摇一摇切歌</title>
<link rel="stylesheet" href="shake.css">
</head>
<body>
<div class="wrap" id="wrap">
<div class="inner"></div>
<div class="above-hand hand" id="up"></div>
<div class="below-hand hand" id="bt"></div>
</div>
<div class="tip" id="tip"> </div>
<div style="display: none;">
<audio id="shaking" src="new_silent.mp3"></audio>
<audio id="found" src="new_silent.mp3"></audio>
</div>
<script type="text/javascript" src="socket.io.js"></script>
<script src="shake.js"></script>
</body>
</html>

样式表(shake.css):

html,body{ width:100%; height:100%; background-color: #000; margin:0; overflow: hidden;}
.wrap{ position: absolute; left:50%; top:50%; width:132px; height: 132px; -webkit-transform: translate(-50%,-50%); transform: translate(-50%,-50%); }
.hand{ position: absolute; left:0; width:100%; height: 50%;background: url(shake.png) no-repeat #000; background-size: 132px 132px; }
.above-hand{ top:0; background-position: 0 0; }
.below-hand{ bottom:0; background-position: 0 -66px; }
.inner{ position:absolute; top:50%; left:50%; width: 50px; height: 90px; background: url(inner.png) no-repeat 0 0;background-size: 50px 90px; -webkit-transform: translate(-50%,-50%); transform: translate(-50%,-50%); }
.above-hand:after,.below-hand:before{ display: none; content:''; position:absolute; left:-100vw; width:200vw; height: 2px; background-color: #BABDC1; }
.above-hand:after{ bottom:0; }
.below-hand:before{ top:0; }
.wrap.active .above-hand{ -webkit-animation: up 1.5s ease; animation: up 1s ease; }
.wrap.active .below-hand{ -webkit-animation: down 1.5s ease; animation: down 1s ease; }
.wrap.active .above-hand:after,.wrap.active .below-hand:before{ display: block; }
.tip{ position: absolute; bottom: 30px; left: 10px; color: #fff; font-family: '楷体'; text-align: center; right: 10px; height: 32px; line-height: 32px; background-color: rgba(255,255,255,.4); border-radius: 3px; }
.tip.active{ -webkit-animation: jump 1.5s linear; animation: jump 1s linear; }
//动画定义见github

脚本逻辑

接下来是移动端JS脚本逻辑的实现,摇一摇的实现需借助html5新增的devicemotion事件,获取设备在位置和方向上的改变速度的相关信息,该事件的基本使用如下:

if(window.DeviceMotionEvent){
window.addEventListener('devicemotion',handler,!1);
}else{
alert('你的浏览器不支持摇一摇功能.');
}

devicemotion事件对象中有一个accelerationIncludingGravity属性,该属性包括:一个包含x、y 和z 属性的对象,在考虑z 轴自然重力加速度的情况下,告诉你在每个方向上的加速度。该API的具体使用大家可以参考网上的资料,非常多,这里就不重复了。

摇一摇的具体逻辑如下:

function handler(e){
var current = e.accelerationIncludingGravity;
var currentTime;
var timeDifference;
var deltaX = 0;
var deltaY = 0;
var deltaZ = 0;
//记录上一次设备在x,y,z方向上的加速度
if ((lastX === null) && (lastY === null) && (lastZ === null)) {
lastX = current.x;
lastY = current.y;
lastZ = current.z;
return;
}
//得到两次移动各个方向上的加速度绝对差距
deltaX = Math.abs(lastX - current.x);
deltaY = Math.abs(lastY - current.y);
deltaZ = Math.abs(lastZ - current.z);
//当差距大于设定的阀值并且时间间隔大于指定阀值时,触发摇一摇逻辑
if (((deltaX > threshold) && (deltaY > threshold)) || ((deltaX > threshold) && (deltaZ > threshold)) || ((deltaY > threshold) && (deltaZ > threshold))) {
currentTime = new Date();
timeDifference = currentTime.getTime() - lastTime.getTime();
if (timeDifference > timeout) {
dealShake();
lastTime = new Date();
}
} lastX = current.x;
lastY = current.y;
lastZ = current.z;
}

由于摇一摇需要播放摇一摇的声音以及切换歌曲成功后的声音,但由于手机大部分是禁止音频的自动播放,必须需要用户真实点击才能播放音频。这里没有彻底的解决办法,只是换了一个思路,利用用户随时触摸屏幕的习惯,对document进行touchstart事件监听。当用户触摸到屏幕时,先播放一个1S的无声音频,接着将touchstart事件移除,然后摇一摇的时候切换声音源,播放摇一摇的声音,这样便可以达到类似的目的。代码如下所示:

document.addEventListener('touchstart',autoplay,!1);
function autoplay(){
shaking.play();
found.play();
document.removeEventListener('touchstart',autoplay);
}

2、PC端前端页面脚本逻辑实现

html文档

PC端界面也是仿的网上的一个html5音乐播放器的界面,效果如下所示:



HTML(shake_pc.html)布局代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>随心听</title>
<meta name="referrer" content="never">
<link rel="stylesheet" href="reset2.0.css">
<link rel="stylesheet" href="shake_pc.css">
</head>
<body>
<!-- 控制背景图效果 -->
<div class="bg" id="bg">
</div>
<div class="music-player">
<!-- 歌曲信息 -->
<div class="info">
<div class="song-name" id="songName"></div>
<div class="author" id="author">By <span></span></div>
<!-- 播放进度 -->
<div class="progress" id="progress"></div>
</div>
<!-- 歌曲控制 -->
<div class="controls">
<div class="time" id="time">00:00</div>
<div class="play-controls">
<a href="javascript:;" class="prev btn" id="prev">
</a><a href="javascript:;" class="play btn" id="play">
</a><a href="javascript:;" class="next btn" id="next"></a>
</div>
<div class="volume-bar">
<a href="javascript:;" class="vol-muted" id="muted">
</a><a href="javascript:;" class="vol-slider" id="volSilder">
<span class="vol-slider-inner" id="volInner"></span>
</a><a href="javascript:;" class="vol-max" id="volMax"></a>
</div>
</div>
<!-- 歌曲播放 -->
<audio id="audio" controls style="display: none;"></audio>
</div>
<script type="text/javascript" src="socket.io.js"></script>
<script type="text/javascript" src="shake_pc.js"></script>
</body>
</html>

css样式

样式布局非常的简单,没什么好讲的。CSS样式代码(shake_pc.css)如下:

body{ font-family: 'Open Sans', sans-serif;  overflow: hidden;  }
.bg{ position: absolute; left:0; right: 0;top:0; bottom: 0;margin:-30px; filter: blur(30px); -webkit-filter: blur(30px); background: url(./imgaes/bg.jpg) no-repeat; background-size: cover;}
.music-player{ position: relative; width: 350px; height: 290px; margin: 150px auto; box-shadow: 0 0 60px rgba(0, 0, 0, 0.8); border-radius: 7px; background: #222; overflow: hidden; z-index: 0; }
.info{ position: relative; width: 100%; height: 80px; padding-top: 20px; color:#585858; text-align: center; }
.info .song-name{ height: 30px; font-size: 30px; font-weight: 300; }
.info .author{ margin-top: 14px; font-size: 14px; }
.progress{ position: absolute; left:0; bottom:0; width: 0; height: 3px; background-color: #ed553b; }
.controls{ height: 190px; background-color:rgba(152, 46, 75, 0.6); text-align: center; }
.controls .time{ font-size:48px; height: 84px; line-height: 84px; color:rgba(225, 225, 225, 0.4); }
.play-controls .btn{ display: inline-block; width:95px; height: 40px; vertical-align: top; }
.play-controls .btn.prev{ background:url(./imgaes/prev.png) no-repeat center center; }
.play-controls .btn.play{ background:url(./imgaes/play.png) no-repeat center center; }
.play-controls .btn.next{ background:url(./imgaes/next.png) no-repeat center center; }
.volume-bar{ position: relative; width:250px; height: 2px; margin: 30px auto 0; }
.volume-bar .vol-muted{ position:absolute; left:0; top:-6px; width: 10px; height:13px;background:url(./imgaes/muted.png) no-repeat center center; }
.volume-bar .vol-slider{ position: absolute; left:14px; right:28px; top:0; height:100%; background-color:rgba(255,255,255,.3); }
.volume-bar .vol-slider-inner{ display: block; width:50%; height: 100%; background-color:#ed553b; }
.volume-bar .vol-max{ position:absolute; right:0; top:-8.5px; width: 22px; height: 18px; background: url(./imgaes/max.png) no-repeat center center;}

脚本逻辑

文档加载完成后,随机获取一首音乐,然后播放。点击上一曲或下一曲都是随机切换歌曲,以及可以对音量进行控制,有兴趣的朋友还可以自行实现歌词的同步播放。有关html5媒体原生API,大家可以参考HTML5的Video标签的属性,方法和事件汇总

部分代码如下:

var mediaEvts = ['loadedmetadata','ended','timeupdate','error'];
//随机获取一首音乐
function getSong(){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
}else{
reject('发生错误');
}
}
};
xhr.open('get', 'http://api.jirengu.com/fm/getSong.php?channel=1', !0);
xhr.send();
});
}
function dealSong(responseText){
var songObj = JSON.parse(responseText),
song = songObj.song[0];
updateUI(song);
setMedia(song);
return song;
} function setMedia(song){
var songSrc = song.url,
lrc = song.lrc;
player.src = songSrc;
player.volume = 0.5;
player.play();
} function updateUI(song){
var name = song.title,
artist = song.artist,
img = song.picture;
songName.innerText = name;
author.querySelector('span').innerText = artist;
bg.style.backgroundImage = 'url('+img+')';
}
//初始化audio元素事件监听
function initMediaEvents(player){
mediaEvts.forEach(function(evt,idx,arr){
var cb = evt+'CB';
player.addEventListener(evt,window[cb],!1);
});
}

3、后台实现

使用express+socket.io实现长连接,socket.io可以利用npm进行安装。根据UA实现PC+移动端渲染不同的页面,将移动端的发送的命令广播给PC端,然后达到移动端控制PC的效果,代码如下所示:

const http = require('http');
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
app.use(express.static('./')); app.get('/',(req,res)=>{
var userAgent = req.headers['user-agent'].toLowerCase();
if(/(android|iphone|mobile)/.test(userAgent)){
res.sendFile(__dirname+'/shake_m.html');
}else{
res.sendFile(__dirname+'/shake_pc.html');
}
}); io.on('connection',function(socket){
var usrname = '',
sendData = {};
console.log('a client connect...'+socket.id);
socket.on('disconnect',function(){
console.log(`设备${socket.id}断开连接.`);
}); socket.on('message',function(data){
var cmd = data.cmd;
//next命令是由移动端发送,OK命令是由PC切歌成功后发送的命令
if(cmd == 'next'){
socket.broadcast.emit('next');
}else if(cmd == 'ok'){
socket.broadcast.emit('ok',data.data);
}
});
});
server.listen(3000,function(){
console.log('start listening on 3000 port...');
});

4、移动端和PC端加上socket.io

首先在页面中引入socket.io.js,然后连接socket服务器,接着监听事件即可,如下所示:

//移动端socket逻辑
socket.on('connect',function(){
console.log('websocket连接已建立...');
}); socket.on('ok',function(data){
if(found.src!=host+'found.mp3'){
found.src = 'found.mp3';
}
found.play();
tip.innerText = '正在欣赏:'+data.artist+'--'+data.title;
tip.classList.remove('active');
tip.offsetWidth = tip.offsetWidth;
tip.classList.add('active');
});
function dealShake(){
if(isShaking) return;
isShaking = !0;
if(shaking.src!=host+'shaking.mp3'){
shaking.src = 'shaking.mp3';
}
shaking.play();
wrap.classList.add('active');
setTimeout(function(){
socket.emit('message',{cmd:'next',data:null});
},1000); }
//PC端socket逻辑
function initIOEvts(){
socket.on('connect',function(){
console.log('websocket连接已建立...');
}); socket.on('next',function(data){
getSong().then(function(val){
var song = dealSong(val);
socket.emit('message',{cmd:'ok',data:song});
},function(err){
console.log(err);
});
});
}

当用户摇动设备触发摇一摇时,发送一个next命令的消息给服务端,然后服务端将该消息转发给PC端,PC端接收到该消息后,执行歌曲切换操作,并反馈一个ok命令消息并携带歌曲消息给服务端,服务端再将该消息转发回移动端,移动端播放切歌成功的声音并显示当前PC播放的歌曲。

这个功能主要是我自己使用,可能有些细节没有进行处理,大家可以在该基础上进行改造,还可以做一些多屏互动的效果。

利用HTML5+Socket.io实现摇一摇控制PC端歌曲切换的更多相关文章

  1. 利用HTML5的devicemotion事件实现手机摇一摇抽奖,年会抽奖

    摇一摇JS脚本逻辑:接下来是移动端JS脚本逻辑的实现,摇一摇的实现需借助html5新增的devicemotion事件,获取设备在位置和方向上的改变速度的相关信息,该事件的基本使用如下: if (win ...

  2. 简单的利用JS来判断页面是在手机端还是在PC端打开的方法

    在移动设备应用越来越广泛的今天,许多网站都开始做移动端的界面展示,两者屏幕尺寸差异很大,所以展示的内容也有所差别.于是就遇到一个问题,如何判断你的页面是在移动端还是在PC端打开的,很简单的问题,那我们 ...

  3. HTML5实现摇一摇的功能(实测后)--转

    eviceMotionEvent(设备运动事件)返回设备有关于加速度和旋转的相关信息.加速度的数据将包含三个轴:x,y和z(示意如下图所 示,x轴横向贯穿手机屏幕或者笔记本键盘,y轴纵向贯穿手机屏幕或 ...

  4. socket.io搭配pm2(cluster)集群解决方案

    socket.io与cluster 在线上系统中,需要使用node的多进程模型,我们可以自己实现简易的基于cluster模式的socket分发模型,也可以使用比较稳定的pm2这样进程管理工具.在常规的 ...

  5. 实时通讯之Socket.io

    WebSocket WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术.使用WebSocket,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成 ...

  6. node socket.io web

    soket.io & web http://socket.io/get-started/chat/ 新建一個文件夾 soketWeb ; 在sokertWeb 文件夾內新建一個 package ...

  7. 基于 socket.io, 简单实现多平台类似你猜我画 socket 数据传输

    一.前言 socket.io 实现了实时双向的基于事件的通讯机制,是基于 webSocket 的封装,但它不仅仅包括 webSocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通 ...

  8. 前端笔记之微信小程序(四)WebSocket&Socket.io&摇一摇案例&地图|地理位置

    一.WebSocket概述 http://www.ruanyifeng.com/blog/2017/05/websocket.html Workerman一款开源高性能异步PHP socket即时通讯 ...

  9. 利用HTML5的一个重要特性 —— DeviceOrientation来实现手机网站上的摇一摇功能

      介绍之前做两个声明: 以下代码可以直接运行,当然你别忘了引用jQuery才行. <script> // DeviceOrientation将底层的方向传感器和运动传感器进行了高级封装, ...

随机推荐

  1. 探讨javascript面向对象编程

    (个人blog迁移文章.) 前言: 下面将探讨javascript面向对象编程的知识. 请不要刻意把javascript想成面向对象编程是理所当然的. javascript里面,对象思想不可少,但是不 ...

  2. 大约PF_RING/Intel 82599/透明VPN一些事

    接近崩溃的边缘,如今,在医院这篇文章地方的想法,小病,我宁愿不吃药瓶.一台笔记本电脑,但无法上网,我不称职.想知道的东西.唯一可用3G,不开的热点.由于没人给我报销流程.这个周末,我只有一天,由于下雨 ...

  3. 使用Clean() 去掉由函数自动生成的字符串中的双引号

    有时候由Excel单元格函数軿凑出来的字符串会自带双引号 效果如下: 想这种这个情况,刚好我们軿凑出来的是SQL语句, 执行的时候是去掉双引号, 这时候可以使用Excel自带的函数来去掉双引号 Cle ...

  4. 解决easyui datagrid load时缓存问题

    修改easyui datagrid内容保存后,使用$("#dg").datagrid("reload");或者$("#dg").datagr ...

  5. vs2012运行项目提示无法连接 asp.net development server的解决方案

    更改本项目的.sln文件的端口号即可:如果还不行的话,多试几次:

  6. 捣鼓一个Ajax请求管理器

    随着前端技术的不断发展,现在做的项目里很多页面里都会有大量的ajax请求,随之而来就有了一些问题: 1.没必要的ajax请求怎么处理? 2.ajax链式调用怎么维护? ajax链式调用最原始的写法: ...

  7. [转]网上看到的关于C中内存分配的描述

    1栈   -   有编译器自动分配释放     2堆   -   一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收     3全局区(静态区),全局变量和静态变量的存储是放在一块的,初始 ...

  8. WebService支持多平台上传文件的实现

    WebService支持多平台上传文件的实现   要使用网站上传文件,在ASP.NET的范畴,我基本上能想到的有两类,一类是通过HTTP POST请求获得文件信息,另外一类是通过WebService或 ...

  9. 领域驱动设计(DDD)的实际应用

    领域驱动设计(DDD)的实际应用   笔者先前参与了一个有关汽车信息的网站开发,用于显示不同品牌的汽车的信息,包括车型,发动机型号,车身尺寸和汽车报价等信息.在建模时,我们只需要创建名为Car的实体( ...

  10. HtmlParser的使用-爬虫学习(三)

    关于这个HtmlParser的学习资料,网上真的很匮乏,这个好用的东西不要浪费啊,所以我在这里隆重的介绍一下. HtmlParser是一个用来解析HTML文件的Java包,主要用于转换盒抽取两个方面. ...