使用openlayers给地图添加内发光、外发光、内外阴影、三维立体效果
给地图添加内阴影、外阴影效果,可以使用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);});},//添加MaskaddMask(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);});},//添加MaskaddMask(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>