最近要實現一個從底圖向上發光的功能,着實糾結了好久,起初像是使用polygon 顏色透明度來實現,但畢竟底圖不亮,增加圖層效果不理想呀
一、ui設計是這樣

二、繪制面
為底圖增加一個MultiPolygon來實現效果,
代碼片段
import {styleSwitch} from '@/components/common/set_style';
mapInit(){
this.mapObj = new Map({
target: el,
view: view
});
// 初始geoJson
var vectorSource = new VectorSource({
features: (new GeoJSON()).readFeatures(self.geojsonObject)
});
var vectorLayer = new VectorLayer({
renderMode: "image",
source:vectorSource,
style:self.styleFunction,
maxResolution: 2,
zIndex:2
});
this.mapObj.addLayer(vectorLayer);
},
styleFunction(feature, resolution) {
return styleSwitch(feature.getGeometry().getType(), resolution, feature)
},
set_style.js
import {Fill, Stroke, Style, RegularShape, Text, Icon,Circle,} from 'ol/style';
import { DEVICE_PIXEL_RATIO } from "ol/has";
/**
* 主要景區 polygon ,multipolygon, line , multiline 樣式設置
* 根據geojson類型設置地圖樣式
* @param type
* @param resolution
* @param feature
* @return {string}
*/
export function styleSwitch(type, resolution, feature) {
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
// Generate a rainbow gradient
let gradient = (() => {
let grad = context.createLinearGradient(
220*DEVICE_PIXEL_RATIO,20*DEVICE_PIXEL_RATIO,4220*DEVICE_PIXEL_RATIO,220*DEVICE_PIXEL_RATIO
);
// 設置開始結束顏色
grad.addColorStop(0, "rgba(0,192,50,0.4)");
grad.addColorStop(0.5, "rgba(0,192,50,0.2)");
grad.addColorStop(1, "rgba(1,134,82,0.5)");
return grad;
})();
var text = resolution < 0.0054931640625 ? feature.get('name') : ''; //根據分辨率控制文字顯示級別
let styleItem = [];
switch (type) {
case 'Point':
case 'MultiPoint':
styleItem = [
new Style({
image: new Icon({
src: 'images/left_module/police/police_topSmallIcon_second.png',
size: [24, 26],
color: '#ff0000'
}),
text: new Text({
offsetX: 14,
offsetY: 10,
font: '12px Calibri,sans-serif',
text: text,
fill: new Fill({
color: '#DC143C'
}),
stroke: new Stroke({
color: '#fff',
width: 3
})
})
})
]
break;
case 'MultiLineString':
case 'LineString':
styleItem = [
new Style({
stroke: new Stroke({
color: 'rgba(247,246,46, 0.2)',
lineDash: [5],
width: 8
})
}),
new Style({
stroke: new Stroke({
color: "rgba(247,246,46, 1)",
lineDash: [5],
width: 3
}),
})
]
break;
case 'Polygon':
case 'MultiPolygon':
styleItem = [
new Style({
// fill: new Fill({
// color: gradient
// }),
stroke: new Stroke({
color: "rgba(255,255,255,0.1)",
width: 2
}),
zIndex: 0
}),
new Style({
stroke: new Stroke({
color: 'rgba(245,255,250, 0.2)',
lineCap: 'round',
lineJoin:'bevel',
width: 10
})
}),
new Style({
stroke: new Stroke({
color: "rgba(0,149,32,0.1)",
lineCap: 'round',
lineJoin:'bevel',
width: 2
})
}),
// new Style({
// fill: new Fill({
// color: [0,149,32, 0.8]
// })
// }),
]
break;
case 'GeometryCollection':
styleItem = [
new Style({
stroke: new Stroke({
color: 'magenta',
width: 2
}),
fill: new Fill({
color: 'magenta'
}),
image: new Icon({
src: 'images/left_module/police/police_topSmallIcon_second.png',
size: [24, 26],
color: '#ff0000'
}),
text: new Text({
font: '12px Calibri,sans-serif',
text: text,
fill: new Fill({
color: '#DC143C'
}),
stroke: new Stroke({
color: '#fff',
width: 3
})
})
})
]
break;
case 'Circle':
styleItem = [
new Style({
stroke: new Stroke({
color: 'red',
width: 2
}),
fill: new Fill({
color: 'rgba(255,0,0,0.2)'
}),
image: new Icon({
src: 'images/left_module/police/police_topSmallIcon_second.png',
size: [24, 26],
color: '#ff0000'
}),
text: new Text({
font: '12px Calibri,sans-serif',
text: text,
fill: new Fill({
color: '#DC143C'
}),
stroke: new Stroke({
color: '#fff',
width: 3
})
})
})];
break;
}
return styleItem;
}
效果如下

三、使用.Render3D
后來想是否少了3D效果 ? 這又引用了ol-ext.layer.Render3D,
效果如下

四、使用.Colorize
看還是相距勝遠;於是想到了為底圖增加透明色,於是引入了ol-ext.filter.Colorize,
效果如下

顏色是着上了,可是 看源碼得知
ol_filter_Colorize.prototype.postcompose = function(e) { // Set back color hue var ctx = e.context; var canvas = ctx.canvas; ctx.save(); if (this.get('operation')=='enhance') { var v = this.get('value'); if (v) { var w = canvas.width; var h = canvas.height; ctx.globalCompositeOperation = 'color-burn' ctx.globalAlpha = v; ctx.drawImage (canvas, 0, 0, w, h); ctx.drawImage (canvas, 0, 0, w, h); ctx.drawImage (canvas, 0, 0, w, h); } } else { ctx.globalCompositeOperation = this.get('operation'); ctx.fillStyle = this.get('color'); ctx.fillRect(0,0,canvas.width,canvas.height); } ctx.restore(); }
五、 使用.Mask 加 .Crop
這直接是使用canvas繪制的一個面,很明顯是方的了,最后想來想去想到使用ol-ext.filter.Colorize來為底圖着色,再使用ol-ext.filter.Mask和ol-ext.filter.Crop來根據坐標繪制凸顯的面同時為地圖增加蒙層,但是又遇到了ol-ext.filter.Mask繪制的面和layerVector位置不合問題
預覽效果是這樣的
canvas繪制和layerVector繪制的面偏移較多
|
但地圖縮放時更明顯
|
但地圖縮放時更明顯
|
代碼片段
<script type="text/ecmascript-6"> import 'ol/ol.css'; import Map from 'ol/Map'; import View from 'ol/View'; import {XYZ, Vector as VectorSource} from 'ol/source'; import {Fill} from 'ol/style'; import Point from 'ol/geom/Point'; import Feature from 'ol/Feature'; import GeoJSON from 'ol/format/GeoJSON'; import olExtColorize from 'ol-ext/filter/Colorize' import olExtCrop from 'ol-ext/filter/Crop' import olExtMask from 'ol-ext/filter/Mask' import MultiPolygon from 'ol/geom/MultiPolygon'; export default = { methods:{ mapInit(polygonCoordiantes) { let self = this; let proj = 'EPSG:4326'; let el = this.$refs.map; let padLeft = (val, num, radix) => { let str = val.toString(radix || 10); return (new Array(num).join('0') + str).slice(-num); } var view = new View({ projection: proj, center: [103.37324413479338, 29.544684360197113], minZoom: 10, zoom: 13, maxZoom: 15, extent: [102.1000671387,28.7086486816,104.7244262695,30.0448608398], }); this.mapObj = new Map({ target: el, view: view }); var leshan_tile = this.initLeshanTile(proj, padLeft); this.mapObj.addLayer(leshan_tile); // 點亮地圖 this.lingUpTheMap(leshan_tile); this.drawPolygonAndAddMask(leshan_tile,polygonCoordiantes) }, /** * * 樂山瓦片加載 */ initLeshanTile(proj, padLeft) { // 樂山 瓦片圖層 let layers_leshan = new TileLayer({ source: new XYZ({ crossOrigin: "anonymous", projection: proj, url: 'http://localhost:808/image_map/_alllayers/', tileUrlFunction: function (tileCoord, pixelRatio, proj) { var x = 'C' + padLeft(tileCoord[1], 8, 16); var y = 'R' + padLeft(tileCoord[2] -1, 8, 16); var z = 'L' + padLeft(tileCoord[0], 2, 10); var Newurl = 'http://localhost:808/image_map/_alllayers/' + z + '/' + y + '/' + x + '.png'; return Newurl; } }), visible: true }); return layers_leshan; }, /** * * 點亮地圖 */ lingUpTheMap(osm,){ // Enhance filter var enhance = new olExtColorize({ operation:'enhance'}); osm.addFilter(enhance); // Custom filter var filter = new olExtColorize(); osm.addFilter(filter); enhance.setActive(false); filter.setActive(true); filter.setFilter({ operation:'color', red:Number('0'), green: Number('192'), blue: Number('50'), value: Number('1'), }); }, /** * 繪制面,同時增加蒙層 * @param osm {Object} tile 對象 * @param coordinatesOfPolygon {Array[[]]} 面的坐標數據 */ drawPolygonAndAddMask(osm,coordinatesOfPolygon){ var f = new Feature(new MultiPolygon(coordinatesOfPolygon)); var crop = new olExtCrop({ feature: f, inner: false }); osm.addFilter(crop); var mask = new olExtMask({ feature: f, inner: false, fill: new Fill({ color: [255, 255, 255, 0.8] }) }); osm.addFilter(mask); mask.set('inner',false); crop.set('inner', false); mask.fillColor_ = 'rgba(0,0,0,0.8)'; // Activate mask.set('active', true); crop.set('active', false); }, } } </script>
六、處理canvas 繪制偏移問題
如上雖然實現了底圖着色但是繪制的元素偏移這么多,這顯然不是我想要的結果
后來測試了不知道多少遍,查了不知道多少資料沒有一個是我要的答案!
最后沒辦法再次查看ol-ext.filter.Mask.js源碼
一遍又一遍看其中重要 的drawFeaturePath_ 屬性方法
/** Draw the feature into canvas */ ol_filter_Mask.prototype.drawFeaturePath_ = function(e, out) { var ctx = e.context; var canvas = ctx.canvas; var ratio = e.frameState.pixelRatio; // Transform var m = e.frameState.coordinateToPixelTransform; var tr = function(pt) { return [ (pt[0]*m[0]+pt[1]*m[1]+m[4])*ratio, (pt[0]*m[2]+pt[1]*m[3]+m[5])*ratio ]; } // Old ol version if (!m) { m = e.frameState.coordinateToPixelMatrix; tr = function(pt) { return [ (pt[0]*m[0]+pt[1]*m[1]+m[12])*ratio, (pt[0]*m[4]+pt[1]*m[5]+m[13])*ratio ]; } } // Geometry var ll = this.feature_.getGeometry().getCoordinates(); if (this.feature_.getGeometry().getType()=="Polygon") ll = [ll]; ctx.beginPath(); if (out) { ctx.moveTo (0,0); ctx.lineTo (canvas.width, 0); ctx.lineTo (canvas.width, canvas.height); ctx.lineTo (0, canvas.height); ctx.lineTo (0, 0); } for (var l=0; l<ll.length; l++) { var c = ll[l]; for (var i=0; i<c.length; i++) { var pt = tr(c[i][0]); ctx.moveTo (pt[0], pt[1]); for (var j=1; j<c[i].length; j++) { pt = tr(c[i][j]); ctx.lineTo (pt[0], pt[1]); } } } }
如下這句代碼引起了我的注意
var ratio = e.frameState.pixelRatio;
查看API

大概意思是 幀的像素比率
這個方法明細是使用canvas 根據當前feature的坐標結合當前像素 幀 來繪制元素的, 然 幀的像素比率 會根據地圖縮放而發生改變,所以繪制的面元素也隨着像素變法不停發生偏移,最后想得到不根據幀的像素比率 來繪制元素他的位置不就對了嗎!最后去掉 ratio;修噶代碼為
// Transform var m = e.frameState.coordinateToPixelTransform; var tr = function(pt) { return [ (pt[0]*m[0]+pt[1]*m[1]+m[4]), (pt[0]*m[2]+pt[1]*m[3]+m[5]) ]; }
再運行看效果

這次效果終於要好點了,但是還有待改進,畢竟離ui設計圖還有一些距離,加油繼續…
最后附上官網地址便於查閱

canvas繪制和layerVector繪制的面偏移較多
但地圖縮放時更明顯
但地圖縮放時更明顯