You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Livewire中Leaflet地图隐藏后无法重新渲染的问题求助

解决Laravel Livewire中Leaflet地图隐藏后显示空白的问题

问题根源分析

你的代码存在几个关键问题导致地图显示异常:

  • Livewire事件监听不匹配:组件中emit的是toggleMapVisibility,但JS里监听的是mapVisibilityChanged,事件名不一致导致显示时无法触发尺寸刷新。
  • 隐藏目标错误:用父级div的display:none控制隐藏,但MutationObserver监听的是地图容器本身的style变化,无法感知父级的显示状态变更。
  • 重复初始化逻辑:JS中先后调用init()和单独的initializeMap()、updateMap(),可能导致地图实例混乱。
  • 尺寸刷新时机不对:即使调用invalidateSize(),如果在地图容器还未完全渲染完成时执行,也无法生效。

修复步骤

1. 修正Livewire组件的事件触发

在setMapVisibility方法中,emit事件时传递当前可见性状态:

public function setMapVisibility($visibility = null){
    $this->mapVisibility = $visibility ?? !$this->mapVisibility;
    // 传递当前可见性状态给前端
    $this->emit('toggleMapVisibility', $this->mapVisibility);
}

2. 修改Blade模板的隐藏控制逻辑

改用wire:class控制显示隐藏(如果不用Tailwind,可直接写style属性):

<div>
    <button wire:click="setMapVisibility">
        {{ $mapVisibility ? 'Hide Map' : 'Show Map' }}
    </button>
    <!-- 用wire:class控制父容器的显示状态 -->
    <div wire:class="{{ $mapVisibility ? '' : 'hidden' }}">
        <div id="map-kerjasama" style="width: 100%; height: 500px; z-index: 0;">
        </div>
    </div>
</div>

注:hidden类对应display:none,如果未使用Tailwind,可替换为style="display:{{ $mapVisibility ? 'block' : 'none' }}"

3. 重构前端JS逻辑

修正事件监听、移除重复初始化、调整尺寸刷新时机:

<script>
document.addEventListener('DOMContentLoaded', function() {
    let map = null;
    let markers = [];

    function initializeMap() {
        if (map !== null) return;

        map = L.map('map-kerjasama', {
            scrollWheelZoom: false,
            fullscreenControl: true,
        }).setView([0.78, 113.92], 5);

        setTileLayer();
        setMapInteractionEvents();
    }

    function setTileLayer() {
        L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
            maxZoom: 19,
            attribution: '&copy; OpenStreetMap contributors'
        }).addTo(map);
    }

    function setMapInteractionEvents() {
        const mapElement = document.getElementById('map-kerjasama');
        mapElement.addEventListener('mouseenter', () => map?.scrollWheelZoom.enable());
        mapElement.addEventListener('mouseleave', () => map?.scrollWheelZoom.disable());
    }

    function clearMarkers() {
        markers.forEach(marker => map.removeLayer(marker));
        markers = [];
    }

    function updateMap(data) {
        clearMarkers();
        data.forEach(instansi => {
            const lat = parseFloat(instansi.latitude);
            const lng = parseFloat(instansi.longitude);
            if (!isNaN(lat) && !isNaN(lng)) {
                const marker = L.marker([lat, lng])
                    .addTo(map)
                    .bindPopup(`<b>${instansi.name}</b><br>Lat: ${lat}<br>Lng: ${lng}`);
                markers.push(marker);
            }
        });
        adjustMapBounds();
    }

    function adjustMapBounds() {
        if (markers.length) {
            const group = L.featureGroup(markers);
            map.fitBounds(group.getBounds());
        } else {
            alert("Masih belum ada kerjasama dengan negara ini");
        }
    }

    function refreshMapSize() {
        if (!map) return;
        // 延迟执行确保容器已完全显示
        setTimeout(() => {
            map.invalidateSize({reset: true});
            // 如果有标记,重新调整视野
            if (markers.length) adjustMapBounds();
        }, 200);
    }

    // 初始化逻辑
    function init() {
        initializeMap();
        updateMap(@json($dataKerjaSamaNegara));
        
        // 监听Livewire的数据更新事件
        Livewire.on('dataKerjaSamaNegaraUpdate', updateMap);
        
        // 监听地图可见性切换事件
        Livewire.on('toggleMapVisibility', (isVisible) => {
            if (isVisible) {
                refreshMapSize();
            }
        });
    }

    init();
});
</script>

4. 关键优化点说明

  • 事件名统一:组件emit的toggleMapVisibility和JS监听的事件名保持一致,确保状态变更时能触发尺寸刷新。
  • 延迟执行invalidateSize:用setTimeout延迟200ms,确保地图容器已完全渲染到DOM中,此时调用invalidateSize({reset: true})可以强制Leaflet重新计算容器尺寸。
  • 移除重复初始化:只保留init()中的初始化逻辑,避免重复创建地图实例。
  • 简化交互逻辑:合并鼠标进出的交互控制,代码更简洁。

额外验证方案

如果上述方案仍有问题,可以尝试替代隐藏方式:不用display:none,改用visibility: hidden和height: 0组合,这样地图容器始终存在于DOM中,尺寸计算不会失效:

<div style="visibility: {{ $mapVisibility ? 'visible' : 'hidden' }}; height: {{ $mapVisibility ? '500px' : '0' }}; overflow: hidden;">
    <div id="map-kerjasama" style="width: 100%; height: 500px; z-index: 0;">
    </div>
</div>

这种方式下,显示时只需调用refreshMapSize()即可,无需担心容器尺寸丢失。

内容的提问来源于stack exchange,提问作者Fakhri Rasyad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 04:59:53