给地图添加内阴影、外阴影效果,可以使用ol的扩展库ol-ext
npm i ol-ext
引入两个类Mask遮罩和Crop裁剪
import Mask from "ol-ext/filter/Mask";
import Crop from "ol-ext/filter/Crop.js";
Mask主要用来设置内阴影效果,外阴影则是由Crop设置。
addShadowByExt() {
axios
.get("https://geo.datav.aliyun.com/areas_v3/bound/110000.json")
.then((res) => {
// 将 GeoJSON 数据解析为 ol.Feature 对象
const features = new GeoJSON().readFeatures(res.data);
const source = new VectorSource({
features: features,
});
let layer = new VectorLayer({
source: source,
});
this.map.addLayer(layer);
//内发光
let mask = this.addMask({
fillColor: "#CC92E6",
shadowColor: "#ff0",
feature: features[0],
inner: true,
});
//设置裁切
this.setLayerFilterCrop(layer, features[0]);
layer.addFilter(mask);
});
},
//添加Mask
addMask(options) {
return new Mask({
feature: options.feature,
wrapX: false,
inner: options.inner || false,
fill: new Fill({ color: options.fillColor }),
shadowColor: options.shadowColor || "rgba(0,0,0,0.5)",
shadowWidth: options.shadowWidth || 10,
// shadowMapUnits:true,
});
},
setLayerFilterCrop(layer, feature) {
/**
* 设置图层裁切
*/
const crop = new Crop({
feature: feature,
inner: false,
active: true,
wrapX: true,
shadowWidth: 10,
shadowColor: "#000",
});
layer.addFilter(crop);
},
但是这种阴影不能调节偏移量,因此要设置阴影偏移量达到三维立体效果还需要利用canvas原理。
要想做出这种效果,需要在style的renderer函数中获取ctx上下文和图层用到的坐标数组coordinate。然后使用addOutlineShadow函数添加阴影。
我这里堆叠了好几层阴影,这样看着更立体些。
addRegionLayer() {
let _this = this;
const source = new VectorSource({
url: "https://geo.datav.aliyun.com/areas_v3/bound/110000.json",
format: new GeoJSON(),
});
this.vectorLayer = new VectorLayer({
source: source,
style: new Style({
renderer(coordinate, state) {
let arr = coordinate[0][0];
const ctx = state.context;
_this.addOutlineShadow(ctx, {
fillStyle: "rgba(30, 60, 95,1)",
shadowOffsetY: 30,
shadowOffsetX: 2,
shadowColor: "rgba(30, 60, 95,1)",
strokeStyle: "rgba(30, 60, 95,1)",
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 20,
shadowOffsetX: 2,
shadowColor: "rgba( 56, 113, 139,1)",
strokeStyle: "rgba(30, 60, 95,1)",
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 15,
shadowOffsetX: 2,
shadowColor: "rgba(255,255,255,1)",
strokeStyle: "rgba(30, 60, 95,1)",
shadowBlur: 10,
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 10,
shadowOffsetX: 2,
shadowColor: "rgba(83, 173, 214,1)",
strokeStyle: "rgba(83, 173, 214,1)",
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 8,
shadowOffsetX: 2,
shadowColor: "rgba(255,255,255,1)",
strokeStyle: "rgba(255,255,255,1)",
shadowBlur: 10,
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "#fff",
shadowOffsetY: 5,
shadowOffsetX: 2,
shadowColor: "rgba(70, 133, 171,1)",
strokeStyle: "rgba(70, 133, 171,1)",
shadowBlur: 10,
coodArr: arr,
});
//白色
_this.addOutlineShadow(ctx, {
fillStyle: "rgba(70, 133, 171,1)",
shadowOffsetY: 5,
shadowOffsetX: 10,
shadowColor: "rgba(255,255,255,1)",
strokeStyle: "#50e3ff",
shadowBlur: 15,
coodArr: arr,
lineWidth: 2,
});
},
}),
});
this.map.addLayer(this.vectorLayer);
},
拿到canvas上下文了,就可以使用canvas的方法添加阴影效果了,最后再将拿到的坐标数组用canvas的方法绘制成多边形,就可以在地图上显示了。
事实上,这种方法不仅能绘制阴影,还可以绘制其他效果。
addOutlineShadow(ctx, option) {
// 设置属性控制图形的外观
ctx.fillStyle = option.fillStyle || "transparent";
ctx.strokeStyle = option.strokeStyle || "transparent";
ctx.lineWidth = option.lineWidth || 1;
// 设置Y轴偏移量
ctx.shadowOffsetY = option.shadowOffsetY || 20;
// 设置X轴偏移量
ctx.shadowOffsetX = option.shadowOffsetX || 2;
// 设置模糊度
ctx.shadowBlur = option.shadowBlur || 2;
// 设置阴影颜色
ctx.shadowColor = option.shadowColor || "#000";
ctx.beginPath();
let arr = option.coodArr || [];
for (let i = 0; i < arr.length; i++) {
const data = arr[i];
if (i === 0) {
ctx.moveTo(data[0], data[1]);
} else {
ctx.lineTo(data[0], data[1]);
}
}
ctx.closePath();
ctx.fill();
ctx.stroke();
},
完整代码:
<template>
<div class="box">
<h1>给地图添加内发光、外发光、内外阴影、三维立体效果</h1>
<div id="map"></div>
</div>
</template>
<script>
import GeoJSON from "ol/format/GeoJSON.js";
import Map from "ol/Map.js";
import View from "ol/View.js";
import { OSM, Vector as VectorSource, XYZ } from "ol/source.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import { Fill, Stroke, Text, Style } from "ol/style.js";
import { LineString, MultiPolygon, Point, Polygon } from "ol/geom.js";
import Mask from "ol-ext/filter/Mask";
import Crop from "ol-ext/filter/Crop.js";
import axios from "axios";
export default {
name: "",
components: {},
data() {
return {
vectorLayer: null,
map: null,
};
},
computed: {},
created() {},
methods: {
addShadowByExt() {
axios
.get("https://geo.datav.aliyun.com/areas_v3/bound/110000.json")
.then((res) => {
// 将 GeoJSON 数据解析为 ol.Feature 对象
const features = new GeoJSON().readFeatures(res.data);
const source = new VectorSource({
features: features,
});
let layer = new VectorLayer({
source: source,
});
this.map.addLayer(layer);
//内发光
let mask = this.addMask({
fillColor: "#CC92E6",
shadowColor: "#ff0",
feature: features[0],
inner: true,
});
//设置裁切
this.setLayerFilterCrop(layer, features[0]);
layer.addFilter(mask);
});
},
//添加Mask
addMask(options) {
return new Mask({
feature: options.feature,
wrapX: false,
inner: options.inner || false,
fill: new Fill({ color: options.fillColor }),
shadowColor: options.shadowColor || "rgba(0,0,0,0.5)",
shadowWidth: options.shadowWidth || 10,
// shadowMapUnits:true,
});
},
setLayerFilterCrop(layer, feature) {
/**
* 设置图层裁切
*/
const crop = new Crop({
feature: feature,
inner: false,
active: true,
wrapX: true,
shadowWidth: 10,
shadowColor: "#000",
});
layer.addFilter(crop);
},
addRegionLayer() {
let _this = this;
const source = new VectorSource({
url: "https://geo.datav.aliyun.com/areas_v3/bound/110000.json",
format: new GeoJSON(),
});
this.vectorLayer = new VectorLayer({
source: source,
style: new Style({
renderer(coordinate, state) {
let arr = coordinate[0][0];
const ctx = state.context;
_this.addOutlineShadow(ctx, {
fillStyle: "rgba(30, 60, 95,1)",
shadowOffsetY: 30,
shadowOffsetX: 2,
shadowColor: "rgba(30, 60, 95,1)",
strokeStyle: "rgba(30, 60, 95,1)",
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 20,
shadowOffsetX: 2,
shadowColor: "rgba( 56, 113, 139,1)",
strokeStyle: "rgba(30, 60, 95,1)",
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 15,
shadowOffsetX: 2,
shadowColor: "rgba(255,255,255,1)",
strokeStyle: "rgba(30, 60, 95,1)",
shadowBlur: 10,
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 10,
shadowOffsetX: 2,
shadowColor: "rgba(83, 173, 214,1)",
strokeStyle: "rgba(83, 173, 214,1)",
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "transparent",
shadowOffsetY: 8,
shadowOffsetX: 2,
shadowColor: "rgba(255,255,255,1)",
strokeStyle: "rgba(255,255,255,1)",
shadowBlur: 10,
coodArr: arr,
});
_this.addOutlineShadow(ctx, {
fillStyle: "#fff",
shadowOffsetY: 5,
shadowOffsetX: 2,
shadowColor: "rgba(70, 133, 171,1)",
strokeStyle: "rgba(70, 133, 171,1)",
shadowBlur: 10,
coodArr: arr,
});
//白色
_this.addOutlineShadow(ctx, {
fillStyle: "rgba(70, 133, 171,1)",
shadowOffsetY: 5,
shadowOffsetX: 10,
shadowColor: "rgba(255,255,255,1)",
strokeStyle: "#50e3ff",
shadowBlur: 15,
coodArr: arr,
lineWidth: 2,
});
},
}),
});
this.map.addLayer(this.vectorLayer);
},
addOutlineShadow(ctx, option) {
// 设置属性控制图形的外观
ctx.fillStyle = option.fillStyle || "transparent";
ctx.strokeStyle = option.strokeStyle || "transparent";
ctx.lineWidth = option.lineWidth || 1;
// 设置Y轴偏移量
ctx.shadowOffsetY = option.shadowOffsetY || 20;
// 设置X轴偏移量
ctx.shadowOffsetX = option.shadowOffsetX || 2;
// 设置模糊度
ctx.shadowBlur = option.shadowBlur || 2;
// 设置阴影颜色
ctx.shadowColor = option.shadowColor || "#000";
ctx.beginPath();
let arr = option.coodArr || [];
for (let i = 0; i < arr.length; i++) {
const data = arr[i];
if (i === 0) {
ctx.moveTo(data[0], data[1]);
} else {
ctx.lineTo(data[0], data[1]);
}
}
ctx.closePath();
ctx.fill();
ctx.stroke();
},
},
mounted() {
const view = new View({
projection: "EPSG:4326",
center: [116.389, 39.903],
zoom: 7,
});
this.map = new Map({
layers: [],
target: "map",
view: view,
});
this.addRegionLayer();
// this.addShadowByExt();
},
};
</script>
<style lang="scss" scoped>
#map {
width: 100%;
height: 500px;
}
.box {
height: 100%;
}
</style>