自定义形状
介绍
- 使用
RegularShape
来创建各种几何形状- 正方形:4 个点,45 度旋转
- 矩形:椭圆形状,缩放比例[1, 0.5]
- 三角形:3 个点,旋转 45 度
- 五角星:5 个外点 + 内半径
- 十字形:4 个点,内半径为 0
- X 形:十字形旋转 45 度
- 堆叠形状:两个正方形叠加
js
// RegularShape 参数说明
new RegularShape({
fill: fill, // 填充样式
stroke: stroke, // 边框样式
points: 5, // 顶点数量
radius: 10, // 外半径
radius2: 4, // 内半径(星形)
angle: 0, // 旋转角度
scale: [1, 0.5], // 缩放比例
displacement: [0, 10], // 位移偏移
});
展开代码
vue
<template>
<div class="map-container">
<div ref="mapContainer" id="map"></div>
<div class="controls">
<div class="info">
<h4>自定义形状绘制</h4>
<p>地图上显示了{{ featureCount }}个随机分布的自定义形状</p>
</div>
<div class="control-group">
<label>形状数量:</label>
<input
type="range"
min="50"
max="500"
step="50"
v-model="featureCount"
@input="updateFeatures"
/>
<span>{{ featureCount }}</span>
</div>
<div class="control-group">
<label>形状大小:</label>
<input
type="range"
min="5"
max="20"
v-model="shapeSize"
@input="updateShapeSize"
/>
<span>{{ shapeSize }}px</span>
</div>
<div class="color-controls">
<h5>颜色控制:</h5>
<div class="color-buttons">
<button
v-for="color in colors"
:key="color"
:style="{ backgroundColor: color }"
@click="changeColor(color)"
:class="{ active: currentColor === color }"
>
{{ color }}
</button>
</div>
</div>
<div class="shape-controls">
<h5>形状类型:</h5>
<div class="shape-buttons">
<button
v-for="shape in shapeTypes"
:key="shape"
@click="filterByShape(shape)"
:class="{ active: selectedShape === shape }"
>
{{ shape }}
</button>
<button
@click="showAllShapes"
:class="{ active: selectedShape === 'all' }"
>
全部
</button>
</div>
</div>
<button @click="regenerateFeatures" class="regenerate-btn">
重新生成形状
</button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import Map from "ol/Map";
import View from "ol/View";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Feature from "ol/Feature";
import Point from "ol/geom/Point";
import { Style, Fill, Stroke, RegularShape } from "ol/style";
import "ol/ol.css";
const mapContainer = ref(null);
let map = null;
let vectorSource = null;
let vectorLayer = null;
let styles = {};
// 响应式变量
const featureCount = ref(250);
const shapeSize = ref(10);
const currentColor = ref("red");
const selectedShape = ref("all");
// 颜色选项
const colors = ["red", "blue", "green", "yellow", "aqua", "purple", "orange"];
// 形状类型
const shapeTypes = [
"square",
"rectangle",
"triangle",
"star",
"cross",
"x",
"stacked",
];
// 创建样式函数
const createStyles = (size, color) => {
const stroke = new Stroke({ color: "black", width: 2 });
const fill = new Fill({ color: color });
return {
square: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: size,
angle: Math.PI / 4,
}),
}),
rectangle: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
radius: size / Math.SQRT2,
radius2: size,
points: 4,
angle: 0,
scale: [1, 0.5],
}),
}),
triangle: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 3,
radius: size,
rotation: Math.PI / 4,
angle: 0,
}),
}),
star: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 5,
radius: size,
radius2: size * 0.4,
angle: 0,
}),
}),
cross: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: size,
radius2: 0,
angle: 0,
}),
}),
x: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: size,
radius2: 0,
angle: Math.PI / 4,
}),
}),
stacked: [
new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: size * 0.5,
angle: Math.PI / 4,
displacement: [0, size],
}),
}),
new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: size,
angle: Math.PI / 4,
}),
}),
],
};
};
// 生成要素
const generateFeatures = (count) => {
const features = new Array(count);
const e = 20037508; // Web Mercator 最大范围
for (let i = 0; i < count; ++i) {
const coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
features[i] = new Feature(new Point(coordinates));
// 随机选择形状类型
const shapeType = shapeTypes[Math.floor(Math.random() * shapeTypes.length)];
features[i].set("shapeType", shapeType);
features[i].setStyle(styles[shapeType]);
}
return features;
};
// 更新要素数量
const updateFeatures = () => {
const features = generateFeatures(featureCount.value);
vectorSource.clear();
vectorSource.addFeatures(features);
};
// 更新形状大小
const updateShapeSize = () => {
styles = createStyles(shapeSize.value, currentColor.value);
// 更新所有要素的样式
vectorSource.getFeatures().forEach((feature) => {
const shapeType = feature.get("shapeType");
feature.setStyle(styles[shapeType]);
});
};
// 改变颜色
const changeColor = (color) => {
currentColor.value = color;
styles = createStyles(shapeSize.value, color);
// 更新所有要素的样式
vectorSource.getFeatures().forEach((feature) => {
const shapeType = feature.get("shapeType");
feature.setStyle(styles[shapeType]);
});
};
// 按形状类型过滤
const filterByShape = (shapeType) => {
selectedShape.value = shapeType;
vectorSource.getFeatures().forEach((feature) => {
const featureShapeType = feature.get("shapeType");
if (featureShapeType === shapeType) {
feature.setStyle(styles[featureShapeType]);
} else {
feature.setStyle(null); // 隐藏其他形状
}
});
};
// 显示所有形状
const showAllShapes = () => {
selectedShape.value = "all";
vectorSource.getFeatures().forEach((feature) => {
const shapeType = feature.get("shapeType");
feature.setStyle(styles[shapeType]);
});
};
// 重新生成要素
const regenerateFeatures = () => {
updateFeatures();
showAllShapes();
};
onMounted(() => {
// 初始化样式
styles = createStyles(shapeSize.value, currentColor.value);
// 创建矢量数据源
vectorSource = new VectorSource();
// 创建矢量图层
vectorLayer = new VectorLayer({
source: vectorSource,
});
// 创建视图
const view = new View({
center: [0, 0],
zoom: 2,
});
// 初始化地图
map = new Map({
target: mapContainer.value,
layers: [vectorLayer],
view,
});
// 生成初始要素
const features = generateFeatures(featureCount.value);
vectorSource.addFeatures(features);
});
onUnmounted(() => {
if (map) {
map.setTarget(undefined);
map = null;
}
});
</script>
<style scoped>
.map-container {
width: 100vw;
height: 100vh;
position: relative;
font-family: sans-serif;
}
#map {
width: 100%;
height: 100%;
}
.controls {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
background-color: rgba(255, 255, 255, 0.95);
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
min-width: 280px;
max-height: 90vh;
overflow-y: auto;
}
.info h4 {
margin: 0 0 10px 0;
color: #495057;
font-size: 16px;
}
.info p {
margin: 5px 0;
font-size: 14px;
color: #666;
}
.control-group {
display: flex;
align-items: center;
gap: 10px;
margin: 15px 0;
font-size: 14px;
}
.control-group label {
min-width: 80px;
font-weight: 500;
}
.control-group input[type="range"] {
flex: 1;
min-width: 100px;
}
.control-group span {
min-width: 40px;
text-align: right;
font-weight: bold;
color: #007bff;
}
.color-controls,
.shape-controls {
margin: 15px 0;
}
.color-controls h5,
.shape-controls h5 {
margin: 0 0 8px 0;
font-size: 14px;
color: #495057;
}
.color-buttons,
.shape-buttons {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.color-buttons button {
padding: 5px 10px;
border: 2px solid #ddd;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
color: white;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
transition: all 0.3s ease;
}
.color-buttons button.active {
border-color: #007bff;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}
.shape-buttons button {
padding: 5px 10px;
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
color: #495057;
transition: all 0.3s ease;
}
.shape-buttons button:hover {
background-color: #e9ecef;
}
.shape-buttons button.active {
background-color: #007bff;
color: white;
border-color: #007bff;
}
.regenerate-btn {
width: 100%;
padding: 10px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background-color 0.3s ease;
margin-top: 15px;
}
.regenerate-btn:hover {
background-color: #218838;
}
</style>