利用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 相关文章推荐
浅谈JavaScript中面向对象技术的模拟
Sep 25 Javascript
jQuery ajax在GBK编码下表单提交终极解决方案(非二次编码方法)
Oct 20 Javascript
Javascript图像处理—虚拟边缘介绍及使用方法
Dec 27 Javascript
在javascript中关于节点内容加强
Apr 11 Javascript
js读写(删除)Cookie实例详解
Apr 17 Javascript
jquery使用淘宝接口跨域查询手机号码归属地实例
Nov 28 Javascript
jQuery实现企业网站横幅焦点图切换功能实例
Apr 30 Javascript
JS制作手机端自适应缩放显示
Jun 11 Javascript
如何动态加载外部Javascript文件
Dec 02 Javascript
详解jQuery的表单验证插件--Validation
Dec 21 Javascript
mongodb初始化并使用node.js实现mongodb操作封装方法
Apr 02 Javascript
iview form清除校验状态的实现
Sep 19 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数据库万能引擎类adodb配置使用以及实例集锦
2014/06/12 PHP
PHP使用memcache缓存技术提高响应速度的方法
2014/12/26 PHP
tp5框架前台无限极导航菜单类实现方法分析
2020/03/29 PHP
jquery封装的对话框简单实现
2013/07/21 Javascript
JQuery验证jsp页面属性是否为空(实例代码)
2013/11/08 Javascript
javascript中的作用域和上下文使用简要概述
2013/12/05 Javascript
jquery的clone方法应用于textarea和select的bug修复
2014/06/26 Javascript
javascript进行四舍五入方法汇总
2014/12/16 Javascript
jQuery类选择器用法实例
2014/12/23 Javascript
浅谈JavaScript中运算符的优先级
2015/07/07 Javascript
js实现鼠标点击左上角滑动菜单效果代码
2015/09/06 Javascript
jquery+php实现滚动的数字特效
2015/11/29 Javascript
AngularJS基础 ng-non-bindable 指令详细介绍
2016/08/02 Javascript
VUE饿了么树形控件添加增删改功能的示例代码
2017/10/17 Javascript
NodeJs搭建本地服务器之使用手机访问的实例讲解
2018/05/12 NodeJs
深入理解与使用keep-alive(配合router-view缓存整个路由页面)
2018/09/25 Javascript
Bootstrap实现模态框效果
2019/09/30 Javascript
Vuex的各个模块封装的实现
2020/06/05 Javascript
微信小程序实现导航栏和内容上下联动功能代码
2020/06/29 Javascript
[22:20]初生之犊-TI4第5名LGD战队纪录片
2014/08/13 DOTA
通过代码实例展示Python中列表生成式的用法
2015/03/31 Python
用python3教你任意Html主内容提取功能
2018/11/05 Python
简单了解django处理跨域请求最佳解决方案
2020/03/25 Python
拿来就用!Python批量合并PDF的示例代码
2020/08/10 Python
python如何设置静态变量
2020/09/07 Python
Java中的异常处理机制的简单原理和应用
2013/04/27 面试题
财务会计毕业生自荐信
2013/11/02 职场文书
中学生自我评价范文
2014/02/08 职场文书
小学清明节活动方案
2014/03/08 职场文书
活动总结书
2014/05/08 职场文书
卫生院义诊活动总结
2015/05/07 职场文书
教育教学读书笔记
2015/07/02 职场文书
教师纪律作风整顿心得体会
2016/01/23 职场文书
python字典进行运算原理及实例分享
2021/08/02 Python
漫画「处刑少女的生存之道」第3卷封面公开
2022/03/21 日漫
进阶篇之linux环境下安装MySQL数据库
2022/04/09 MySQL