Browse Source

增加登陆+即将适配手柄遥控器
Signed-off-by: caner <5658514@qq.com>

caner 3 years ago
parent
commit
cadab3f962
6 changed files with 368 additions and 124 deletions
  1. 3 15
      README.md
  2. 1 1
      public/index.html
  3. 132 18
      src/App.vue
  4. 134 0
      src/components/loading.vue
  5. 98 0
      src/components/login.vue
  6. 0 90
      src/page/index.vue

+ 3 - 15
README.md

@@ -1,20 +1,8 @@
 # 控制端
 ```
-vue webrtc 数据通道监听通道同步显示视频
+采用遥控手柄控制
 ```
-
-## How use
+## how use
 ```
-1. yarn
-2. yarn dev
-3. chrome open address
-```
-### demo
-```
-demo see [media-send](https://git.caner.top/Caner/node-datachannel-examples/src/master/media-send)
-```
-## TODO:
-```
-1. APP 封装
-
+1. 房间ID与车端ID需一致
 ```

+ 1 - 1
public/index.html

@@ -6,7 +6,7 @@
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1, user-scalable=no">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <title>Caner 车控</title>
+    <title>Caner</title>
   </head>
   <body>
     <noscript>

+ 132 - 18
src/App.vue

@@ -1,39 +1,153 @@
 <template>
   <div id="app">
-    <Home />
+    <template v-if="isLogin">
+      <template v-if="!showLoading">
+        <video id="v2" autoplay playsinline muted></video>
+        <div class="marke"></div>
+      </template>
+      <Loading v-else />
+    </template>
+    <Login v-else :err="error" @loginBack="login" />
   </div>
 </template>
 
 <script>
-import Home from './page/index'
+const { io } = require("socket.io-client");
+import Login from "@/components/login";
+import Loading from "@/components/loading";
 export default {
-  name: 'App',
-  components: {
-    Home,
+  components: { Login, Loading },
+  data() {
+    return {
+      socket: null,
+      HOST: "wss://car.caner.top",
+      Peer: null,
+      isLogin: false,
+      error: "",
+      remoteVideo: null,
+      showLoading: true,
+    };
   },
-}
+  methods: {
+    intSoketRtc(host) {
+      // int socket
+      this.socket = io(host, {
+        autoConnect: false,
+        transports: ["websocket"],
+      });
+
+      // socket
+      this.socket.on("connect", () => {
+        try {
+          console.log("连接成功", this.socket.auth);
+          this.isLogin = true;
+          // init webrtc
+          this.Peer = new RTCPeerConnection({
+            bundlePolicy: "max-bundle",
+          });
+
+          // listen state
+          this.Peer.onicegatheringstatechange = () => {
+            console.log("GatheringState: ", this.Peer.iceGatheringState);
+            if (this.Peer.iceGatheringState === "complete") {
+              const answer = this.Peer.localDescription;
+              this.socket.emit("msg", answer);
+              this.showLoading = false
+            }
+          };
+
+          // listen track
+          this.Peer.ontrack = (evt) => {
+            console.log("track", evt.streams[0]);
+            this.remoteVideo = document.getElementById("v2");
+            this.remoteVideo.srcObject = evt.streams[0];
+          };
+
+        } catch (error) {
+          this.socket.disconnect();
+          this.isLogin = false;
+          alert("不支持Webrtc");
+        }
+      });
+
+      this.socket.on("msg", async (data) => {
+        console.log(data);
+        if (data.type === "offer") {
+          await this.Peer.setRemoteDescription(data);
+          const answer = await this.Peer.createAnswer();
+          await this.Peer.setLocalDescription(answer);
+        } else if (data.type === "power") {
+          console.log("电量");
+        } else if (data.type === "signal") {
+          console.log("4G信号");
+        }
+      });
+
+      this.socket.on("joined", async () =>
+        this.socket.emit("msg", { type: "startRTC" })
+      );
+
+      this.socket.on("leaved", () => this.close("车端断开"));
+
+      this.socket.on("connect_error", (err) => this.close(err));
+    },
+
+    login(data) {
+      // 设置登录头
+      if (this.socket) {
+        this.socket.auth = {
+          roomID: data.roomID,
+          name: data.name,
+        };
+        this.socket.connect();
+      } else {
+        this.error = "服务器连接失败";
+      }
+    },
+
+    close(err) {
+      if (this.Peer) this.Peer.close();
+      if (this.remoteVideo) this.remoteVideo.srcObject = null;
+      this.socket.disconnect();
+      this.isLogin = false;
+      this.showLoading = false;
+      this.error = err || "";
+    },
+  },
+  mounted() {
+    this.intSoketRtc(this.HOST);
+  },
+};
 </script>
 
 <style>
+video,
+#app,
 html,
 body {
   margin: 0;
   padding: 0;
   user-select: none;
+  width: 100%;
+  height: 100%;
 }
-#app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-  user-select: none;
+
+video {
+  background: none;
+  object-fit: fill;
+  font-size: 0;
 }
-/*去除下标*/
-.amap-logo{
-display: none!important;
+.marke {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  height: 30px;
+  width: 100%;
+  background: rgba(0, 0, 0, 0.3);
 }
-.amap-copyright{
-opacity:0;
+/* 隐藏滚动条 */
+::-webkit-scrollbar {
+  width: 0 !important;
+  display: none;
 }
 </style>

+ 134 - 0
src/components/loading.vue

@@ -0,0 +1,134 @@
+<template>
+  <div class="loading">
+    <figure>
+      <div class="dot white"></div>
+      <div class="dot"></div>
+      <div class="dot"></div>
+      <div class="dot"></div>
+      <div class="dot"></div>
+    </figure>
+  </div>
+</template>
+
+<style scoped>
+.loading{
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+    z-index: 9999;
+    background: rgba(0, 0, 0, 0.7);
+    font-size: 12px;
+}
+figure {
+  position: absolute;
+  margin: auto;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  width: 6.25em;
+  height: 6.25em;
+  animation: rotate 2.4s linear infinite;
+}
+.white {
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: white;
+  animation: flash 2.4s linear infinite;
+  opacity: 0;
+}
+.dot {
+  position: absolute;
+  margin: auto;
+  width: 2.4em;
+  height: 2.4em;
+  border-radius: 100%;
+  transition: all 1s ease;
+}
+.dot:nth-child(2) {
+  top: 0;
+  bottom: 0;
+  left: 0;
+  background: #ff4444;
+  animation: dotsY 2.4s linear infinite;
+}
+.dot:nth-child(3) {
+  left: 0;
+  right: 0;
+  top: 0;
+  background: #ffbb33;
+  animation: dotsX 2.4s linear infinite;
+}
+.dot:nth-child(4) {
+  top: 0;
+  bottom: 0;
+  right: 0;
+  background: #99cc00;
+  animation: dotsY 2.4s linear infinite;
+}
+.dot:nth-child(5) {
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: #33b5e5;
+  animation: dotsX 2.4s linear infinite;
+}
+
+@keyframes rotate {
+  0% {
+    transform: rotate(0);
+  }
+  10% {
+    width: 6.25em;
+    height: 6.25em;
+  }
+  66% {
+    width: 2.4em;
+    height: 2.4em;
+  }
+  100% {
+    transform: rotate(360deg);
+    width: 6.25em;
+    height: 6.25em;
+  }
+}
+
+@keyframes dotsY {
+  66% {
+    opacity: 0.1;
+    width: 2.4em;
+  }
+  77% {
+    opacity: 1;
+    width: 0;
+  }
+}
+@keyframes dotsX {
+  66% {
+    opacity: 0.1;
+    height: 2.4em;
+  }
+  77% {
+    opacity: 1;
+    height: 0;
+  }
+}
+
+@keyframes flash {
+  33% {
+    opacity: 0;
+    border-radius: 0%;
+  }
+  55% {
+    opacity: 0.6;
+    border-radius: 100%;
+  }
+  66% {
+    opacity: 0;
+  }
+}
+</style>

+ 98 - 0
src/components/login.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="login">
+    <div>
+      <div class="logo">登陆</div>
+      <input type="text" placeholder="房间" maxlength="20" v-model="roomID" />
+      <input type="text" placeholder="昵称" maxlength="20" v-model="name" />
+      <div class="err">{{ err || error }}</div>
+      <button @click="login">加入</button>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    err: {
+      type: String,
+      default: () => {
+        return "";
+      },
+    },
+  },
+  data() {
+    return {
+      name: "",
+      roomID: "",
+      error: ""
+    };
+  },
+  methods: {
+    async login() {
+      if(!this.roomID){
+        this.error = "请输入房间号";
+      }else if(!this.name){
+        this.error = "请输入昵称";
+      }else{
+        this.$emit("loginBack", { name: this.name, roomID: this.roomID });
+      }
+    },
+  },
+};
+</script>
+<style scoped>
+.login {
+  width: 100%;
+  height: 100%;
+  font-size: 16px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.logo {
+  width: 100px;
+  height: 50px;
+  margin: 0 auto;
+  text-align: center;
+}
+input {
+  display: block;
+  border: 0;
+  border-bottom: solid 1px #ccc;
+  text-indent: 10px;
+  margin: 0 auto;
+  margin-bottom: 20px;
+  outline: none;
+  border-radius: 0;
+  background: none;
+}
+
+button {
+  display: block;
+  width: 130px;
+  height: 30px;
+  background: #79b8fa;
+  border: none;
+  color: white;
+  font-size: 17px;
+  font-weight: 500;
+  border-radius: 5px;
+  cursor: pointer;
+  margin: 0 auto;
+  margin-top: 20px;
+  line-height: 30px;
+}
+button:hover {
+  background: #2d8cf0;
+}
+.err {
+  width: 175px;
+  margin: 0 auto;
+  font-size: 12px;
+  color: red;
+  text-align: left;
+  text-indent: 10px;
+}
+input:nth-child(3) {
+  margin-bottom: 5px;
+}
+</style>

+ 0 - 90
src/page/index.vue

@@ -1,90 +0,0 @@
-<template>
-  <div class="box">
-    <video id="v2" autoplay playsinline muted></video>
-  </div>
-</template>
-<script>
-const { io } = require("socket.io-client");
-export default {
-  data() {
-    return {
-      socket: null,
-      HOST: "wss://car.caner.top",
-      Peer: null,
-    };
-  },
-  methods: {
-    intSoketRtc(host) {
-      // int socket
-      this.socket = io(host, {
-        // autoConnect: false,
-        auth: {
-          roomID: "test",
-          name: "123",
-        },
-        transports: ["websocket"],
-      });
-
-      // socket
-      this.socket.on("connect", () => {
-        this.Peer = new RTCPeerConnection({
-          bundlePolicy: "max-bundle"
-        });
-
-        // listen state
-        this.Peer.onicegatheringstatechange  = () => {
-          console.log("GatheringState: ", this.Peer.iceGatheringState);
-          if (this.Peer.iceGatheringState === "complete") {
-            const answer = this.Peer.localDescription;
-            this.socket.emit("msg", answer);
-          }
-        };
-
-        // listen track
-        this.Peer.ontrack = (evt) => {
-          console.log("track", evt.streams[0]);
-          const video = document.getElementById("v2");
-          video.srcObject = evt.streams[0];
-        };
-
-        console.log("connected");
-      });
-
-      this.socket.on("msg", async (data) => {
-        console.log(data);
-        if (data.type == "offer") {
-          await this.Peer.setRemoteDescription(data);
-          const answer = await this.Peer.createAnswer();
-          await this.Peer.setLocalDescription(answer);
-        }
-      });
-
-      this.socket.on("joined", async (user) => {
-        console.log(`${user.name}_${user.ip}_joined_${user.roomID}`);
-        this.socket.emit("msg", { type: "startRTC" });
-      });
-
-      this.socket.on("leaved", (user) => {
-        console.log(`${user.name}_${user.ip}_leaved_${user.roomID}`);
-      });
-
-      this.socket.on("connect_error", (err) => {
-        console.log("connect_error", err);
-      });
-    },
-  },
-  mounted() {
-    this.intSoketRtc(this.HOST);
-  },
-};
-</script>
-<style scoped>
-video {
-  width: 500px;
-  height: 500px;
-  background: none;
-  object-fit: fill;
-  border: solid 1px red;
-  margin: 0 auto;
-}
-</style>