Random Thoughts on Random Things by Tanomsak
Random header image... Refresh for more!

การใช้รูปภาพแทนปุ่มซูมเข้าออก (Custom control)

27 Jul 2009

ถ้าใครเคยเข้าไปเวป ddproperty.com จะเห็นว่าปุ่มซูมเข้าซูมออกจะไม่เหมือนแบบปกติของ Google Maps และมีปุ่มย่อขยายแผนที่เพิ่มขึ้นมาด้วย การทำแบบนี้เค้าเรียกว่า Custom Control ครับ

ddproperty-maps-crop.jpg

คราวนี้เราจะพูดถึงเรื่องการสร้าง Control ของ Google Maps ให้เป็นแบบเฉพาะของเวปเรากันครับ

ทำความรู้จักกับ Control ใน Google Maps

ก่อนอื่นมาทำความรู้จักกับ Control ใน Google Maps API กันก่อน ในตัว Google Maps API มี Control มาตราฐานมาให้เราใช้อยู่หลายอย่างครับ

  • GLargeMapControl – เป็น Control ที่มีครบทั้งซูมเข้าออก,เลื่อนแผนที่ซ้ายขวา และ Slider
  • GSmallMapControl – จะมีแค่ซูมเข้าออก,เลื่อนแผนที่ซ้ายขวา
  • GSmallZoomControl – มีแต่ ซูมเข้าออกเล็กๆ
  • GScaleControl – scale บอกขนาดแผนที่
  • GLargeMapControl3D – เหมือน GLargeMapControl แต่ว่าดูสวยกว่า :)
  • GSmallZoomControl3D – เหมือน GSmallZoomControl แต่ว่าดูสวยกว่า
  • GMapTypeControl – เอาไว้เลือกประเภทแผนที่ ว่าจะเป็นแผนที่ธรรมดา หรือภาพถ่ายดาวเทียม
  • GHierarchicalMapTypeControl – Control แบบที่มี Drop down ให้เลือกได้
  • GOverviewMapControl – แผนที่ขนาดเล็กที่ปกติอยู่ทางขวาล่าง
  • GNavLabelControl – Label แสดง “ที่อยู่” ณ ตำแหน่งปัจจุบัน

ปกติเราเรียกใช้งาน Control พวกนี้โดยคำสั่งแบบนี้ครับ

map.addControl(new GLargeMapControl());

มาสร้าง Control ของเรากันครับ

ขอเริ่มจาก source code ละกันนะครับ

// ก่อนอื่นสร้าง function myZoomControl ขึ้นมาก่อน
function myZoomControl() {
}
// ทำ "subclass" GControl โ้้ดยการกำหนดให้ prototype object เป็น Instance ของ GControl
myZoomControl.prototype = new GControl();

myZoomControl.prototype.initialize = function(map) {
  // เริ่มต้นก็สร้าง div ว่างๆมาหนึ่งอัน เอาไ้ว้เป็น container
  var container = document.createElement("div");

  container.style.width = "37px";
  container.style.height = '100px';

  // สร้าง div อีกอันเอาไว้เป็นปุ่ม ZoomIn
  var zoomInDiv = document.createElement("div");
  // กำหนด style ให้กับปุ่ม -> ไป call function ด้านล่าง
  this.setButtonStyle_(zoomInDiv,"zoonin.png");
  // เพิ่ม div ของปุ่ม ZoomIn ไว้ใส่ container
  container.appendChild(zoomInDiv);

  // กำหนดหน้าที่ให้กับปุ่ม
  GEvent.addDomListener(zoomInDiv, "click", function() {
    map.zoomIn();
  });

  // สร้าง div อีกอันเอาไว้เป็นปุ่ม ZoomOut
  var zoomOutDiv = document.createElement("div");
  // กำหนด style ให้กับปุ่ม -> ไป call function ด้านล่าง
  this.setButtonStyle_(zoomOutDiv,"zoomout.png");
  // เพิ่ม div ของปุ่ม ZoomIn ไว้ใส่ container
  container.appendChild(zoomOutDiv);

  // กำหนดหน้าที่ให้กับปุ่ม
  GEvent.addDomListener(zoomOutDiv, "click", function() {
    map.zoomOut();
  });

  // นำ div container ใส่เข้าไปในแผนที่
  map.getContainer().appendChild(container);
  return container;
}

myZoomControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7, 7));
}

myZoomControl.prototype.setButtonStyle_ = function(button,button_pic) {

    button.style.backgroundImage = "url(/"+button_pic+")";
    button.style.width = "31px";
    button.style.height = "31px";
    button.style.margin = "0px 0px 3px 3px";
    //styleFloat  for IE
    if (navigator.appName.indexOf("Netscape") != -1) {
       button.style.cssFloat = "left";
    }
    else {
       button.style.styleFloat = "left";
    }
    button.style.cursor = "pointer";
}

function load() {
    if (GBrowserIsCompatible()) {
       var map = new GMap2(document.getElementById("map"));

       // เรียกใช้ Custom Control ที่สร้าง
       map.addControl(new myZoomControl());
       map.setCenter(new GLatLng(37.4419, -122.1419), 13);
    }
}

ดูตัวอย่างกันครับว่าออกเป็นแบบไหน

การสร้าง Custom Control จริงแล้วไม่ยากเลยครับ ส่วนที่สำคัญๆมีดังนี้ครับ

บรรทัดที่ 1-5 กำหนด prototype ของ Custom Control ที่จะสร้าง ให้เป็น instance ของ GControl ขั้นตอนนี้้เป็นการทำ “subclass” จาก GControl แต่ว่า Javascript ไม่มีการ inherit Class เหมือนพวก OOP แต่มีการทำ prototype แทนครับ ไม่ต้องงงครับ  copy ไปเลย :)

มี function ที่สำคัญ 2 อันคือ initialize บรรทัดที่ 7-41 กับ getDefaultPosition บรรทัด 43-45 ห้ามตกหล่นไม่งั้นไม่ work

โดย initialize ทำหน้าที่กำหนดหน้าตาของ Custom Control และ กำหนด Action เวลากดปุ่มต่าง มาดูกันทีละบรรทัดครับ

บรรทัด 9-11 เริ่มต้นด้วยการสร้าง div เปล่าๆขึ้นมาและกำหนดขนาดให้มัน div อันนี้จะเรียกว่า container ครับ โดยเราจะสร้างปุ่มไว้ข้างใน แล้วก็ append ทั้ง container เข้าไปใน Google maps ของเราตอนท้าย คอยดูนะครับ

บรรทัด 15-17 เริ่มสร้างปุ่ม ZoomIn โดยการสร้าง div อีกอันนึง แล้วก็ กำหนด style ให้มันโดยเรียก function setButtonStyle_ ถ้าตามไปดูข้างในก็จะเห็นว่าเป็นการกำหนด background ให้เป็นรูปปุ่ม และก็กำหนดขนาดนั้นเอง

บรรทัด 19 ใส่ div ZoomIn เข้าไปใน Container

บรรทัด 22-24 เป็นการตั้ง Listener ให้ซูมเข้าเวลามรการ “click” ที่ div ZoomIn ของเรา

จากนั้น บรรทัด 26-36 ก็ทำซ้ำสำหรับปุ่ม ZoomOut

บรรทัด 39 ก็ append container ที่เราใส่ปุ่ม ZoomIn/ZoomOut แล้วเข้าไปในแผนที่

ส่วน getDefaultPosition ทำหน้าที่กำหนดตำแหน่งว่า Control ของเราจะอยู่ตรงไหนของแผนที่ครับ

สุดท้ายก็เรียกใช้ myZoomControl ก็เป็นอันเรียบร้อย

Update:

คุณ @iampz แนะนำให้ใช้ id/class กำหนด style แทน ผมทดลองแล้วตามแล้วลองดูตัวอย่างนะครับ

  • uoi
    คือว่าถ้าต้องการให้มานซูมที่ marker ละค่ะต้องเขียนโค๊ดยังไง แบบพอกดที่ marker ก็ซูมเข้าไปได้อ่ะค่ะ
  • tanomsak
    เข้าใจว่าต้องการให้กด marker แล้ว zoom ไปใกล้ๆใช่ไหมครับ แบบว่าไมได้ zoom ทีละ step

    การ zoom ไปยัง zoom level หนึ่งๆทำได้โดยใช้ map.setZoom ครับ

    วิธีทำก็ต้อง สร้าง listener สำหรับรอ event click บน marker จากนั้นก็สั้งให้ setZoom เมื่อมีการกดครับ แบบนี้ครับ

    GEvent.addListener(marker, "click", function() {
    map.setZoom(16);
    });
  • uoi
    ขอบคุณมากนะค่ะสำหรับคำตอบ เอาไปลองแล้ววค่ะ ช่วยได้เยอะเลยค่ะ อันนี้เป็นการกดหมุดแล้วซูมกว้างๆๆช่ายป่ะค่ะ แล้วถ้าอยากให้กดหมุดแล้วซูมอยู่ที่หมุดๆๆนั้นอ่ะค่ะ แบบว่าพอกดซูมแล้วก็ยังเจอหมุดอ่ะค่ะ
  • tanomsak
    งงครับ :)

    เดาว่าหมายถึงให้กดหมุดแล้วซูมเข้าไปเรื่อยๆ ถ้าใช่ก็เปลี่ยนจาก map.setZoom(16) เป็น map.zoomIn() ครับ
  • uoi
    คือหมายถึงว่าพอกดหมุดอ่ะค่ะ แล้วแผนที่มันก็ขยายช่ายป่ะ หมุดที่กดก็ไม่รู้ไปอยู่ไหน ต้องมานั่งหาหมุดอีก อยากให้พอกดหมุดซูมแล้วก็ยังเจอหมุดอ่ะค่ะ
  • tanomsak
    ปกติแล้วการ zoom จะรักษาตำแหน่ง center ของ map ไว้ที่เดิม แต่ถ้าหมุดเราไม่ได้อยู่ตรงกลางมันก็จะหมายไปจากแผนที่ครับ

    แก้ไขได้โดย พอกด zoom ก็ตั้งค่า center ให้อยู่ตำแหน่งเดียวกับ marker ก่อน

    map.setCenter( marker.getLatLng() );
    map.zoomIn();

    อันนี้ใช่ได้กับ marker เดียวครับ เพราะ ถ้ามีหลายๆ marker จะให้ focus ที่อันไหนดี ก็ต้องมีวิธีเลือกก่อนครับ
  • uoi
    เย้ๆๆๆ ได้แล้วค่ะ ขอบบคุณค่ะที่ให้ความรู้ ว่างๆๆ จะมาถามอีกนะค่ะ
  • tanomsak
    ยินดีครับ
  • mie
    T^T มีคำถามอีกแร้วคร๊ะ

    คือยังเชื่อม database ไม่ได้เรยอ่ะคร๊ะ อ่านแร้ว งงคร๊ะ

    คือตัวเราเองได้ database เป็น SQL 2008 จากเพื่อนคร๊ะ แระเพื่อนให้เชื่อมต่อ กูเกิลกะฐานดาต้าเบส

    ซึ่งดาต้าเบสคราวๆกะจะมี โรงพยาบาล โรงแรม ห้าง ปั้มน้ำมัน และอื่นๆ

    อ่านที่พี่เขียนแร้วนะคร๊ะ แต่ยังไม่เข้าจัยเรย ไม่เข้าจัยว่าจะเชื่อมกันได้ยังไง

    คือไม่แม่น SQL อยู่แร้วด้วย

    แระนอกจากจะเชื่อมฐานดาต้าเบส

    อาจารย์ยังสั่งให้ หาวิธีเดินทางจากจุดสองจุด โดยเร้วที่สุด พร้อมกับแสดงเส้นทาง

    แระถ้าเวลาผ่านไปซัก 10 นาที ระยะจะเปงเท่าไร พร้อมทั้งแสดงเส้นทางอันใหม่อีกหนึ่งเส้นอ่ะคร๊ะ

    อยากทราบว่ากูเกิลมีเมตตอดอารัยไว้หามั้ยคร๊ะ

    T^T

    งานส่งวันจันทร์แร้ว แต่ ยังไม่เถิงไหนเรยคร๊ะ

    รบกวนพี่ๆด้วยนะคร๊ะ

    ขอบคุณคร๊ะ
  • โห...ขอบคุณมากครับ..ผม ขอเอาไปลองใช้ดูหน่อยละกันนะครับ
  • lookwai
    พอดีอยากได้ไอเดียตรงนี้มาดัดแปลงทำโปรเจ็ค ไม่ทราบว่าพอจะรับเป็นติวเตอร์ได้ไหมครับ
  • tanomsak
    ถ้าอะไรก็สงสัยก็ถามเลยครับ ไม่รับประกันว่าจะช่วยได้ :) แต่จะพยายามครับ
  • auie
    ขอโทษครับพี่ ใส่แหลกไปหน่อย ^^

    Backend อาจารย์บอกว่าเป็นส่วนของ Admin ที่ทำหน้าที่ดูและเว็บอะครับ ต้องทำการแก้ไขในส่วนของข้อมูลได้ด้วย เพราะให้เว็บของเรา update อยู่เสมอ แล้วก็ที่บอกว่าเขียนไฟล์ส่ง JavaScript อะครับคือเวลาที่ผมทำอะไรบนหน้าที่เป็น map แล้วอะครับ แล้วถ้าผมกดปุ่ม Save อย่างนี้ครับมันจะทำการ Gen Code ที่ทำได้ทำไว้กลายเป็น .js เพื่อที่คราวหลังมาเปิด Code ตัวนี้ก็จะแสดงผลลัพท์เหมือนที่เราได้ทำไว้ตอน Save อะครับ ถ้าได้ก็จะโล่งไปอีก 1 เปาะ T_T ไม่เคยเครียดขนาดนี้มาก่อน
  • auie
    เออ พี่ครับ สงสัยผมจะได้ทำ Backend สำหรับ Admin ตอนนี้แล้วผมจะเขียนไฟล์จากหน้า GoogleMaps ลง Javascript File ยังไงอะครับ เพราะว่าผมทำได้แค่เก็บไฟล์ลงใน JavaScript เท่านั้น

    Ps.ที่สำคัญเจอประโยคนี้ในเว็บ http://econym.org.uk/gmap/
    Using the Google Map API is not easy if you don't have much Javascript experience.

    แล้วบังเอิญประสบการณ์ไม่มีซะด้วย งานเข้าแล้วครับ เปลี่ยนหัวข้อโปรเจคเดี๋ยวจะไม่จบ 4 ปีเอา
  • tanomsak
    งงครับผม backend ที่ว่าหมายถึงทำอะไรครับ แล้ว "เขียนไฟล์จากหน้า GoogleMaps ลง Javascript File" หมายถึงอะไรครับ ตามไม่ทัน :)
  • Ratchayothin
    ได้ความรู้มากเลยคับ
    กำลังหาอ่านอยู่พอดี
  • auie
    ได้เลยครับ

    เอาพี่ลง Credit Project ไปด้วยเลยละกัน อิอิ
  • auie
    ขอบคุณมากครับ วันนี้ไปพบอาจารย์มา แนวว่าผมต้องศึกษาใหม่ทั้งหมดเลย พี่ศึกษาพวก Method ต่างๆ
    ของ GoogleMaps จากเอกสารของ Google อย่างเดียวเลยรึเปล่าครับ
  • tanomsak
    หลักๆก็ศึกษาจากเอกสาร Google ครับ อีกที่นึงก็ที่ link ข้างล่างครับ

    http://econym.org.uk/gmap/

    เค้าเป็นกูรูด้านนี้่เลยครับ ผมเห็นเค้าบอกคำถามใน Google Maps Group ตลอดเลย

    http://groups.google.com/group/Google-Maps-API

    ลองอ่านศึกษาดูนะครับ เอาใจช่วยครับ ถ้าเสร็จแล้วเอามาโชว์ให้ดูด้วยนะครับ
  • auie
    เป็นกรอบข้อความแบบคล้ายๆกับเวลาที่เราเอาเมาส์ไปวางบน icon บน desktop อะครับแล้วมันจะแสดงเป็นกรอบเล็กๆแสดงรายละเอียดอะครับ
  • tanomsak
    อ่า อันนั้นง่ายเลยครับ เป็น options ชื่อ title ครับ เอาไว้แสดง hint เป็นข้อความ กำหนดแบบนี้ครับ

    var marker = new GMarker(latlng, {title: displayname});
  • auie
    พี่ครับ จะให้ custom marker แสดงข้อความเหมือนในเว็บ DD ของพี่ยังไงหรอครับ แบบรายละเอียดสถานที่แบบนั้นอะครับ
  • tanomsak
    หมายถึง เวลาเอา mouse ไปวางบนหมุดแล้วมี block แสดงรูปภาพกับข้อมูลหรือเปล่าครับ

    ขออธิบายย่อๆก่อนนะีึครับ ถ้ามีเวลาแล้วจะสรุปเป็นบทความใหม่ครับ

    1. กล่องข้อความถูกสร้างขึ้นเป็น DOM ตัวใหม่ และ่ซ่อนไว้่โดยใช้ display:none โดบไม่ได้เกี่ยวอะไรกับ Google Maps เลยครับ
    2. ตอนสร้าง marker ก็เพิ่ม event พวก mouseover, mouseout ไว้ด้วย ถ้ามีการเอา mouse ไปว่างบน marker ก็แสดงกล่้องข้อความ ถ้าเอา Mouse ออกก็ซ่อนไว้เหมือนเดิม

    GEvent.addListener(marker, "mouseover", function() {
    // ตั้ง style ให้ display ไม่เป็น none
    });

    GEvent.addListener(marker, "mouseout", function() {
    // ตั้ง style ให้ display กลับไปเป็น none ใหม่
    });

    3. มันจะยากหน่อยตรง check ว่าจะตั้งค่า left, top เป็นเท่าไหร่ดี ถึงจะดูว่ากล่องมันแสดงใกล้ๆ marker ครับ
  • auie
    ตอนนี้กำลังทำเส้นทางเดินรถอยู่ครับ แต่ว่าวิธีทำค่อนข้างจะถึกไปหน่อยคือ ต้องแปลงฟังก์ชั่นขึ้นมาหา latitude ที่ละตำแหน่งจากนนั้นจึงจะทำไปใส่ไว้ใน code แล้วฟังก์ชั้นนั้นต้องนำค่าไปใส่ไว้ด้วยตัวเองด้วย วิธีที่ดีกว่าที่ผมทำอยู่ไหมครับ (ตอนนี้ก็พยายามจะเอาตัว Marker แบบ Dragable ได้พอปล่อยปุ๊บก็จะแสดงค่าจะให้ความแม่นยำกว่าแต่ว่าอยากได้แบบกดจุดแล้วเส้นต่อกันเลยมากกว่า ไม่อย่างนั้นคาดว่าต้องใช้เวลาทำโปรเจคจบหลายปี ฮะฮะ)
  • tanomsak
    หมายถึงจะกำหนดจุดสองจุดในแผนที่ แล้วหาเส้นทางเดินรถระหว่างสองสุดหรือเปล่าครับ

    เท่าที่ผมคิด วิธีนึงน่าจะเป็นแบบนี้ครับ

    1. กำหนด global variable ตัวเป็น จำนวนของ marker ในแผนที่
    2. กำหนด global variable อีกตัว เป็น array ของ marker สองตำแหน่ง โดยจุดเริ่มต้นเป็น array ตัวแรก จุดสิ้นสุดเป็น array ตัวที่สอง
    2. ถ้าจำนวน marker = 2 ก็เริ่มหา้เส้นทาง โดยหา lat,lng ของแต่ละ marker ประมาณว่า

    latlng1 = marker[0].getLatLng();

    var lng1 = latlng1.lng();
    var lat1 = latlng1.lat();

    latlng2 = marker[1].getLatLng();

    var lng2 = latlng2.lng();
    var lat2 = latlng2.lat()

    5. ตอนสร้าง marker ก็เพิ่ม event พวก dragend ไว้ทั้งสอง marker ด้วย ถ้ามีการ drag จะได้หาเส้นทางใหม่ทันที

    GEvent.addListener(marker[0], "dragend", function() {
    // หาเส้นทาง
    });
  • joyjoyjoy
    สวัสดีคร้า คุณพี่ หนูได้เข้าไปดูเว็บ ddproperty.com ของคุณพี่ เห็นตรงแผนที่ของคุณพี่ ทำไมมีตำแหน่งของห้างสรรพสินค้า ปั๊มน้ำมัน และอื่นๆอีก อยากถามคุณพี่ว่า คุณพี่ไปดึงข้อมูลตำแหน่งเหล่านี้จากไหนมา เหรอคร้า ถ้าเป็นไปได้นะคร้า อยากให้คุณพี่ ช่วยสอนเรื่องนี้ในบทความถัดไป ในหมวด Google Map บนเว็บไซต์ Tanomsak.com ได้ไหมล่ะคร้า คุณพี่...
  • auie
    ขอบคุณมากๆครับ ^^
  • auie
    กำลังทำ project เกี่ยวกับ googlemaps อยู่เลยอะครับ

    ขอไอเดียตรงนี้ไปพัฒนาต่อนะครับ ^^

    แต่ว่าไม่เคยทำ GoogleMaps API + Javascript มาก่อนทำงานเลยค่อนข้างช้าเลยหละครับ Y_Y
  • tanomsak
    ยินดีมากครับ ถ้ามีคำถามก็ลองถามใน blog นี้ก็ได้ครับ เผื่อผมหรือผู้อ่านท่านอื่นๆช่วยได้ครับ
  • iampz
    อธิบายเป็นขั้นเป็นตอน เข้าใจง่ายดีจังครับ

    เพิ่มเติม
    ผมว่าใช้ javascript กำหนด id และ/หรือ class ก็พอนะ
    ส่วน style ให้ไปอยู่ใน css แล้ว code จะแจ่มขึ้นครับ
  • tanomsak
    จริงอย่างที่ีบอกครับ ถ้ากำหนด id/class จะไม่ต้องยืดยาวแบบข้างบน แล้วทำให้แก้ไข style ที่ css ได้โดยไม่ต้องมาแก้ที่ javascript ขอบคุณที่แนะนำครับ ในเวป ddproperty.com ดันนึกไม่ถึง code ซะยาวเยียดเลยครับ :)

    ผมเพิ่มตัวอย่างตามที่คุณ iampz แนะนำไว้ใน blog ด้วยนะครับ
  • iampz
    ยินดีครับ
blog comments powered by Disqus