前段時間,因為作業需要,借助百度地圖api,寫了一個小小的web工具,用于按關鍵詞標注一些地點并展示出來,
解決了前期的關鍵點,作業完成了七七八八之后,我發現,yii2自帶的bootstrap3和百度地圖有點點沖突,
具體表現是,yii2自帶的layout檔案的第一行是 <!doctype html>,百度地圖給的demo檔案第一行是<html>
如果用yii2的模板,顯示百度地圖時會顯示不正常,具體是不能把它和其它的bootstrap組件放在一起,特別是不能把地圖容器放在其它div下面,不然不顯示地圖,
如果用百度地圖的<html>做第一行,bs3的導航選單顯示就會有多余的空白出現,很難看,
(在寫這篇文章時,我發現百度地圖的iframe的第一行如果用yii2的,也能正常顯示,以后再測驗吧)
疫情隔離在家,正好有時間,我想好好解決一下這個問題,
我想到的方案是,在yii2的常規頁面里,嵌入一個iframe,這個iframe里使用百度地圖的顯示代碼,這樣二者就不發生沖突了,
但又出現下面一些問題,
- 主頁面如何訪問iframe里的元素(主要是map變數)
- iframe里如何訪問主頁面的元素
- 如何判斷iframe已加載完成
- 如何在iframe加載完成后,執行呼叫頁面的自定義代碼
- 百度地圖示注時,不能顯示自定義icon
一、主頁面如何訪問iframe里的map變數
上網查了解決方案:在顯示iframe的主頁面里設定全域變數,然后iframe里生成了map物件后,將之保存到這個全域變數里,
(主頁面代碼)
<script> // 定義全域變數給iframe生成了地圖之后傳回來 map=null; local=null; </script>
(iframe頁面代碼)
<script type="text/javascript">
// 百度地圖API功能
var map = new BMapGL.Map("container");
map.centerAndZoom(new BMapGL.Point(117.16526, 35.11891), 13);
map.enableScrollWheelZoom(true);
var scaleCtrl = new BMapGL.ScaleControl(); // 添加比例尺控制元件
map.addControl(scaleCtrl);
var local = new BMapGL.LocalSearch(map, {
renderOptions:{map: map}
});
map.addEventListener('click', function (e) {
// jquery如何獲取iframe https://www.muzhuangnet.com/show/53186/2.html
$(window.parent.document).find('#coor').val(e.latlng.lng + ',' + e.latlng.lat);
// alert('點擊位置經緯度:' + e.latlng.lng + ',' + e.latlng.lat);
});
//把自己的變數傳回父視窗的全域變數
parent.map=map;
parent.local=local;
</script>
二、iframe里如何訪問主頁面的元素
有時候,我在iframe里的地圖上點擊后,需要更新主頁面上的元素(填充點擊位置的坐標值),這里還用到了jquery,具體方法是:
map.addEventListener('click', function (e) {
// jquery如何獲取iframe https://www.muzhuangnet.com/show/53186/2.html
$(window.parent.document).find('#coor').val(e.latlng.lng + ',' + e.latlng.lat);
// alert('點擊位置經緯度:' + e.latlng.lng + ',' + e.latlng.lat);
});
三、如何判斷iframe已加載完成
主頁面上要執行的一些操作,需要在iframe已加載完成,map物件創建成功之后才可以繼續執行,不然會出錯,
上網查了一下,這里 給出了解決方案,用在我的代碼里是這樣:
<script>
// iframe子頁面與父頁面元素的訪問以及js變數的訪問 https://www.cnblogs.com/Capricorn-HCL/articles/4216302.html
$(document).ready(function(){
if (can_load_iframe()) load_iframe();
});
function can_load_iframe()
{
var url = window.location.search;
action4=url.substr(3,4);
return action4=='site';
}
function load_iframe()
{
var iframe = document.createElement("iframe");
iframe.src = "<?=Url::to(['site/map-frame', 'type'=>0])?>";
if (iframe.attachEvent){
iframe.attachEvent("onload", function(){
console.log("Local iframe is now loaded1.");
});
} else {
iframe.onload = function(){
frame_onload()
};
}
document.getElementById('container').appendChild(iframe);
h=window.innerHeight - document.getElementById('w0').height - document.getElementById('footer').height;
h=h-$("[id^='w2-']").height();
iframe.width='100%';
iframe.height=h;
iframe.id='iframe_map';
}
function frame_onload() {,,,}
</script>
四、如何在iframe加載完成后,執行呼叫頁面的自定義代碼
前面實作的是通用的layout,顯示地圖的地方都會呼叫它,但是每一個頁面的腳本動作各有不同,比如有的頁面需要在一開始時標注出若干個地圖上的點,
如果在呼叫者頁面上直接使用onload,還是會面臨前面的iframe是否已經加載完成的問題,所以我給各頁面里增加了一個函式 my_init(),然后在主layout模板中,在iframe的onload事件中,加一個判斷,如果函式my_init()存在,則呼叫之,
這樣就解決了不同頁面的定制化需求的問題,
五、百度地圖示注時,不能顯示自定義icon
這個問題在原來的版本中并不存在,而是在引入了iframe之后,我發現,標注的點,都不再顯示自定義的icon圖示,
說明一下:標注代碼是寫在iframe的父頁面的,
1 <script> 2 function add_point(map, x, y, title1, info, type){ 3 marker=add_marker(map, x, y, type); 4 // console.log("add_point(map, "+x+","+y+",'"+title1+"','"+info+"',"+type+");"); 5 6 // 創建資訊視窗 7 var opts = { 8 width: 200, 9 height: 100, 10 title: title1 11 }; 12 var infoWindow = new BMapGL.InfoWindow(info, opts); 13 // 點標記添加點擊事件 14 marker.addEventListener('click', function () { 15 map.openInfoWindow(infoWindow, new BMapGL.Point(x,y)); // 開啟資訊視窗 16 }); 17 18 add_label(map, x, y, title1); 19 } 20 21 function add_marker(map, x, y, type) 22 { 23 marker_url="images/mark"+type+".png"; 24 // console.log(marker_url + ' x=' + x + ' y=' + y + ', type=' + type); 25 var myIcon = new BMapGL.Icon(marker_url, new BMapGL.Size(32, 32), { 26 offset: new BMapGL.Size(10, 25), // 指定定位位置(不然圖示會偏下一些) 27 }); 28 console.log(myIcon); 29 if (type) 30 var marker1 = new BMapGL.Marker(new BMapGL.Point(x,y), {icon:myIcon}); 31 else 32 var marker1 = new BMapGL.Marker(new BMapGL.Point(x,y)); 33 // var marker1 = new BMapGL.Marker(new BMapGL.Point(x,y)); 34 // 在地圖上添加點標記 35 map.addOverlay(marker1); 36 return marker1; 37 } 38 39 function add_label(map, x, y, msg) 40 { 41 var opts = { 42 position: new BMapGL.Point(x,y), // 指定文本標注所在的地理位置 43 offset: new BMapGL.Size(20, -30) // 設定文本偏移量 44 }; 45 // 創建文本標注物件 46 var label = new BMapGL.Label(msg, opts); 47 // 自定義文本標注樣式 48 label.setStyle({ 49 color: 'blue', 50 borderRadius: '5px', 51 borderColor: '#ccc', 52 padding: '10px', 53 fontSize: '16px', 54 height: '30px', 55 lineHeight: '10px', 56 fontFamily: '微軟雅黑' 57 }); 58 map.addOverlay(label); 59 } 60 </script>
為什么原來的寫法就可以,現在就不行呢?用笨辦法逐一排除,最后發現,當在add_point()等方法放在iframe中并呼叫時,icon顯示正常,
初步懷疑是iframe還是觸碰了某種安全機制,網上找到一篇“百度地圖api自定義marker圖片不顯示問題”,提到了是跨域的原因,但仍然沒有試出解決辦法,
怎么辦呢?實在不甘心把自定義的標注代碼寫在iframe里面,,
想了一個辦法:函式放在iframe里面,然后呼叫時,從父頁面呼叫,像這樣:
foreach ($units as $unit) { echo "document.getElementById('iframe_map').contentWindow.add_point(map, $unit->coor , '$unit->name1', '$unit->id', $unit->tagType);\n";// echo "add_point(map, $unit->coor , '$unit->name1', '$unit->id', $unit->tagType);\n"; --原來的呼叫方式
}
總結
以上的方案,由頁面的主模板生成并插入iframe,在iframe中把生成的map等物件保存到主模板的全域變數中,在主模板中監聽iframe的onload事件,然后執行呼叫者頁面的my_init()函式,該方案可解決現有問題,但中間各種同步、異步,耦合度較高,互相呼叫的時序問題、變數問題等復雜度直線上升,不是一個理想的解決方案,此處先記錄下來,不管怎么說,也是一些經驗了,等待更好的方案的出現,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/524969.html
標籤:其他
上一篇:網路工程師基礎知識(一)
