[貝聊科技]「散點圖工具包」正式開源

貝聊科技發表於2019-03-04

作者:韓永豪 前端開發部 前端開發工程師

背景

之前釋出的文章《一個炫酷大屏展示頁的打造過程》反響非常熱烈,在感謝大家支援的同時,我挑了四個問題,在這裡解答一下:

  • Q1: 有用到第三方庫麼?

  • A1:頁面邏輯都是原生寫的,簡單封裝了一些對節點的操作。

  • Q2:地圖是怎麼畫出來的?

  • A2:地圖是由設計師提供的圖片,而地圖上閃動的光點和冒出的氣泡是DOM節點,樣式和動畫都是通過CSS實現的。

  • Q3:怎麼適配不同的解析度?

  • A3:適配不同螢幕儘量還是用百分比,至於字型大小或者某些固定大小的元素,這裡用到了rem適配,根據螢幕解析度計算根節點的字型大小,從而調節整體尺寸。

  • Q4: 能提供DEMO嗎?

  • A4: 這個問題是大家最感興趣,也是最多人提出的。由於專案本身涉及到公司機密,因此不方便提供完整的原始碼,但是經過一番整理,已經把主要的功能獨立成一個開源專案。本文的主要內容就是介紹這個開源專案,即 Scatter Map Toolkit」(散點圖工具包)

專案概述

先回顧一下大屏展示頁的地圖展示模組,其主要功能就是把資料轉化為光點顯示在地圖對應的省份區域內:

預期效果

要實現這個效果,首先碰到的問題就是:地圖上的省份是不規則圖形,很難界定一個省份的區域,所以本開源專案的第一個功能就是以圖形化的介面輔助開發者劃定每個不規則的區域,並且輸出為JSON資料。而第二個功能就是根據這份資料把光點定位到具體的區域。

區域劃定

按照微積分的原理,要計算一個不規則圖形的面積,可以將其切割成若干個矩形,矩形越小,數量越多,覆蓋面積越廣,矩形面積之和就越接近這個不規則圖形的面積,這個工具就是利用這個原理劃定區域。

區域切片

要使用這個工具,先從Github克隆本專案到本地,在命令列切換到專案路徑,執行:

$ npm install
$ npm start
複製程式碼

然後用瀏覽器訪問「 http://localhost:8000/ 」即可進入介面,後續操作步驟如下:

  1. 選擇圖片即可建立新的畫布。
初始介面
繪製介面
  1. 以其中一個省份為例,用按下滑鼠左鍵拖動,可以繪製出一個矩形。如果一個矩形不能完全覆蓋該省份,則多畫幾個。如果有畫錯的矩形可以把滑鼠移至該矩形上方,點選右上角的刪除按鈕。
繪製矩形
  1. 如果某些省份太小,操作困難,可以使用縮放功能。
繪圖操作
  1. 繪製完畢後可以為該組重新命名,點選右上角的加號還可以繼續新增分組。如果有不想要的分組,可以選擇整組刪除。如果有分組互相遮擋,可以點選隱藏按鈕進行隱藏。(與Photoshop中的圖層類似)
分組操作
  1. 繪製完畢後,可以下載配置項,把資料儲存下來,以便使用或者修改。
配置操作

最終效果如下:

整體效果

生成的JSON檔案如下:

{
  "image": "https://qiniu-pic.ibeiliao.com/o_1c93ofmqlug61uid15qe174d5ke7.png",
  "data": {
    "廣東": {
      "isShow": true,
      "areas": [
        {
          "x1": "58.778625954198475%",
          "x2": "64.02035623409668%",
          "y1": "71.23695976154993%",
          "y2": "78.09239940387481%"
        },
        {
          "x1": "64.02035623409668%",
          "x2": "70.38167938931298%",
          "y1": "73.91952309985098%",
          "y2": "78.09239940387481%"
        },
        
        ...
        
      ]
    }
  },
  "setting": {
    "resize": true,
    "width": 1310,
    "height": 895
  }
}
複製程式碼

區域資料使用

生成這份區域資料之後要如何使用呢?首先,一個光點必然是顯示在某個省的區域內的,所以要從資料中找到該省份的配置:

// 所有的區域資料都存在這個json檔案內
ajax(`options.json`).then((options) => {
    // 廣東省的矩形區域集合
    options.data[`廣東`].areas;
});

複製程式碼

其次,在矩形區域集合內隨機選擇一個區域,並在該區域內隨機選擇一個點:

// 在區域中隨機選取一個座標點
function radom(area) {
    let x1 = parseFloat(area.x1);
    let x2 = parseFloat(area.x2);
    let y1 = parseFloat(area.y1);
    let y2 = parseFloat(area.y2);
    
    return {
        x: x1 + ((x2 - x1) * Math.random()) + `%`,
        y: y1 + ((y2 - y1) * Math.random()) + `%`
    };
}

// 從集合中隨機獲取一個矩形區域
function getArea(areas) {
    return areas[Math.floor(Math.random() * areas.length)];
}

// 所有的區域資料都存在這個json檔案內
ajax(`options.json`).then((options) => {
    // 隨機獲取一個矩形區域
    let area = getArea(options.data[`廣東`].areas);
    
    // 隨機獲取一個座標點
    random(area);
});
複製程式碼

然而,這種做法會出現一個問題:面積大的矩形裡面的點被選中的概率和麵積小的矩形被選中的概率不一致。假設一個省份包含兩個矩形區域:

平均計算

具體的體現就是面積小的矩形裡面的點會更密集。為了解決這個問題,我們對隨機選取矩形的演算法進行了改良,面積越大被選中的概率就越大。思路很簡單,把所有矩形的面積轉化成一條線段(200個點+50個點=250個點的線段),在這條線上面隨機獲取一個點,這個點所在的線段對應的矩形就是隨機獲取到的矩形,因為矩形A線上段上佔比更大,所以獲取到點的概率也更大:

面積轉化成線段

實現程式碼如下:


/** 根據每個矩形的權重,隨機獲取一個矩形區域
* @param {Array} areas 矩形集合
*   @param {Number} areas[i].sumArea 改矩形的面積
* @param {Number} sumArea 所有矩形的總面積
*/
function getArea(areas, sumArea) {
    let currentSumArea = 0; // 當前累計總面積
    let result = null; // 查詢結果
	this.areas.some((area) => {
        // 查詢這個數落在哪條線段上
		if (
		    random >= currentSumArea &&
		    random <= currentSumArea + area.sumArea
	    ) {
			result = area;
			return true;
		}
		currentSumArea += area.sumArea;
	});

	return result;
}
複製程式碼

這樣改良後,兩個矩形裡面的點被選中的概率都是一樣的:

權重計算

這裡的功能已經封裝為專案中「 /dist/scatter-map.min.js 」的randomFromGroup方法

案例

最後迴歸到一開始說的地圖展示模組,現在只需要先用區域劃定工具生成區域資料,然後引入庫,再呼叫它的方法,就可以獲取隨機座標點。

<scrpit type="text/javascript" src="dist/scatter-map.min.js"></script>

<scrpit type="text/javascript">

ajax(`options.json`).then((options) => {
    // 工具例項化
    let scatterMap = new ScatterMap(options);

    // 隨機獲取廣東省的一個座標點,格式如下:
    // { x: `50%`, y: `50%` }
    scatterMap.randomFromGroup(`廣東`);
});

</script>

複製程式碼

然後,只需要根據這個座標點設定光點的位置,加上樣式和動畫即可完成上述效果。

總結與展望

  • 雖然專案的整體功能都已經完成,但是由於時間限制,區域劃定工具還存在一些相容性問題,目前建議通過谷歌瀏覽器開啟使用,後續會完善相容程式碼。

  • 當前版本的工具包不含光點繪製功能,例子中的光點繪製是通過DOM節點實現的,如果數量過多,有可能會造成頁面卡頓,瀏覽器佔用記憶體過大。因此,接下來會考慮把使用SVG或Canvas實現光點繪製。

  • 當前版本的區域劃定功能還不夠方便,需要繁瑣的手動操作,後續希望可以做到Photoshop魔法棒的效果,可以根據色差判斷不規則區域的覆蓋範圍,這樣繪製的效率將會大大提高。

專案地址

最後再貼一下地址,希望大家多多支援:github.com/beiliao-mob…

相關文章