本文介绍了如何使用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;
}效果:
评论交流
支持作者