利用HTML5 Canvas動態產生文字圖示
23 | 32,060 |
前幾篇Google Map API文章,一直有用到Google地圖加上Marker的做法。(即下圖的紅色大頭針圖案)
不過,若全部的標示點用一樣的圖示,會顯得無趣且容易混淆(如下圖所示),雖然將滑鼠移到標示圖案上方會顯示名稱,在使用者體驗上總覺得還有改善空間。
事實上,Google Maps API在新增地標時,是可以自訂圖示的。MarkerOptions提供icon參數可指定圖檔URL,另外有shadow參點可指定陰影圖檔的URL,以取代預設的黑點紅色大頭針圖示。
只是有個小問題,在消防分隊位置顯示範例中,台北市共有44個消防分隊,即使能自訂圖示,可能也只是用44台消防車換掉44根大頭針,對於識別度並沒有太多提升,除非我們能預先製作44個刻有消防分隊名稱的圖檔,再一一對應到44個Marker使用...
身為程式魔人,44個圖檔用手工做是不被允許的,當然要自動產生才不會被人恥笑。我第一個想到是用ASP.NET動態產生圖檔的技巧,不過再轉念一想,何不用HTML5的Canvas來實做,完全在Client解決? (IE6/7/8: 那我們怎麼辦? 念你們曾縱橫江湖多年,也算時代英雄,你們自盡吧!)
HTML5 Canvas要即時產生圖檔不是問題,再配合Canvas.toDataURL()就可取代圖檔案URL,作為MarkerOptions.icon參數的設定值,就能達到當場動態產生Marker圖示的目的。
我試寫了一個小函數,用Canvas作圖,再用toDataURL()輸出作為<img>的src來源:
<!DOCTYPE html>
<html>
<head runat="server">
<title>動態Canvas圖標</title>
<script type='text/javascript'
src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js'></script>
<style>
body,input { font-size: 9pt; }
img { margin: 5px }
</style>
<script>
$(function () {
//利用canvas產生一個內含文字的圖檔
function createMarkerIcon(text, opt) {
//定義預設參數
var defaultOptions = {
fontStyle: "normal", //normal, bold, italic
fontName: "Arial",
fontSize: 12, //以Pixel為單位
bgColor: "darkblue",
fgColor: "white",
padding: 4,
arrowHeight: 6 //下方尖角高度
};
options = $.extend(defaultOptions, opt);
//建立Canvas,開始幹活兒
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d");
//評估文字尺寸
var font = options.fontStyle + " " + options.fontSize + "px " +
options.fontName;
context.font = font;
var metrics = context.measureText(text);
//文字大小加上padding作為外部尺寸
var w = metrics.width + options.padding * 2;
//高度以Font的大小為準
var h = options.fontSize + options.padding * 2 +
options.arrowHeight;
canvas.width = w;
canvas.height = h;
//邊框及背景
context.beginPath();
context.rect(0, 0, w, h - options.arrowHeight);
context.fillStyle = options.bgColor;
context.fill();
//畫出下方尖角
context.beginPath();
var x = w / 2, y = h, arwSz = options.arrowHeight;
context.moveTo(x, y);
context.lineTo(x - arwSz, y - arwSz);
context.lineTo(x + arwSz, y - arwSz);
context.lineTo(x, y);
context.fill();
//印出文字
context.textAlign = "center";
context.fillStyle = options.fgColor;
context.font = font;
context.fillText(text,
w / 2,
(h - options.arrowHeight) / 2 + options.padding);
//傳回DataURI字串
return canvas.toDataURL();
}
$("<img />", { src: createMarkerIcon("Jeffrey") })
.appendTo("body");
$("<img />", { src: createMarkerIcon("黑暗執行緒", {
//注意: fontName所指定字型,需使用者機器上有安裝才算數
fontName: "微軟正黑體", fontSize: 14, fontStyle: "bold",
padding: 5, bgColor: "#6199DF", fgColor: "white"
}) }).appendTo("body");
});
</script>
</head>
<body>
</body>
</html>
薑薑薑薑~~ 這樣子只要輸入文字,就可以當場產生圖示囉!
簡單修改程式,加入icon參數指向createMarkerIcon():
var marker = new google.maps.Marker({
position: latlng, //經緯度
title: "八角亭", //顯示文字
icon: createMarkerIcon("八角亭", {
bgColor: "brown"
}),
map: map //指定要放置的地圖對象
});
原本黑頭大頭針就換成帶文字的告示牌,是不是清楚多了呢?
到了該為HTML5歡呼的時刻,讚啦!!
Comments
# by Vexed
http://blog.xuite.net/vexed/tech/59848483 滿類似的 :p
# by Jeffrey
to Vexed, 哈! 載入底圖這招省力又精美,學習了。謝謝分享~
# by 小黑
原來IE8 這麼不支援 html5 , 那使用 asp.net mvc 4 開發的網站,IE8 也不一定能正常顯示就對了!
# by Jeffrey
to 小黑,針對不支援HTML5的瀏覽器,有些技巧可以讓HTML5網頁盡可能正常顯示與運作。ASP.NET MVC 4雖然大量引用HTML5,但針對老瀏覽器仍透過Modernizr等外掛維持相容( http://blog.ericsk.org/archives/1444),預設的範本可以跨瀏覽器正常執行無誤。除非你加入了IE6/7/8不支援的程式碼,又不肯加入相容性處理(像本文用了Canvas就不顧老瀏死活是壞榜樣,請大家不要跟我做好朋友),才會出現問題。 所以預設ASP.NET MVC4可以跨瀏覽器,但一路開發下來要維持原則不崩壞,對開發者是項挑戰就是了 XD
# by 小黑
謝謝指導,看來真的是場硬仗了!
# by andrew
照你的方式做,可是出現的icon卻很小,連字都看不太清楚,請問這是什麼原因?
# by Jeffrey
to andrew, 由你的描述無法判斷問題,恐需要再提供程式碼範例等進一步資訊較能看出端倪。
# by 小張
黑暗執行緒您好,感謝你設計出這麼實用的canvas物件, 想請問是否可修改您的程式碼在我的作業上面,剛好在 研究一些關於Google map的東西,原本我是用markwithlabel 在icon上呈現姓名標幟,現在有了html5 canvas後其實只要 有姓名就可以了,相對更為實用。
# by 小張
黑暗執行緒您好,感謝你設計出這麼實用的canvas物件, 想請問是否可修改您的程式碼在我的作業上面,剛好在 研究一些關於Google map的東西,原本我是用markwithlabel 在icon上呈現姓名標幟,現在有了html5 canvas後其實只要 有姓名就可以了,相對更為實用。
# by Jeffrey
to 小張,歡迎引用,加註程式碼參考來源即可。
# by 小張
黑暗大您好,如果說在文字的部分希望能分成上下兩行,如上行是姓名,下行是身份證字號,該如何去設計,加了<br>標籤或把一開始的text改成html都沒有辦法,是否有解決的方案呢?
# by Jeffrey
to 小張,要印成兩行,可分兩次呼叫context.fillText(),透過Y座標參數控制上下位置即可。
# by 小張
黑暗大謝謝,已調整成功。
# by 小猴
你好請問一下:最後那個加入ICON 要如何使用 要加在哪 為什麼我加在後面都沒東西
# by Jeffrey
to 小猴,你是指icon: createMarkerIcon("八角亭", {bgColor: "brown"})嗎?icon原本可傳入png或gif URL指向圖示圖檔,我的程式改用HTML5當場繪製。你可先試試寫死URL看是否能正常顯示,若寫死URL可以換成createMarkerIcon()不行,有可能是createMarkerIcon裡的邏輯出了問題。
# by 小猴
不是 我意思是最後那個地圖加上八角亭的圖 怎麼做出來的? 因為我把上面的程式碼加在一起後,沒辦法呈現出來 可以讓我看一下那張圖得程式碼嗎(最後八角亭那張) 不好意思 我是新手 正在學寫地圖很爛 謝謝你黑暗大大
# by Jeffrey
to 小猴,我做了一個Live Demo: https://jsbin.com/fevuxi/1/edit?html,output 請參考
# by 小猴
謝謝你 解決了我的問題 感謝你耐心的指導~~
# by JING
黑暗大大你好 請問一下 那個最後八角停 因為顯示出來 會擋到地圖 想請問一下要如何使用透明元素 想呈現畫出來的背景是"半透明 文字不透明" 我嘗試過opacity=0.5但效果都沒出來 想請問一下大大能幫忙解決嗎?
# by Jeffrey
to JING, 試試marker.setOpacity(0.5);,參考:https://developers.google.com/maps/documentation/javascript/3.exp/reference#Marker
# by JING
黑暗大大 請問一下是要加在哪裡? 因為我剛看了參考網頁 再去網路找資訊還是無法了解要加在哪 var marker = new google.maps.Marker({ position: latlng, //經緯度 title: "八角亭", //顯示文字 icon: createMarkerIcon("八角亭", { bgColor: "brown" }), marker.setOpacity(0.5), map: map //指定要放置的地圖對象 }); 是加在這裏面嗎 嘗試很久還是跑不出來 希望大大能幫忙
# by Jeffrey
to JING, 要在marker物件建立後呼叫,也就是 var marker = new google.maps.Marker({...}); marker.setOpacity(0.5); 範例:https://jsbin.com/dafiji/edit?html,output
# by JING
謝謝大大指導~~~