Link home to achieve the effect

Recently, I received a demand that I need to use Goose Factory map to realize the function of map housing search similar to Lianjia. Then I went to the Internet to have a look, and basically I used Baidu Map. So I decided to encapsulate it a little bit, so that it would be more convenient to use.

Analysis of 01.

When I got this demand, I was also confused in a face, I did not know how to start, take a look on the Internet, there is said to use point aggregation to achieve. The following is an example provided on the official website: lbs.qq.com/javascript_…

Point polymerization effect

Looks like this is I to achieve function, I try to write and found that this style is difficult to change, and need a will all data requests to come over, at the time when the request is a large amount of data need to spend time will be very much, friendly to the user experience is not enough, so this method does not continue.

Later, I opened the official website of Lianjia alone, seized the data of Lianjia, and worked out the routine of Lianjia. Lianjia’s house search is mainly divided into three layers. The first layer is the urban layer, such as Nanshan, Luohu, etc.; The second floor is the area, such as nantou, science park, etc. The third floor is the residential area.

Because layer 1 and Layer 2 don’t have that much data, both interfaces return all the data to the front end at once. However, the data volume of the third layer is very huge. What Lianjia takes is to return part of the data, and send the maximum longitude and latitude and minimum longitude and latitude displayed on the front page to the background, and then the background will return the filtered data to the front end. (Interface address you can use Chrome development tools for packet capture, here need to note that the link home interface uses the form of JSONP, so need to grab JS)

Developer tools

For Windows, click F12 to bring up developer tools. For Mac, click Command + Option + I

02. Achieve

With the theory out of the way, it’s a matter of implementation. Looking at Tencent map API, I feel that only custom overlay is more suitable for this demand. Because custom coverings are more flexible, we can draw the styles we need just like we would with HTML.

The first need to add Tencent map API, here I recommend the use of asynchronous loading. Because the project uses Vue to develop a single-page application, users may not enter the page of map search, so it is suggested to add Tencent Map API when opening the page of map search.

Asynchronous loading needs to avoid a reloading problem where the map API is the same no matter how many times the user opens the map to find a room. In order to reduce the code complexity, the singleton pattern is not used here. The specific code is as follows:

const TXMap = {
  map: undefined.// Map instance
  // Asynchronous load fetch API
  getApi (funName) {
    let script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = `http://map.qq.com/api/js?v=2.exp&callback=${funName}`
    document.body.appendChild(script)
  }
}Copy the code

SRC is the address of Tencent map API. SRC contains a callback parameter, indicating that funName will be called after js is loaded. After adding the map API, the Window object will have a Qm. maps object, which we can use to determine whether the API has been added to avoid adding the API repeatedly.

The next step is to implement the custom overlay method. Refer to the official documentation (lbs.qq.com/javascript_…

const TXMap = {
  map: undefined.overlays: [].// Store all coverings
  sourceData: [], // Raw data
  listener: undefined.// Event listener for map zooming or panning

  getApi () {}, /* the */ is omitted

  // Implement custom coverings
  drawOverlay (options) {
    let _this = this // There are multiple window object methods below to avoid this pointing problem
    this.sourceData = options.data // Store raw data
    // Clean up the previously drawn overlay before drawing it
    this.clearOverlays()

    // If the initMap method is already implemented, then we can call it directly, otherwise we need to define it
    if (window.initMap === undefined) {
      window.initMap = function () {} // Draw a concrete implementation of the overlay

      // Call getApi if the map API is not introduced, otherwise call initMap ()
      window.qq === undefined ? this.getApi('initMap') : window.initMap()
    } else {
      window.initMap()
    }
  },
  // Clear custom coverings
  clearOverlays () {
    let overlay
    while (overlay = this.overlays.pop()) {
      overlay.onclick = null // Remove the click event
      overlay.parentNode.removeChild(overlay) // Remove the DOM element}},// Calls beforeDestroy of Vue components to reset the map and remove time to listen to avoid memory leaks
  clearMap () {
    this.map = undefined
    if (this.listener) {
      window.qq.maps.event.removeListener(this.listener)
    }
  }
}Copy the code

This is where the shelf for the map search is set up, and then we’ll look at the implementation of the drawing overlay, initMap.

window.initMap = function () {
  if (_this.map === undefined) {
    // Map drawing is required when the map object is undefined
    _this.map = new window.qq.maps.Map(document.getElementById(options.containerId), {
      // Initializes the map center
      center: new window.qq.maps.LatLng(options.lat || 22.702, options.lng || 114.09),
      // Initialize the scaling level
      zoom: options.zoom || 10.// Minimum zoom level for the map
      minZoom: 10.// Disable the zoom control
      zoomControl: false.// Deactivate the map type control
      mapTypeControl: false
    })
    // The idle event is triggered when the map is zoomed or panned
    _this.listener = window.qq.maps.event.addListener(_this.map, 'idle', () = > {// Get the maximum latitude and longitude of the current visual range of the map
      let bounds = _this.map.getBounds()
      // Get the zoom level of the current map
      let zoom = _this.map.getZoom()
      // Call the Vue component's idle event handler
      options.callback && options.callback(bounds, zoom)
    })
  }

  // Custom overlay
  if (window.CustomOverlay === undefined) {
    window.CustomOverlay = function (lat, lng, name, houseCount) {
      // Call the map API to calculate the location of the overlay
      this.position = new window.qq.maps.LatLng(lat, lng)
      this.name = name / / area
      this.houseCount = houseCount // Number of listings
    }
    / / Overlay inheritance
    window.CustomOverlay.prototype = new window.qq.maps.Overlay()
    // Custom overlay constructor, define overlay as the DOM structure, DOM structure, style you can draw their own needs
    window.CustomOverlay.prototype.construct = function () {
      let div = this.div = document.createElement('div')
      div.className = 'my-overlay' // Override the class name
      // Overlay HTML structure
      this.div.innerHTML = `<p class="count" >The ${this.houseCount}< span > set < / span > < / p > < p class = "name" >The ${this.name}</p>`
      // Add THE DOM to the overlay layer, the sequence container 5 of the overlayMouseTarget, which contains the transparent mouse element used to receive the mouse event of the Marker
      this.getPanes().overlayMouseTarget.appendChild(div)
      // Add div to overlays for subsequent processing
      _this.overlays.push(div)
      // Define the click event for the overlay
      let center = this.position
      this.div.onclick = function () {
        // Click to zoom and pan the map
        let zoom = _this.map.getZoom()
        if (zoom < 13) {
          _this.map.setCenter(center)
          _this.map.setZoom(13)}else if (zoom >= 13 && zoom < 15) {
          _this.map.setCenter(center)
          _this.map.setZoom(15)}}}// Implement the DRAW interface to draw DOM elements
    window.CustomOverlay.prototype.draw = function () {
      let overlayProjection = this.getProjection()
      // Get the relative pixel coordinates of the covering container
      let pixel = overlayProjection.fromLatLngToDivPixel(this.position)
      let divStyle = this.div.style
      // Adjust the location according to the DOM element
      divStyle.top = pixel.y - 53 + 'px'
      divStyle.left = pixel.x - 30 + 'px'}}// Draw the overlay according to the interface data
  if (_this.sourceData.length > 0) {
    _this.sourceData.map(item= > {
      let customOverlay = new window.CustomOverlay(item.latitude, item.longitude, item.name, item.house_count)
      customOverlay.setMap(_this.map)
    })
  }
}Copy the code

At this point, map search to draw overlay method encapsulation is complete, the next only need to expose TXMap, and then introduced in the Vue component, and then to the following method can be used

TXMap.drawOverlay({
  containerId: 'map-box'.data: res.data
})Copy the code

03. Achieve results

In this example, I made two layers with the data of Lianjia, and you can modify them according to your own needs.

Implementation effect

The project is on GitHub

If the article is helpful to you, then please click on it ❤ Because of my limited level, if there are mistakes, welcome to correct. If you find some mistakes or problems that you haven’t mentioned in the process of operation, please feel free to leave a comment and discuss them together to learn and progress together!

This article will be published on my official account as soon as possible. If you are interested, please follow my official account – front-end Develop