前兩大段都是閒侃,正文可從【準備工作】開始看
關於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>
標籤中的width
和height
去掉,因爲要做成自適應寬度 <svg>
中的viewBox
屬性(記住B大寫)是視區盒子,用來規定畫板大小而非我們最終所看到的大小,可以看一篇張鑫旭的文章深刻理解- 給每個熱區添加類
hotarea
並將內聯的fill
和opacity
樣式去掉,以方便用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效果
360效果
另外網上也有人提出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', '...')
來實現
資料整理
image-maps - 在線可視化製作熱區
Raphaël—JavaScript Library
HTML 5 標籤
SVG 教程 | 菜鳥教程
理解SVG的viewport,viewBox,preserveAspectRatio
一張圖輕鬆搞懂javascript event對象的clientX,offsetX,screenX,pageX區別