LMX
博客精选作品支持关于
Command Palette

Search for a command to run...

博客

OpenLayers实现小车的轨迹查询的功能

LMX2019年6月4日12 分钟阅读#JavaScript#WebSocket

本文介绍了如何使用OpenLayers实现小车轨迹查询功能,包括地图初始化、轨迹数据获取、动画播放等关键步骤,帮助读者快速上手地图开发。

支持实现的功能

  • 限制3天的时间跨度
  • 小车在运动中改变速度
  • 小车重新运动
  • 小车运动的点位支持打开详情信息

这是自己当时刚从后端切到前端时,实现的第一个功能,当时都没有接触过前后端分离的开发模式,更不知道react,抱着现学现做的态度做的.....,可能有需要的同学,做个参考吧。

import React, { Component } from "react";
import {
  DatePicker,
  Input,
  Icon,
  Button,
  Table,
  Progress,
  Slider,
  notification,
} from "antd";
import ol from "openlayers";
import moment from "moment";
import axios from "axios";
import playback from "../../Images/playback.png";
import speedcar from "../../Images/car.png";
import move from "../../Images/move.png";
import direction from "../../Images/direction.png";
import stop from "../../Images/stop.png";
import daemarker from "../../Images/direction.png";
import "./CarMove.less";
import "openlayers/css/ol.css";

const RangePicker = DatePicker.RangePicker;

//地图上原先的vector
let prevVector;
//确定地图放缩级别
let zoomSize = [
  100, 200, 500, 1000, 2000, 8000, 14000, 20000, 25000, 50000, 100000, 200000,
  500000, 1000000, 2000000,
];
let zoomlevel = [17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3];

class CarMove extends Component {
  constructor(props) {
    super(props);
    this.state = {
      columns: [], //table的表头信息
      DAENumber: "", //默认的编号
      DAEStatus: "", //默认的状态
      DEAAddress: "", //默认的地址
      queryKeyWord: this.props.keyword === undefined ? "" : this.props.keyword, //用户输入的查询关键字
      pathTableData: [], //table中的数据
      scrollPathTableHeight: "", //设置table出现滑动条的高度
      userChooseStartTime:
        this.props.starttime === undefined ? "" : this.props.starttime, //用户选择的起始时间
      userChooseEndTime:
        this.props.endtime === undefined ? "" : this.props.endtime, //用户选择的结束时间
      defaultShowBtnFlag:
        this.props.showBtnFlag === undefined ? false : this.props.showBtnFlag, //用户传入是否显示返回按钮
      defaultModeFlag:
        this.props.modeFlag === undefined ? false : this.props.modeFlag, //区分用户进入方式
      tableBodyData: [], //默认查询到的数据的总条数
      showAdjustSpeed: false, //默认的调节小车行驶的进度条不显示
      carProgerss: 0, //默认的小车在轨迹上行驶的进度
      canRefreshAnimation: true, //默认小车的重新运动按钮可用
      mapCenterLongitude: 105.442574, //默认的地图显示中心的经度
      mapCenterLatitude: 28.871718, //默认的地图显示中心的纬度
      mapUrl:
        "http://www.google.cn/maps/vt/pb=!1m4!1m3!1i{z}!2i{x}!3i{y}!2m3!1e0!2sm!3i345013117!3m8!2szh-CN!3scn!5e1105!12m4!1e68!2m2!1sset!2sRoadmap!4e0", //默认的在线地图的服务地址
    };
    this.map = null; //定义的地图容器句柄
    this.marker = null; //默认点击出现弹出框
    this.index = 0; //默认的小车在轨迹的位置
    this.defaultCarSpeed = 12; //默认小车的行驶速度
  }

  //构造查询轨迹table的头信息
  // eslint-disable-next-line react/sort-comp
  generateTableHeaderData() {
    const columns = [
      {
        title: "采集时间",
        dataIndex: "column0",
        key: "1",
        width: 150,
        align: "center",
      },
      {
        title: "采集器序列号",
        dataIndex: "column1",
        key: "2",
        width: 150,
        align: "center",
      },
      {
        title: "位置",
        dataIndex: "column2",
        key: "3",
        width: 180,
        align: "center",
      },
    ];
    this.setState({
      columns: columns,
    });
  }

  // 输入插叙的关键字
  userInputKeyWord = (e) => {
    this.setState({
      queryKeyWord: e.target.value,
    });
  };

  //清空输入的查询关键词
  clearUserKeyWord = () => {
    this.setState({
      queryKeyWord: "",
    });
  };

  //用户选择查询的时间时
  userChangeQueryTime = (date, dateString) => {
    this.setState({
      userChooseStartTime: dateString[0],
      userChooseEndTime: dateString[1],
    });
  };

  //用户点击查询轨迹
  getPathDatas = () => {
    let mapData = [],
      tableData = [];
    let queryKey = this.state.queryKeyWord;
    //当用户选择的时间间隔超过三天的给出提示
    let startTime = this.state.userChooseStartTime;
    let endTime = this.state.userChooseEndTime;
    //把时间转化为毫秒,计算差值
    let time = new Date(endTime).getTime() - new Date(startTime).getTime();
    //必填项的校验
    if (queryKey === "" || queryKey == null) {
      notification.warn({
        message: "输入的车牌号或标签号不能为空",
        description: `请重新输入车牌号或者标签号进行查询`,
        duration: 3,
        placement: "bottomRight",
      });
      return;
    }
    if (startTime === "" || endTime === "") {
      notification.warn({
        message: "请选择查询的时间段",
        description: `查询的时间段不能为空,请重新选择时间进行查询`,
        duration: 3,
        placement: "bottomRight",
      });
      return;
    }
    if (time > 259200000) {
      notification.warn({
        message: "时间间隔不能超过三天",
        description: `查询的时间段不能超过三天,请重新选择时间进行查询`,
        duration: 3,
        placement: "bottomRight",
      });
      return;
    }

    //数据请求的方法
    var url = "http://localhost:8080/getAllDaeInfo";
    axios
      .get(url)
      .then(function (response) {
        console.log("response-------------->", response.data);
      })
      .catch(function (error) {
        console.log(error);
      });

    let enterTime,
      daeStatus,
      longitude,
      latitude,
      maxDistance = 0,
      distance = 0;
    let carPathData = new Array();
    //生成模拟数据
    for (let i = 0; i < 20; i++) {
      let time = Date.now() + i * 1000;
      let carPathDataItem = {};
      carPathDataItem.collectTime = time;
      carPathDataItem.devStatus = 0;
      carPathDataItem.devId = "ts10001" + i;
      carPathDataItem.devAddr = "XX市市测试区测试街道" + i;
      carPathDataItem.longitude =
        107.4425790689 + (Math.random() * (i + 1)) / 500;
      carPathDataItem.latitude =
        29.8717183035 + (Math.random() * (i + 1)) / 500;
      carPathData.push(carPathDataItem);
    }

    for (let i = 0; i < carPathData.length; i++) {
      enterTime = moment(carPathData[i].collectTime).format(
        "YYYY-MM-DD HH:mm:ss",
      );
      if (carPathData[i].devStatus === 0) {
        daeStatus = "离线";
      } else {
        daeStatus = "在线";
      }
      //计算经纬度之间最大的距离
      if (i < carPathData.length - 1) {
        distance = this.getDistance(
          carPathData[i].latitude,
          carPathData[i].longitude,
          carPathData[i + 1].latitude,
          carPathData[i + 1].longitude,
        );
        if (distance > maxDistance) {
          maxDistance = distance;
        }
      }
      //表格数据的构造
      tableData.push({
        key: `${i}`,
        column0: enterTime,
        column1: carPathData[i].devId,
        column2: carPathData[i].devAddr,
      });
      longitude = carPathData[i].longitude;
      latitude = carPathData[i].latitude;
      //地图数据的构造
      mapData.push([
        longitude,
        latitude,
        carPathData[i].devId,
        daeStatus,
        carPathData[i].devAddr,
      ]);
    }
    if (mapData.length > 0) {
      let level = 13;
      //确定地图的放缩等级
      for (let i = 0; i < zoomSize.length; i++) {
        if (maxDistance > 100) {
          if (maxDistance < zoomSize[i]) {
            level = zoomlevel[i - 1];
            break;
          }
        } else {
          if (maxDistance === 0) {
            level = 5;
            break;
          } else {
            level = 17;
            break;
          }
        }
      }
      this.setState({
        tableBodyData: tableData,
        canRefreshAnimation: true,
      });
      //每次添加新的坐标点时清空原先的坐标点
      this.map.removeLayer(prevVector);
      //清空以移动的小车
      this.map.un("postcompose", this.moveFeature);
      //当用户打开弹出窗口的时候,点击查询的情况情况
      this.closeMapMarker();
      this.map.getView().setZoom(level);
      //重新设置地图的中心(不设置的话小车移动报错)
      this.map
        .getView()
        .setCenter(
          ol.proj.transform(
            [mapData[0][0], mapData[0][1]],
            "EPSG:4326",
            "EPSG:3857",
          ),
        );
      this.generateCarMoveOnMap(mapData);
    } else {
      this.setState({
        tableBodyData: tableData,
        carProgerss: 0,
        canRefreshAnimation: false,
      });
      //每次添加新的坐标点时清空原先的坐标点
      this.map.removeLayer(prevVector);
      //清空以移动的小车
      this.map.un("postcompose", this.moveFeature);
      //当用户打开弹出窗口的时候,点击查询的情况情况
      this.closeMapMarker();
      //重新设置地图的中心(不设置的话小车移动报错)
      this.map
        .getView()
        .setCenter(
          ol.proj.transform(
            [this.state.mapCenterLongitude, this.state.mapCenterLatitude],
            "EPSG:4326",
            "EPSG:3857",
          ),
        );
      this.generateCarMoveOnMap(mapData);
      notification.info({
        message: "没有查询到轨迹信息",
        description: `根据你输入车牌号或者标签号以及时间没有查询到信息`,
        duration: 3,
      });
    }
  };

  //计算经纬度之间的距离(单位米)
  getDistance = (lat1, lng1, lat2, lng2) => {
    let radLat1 = (lat1 * Math.PI) / 180.0;
    let radLat2 = (lat2 * Math.PI) / 180.0;
    let a = radLat1 - radLat2;
    let b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0;
    let s =
      2 *
      Math.asin(
        Math.sqrt(
          Math.pow(Math.sin(a / 2), 2) +
            Math.cos(radLat1) *
              Math.cos(radLat2) *
              Math.pow(Math.sin(b / 2), 2),
        ),
      );
    s = s * 6378.137; // EARTH_RADIUS;
    s = Math.round(s * 10000) / 10;
    return s;
  };

  componentDidMount() {
    //设置表格默认滚动区域
    this.setState({
      scrollPathTableHeight: this.tableContrainer.clientHeight - 60,
    });
    //监听窗口的放缩,动态的设置table出现出现滚动的高度
    window.onresize = () => {
      let tableContainer = this.tableContrainer;
      if (tableContainer) {
        let tableHeight = tableContainer.clientHeight - 60;
        this.setState({
          scrollPathTableHeight: tableHeight,
        });
      }
    };
    //调用构造table头信息的函数
    this.generateTableHeaderData();

    //生成地图层
    let raster = new ol.layer.Tile({
      source: new ol.source.XYZ({ url: this.state.mapUrl }),
    });
    //总地图
    this.map = new ol.Map({
      target: "pathMapContainer",
      layers: [raster],
      view: new ol.View({
        center: ol.proj.transform(
          [this.state.mapCenterLongitude, this.state.mapCenterLatitude],
          "EPSG:4326",
          "EPSG:3857",
        ),
        //指定地图投影类型
        projection: "EPSG:3857",
        //定义地图显示的层级
        zoom: 13,
        maxZoom: 18,
        minZoom: 5,
      }),
    });
    //用户打开坐标显示
    this.openMapMarker(this.map);
  }

  //构造轨迹回放动画
  generateCarMoveOnMap = (coordinate) => {
    let that = this;
    //标记层
    let layer = new ol.layer.Vector({
      source: new ol.source.Vector(),
    });
    let styles = {
      //线路的样式
      route: new ol.style.Style({
        stroke: new ol.style.Stroke({ width: 5, color: "#66ACED" }),
      }),
      //起点的样式
      start: new ol.style.Style({
        image: new ol.style.Icon({ scale: 0.75, src: direction }),
      }),
      //起点的样式
      end: new ol.style.Style({
        image: new ol.style.Icon({
          scale: 0.8,
          anchor: [0.5, 0.82],
          src: stop,
        }),
      }),
      //小车的样式
      geoMarker: new ol.style.Style({
        image: new ol.style.Icon({
          scale: 0.65,
          anchor: [0.5, 0.8],
          src: move,
        }),
      }),
      //真实点的样式
      point: new ol.style.Style({
        image: new ol.style.Icon({ scale: 1, src: daemarker }),
      }),
    };

    let animating = false,
      now,
      speed;
    let routeCoords, routeLength, geoMarker;
    let startButton = document.getElementById("start-animation");
    let traversed = 0; //走过的路程
    let elapsedTime = 0; //用过的时间
    let retime = 0; //保存上次运动所用的时间

    if (coordinate.length > 2) {
      let geometry = new ol.geom.LineString();
      let anchor;
      let minScale = 0.001;
      let lngValue, latValue, times;
      let lnglatArray = new Array();
      //构造坐标点之间的线路
      for (let i = 0; i < coordinate.length; i++) {
        //构造坐标点的时候构造路径
        if (i > 0) {
          //构造坐标点之间的路径
          lngValue = coordinate[i][0] - coordinate[i - 1][0];
          latValue = coordinate[i][1] - coordinate[i - 1][1];
          //有一种特殊的情况当相邻的两个坐标点重合,或者当两个坐标点非常近的时候
          let zLength = Math.sqrt(lngValue * lngValue + latValue * latValue);
          if (zLength === 0 || Math.round(zLength / minScale) === 0) {
            geometry.appendCoordinate(
              ol.proj.transform(
                [coordinate[i][0], coordinate[i][1]],
                "EPSG:4326",
                "EPSG:3857",
              ),
            );
          } else {
            times = Math.round(zLength / minScale);
            let xminScale = lngValue / times;
            let yminScale = latValue / times;
            for (let j = 0; j < times; j++) {
              lnglatArray[0] = coordinate[i - 1][0] + j * xminScale;
              lnglatArray[1] = coordinate[i - 1][1] + j * yminScale;
              // eslint-disable-next-line no-use-before-define
              anchor = setAnchorStyle(geometry, lnglatArray);
              layer.getSource().addFeature(anchor);
            }
          }
        }
      }

      //设置坐标点之间连线点的坐标
      // eslint-disable-next-line no-inner-declarations
      function setAnchorStyle(geometry, lnglatArray) {
        geometry.appendCoordinate(
          ol.proj.transform(lnglatArray, "EPSG:4326", "EPSG:3857"),
        );
        let anchor = new ol.Feature({
          geometry: new ol.geom.Point(
            ol.proj.transform(lnglatArray, "EPSG:4326", "EPSG:3857"),
          ),
        });
        return anchor;
      }

      //标记小车运动轨迹上真实的坐标点
      let reallyGeometry = new ol.geom.LineString();
      //从第二个点到倒数第二个点
      for (let i = 1; i < coordinate.length - 1; i++) {
        reallyGeometry.appendCoordinate(
          ol.proj.transform(
            [coordinate[i][0], coordinate[i][1]],
            "EPSG:4326",
            "EPSG:3857",
          ),
        );
      }
      let reallyCoords = reallyGeometry.getCoordinates();
      let reallyPoint = new Array();
      for (let i = 0; i < reallyCoords.length; i++) {
        //构造小车运动的线路上真实经过的坐标点
        reallyPoint.push(
          new ol.Feature({
            type: "point",
            state: 0,
            num: coordinate[i + 1][2],
            status: coordinate[i + 1][3],
            address: coordinate[i + 1][4],
            geometry: new ol.geom.Point(reallyCoords[i]),
          }),
        );
      }

      routeCoords = geometry.getCoordinates();
      routeLength = routeCoords.length;

      //小车行走的线路
      let routeFeature = new ol.Feature({
        type: "route",
        state: 1,
        geometry: geometry,
      });
      //运动的小车
      geoMarker = new ol.Feature({
        type: "geoMarker",
        state: 1,
        geometry: new ol.geom.Point(routeCoords[0]),
      });
      //轨迹开始坐标
      let startMarker = new ol.Feature({
        type: "start",
        state: 0,
        num: coordinate[0][2],
        status: coordinate[0][3],
        address: coordinate[0][4],
        geometry: new ol.geom.Point(routeCoords[0]),
      });
      //轨迹终止坐标
      let endMarker = new ol.Feature({
        type: "end",
        state: 0,
        num: coordinate[coordinate.length - 1][2],
        status: coordinate[coordinate.length - 1][3],
        address: coordinate[coordinate.length - 1][4],
        geometry: new ol.geom.Point(routeCoords[routeLength - 1]),
      });

      //轨迹上所有的坐标的集合
      let vector = new ol.layer.Vector({
        source: new ol.source.Vector({
          features: [
            routeFeature,
            ...reallyPoint,
            geoMarker,
            startMarker,
            endMarker,
          ],
        }),
        style: function (feature) {
          if (animating && feature.get("type") === "geoMarker") {
            return null;
          }
          return styles[feature.get("type")];
        },
      });

      //保存上一次的layer
      prevVector = vector;
      //在地图上添加坐标的点集
      that.map.addLayer(vector);

      // eslint-disable-next-line no-use-before-define
      startButton.addEventListener("click", startAnimation, false);
      // eslint-disable-next-line no-use-before-define
      setTimeout(() => startAnimation(), 50);
    } else if (coordinate.length > 0 && coordinate.length < 3) {
      //当做标点小于3个大于0个的时候
      let geometry = new ol.geom.LineString();
      for (let i = 0; i < coordinate.length; i++) {
        geometry.appendCoordinate(
          ol.proj.transform(
            [coordinate[i][0], coordinate[i][1]],
            "EPSG:4326",
            "EPSG:3857",
          ),
        );
      }
      routeCoords = geometry.getCoordinates();
      routeLength = routeCoords.length;

      //小车行走的线路
      let routeFeature = new ol.Feature({
        type: "route",
        state: 1,
        geometry: geometry,
      });
      //运动的小车
      geoMarker = new ol.Feature({
        type: "geoMarker",
        state: 1,
        geometry: new ol.geom.Point(routeCoords[0]),
      });
      //轨迹开始坐标
      let startMarker = new ol.Feature({
        type: "start",
        state: 0,
        num: coordinate[0][2],
        status: coordinate[0][3],
        address: coordinate[0][4],
        geometry: new ol.geom.Point(routeCoords[0]),
      });
      //轨迹终止坐标
      let endMarker = new ol.Feature({
        type: "end",
        state: 0,
        num: coordinate[coordinate.length - 1][2],
        status: coordinate[coordinate.length - 1][3],
        address: coordinate[coordinate.length - 1][4],
        geometry: new ol.geom.Point(routeCoords[routeLength - 1]),
      });

      //轨迹上所有的坐标的集合
      let vector = new ol.layer.Vector({
        source: new ol.source.Vector({
          features: [routeFeature, geoMarker, startMarker, endMarker],
        }),
        style: function (feature) {
          if (animating && feature.get("type") === "geoMarker") {
            return null;
          }
          return styles[feature.get("type")];
        },
      });

      //保存上一次的layer
      prevVector = vector;
      //在地图上添加坐标的点集
      that.map.addLayer(vector);

      // eslint-disable-next-line no-use-before-define
      startButton.addEventListener("click", startAnimation, false);
      // eslint-disable-next-line no-use-before-define
      setTimeout(() => startAnimation(), 50);
    }

    //小车运动函数
    this.moveFeature = function (event) {
      let vectorContext = event.vectorContext;
      let frameState = event.frameState;
      if (animating) {
        if (retime === 0) {
          elapsedTime = frameState.time - now;
        } else {
          elapsedTime = frameState.time - retime;
        }
        retime = frameState.time;
        let index = Math.round((that.defaultCarSpeed * elapsedTime) / 1000);
        traversed += index;
        that.index = traversed;
        if (traversed >= routeLength) {
          //当小车运动到终点的时候
          // eslint-disable-next-line no-use-before-define
          moveEnd(true);
          return;
        }
        let currentPoint = new ol.geom.Point(routeCoords[traversed]);
        let feature = new ol.Feature(currentPoint);
        vectorContext.drawFeature(feature, styles.geoMarker);
      }
      //设置运动的进度条
      that.setState({
        carProgerss: Math.round(((that.index + 1) / routeLength) * 100),
      });
      that.map.render();
    };

    //开始小车的运动
    function startAnimation() {
      traversed = 0; //走过的路程
      elapsedTime = 0; //用过的时间
      retime = 0; //保存上次运动所用的时间
      if (animating) {
        // eslint-disable-next-line no-use-before-define
        refreshAnimation();
      } else {
        animating = true;
        now = new Date().getTime();
        //speed = that.defaultCarSpeed;
        geoMarker.setStyle(null);
        that.map.on("postcompose", that.moveFeature);
        that.map.render();
      }
    }

    //当小车运动到终点
    function moveEnd(isend) {
      animating = false;
      let coord = isend ? routeCoords[routeLength - 1] : routeCoords[0];
      geoMarker.getGeometry().setCoordinates(coord);
      //防止出现问题当调用次函数的时在设置一下进度条为100%
      that.setState({
        carProgerss: 100,
      });
      that.map.un("postcompose", that.moveFeature);
    }

    //小车重新运动
    function refreshAnimation() {
      if (that.state.carProgerss === 100) {
        that.setState({
          carProgerss: 0,
        });
      }
      animating = false;
      if (that.state.canRefreshAnimation) {
        startAnimation();
      }
    }
  };

  //显示调节小车运动速度
  adjustSpeed = () => {
    this.setState((prevState) => ({
      showAdjustSpeed: !prevState.showAdjustSpeed,
    }));
  };

  //改变小车队的行驶速度
  setNewSpeed = (value) => {
    this.defaultCarSpeed = value + 4;
  };

  //关闭用户打开的显示框
  closeMapMarker = () => {
    this.marker.setPosition(undefined);
    return false;
  };

  //监听地图的点击事件
  openMapMarker = (map) => {
    let that = this;
    let element = that.alertContainer;
    that.marker = new ol.Overlay({
      element: element,
      positioning: "bottom-center",
      stopEvent: true,
      offset: [-5, -24],
      autoPan: true,
      autoPanAnimation: {
        duration: 250,
      },
    });
    map.addOverlay(that.marker);
    let dom = document.createElement("div");
    dom.setAttribute("id", "closeAlert");
    that.alertContainer.appendChild(dom);
    map.on("click", function (event) {
      let feature = map.forEachFeatureAtPixel(event.pixel, function (feature) {
        return feature;
      });
      if (feature) {
        //点击地图上大的坐标点的时候出现弹出框
        if (feature.get("state") === 0) {
          let coordinatePoint = feature.getGeometry().getCoordinates();
          that.marker.setPosition(coordinatePoint);
          //弹出框显示出来
          element.style.display = "block";
          dom.onclick = that.closeMapMarker;
          that.setState({
            DAENumber: feature.get("num"),
            DAEStatus: feature.get("status"),
            DEAAddress: feature.get("address"),
          });
        }
      }
    });
  };

  //返回上一级菜单
  goBackUpperLevel = () => {
    this.props.backPrevLavel(this.state.defaultModeFlag);
  };

  render() {
    let { queryKeyWord } = this.state;
    const suffix = queryKeyWord ? (
      <Icon type="close-circle" onClick={this.clearUserKeyWord} />
    ) : null;
    return (
      <div className="pathContainer">
        <div className="headerContainer">
          <Input
            placeholder="请输入车牌号进行查询"
            suffix={suffix}
            onChange={this.userInputKeyWord}
            value={queryKeyWord}
            className="userInputKeyWord"
          />
          <RangePicker
            onChange={this.userChangeQueryTime}
            format="YYYY-MM-DD HH:mm"
            showTime
            className="userSelectQueryTime"
            value={
              this.state.userChooseStartTime
                ? [
                    moment(this.state.userChooseStartTime),
                    moment(this.state.userChooseEndTime),
                  ]
                : null
            }
          />
          <Button
            onClick={this.getPathDatas}
            icon="search"
            className="queryButton"
          >
            查询轨迹
          </Button>
        </div>
        <div className="displayContainer">
          <div className="displayLeftContainer">
            <div id="pathMapContainer" className="pathMapContainer">
              {this.state.defaultShowBtnFlag && (
                <Button
                  onClick={this.goBackUpperLevel}
                  icon="left"
                  className="goBack"
                >
                  返回
                </Button>
              )}
              <div
                className="alertContainer"
                ref={(box) => {
                  this.alertContainer = box;
                }}
              >
                <br />
                <div className="alertItem">
                  <span className="alertSpan">采集器编号:</span>
                  <span className="alertSpanValue">{this.state.DAENumber}</span>
                </div>
                <div className="alertItem">
                  <span className="alertSpan">采集器状态:</span>
                  <span className="alertSpanValue">{this.state.DAEStatus}</span>
                </div>
                <div className="alertItem">
                  <span className="alertSpan">采集器地址:</span>
                  <span className="alertSpanValue">
                    {this.state.DEAAddress}
                  </span>
                </div>
                <div className="triangleDown"></div>
              </div>
              <div className="adjustSpeed">
                <img src={playback} className="imgSize" id="start-animation" />
                <Progress
                  percent={this.state.carProgerss}
                  status="active"
                  className="progress"
                />
                <img
                  src={speedcar}
                  className="carImgSize"
                  onClick={this.adjustSpeed}
                />
                {this.state.showAdjustSpeed && (
                  <div className="adjustSlider">
                    <Slider
                      vertical
                      min={1}
                      defaultValue={this.defaultCarSpeed}
                      className="sliderChoose"
                      onChange={this.setNewSpeed}
                    />
                  </div>
                )}
              </div>
            </div>
          </div>
          <div className="displayRightContainer">
            <div className="pathTitle">
              <span className="spanTitle">车辆轨迹查询</span>
            </div>
            <div
              className="pathTable"
              ref={(box) => {
                this.tableContrainer = box;
              }}
            >
              <Table
                pagination={false}
                columns={this.state.columns}
                dataSource={this.state.tableBodyData}
                scroll={{ y: this.state.scrollPathTableHeight }}
              />
            </div>
          </div>
        </div>
      </div>
    );
  }
}

export default CarMove;
.pathContainer {
  width: 100%;
  height: calc(~"100% - 60px");
  margin-top: 60px;
}
.headerContainer {
  width: 100%;
  height: 40px;
  padding-top: 4px;
}
.displayContainer {
  width: 100%;
  height: calc(~"100% - 40px");
}
.userInputKeyWord {
  width: 280px;
  margin-left: 10px;
}
.userSelectQueryTime {
  width: 280px;
  margin-left: 4px;
}
.queryButton {
  margin-left: 4px;
  width: 100px;
}
.displayLeftContainer {
  width: calc(~"100% - 480px");
  height: 100%;
  float: left;
}
.displayRightContainer {
  width: 480px;
  height: 100%;
  z-index: 1;
  float: right;
  background-color: rgb(236, 236, 236);
}
.pathTitle {
  width: 100%;
  height: 35px;
  padding: 7px;
}
.spanTitle {
  font-size: 14px;
  color: #4291e9;
  margin-left: 5px;
}
.pathTable {
  width: 100%;
  height: calc(~"100% - 35px");
}
.pathMapContainer {
  height: 100%;
  position: relative;
  .ol-zoom {
    left: calc(~"100% - 46px");
    top: calc(~"100% - 72px");
    padding: 0px;
    box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.15);
    .ol-zoom-in {
      height: 26px;
      width: 26px;
      background-color: rgb(255, 255, 255);
      font-size: 20px;
      color: rgb(150, 150, 150);
      cursor: pointer;
      overflow: hidden;
      ::selection {
        color: white !important;
      }
    }
    .ol-zoom-out {
      cursor: pointer;
      height: 26px;
      width: 26px;
      background-color: rgb(255, 255, 255);
      font-size: 20px;
      color: rgb(150, 150, 150);
      overflow: hidden;
    }
  }
  .ol-attribution {
    display: none;
  }
}
.goBack {
  position: absolute;
  top: 17px;
  left: 35px;
  z-index: 1;
  color: rgb(100, 173, 255);
}
.adjustSpeed {
  width: 500px;
  height: 40px;
  position: absolute;
  z-index: 1;
  bottom: 2.7%;
  border-radius: 5px;
  background-color: rgb(254, 254, 254);
  left: 50%;
  margin-left: -250px;
}
.imgSize {
  width: 35px;
  height: 35px;
  margin-top: 4px;
  margin-left: 5px;
  cursor: pointer;
}
.progress {
  width: 400px;
  margin-left: 4px;
  position: absolute;
  margin-top: 11px;
}
.carImgSize {
  width: 35px;
  height: 35px;
  position: absolute;
  cursor: pointer;
  margin-left: 414px;
  margin-top: 2px;
}
.adjustSlider {
  background-color: snow;
  width: 28px;
  height: 181px;
  position: absolute;
  margin-top: -229px;
  border-radius: 6px;
  margin-left: 456px;
  padding-bottom: 16px;
  padding-top: 5px;
}
.alertContainer {
  width: 190px;
  height: 120px;
  background-color: white;
  border-radius: 5px;
  display: none;
  border: 1px solid lightgrey;
  -moz-user-select: text;
  user-select: text;
}
#closeAlert {
  position: absolute;
  right: 5%;
  top: 3%;
  width: 15px;
  height: 15px;
  cursor: pointer;
  background: url("../../Images/loginout.png") no-repeat;
}
.alertItem {
  width: 93.6%;
  white-space: normal;
  word-break: break-all;
  margin-left: 8px;
}
.alertSpan {
  font-family: "Microsoft YaHei";
  font-size: 12px;
  color: #111111;
}
.alertSpanValue {
  font-family: "Microsoft YaHei";
  font-size: 12px;
  margin-left: 7px;
}
.triangleDown {
  position: absolute;
  width: 0;
  height: 0;
  border-left: 15px solid transparent;
  border-right: 15px solid transparent;
  border-top: 20px solid white;
  left: 50%;
  margin-left: -10px;
  bottom: -13px;
}

效果:

评论交流

支持作者
上一篇

WebSocket教程

下一篇

React中理解并使用Redux

LMX BlogWeb · TypeScript · Vue · Node.js
创作者
@LMXZQQ
版本
暂无
日期
2026-10-30
文章
29
部署平台
Nginx
模板源码
GitHub
许可证
MIT License
字体
Geist
技术栈
  • next@16.4.3
  • react@19.3.1
  • @base-ui/react@1.8.0
  • shadcn@4.12.0
  • tailwindcss@4.3.3
访问统计
  • OpenPanel
  • Google Analytics
供智能体阅读
  • llms.txt
  • index.md
灵感来源
  1. Tailwind CSS
  2. shadcn/ui
  3. Vercel
  4. Apple
  5. Evil Charts
  6. Devouring Details
  7. Skiper UI
  8. Making Software
  9. shadcncraft
维护者
静水流深 / LMX
1602
Command Palette

Search for a command to run...