javascript - Ionic Angular Leaflet - 许多 svg 标记的高性能渲染
问题描述
我想在我的地图上渲染许多自定义(svg)标记。在我最初的研究之后,我发现了几种方法,但这些方法似乎都不适用于我的情况。我正在使用ionic/angular 5.0.0
和传单1.7.1
。
这就是我所拥有的:
displayMarkers(foos: Foo[]) {
// Transform foos into clickable markers
this.markers = foos.map((foo) => {
const i = icon({
iconUrl: '/assets/img/icon.svg', // I'd like to use this svg for my markers
iconSize: [20, 20], // size of the icon
});
const marker = circleMarker([foo.lat, foo.long]); // ADD ICON HERE
return marker.on('click', () => this.onSelectMarker(foo, marker));
});
// Filter markers only in bounds of the map
this.markers = this.markers.filter(m => this.map.getBounds().contains(m.getLatLng()));
// Draw the markers onto the map
this.markers.forEach(marker=> marker.addTo(this.map));
}
我想leaflet circleMarker
用我的替换或自定义,svg
或者找到一种高性能的方式在我的地图(数千个)中呈现大量 svg 元素。
我知道,我可以markers
用来 display svg icons
,但是一旦你达到几百个标记,应用程序的性能就会受到极大的影响。
我希望可以像这样初始化地图:
new Map('mapId', {preferCanvas: true})
或者能够使用自定义渲染器,如下所示:
const marker = circleMarker([foo.lat, foo.long], {renderer: canvas()});
这样,标记将被绘制到画布上,而不是被视为单个 DOM 元素。
我试图实现这个解决方案,但我无法将它正确地集成到我的 angular-typescript 应用程序中。
我还查看了这个问题并安装并测试了所有建议的库。然而,这个问题太开放了,图书馆对我来说并不满意,似乎只是为了一个简约的目的。也许我只是愚蠢地正确整合它们(不过我不想失去角度和打字稿的好处)......
我觉得这里必须有一个简单的解决方案,但我似乎找不到它。我在这里错过了什么吗?
任何帮助是极大的赞赏。谢谢!
解决方案
好的,经过数小时的反复试验,我最终弄明白了。我使用并更改了几个答案和示例中的代码以适合我的特定用例。因此,如果有人对我的所作所为感到好奇,那就去吧……
为方便起见,我将所有代码放入一个文件中。
map.page.ts
:
@Component({
selector: 'app-map',
templateUrl: './map.page.html',
styleUrls: ['./map.page.scss'],
})
export class MapPage implements OnInit {
map: Map; // Leaflet map
userLocation: Marker; // Leaflet marker
foos$: Observable<Foo[]>; // Your data
// Some other variables ...
constructor(
private geocoder: NativeGeocoder,
private fooStore: Store<fromFoo.FooState>,
//...
) {}
ionViewDidEnter() {
this.map = this.getInitialMap(); // Init map
this.fooStore.dispatch(...); // Load foos to display
this.foos$ = this.fooStore.pipe(select(fromFoo.getFoos));
this.foos$.subscribe(foos => {
if (foos && foos.length > 0) {
this.displayFoos(foos);
}
});
// Some more stuff here...
}
getInitialMap() {
const layer = tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
maxZoom: 19
});
return new Map('mapId', {
zoomControl: !Browser.mobile,
layers: [layer],
center: [???, ???], // Define arbitrary location
zoom: 19,
});
}
displayFoos(foos: Foo[]) {
const renderer = new Canvas(); // Important! use a canvas to render your data
// Map the foos (your data) to leaflet markers
const fooMarkers = foos.map((foo) =>
new CustomMarker([foo.lat, foo.long], {renderer})
); // Note the CustomMarker here (See below for implementation)
// Draw the markers onto the map
fooMarkers.forEach(fooMarker => fooMarker.addTo(this.map));
}
// More functions here...
}
// This is important!
// Create a class for your custom markers that extend the CircleMarker from Leaflet
class CustomMarker extends CircleMarker {
_updatePath() { // Make sure to name it "_updatePath()"
// @ts-ignore
this._renderer._displayCustomSVG(this); // Call the _renderer, which
// to my understanding is a property on a circle marker that
// refers to a canvas. You can extend the Canvas by your
// own custom render function (see below)
}
}
const imageBitmap = new Image(); // Create a bitmap. Found on another answer
// I defined the new image outside the _displayCustomSVG to increase performance.
// That way the image instance is only created once, rather than foo.length times.
// Include ("extend") the leaflet canvas by your custom render function
Canvas.include({
_displayCustomSVG(layer) {
if (!this._drawing || layer._empty()) {
return;
}
const p = layer._point;
// Insert your own svg as string. (I stripped the svg here)
const svgStr = `<svg width="10px" height="10px" viewBox="0.0 0.0 100.0 113.75853018372703" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"></svg>`;
// (I used an online editor to upload my svg and turn it
// into an svg string.)
// You might want to change the width or height of your svg
imageBitmap.src = 'data:image/svg+xml;base64,' + window.btoa(svgStr);
const ctx = this._ctx;
imageBitmap.onload = ctx.drawImage(imageBitmap, p.x, p.y);
},
});
这对我有用,但是我不知道是否有更高性能或更好的方法来做到这一点。无论如何,我希望它有所帮助。
编辑
我意识到,如果你把它放在const imageBitmap = new Image();
外面,_displayCustomSVG()
你可能会遇到与drawImage
传单中的功能不一致的地方。
推荐阅读
- python - Pandas 从包含字典列表的行创建新数据框的方法
- python - 到达边界时如何重置位置
- microsoft-planner - 使用 Power Automate 的 Microsoft Planner 的重复任务
- typescript - 键入泛型函数时,可以省略返回类型吗?
- azure-devops - 如何在 Azure Pipelines 上为 Azure Artifact 源运行“dotnet list package --deprecated”
- excel - 如何在 sub 中包含用户定义的函数 MakeFolderIfNotExist?
- kubernetes - 节点有卷节点亲和性冲突
- python - 从 Pycharms 重复检查中排除文件夹
- kubernetes - Azure Kubernetes - Jaeger UI 未显示作为 ISTIO 的一部分部署的服务?
- javascript - 如何将 javascript 日期初始化为特定时区?