利用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 相关文章推荐
获取HTML DOM节点元素的方法的总结
Aug 21 Javascript
javascript客户端解决方案 缓存提供程序
Jul 14 Javascript
基于JQuery的一个简单的鼠标跟随提示效果
Sep 23 Javascript
javascript 星级评分效果(手写)
Dec 24 Javascript
JavaScript中数据结构与算法(一):栈
Jun 19 Javascript
js时钟翻牌效果实现代码分享
Jul 31 Javascript
jQuery基于BootStrap样式实现无限极地区联动
Aug 26 Javascript
vue.js全局API之nextTick全面解析
Jul 07 Javascript
Angularjs实现多图片上传预览功能
Jul 18 Javascript
对类Vue的MVVM前端库的实现代码
Sep 07 Javascript
七行JSON代码把你的网站变成移动应用过程详解
Jul 09 Javascript
jQuery treeview树形结构应用
Mar 24 jQuery
使用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
全文搜索和替换
2006/10/09 PHP
浅析PHP中的字符串编码转换(自动识别原编码)
2013/07/02 PHP
gearman中worker常驻后台,导致MySQL server has gone away的解决方法
2020/02/27 PHP
PHP实现抽奖功能实例代码
2020/06/30 PHP
简单通用的JS滑动门代码
2008/12/19 Javascript
Java 正则表达式学习总结和一些小例子
2012/09/13 Javascript
js 手机号码合法性验证代码集合
2012/09/29 Javascript
jquery easyui combox一些实用的小方法
2013/12/25 Javascript
Javascript从数组中随机取出不同元素的两种方法
2016/09/22 Javascript
js中json处理总结之JSON.parse
2016/10/14 Javascript
EasyUI学习之Combobox下拉列表(1)
2016/12/29 Javascript
jQuery表单设置值的方法
2017/06/30 jQuery
vue中各组件之间传递数据的方法示例
2017/07/27 Javascript
VUE页面中加载外部HTML的示例代码
2017/09/20 Javascript
原生js实现仿window10系统日历效果的实例
2017/10/31 Javascript
Angular @HostBinding()和@HostListener()用法
2018/03/05 Javascript
详解使用mpvue开发github小程序总结
2018/07/25 Javascript
原生JS实现前端本地文件上传
2018/09/08 Javascript
p5.js临摹动态图形实现方法详解
2019/10/23 Javascript
原生JavaScript写出Tabs标签页的实例代码
2020/07/20 Javascript
详解webpack的clean-webpack-plugin插件报错
2020/10/16 Javascript
Python中set与frozenset方法和区别详解
2016/05/23 Python
Python二叉树的遍历操作示例【前序遍历,中序遍历,后序遍历,层序遍历】
2018/12/24 Python
python实现给微信指定好友定时发送消息
2019/04/29 Python
如何在sublime编辑器中安装python
2020/05/20 Python
HTML5新特性之语义化标签
2017/10/31 HTML / CSS
Html5移动端网页端适配(js+rem)
2021/02/03 HTML / CSS
德国机场停车位比较和预订网站:Ich-parke-billiger
2018/01/08 全球购物
大学军训感言200字
2014/02/26 职场文书
英语专业职业生涯规划范文
2014/03/05 职场文书
政治思想表现评语
2014/05/04 职场文书
解除劳动合同协议书范本2014
2014/09/25 职场文书
学习心得体会
2019/06/20 职场文书
python识别围棋定位棋盘位置
2021/07/26 Python
JPA 通过Specification如何实现复杂查询
2021/11/23 Java/Android
Python帮你解决手机qq微信内存占用太多问题
2022/02/15 Python