制作一个摇杆按钮

发布于 2023-09-19  110 次阅读


拖动时候用到的三个事件:mousedownmousemovemouseup在移动端都不起任何作用。毕竟移动端是没有鼠标的,查资料后发现,在移动端与之相对应的分别是:touchstarttouchmovetouchend事件。还有一点要注意的是在PC端获取当前鼠标的坐标是:event.clientXevent.clientY,在移动端获取坐标位置则是:event.touches[0].clientXevent.touches[0].clientY

制作一个摇杆按钮

<template>
  <div style="position: relative;">
    <!-- 事实传输数据 -->
    <!-- <websocket :x="sendleft" :y="sendtop" ref="ws" /> -->
    <div
      class="toucharea"
      @touchstart="onTouchStart"
      @touchmove="onTouchMove"
      @touchcancel="onTouchEnd"
      @touchend="onTouchEnd"
      @mousedown="onTouchStartPC"
      @mousemove="onTouchMovePC"
      @mouseup="onTouchEndPC"
    >
      <div
        style="border-radius: 50%;"
        :style="{width:touchRadius*2+'px',height:touchRadius*2+'px'}"
      ></div>
    </div>
    <div
      class="ball"
      :style="{left:left+'px',top:top+'px'}"
      :class="(inDraging === false && transition)?'animation':''"
    >
      <slot name="ball">
        <div style="width: 60px;height: 60px;border-radius: 50%;"></div>
      </slot>
    </div>
    <!-- :class="{animation:inDraging===false&&transition}" -->

    <div
      class="stick"
      :class="(inDraging === false && transition)?'animation':''"
      :style="{height: stickHeight+'px',transform:'translateX(-50%)'}"
    >
      <div
        :style="{transform:'rotate('+(angle/(3.14159/180)-90)+'deg)'}"
        style="transform-origin: 50% 0%;width: 100%;height: 100%;"
      >
        <slot name="stick"></slot>
      </div>
    </div>
    <div class="bottom">
      <slot name="bottom"></slot>
    </div>
  </div>
</template>

<script>
// import websocket from "./websocket.vue";
import { writeApi, readApi } from "../request/api.js";
import mqtt from "mqtt/dist/mqtt";

// import AppVue from "@/App.vue";

var startLeft, startTop;
var timer;
var getDistance = function(x1, y1, x2, y2) {
  var _x = Math.abs(x1 - x2); //返回绝对值
  var _y = Math.abs(y1 - y2);
  // console.log("x坐标是", _x);
  // console.log("y坐标是", _y);
  return Math.sqrt(_x * _x + _y * _y); //返回平方根,到圆心的距离
};

export default {
  data() {
    return {
      // sendleft: 156,
      // sendtop: 152,
      sendleft: 0,
      sendtop: 0,
      left: 0,
      top: 0,
      stickHeight: 0,
      angle: 0,
      inDraging: false,
      reqTimer: null,

      //心跳检测装置
      heartCheck: {
        thimeout: 2000,
        timeoutObj: null,
        reset: function() {
          //接收成功一次推送,就将心跳检测的计时器为2秒
          clearTimeout(this.timeoutObj);
          this.start();
        },
        start: function() {
          this.timeoutObj = setTimeout(() => {
            var message = {
              type: "t10010",
              servive: "发送维持连接消息!"
            };
            console.log("发送维持连接消息");
            ws.send(JSON.stringify(message)); //启动心跳
          }, this.thimeout);
        }
      },
      bootConnect: {
        shipId: "",
        tcp: ""
      },
      //

      client: "",
      options: {
        port: 8083, //端口号
        connectTimeout: 4000,
        // clientId:
        //   "mqtt_lih" +
        //   Math.random()
        //     .toString(16)
        //     .substr(2, 8),

        // clientId:
        //   "mqtt_lih" +
        //   Math.random()
        //     .toString(16)
        //     .substr(2, 8),
        clientId: "mqtt_lih",

        username: JSON.parse(localStorage.getItem("userMsg")).username || "",
        password: JSON.parse(localStorage.getItem("userMsg")).password || "",
        clean: true
      }
    };
  },
  props: {
    //触摸识别区域的半径
    touchRadius: {
      type: Number,
      default: 100
    },
    //杆头的移动范围半径,也就是杆体的长度
    ballMoveRadius: {
      type: Number,
      default: 50
    },
    transition: {
      type: Boolean,
      default: false
    }
  },
  created() {
    // let _this = this;
    // var baseApi = localStorage.getItem("baseApi") || "";
    // this.client = mqtt.connect(
    //   "ws://" + baseApi + ":" + "1883" + "/mqtt",
    //   _this.options
    // );
    // this.client = mqtt.connect(
    //   "ws://" + "192.168.221.62" + ":" + "1883" + "/mqtt",
    //   _this.options
    // );
  },

  mounted() {
    document.addEventListener("mouseup", this.onTouchEndPC);

    this.loop();
    //启用定时器
    let _this = this;
    this.reqTimer = setInterval(() => {
      if (
        this.$store.state.bootMsg.CurrentMode == 1

        // this.$store.state.bootMsg.Status == 2 ||
        // this.$store.state.bootMsg.Status == 4
      ) {
        console.log("不发送");
      } else {
        _this.sendMsg();
        console.log("发送的是" + this.sendleft + "和" + this.sendtop);
      }
    }, 500);
  },
  methods: {
    sendMsg() {
      // alert(1);
      // AppVue.aNewFunction();
      // this.$root.aNewFunction();
      let keyWord =
        "0 67 4 124 " +
        "0 " +
        this.sendleft.toString() +
        " 1 " +
        this.sendtop.toString() +
        " " +
        Math.floor((this.sendleft + this.sendtop + 1) / 256).toString() +
        " " +
        ((this.sendleft + this.sendtop + 1) % 256).toString() +
        " 13 10";
      this.bootConnect = JSON.parse(localStorage.getItem("bootConnect"));
      // //MainType: 18,
      // // shipId: "110", //船id
      // // tcp: "fg.yunenjoy.cn:50113",
      // writeApi({
      //   ...this.bootConnect,

      //   command: keyWord
      // }).then(res => {
      //   console.log(res);
      // });
      // this.client.publish.topic
      // this.mqttConnect.doPublish(
      //   JSON.stringify({ ...this.bootConnect, command: keyWord })
      // );
      this.doPublish(JSON.stringify({ ...this.bootConnect, command: keyWord }));
    },

    //MQTT发送数据
    doPublish(payload) {
      let obj = {
        timestamp: 111,
        type: "manualControl",
        framecnt: 1,
        data: {
          throttle: this.sendtop,
          rudder: this.sendleft
        }
      };

      this.mqttConnect.doPublish(JSON.stringify(obj));
      // this.mqttConnect.doPublish(payload);
      // this.client.publish(
      //   "ship/push/" +
      //     JSON.parse(localStorage.getItem("userInfo")).lastOperationShip.seq ||
      //     "",
      //   payload,
      //   { qos: 0 },
      //   error => {
      //     if (error) {
      //       console.log("Publish error", error);
      //     }
      //   }
      // );
    },
    sssfn() {
      alert(1);
    },
    onTouchStart(e) {
      var curTouch = e.touches[0];
      startLeft = curTouch.clientX - this.left;
      startTop = curTouch.clientY - this.top;
      this.inDraging = true;
    },

    onTouchStartPC(e) {
      console.log("-------onTouchStartPC---------");
      // alert(this.left);

      console.log(e);
      startLeft = e.clientX - (this.left || 0);
      startTop = e.clientY - (this.top || 0);
      this.inDraging = true;
    },

    onTouchMovePC(e) {
      // console.log("-------onTouchMovePC---------");
      // console.log(e);
      if (this.inDraging) {
        var tleft = e.clientX - startLeft;
        var ttop = e.clientY - startTop;

        var distance = getDistance(tleft, ttop, 0, 0);

        if (distance >= this.ballMoveRadius) {
          // alert("大于半径");
          distance = this.ballMoveRadius;
        }

        var angle = Math.atan2(ttop - 0, tleft - 0);
        this.left = Math.cos(angle) * distance;
        this.top = Math.sin(angle) * distance;

        console.log();

        this.stickHeight = distance;
        this.angle = angle;

        //需要发送的距离
        // /100 -> /65
        // this.sendleft = Math.floor(150 + (this.left / 65) * 38);
        // this.sendtop = Math.floor(152 - (this.top / 65) * 46);

        this.sendleft = (this.left / 65) * 100;
        this.sendtop = -(this.top / 65) * 100;
      }

      console.log("---------e.clientX-------" + e.clientX + "---------------");
      console.log("---------startLeft-------" + startLeft + "---------------");
      console.log("---------left-------" + this.left + "---------------");
      // this.$refs.ws.sendPostion();
    },
    onTouchEndPC(e) {
      // console.log("-------onTouchEndPC---------");
      // console.log(e);

      this.stickHeight = this.left = this.top = 0;

      this.inDraging = false;
      //复原便宜坐标
      this.sendleft = 0;
      this.sendtop = 0;
    },
    onTouchMove(e) {
      // console.log(e);
      var curTouch = e.touches[0];
      var tleft = curTouch.clientX - startLeft;
      var ttop = curTouch.clientY - startTop;

      var distance = getDistance(tleft, ttop, 0, 0);

      if (distance >= this.ballMoveRadius) {
        // alert("大于半径");
        distance = this.ballMoveRadius;
      }

      var angle = Math.atan2(ttop - 0, tleft - 0);
      this.left = Math.cos(angle) * distance;
      this.top = Math.sin(angle) * distance;

      this.stickHeight = distance;
      this.angle = angle;

      //需要发送的距离
      // /100 -> /65
      // this.sendleft = Math.floor(150 + (this.left / 65) * 38);
      // this.sendtop = Math.floor(152 - (this.top / 65) * 46);

      this.sendleft = (this.left / 65) * 100;
      this.sendtop = -(this.top / 65) * 100;

      // this.$refs.ws.sendPostion();
    },
    onTouchEnd(e) {
      this.stickHeight = this.left = this.top = 0;

      this.inDraging = false;
      //复原便宜坐标
      this.sendleft = 0;
      this.sendtop = 0;
      // this.$emit("onJoyStickCancel");
    },
    loop() {
      requestAnimationFrame(this.loop);

      if (this.inDraging) {
        // console.log(
        //   "角度:" +
        //     this.angle +
        //     ",x偏移" +
        //     this.left +
        //     ",y偏移" +
        //     this.top +
        //     ",power:" +
        //     this.stickHeight / this.ballMoveRadius
        // );
        // this.$emit("onJoyStickUpdate", {
        //   angle: this.angle,
        //   direction: { x: this.left, y: this.top },
        //   power: this.stickHeight / this.ballMoveRadius
        // });
      }
    },
    readBoot() {
      this.bootConnect = JSON.parse(localStorage.getItem("bootConnect"));

      readApi({
        //MianType: 18,
        // tcp: "fg.yunenjoy.cn:50113",
        // shipId: "110"
        ...this.bootConnect
      });
    }
  },
  // components: { websocket },

  beforeDestroy() {
    document.removeEventListener("mouseup");
    clearInterval(this.reqTimer);
    this.reqTimer = null;
    this.bootConnect = JSON.parse(localStorage.getItem("bootConnect"));

    //退出手动模式指令
    // writeApi({
    //   //MainType: 18,
    //   // shipId: "110", //船id
    //   // tcp: "fg.yunenjoy.cn:50113",
    //   ...this.bootConnect,
    //   command: "0 67 3 124 194 13 10"
    // }).then(res => {
    //   console.log(res);
    // });
  },
  destroyed() {
    clearInterval(this.reqTimer);
    this.reqTimer = null;
  }
};
</script>

<style lang="less" scoped>
.toucharea {
  user-select: none;

  position: absolute;
  z-index: 4;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}
.ball {
  position: absolute;
  z-index: 3;
  transform: translate(-50%, -50%);
}
.stick {
  position: absolute;
  z-index: 2;
}
.ball.animation {
  transition: left 0.1s ease-out, top 0.1s ease-out;
}
.stick.animation {
  transition: all 0.2s ease-out;
}
.bottom {
  position: absolute;
  z-index: 1;
  transform: translate(-50%, -50%);
}
</style>

源码:https://github.com/ezshine/ezjoystick
csdn友连:https://blog.csdn.net/ezshine/article/details/124312109


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。