利用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 相关文章推荐
jqPlot Option配置对象详解
Jul 25 Javascript
扩展jquery实现客户端表格的分页、排序功能代码
Mar 16 Javascript
js验证模型自我实现的具体方法
Jun 21 Javascript
使用非html5实现js板连连看游戏示例代码
Sep 22 Javascript
js如何获取兄弟、父类等节点
Jan 06 Javascript
jquery实现保存已选用户
Jul 21 Javascript
jQuery基本选择器(实例及表单域value的获取方法)
May 20 Javascript
js replace(a,b)之替换字符串中所有指定字符的方法
Aug 17 Javascript
Jquery Easyui搜索框组件SearchBox使用详解(19)
Dec 17 Javascript
微信小程序踩坑记录之解决tabBar.list[3].selectedIconPath大小超过40kb
Jul 04 Javascript
JS实现的获取银行卡号归属地及银行卡类型操作示例
Jan 08 Javascript
vue 获取url参数、get参数返回数组的操作
Nov 12 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数据库连接
2006/10/09 PHP
php输出xml必须header的解决方法
2014/10/17 PHP
thinkPHP框架实现多表查询的方法
2018/06/14 PHP
用Laravel Sms实现laravel短信验证码的发送的实现
2018/11/29 PHP
PHP如何通过表单直接提交大文件详解
2019/01/08 PHP
学习YUI.Ext 第四天--对话框Dialog的使用
2007/03/10 Javascript
JQUERY1.6 使用方法四 检测浏览器
2011/11/23 Javascript
js实现倒计时(距离结束还有)示例代码
2013/07/24 Javascript
Node.js中对通用模块的封装方法
2014/06/06 Javascript
JS实现浏览器状态栏显示时间的方法
2015/10/27 Javascript
深入浅析同源策略和跨域访问
2015/11/26 Javascript
实例讲解javascript注册事件处理函数
2016/01/09 Javascript
深入浅析JS是按值传递还是按引用传递(推荐)
2016/09/18 Javascript
详解jquery选择器的原理
2017/08/01 jQuery
快速将Vue项目升级到webpack3的方法步骤
2017/09/14 Javascript
node.js中http模块和url模块的简单介绍
2017/10/06 Javascript
vue+vuex+axios实现登录、注册页权限拦截
2018/03/09 Javascript
Node.js的Koa实现JWT用户认证方法
2018/05/05 Javascript
回顾Javascript React基础
2019/06/15 Javascript
Vue学习笔记之计算属性与侦听器用法
2019/12/07 Javascript
python中argparse模块用法实例详解
2015/06/03 Python
详解在Python程序中解析并修改XML内容的方法
2015/11/16 Python
Python处理JSON时的值报错及编码报错的两则解决实录
2016/06/26 Python
关于Python中异常(Exception)的汇总
2017/01/18 Python
在python中利用numpy求解多项式以及多项式拟合的方法
2019/07/03 Python
Python进程的通信Queue、Pipe实例分析
2020/03/30 Python
python中xlrd模块的使用详解
2021/02/01 Python
奥地利汽车配件店:Pkwteile.at
2017/03/10 全球购物
西班牙最大的在线滑板和街头服饰商店:Fillow.net
2019/04/15 全球购物
英国高街奥特莱斯:Highstreet Outlet
2019/11/21 全球购物
美国婴儿和儿童服装购物网站:PatPat
2020/10/01 全球购物
电信营业员自我评价分享
2014/01/17 职场文书
服装促销活动方案
2014/02/23 职场文书
关于读书的演讲稿600字
2014/08/27 职场文书
CSS 使用 resize 实现图片拖拽切换预览功能(强大功能)
2021/08/23 HTML / CSS