commit all stuff
parent
d0b99de71c
commit
8f60511606
|
@ -9,6 +9,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"vue": "^3.2.13"
|
"vue": "^3.2.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
153
src/App.vue
153
src/App.vue
|
@ -1,26 +1,153 @@
|
||||||
<template>
|
<template>
|
||||||
<img alt="Vue logo" src="./assets/logo.png">
|
<div id="main">
|
||||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
<form id="connection" @submit.prevent="connectionFormSubmit">
|
||||||
|
<input type="text" v-model="host">
|
||||||
|
<input type="submit" :value="isConnected ? 'Disconnect' : 'Connect'">
|
||||||
|
</form>
|
||||||
|
<div id="camera-view" v-if="isConnected" style="float:left;">
|
||||||
|
<img v-if="camera_image_src !== null" :src="camera_image_src" alt="Camera View">
|
||||||
|
</div>
|
||||||
|
<div id="controls" v-if="isConnected" style="float: right">
|
||||||
|
<input type="range" v-model="driveThrottle">
|
||||||
|
<JoyStick @change="joystickChanged" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HelloWorld from './components/HelloWorld.vue'
|
import JoyStick from "@/components/JoyStick";
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {JoyStick},
|
||||||
HelloWorld
|
data () {
|
||||||
|
return {
|
||||||
|
connection: null,
|
||||||
|
host: 'rc-car-esp32.local',
|
||||||
|
camera_image_src: null,
|
||||||
|
stickData: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
speed: 0,
|
||||||
|
angle: 0
|
||||||
|
},
|
||||||
|
steerValue: 'straight',
|
||||||
|
driveSide: 'stop',
|
||||||
|
driveThrottle: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isConnected() {
|
||||||
|
return this.connection instanceof WebSocket;
|
||||||
|
},
|
||||||
|
command_str() {
|
||||||
|
return `${this.driveSide}|${this.driveThrottle}|${this.steerValue}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
connectionFormSubmit() {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
this.connection = new WebSocket(`ws://${this.host}`)
|
||||||
|
|
||||||
|
this.connection.onmessage = _.throttle((event) => {
|
||||||
|
if (event.data instanceof Blob) {
|
||||||
|
this.camera_image_src = URL.createObjectURL(event.data);
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
this.connection.onopen = () => {
|
||||||
|
console.log("Successfully connected to the websocket server.");
|
||||||
|
};
|
||||||
|
this.connection.onclose = () => {
|
||||||
|
console.log("Connection to the websocket server closed.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.connection.close();
|
||||||
|
this.connection = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
joystickChanged: _.throttle(function (data) {
|
||||||
|
this.stickData = data;
|
||||||
|
}, 100),
|
||||||
|
scale(number, fromLeft, fromRight, toLeft, toRight) {
|
||||||
|
return toLeft + (number - fromLeft) / (fromRight - fromLeft) * (toRight - toLeft);
|
||||||
|
},
|
||||||
|
handleKeyDown: function (e) {
|
||||||
|
const keyCode = String(e.keyCode || e.code || e.keyIdentifier);
|
||||||
|
|
||||||
|
if (keyCode === "37") {
|
||||||
|
this.steerValue = 'left';
|
||||||
|
}
|
||||||
|
if (keyCode === "39") {
|
||||||
|
this.steerValue = 'right';
|
||||||
|
}
|
||||||
|
if (keyCode === "38") {
|
||||||
|
this.driveSide = 'forward';
|
||||||
|
}
|
||||||
|
if (keyCode === "40") {
|
||||||
|
this.driveSide = 'backward';
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
handleKeyUp: function (e) {
|
||||||
|
const keyCode = String(e.keyCode || e.code || e.keyIdentifier);
|
||||||
|
|
||||||
|
if (keyCode === "37" || keyCode === "39") {
|
||||||
|
this.steerValue = 'straight';
|
||||||
|
}
|
||||||
|
if (keyCode === "38" || keyCode === "40") {
|
||||||
|
this.driveSide = 'stop';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
stickData(newValues) {
|
||||||
|
const xDeadzone = 5;
|
||||||
|
const yDeadzone = 5;
|
||||||
|
|
||||||
|
const true_values = {
|
||||||
|
x: this.scale(newValues.x, -96, 96, 0, 100),
|
||||||
|
y: this.scale(newValues.y, 96, -96, 0, 100)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_.inRange(true_values.x, 50 - xDeadzone, 50 + xDeadzone)) {
|
||||||
|
this.steerValue = 'straight';
|
||||||
|
}
|
||||||
|
else if (true_values.x > 50) {
|
||||||
|
this.steerValue = 'right';
|
||||||
|
}
|
||||||
|
else if (true_values.x < 50) {
|
||||||
|
this.steerValue = 'left';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.inRange(true_values.y, 50 - yDeadzone, 50 + yDeadzone)) {
|
||||||
|
this.driveSide = 'stop';
|
||||||
|
this.driveThrottle = 0;
|
||||||
|
}
|
||||||
|
else if (_.inRange(true_values.y, 50 - yDeadzone, 0)) {
|
||||||
|
this.driveThrottle = this.scale(true_values.y, 50, 0, 0, 100);
|
||||||
|
this.driveSide = 'backward';
|
||||||
|
}
|
||||||
|
if (_.inRange(true_values.y, 50 + yDeadzone, 100)) {
|
||||||
|
this.driveThrottle = this.scale(true_values.y, 50, 100, 0, 100);
|
||||||
|
this.driveSide = 'forward';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
command_str(newCmd) {
|
||||||
|
if (this.isConnected && this.connection.readyState === WebSocket.OPEN) {
|
||||||
|
this.connection.send(newCmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener('keydown', this.handleKeyDown);
|
||||||
|
window.addEventListener('keyup', this.handleKeyUp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#app {
|
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
text-align: center;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-top: 60px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
<p>
|
|
||||||
For a guide and recipes on how to configure / customize this project,<br>
|
|
||||||
check out the
|
|
||||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
|
||||||
</p>
|
|
||||||
<h3>Installed CLI Plugins</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Essential Links</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
|
||||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
|
||||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
|
||||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
|
||||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Ecosystem</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
|
||||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
|
||||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
props: {
|
|
||||||
msg: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
<style>
|
||||||
|
.vue-joystick {
|
||||||
|
display: inline-block;
|
||||||
|
background: white;
|
||||||
|
height: 256px;
|
||||||
|
width: 256px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
border: solid 4px var(--color);
|
||||||
|
}
|
||||||
|
.vue-joystick::before,
|
||||||
|
.vue-joystick::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.vue-joystick::before {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: -32px;
|
||||||
|
background: var(--color);
|
||||||
|
height: 64px;
|
||||||
|
width: 64px;
|
||||||
|
border-radius: 50%;
|
||||||
|
transform: translateX(var(--x)) translateY(var(--y));
|
||||||
|
}
|
||||||
|
.vue-joystick::after {
|
||||||
|
left: 126px;
|
||||||
|
bottom: 128px;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 4px;
|
||||||
|
background: var(--color);
|
||||||
|
transform: rotate(var(--angle));
|
||||||
|
transform-origin: bottom center;
|
||||||
|
height: var(--speed);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="vue-joystick"
|
||||||
|
:style="style"
|
||||||
|
@touchmove="handleTouch"
|
||||||
|
@mousemove="handleMove"
|
||||||
|
@mousedown="handleStart"
|
||||||
|
@mouseup="handleRelease"
|
||||||
|
@touchend="handleRelease"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: "#25B"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
angle: 0,
|
||||||
|
speed: 0,
|
||||||
|
isMouseDown: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
style() {
|
||||||
|
return {
|
||||||
|
"--x": `${this.x + 128}px`,
|
||||||
|
"--y": `${this.y + 128}px`,
|
||||||
|
"--speed": `${this.speed}px`,
|
||||||
|
"--angle": `${this.angle}deg`,
|
||||||
|
"--color": `${this.color}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleStart() {
|
||||||
|
this.isMouseDown = true;
|
||||||
|
},
|
||||||
|
handleTouch({ touches: [touch] }) {
|
||||||
|
const { clientX, clientY } = touch;
|
||||||
|
const { offsetLeft, offsetTop } = this.$el;
|
||||||
|
const x = Math.round(clientX - offsetLeft - 128);
|
||||||
|
const y = Math.round(clientY - offsetTop - 128);
|
||||||
|
this.updatePosition(x, y);
|
||||||
|
},
|
||||||
|
handleMove({ clientX, clientY }) {
|
||||||
|
if (!this.isMouseDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { offsetLeft, offsetTop } = this.$el;
|
||||||
|
const x = Math.round(clientX - offsetLeft - 128);
|
||||||
|
const y = Math.round(clientY - offsetTop - 128);
|
||||||
|
this.updatePosition(x, y);
|
||||||
|
},
|
||||||
|
handleRelease() {
|
||||||
|
this.emitAll("release");
|
||||||
|
this.isMouseDown = false;
|
||||||
|
this.updatePosition(0, 0);
|
||||||
|
},
|
||||||
|
updatePosition(x, y) {
|
||||||
|
const offset = 128 - 32;
|
||||||
|
const radians = Math.atan2(y, x);
|
||||||
|
const angle = Math.round((radians * 180) / Math.PI, 4);
|
||||||
|
this.angle = angle + (angle > 90 ? -270 : 90);
|
||||||
|
this.speed = Math.min(
|
||||||
|
Math.round(Math.sqrt(Math.pow(y, 2) + Math.pow(x, 2))),
|
||||||
|
128
|
||||||
|
);
|
||||||
|
this.x = this.speed > offset ? Math.cos(radians) * offset : x;
|
||||||
|
this.y = this.speed >= offset ? Math.sin(radians) * offset : y;
|
||||||
|
this.emitAll();
|
||||||
|
},
|
||||||
|
emitAll(name = "change") {
|
||||||
|
this.$emit(name, {
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
speed: this.speed,
|
||||||
|
angle: this.angle
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.emitAll();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in New Issue