使用SVG製作熱區圖

前兩大段都是閒侃,正文可從【準備工作】開始看

關於SVG

SVG(Scalable Vector Graphics)是可縮放 矢量圖形 ,矢量圖與常見的位圖(bitmap)的區別就在於對圖形的定義,位圖是精確到每一個像素是什麼顏色,而矢量圖僅是對圖形特徵(比如一條直線的起點和終點)描述
因爲位圖其實是很多小方格拼在一起,將其放大後就能看到一個一個小像素點了,這也是爲什麼斜線會有鋸齒了;而矢量圖因爲定義的圖形特徵,無論怎樣縮放都能保證圖形邊緣的平滑準確
個人認爲,位圖可以表現出更復雜的圖形,但是越高清(高分辨率)的圖形自然也就越複雜,但是非常有層次感,很自然;而位圖可以用一些特徵去表現出一幅任意分辨率的圖形,自然不適合去表達複雜的圖像(比如一幅風景畫),而且通常很抽象,顏色是一塊一塊的,但是非常適合做扁平化設計(比如小圖標什麼的),不僅節省了大量空間,更是不用擔心鋸齒虛化等問題了
編輯位圖的工具有photoshop(ps)、畫圖等;編輯矢量圖的工具有Adobe Illustrator(AI),coreldraw等
而SVG就是W3C爲網頁矢量圖所定製的標準,SVG的競爭對手爲flash,可以斷定SVG已經取代了flash的一些地位了

簡短說明

因爲需要做這麼一個功能,在一張平面圖上設置熱區,用戶可點擊熱區並可以執行相應操作
一開始用html的<area>標籤實現,但是發現這個標籤有太多不方便,比如沒法使用hover僞類(網上提示說可以用onmousemove等事件+替換背景圖來模擬hover效果)、沒法自適應寬度(<area>標籤需要精準的coords座標)、<area>中的精確座標很難確定(找到一個在線可視化製作熱區的網站)等
綜上,我想到我從未接觸過的SVG,本來以爲可以找到什麼插件來完成的,但只找到Raphaël這個強大的矢量圖形庫,其實對我並沒用…所以想將此次幾乎是從零開始(至少還是接觸過矢量圖的)的經歷記錄下來


準備工作

  • 一張平面圖 - 隨便在網上找吧(一張帥炸天的瑪薩拉蒂)
    平面圖
  • 矢量圖編輯工具 - 我用的AI(Adobe Illustrator CC)
  • jQuery - 一個百度靜態資源庫的CDN解決

處理圖片

將圖片拖入AI中,之後用鋼筆或者儘量規則的路徑勾勒出各個區域,下面列幾個tips吧

  • 需要調整畫板大小,在標題欄的文檔設置 -> 編輯畫板
  • 勾勒區域儘量減少錨點,這樣存儲的信息就越小,代碼也就越少了
  • 用小白箭頭選取路徑區域可以設置區域填充顏色、透明度、邊框等

我勾出了前窗、側窗、前輪、前燈和LOGO
圖片處理

之後保存格式爲svg我們就得到了一個svg文件了,這個svg文件可以直接引入,但是我不這樣做,因爲不方便我控制樣式(CSS)和腳本(JS)

放入HTML中

將svg文件中<svg>標籤及其全部內容放到HTML文檔中,並稍加修改,下面是修改的具體內容和最終svg代碼

  • <svg>標籤中的widthheight去掉,因爲要做成自適應寬度
  • <svg>中的viewBox屬性(記住B大寫)是視區盒子,用來規定畫板大小而非我們最終所看到的大小,可以看一篇張鑫旭的文章深刻理解
  • 給每個熱區添加類hotarea並將內聯的fillopacity樣式去掉,以方便用CSS控制,注意 svg中背景色屬性是fill,而描邊屬性是stroke
<!-- 將width和height去掉,因爲要做自適應寬度 -->
<svg version="1.1" id="hotmap" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1000px" height="563px" viewBox="0 0 1000 563" enable-background="new 0 0 1000 563" xml:space="preserve">
    <image overflow="visible" width="1000" height="563" id="car" xlink:href="./img/bg.jpg">
    </image>
    <path class="hotarea" id="frontWin" d="M437,225.333c-20.5-46.667,24-60.5,26.833-63.833
    c3.01-2.308,89.333-51.333,110-43c20.667,8.333,73.5,50.667,82.833,58.667c9.333,8,15.5,18.167,0,37.667
    S561.976,265.432,551,266.167S456,273.5,450.167,262C444.333,250.5,445.833,250.333,437,225.333z" />
    <path class="hotarea" id="sideWin" d="M343.25,156.5c3.833-6.25,49.5,16.833,65.5,26.25s17.614,26.472,14.5,41.5
    s-3.5,15.75-3.833,18.5c-0.333,2.75-11.417,6.167-26.583,0c-15.167-6.167-38.583-20.583-49.583-27.667c-11-7.083-8-25.25-8-25.25
    S339.417,162.75,343.25,156.5z" />
    <path class="hotarea" id="frontWheel" d="M576,438c0,29.915-16.439,40.667-37.333,40.667s-69-49.085-69-79
    c0-29.915,12.439-41.333,33.333-41.333S576,408.085,576,438z" />
    <path class="hotarea" id="rightLight" d="M686.5,391c0,0,33.75,18,54.5,33.25c15.25,12.75,2.5,16-6.5,19.25
    s-66-3-77.25-7.5s-4.188-17.563-1.75-21C657.75,411.25,686.5,391,686.5,391z" />
    <path class="hotarea" id="logo" d="M868.625,391.75c3.25-3.625,12.625,4.25,12.625,4.25s7.875,9.625,4.875,12.875
    s-14-5.375-14-5.375S865.375,395.375,868.625,391.75z" />
</svg>

接下來的響應操作就簡單了,不管是onmouseover還是click都能爲你所用了

自適應寬度 & 確定點擊位置

自適應寬度

自適應寬度想着很簡單,只要在CSS中width: 100%就解決了,但是經過我手頭上現有瀏覽器的測試,chorme(版本 52)和edge(版本 38)是沒問題的;但在ie11下,其自適應似乎有個臨界值,大於某個臨界值則不再自適應;而在360瀏覽器(版本 7.1)下,雖然是會自適應,但是在svg頂部會有莫名其妙的留白(丟給小夥伴的360 v8.1下就沒問題了,ps:可設置preserveAspectRatio="xMidYMin meet"但依舊會有底部留白)

  • ie11效果
    ie11效果

  • 360效果
    ie11效果

另外網上也有人提出js動態控制svg的絕對寬度,試了下是可行,但上面的問題也依舊存在,下面給出js代碼

function svgFitWidth() {
  function fitWidth() {
    $('#hotmap').css('width', $('#mainContent').width() + 'px');
  };
  fitWidth();
  $(window).resize(function() {
    fitWidth();
  });
}

確定點擊位置

比如說我要在熱區彈出一個菜單,且彈出位置剛好是鼠標點擊位置,這時候就需要 鼠標相對於svg容器的相對位置 了(這裏我用到bootstrap的下拉菜單dropdown來輔助)

  • e.clientX | e.clientY - clientX事件屬性返回當事件被觸發時鼠標指針向對於瀏覽器頁面(或客戶區)的水平座標(clientY類推)
  • document.body.scrollLeft | document.body.scrollTop - 水平或者垂直滾動條的偏移量
  • $().offset().top | $().offset().left - 某元素相對於整個文檔的偏移量

綜上我們可以獲得鼠標點擊時相對於svg容器的位置

var x = e.clientX + $('body').scrollLeft() - $('#mainContent').offset().left;
var y = e.clientY + $('body').scrollTop() - $('#mainContent').offset().top;

圖解位置
點擊示例

其次需要注意的是,jq操作svg內的class不能使用addClass或者removeClass,具體爲何不知道,但是可以用attr('class', '...')來實現

demo
source


資料整理

image-maps - 在線可視化製作熱區
Raphaël—JavaScript Library
HTML 5 標籤
SVG 教程 | 菜鳥教程
理解SVG的viewport,viewBox,preserveAspectRatio
一張圖輕鬆搞懂javascript event對象的clientX,offsetX,screenX,pageX區別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章