利用HTML5+Socket.io实现摇一摇控制PC端歌曲切换


Posted in Javascript onJanuary 13, 2017

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

1.移动端前端页面+脚本逻辑实现

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

3.后台逻辑实现

4.加入socket.io实现长连接

5.实现移动端控制PC端逻辑

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

html页面编写

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

利用HTML5+Socket.io实现摇一摇控制PC端歌曲切换

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

利用HTML5+Socket.io实现摇一摇控制PC端歌曲切换

移动端界面

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; 
	  }

脚本逻辑

接下来是移动端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音乐播放器的界面,效果如下所示:

利用HTML5+Socket.io实现摇一摇控制PC端歌曲切换

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播放的歌曲。

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

demo下载:http://xiazai.3water.com/201701/yuanma/shake-music_3water.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js常用代码段收集
Oct 28 Javascript
javascript常用的正则表达式实例
May 15 Javascript
jQuery动态效果显示人物结构关系图的方法
May 07 Javascript
浅谈js 闭包引起的内存泄露问题
Jun 22 Javascript
Ionic如何实现下拉刷新与上拉加载功能
Jun 03 Javascript
Javascript 引擎工作机制详解
Nov 30 Javascript
微信小程序 增、删、改、查操作实例详解
Jan 13 Javascript
Bootstrap中glyphicons-halflings-regular.woff字体报404错notfound的解决方法
Jan 19 Javascript
jQuery插件jqGrid动态获取列和列字段的方法
Mar 03 Javascript
如何在 Vue.js 中使用第三方js库
Apr 25 Javascript
利用JS实现一个同Excel表现的智能填充算法
Aug 13 Javascript
微信小程序实现弹出层效果
May 26 Javascript
使用jQuery的ajax方法向服务器发出get和post请求的方法
Jan 13 #Javascript
jquery,js简单实现类似Angular.js双向绑定
Jan 13 #Javascript
jQuery实现的简单排序功能示例【冒泡排序】
Jan 13 #Javascript
js实现淡入淡出轮播切换功能
Jan 13 #Javascript
js仿百度音乐全选操作
Jan 13 #Javascript
js实现tab选项卡切换功能
Jan 13 #Javascript
js制作可以延时消失的菜单
Jan 13 #Javascript
You might like
PHP开发中常用的8个小技巧
2008/08/27 PHP
PHP 危险函数全解析
2009/09/09 PHP
PHP 导出数据到淘宝助手CSV的方法分享
2010/02/27 PHP
PHP微信开发之二维码生成类
2015/06/26 PHP
验证用户是否修改过页面的数据的实现方法
2008/09/26 Javascript
jquery实现的图片点击滚动效果
2014/04/29 Javascript
JS获得浏览器版本和操作系统版本的例子
2014/05/13 Javascript
JavaScript极简入门教程(一):基础篇
2014/10/25 Javascript
js代码验证手机号码和电话号码是否合法
2015/07/30 Javascript
js获取鼠标位置实例详解
2015/12/09 Javascript
使用javascript插入样式
2016/03/14 Javascript
Bootstrap自定义文件上传下载样式
2016/05/26 Javascript
原生js实现弹出层效果
2017/01/20 Javascript
Angular利用内容投射向组件输入ngForOf模板的方法
2018/03/05 Javascript
BootStrap模态框闪退问题实例代码详解
2018/12/10 Javascript
js实现div色块拖动录制
2020/01/16 Javascript
Vue如何提升首屏加载速度实例解析
2020/06/25 Javascript
解决vue动态下拉菜单 有数据未反应的问题
2020/08/06 Javascript
[00:37]DOTA2上海特级锦标赛 Secert 战队宣传片
2016/03/03 DOTA
Python的Django框架可适配的各种数据库介绍
2015/07/15 Python
Python实现矩阵转置的方法分析
2017/11/24 Python
python3去掉string中的标点符号方法
2019/01/22 Python
Python异常处理例题整理
2019/07/07 Python
Python中生成一个指定长度的随机字符串实现示例
2019/11/06 Python
Python的形参和实参使用方式
2019/12/24 Python
基于MSELoss()与CrossEntropyLoss()的区别详解
2020/01/02 Python
使用 Python 读取电子表格中的数据实例详解
2020/04/17 Python
Python matplotlib图例放在外侧保存时显示不完整问题解决
2020/07/28 Python
python 使用tkinter+you-get实现视频下载器
2020/11/17 Python
HTML5本地存储之Web Storage应用介绍
2013/01/06 HTML / CSS
使用canvas生成含有微信头像的邀请海报没有微信头像问题
2019/10/29 HTML / CSS
美国球鞋寄卖网站:Stadium Goods
2018/05/09 全球购物
专科应届生求职信
2013/11/24 职场文书
小学语文业务学习材料
2014/06/02 职场文书
信息员培训方案
2014/06/12 职场文书
市场部岗位职责
2015/02/12 职场文书