什麼是RaphaelJS?
RaphaelJS是一個矢量繪圖包,用來在瀏覽器中繪製圖形。
雙頭和三個字母
差不多十年前,我們有一個技術大突破稱為:可縮放矢量圖形(SVG)。SVG可縮放矢量圖形(Scalable Vector Graphics)是基於可擴展標記語言(XML),用於描述二維矢量圖形的一種圖形格式。SVG是W3C("World Wide Web ConSortium" 即 " 國際互聯網標準組織")在2000年8月製定的一種新的二維矢量圖形格式,也是規範中的網絡矢量圖形標準。SVG嚴格遵從XML語法,並用文本格式的描述性語言來描述圖像內容,因此是一種和圖像分辨率無關的矢量圖形格式。盡管SVG基於VML發展而來,但是SVG和VML不能在一個瀏覽器中同時協調工作。SVG可以運行在所有除了版本低於IE9的的IE之外的瀏覽器。
兩個問題的結合以及RaphaelJS的誕生
Javascript--求同存異
瀏覽器支持
什麼事RaphaelJS,什麼不是?
一些關於矢量圖和位圖的知識
其實就是講圖形有兩種類型:位圖和矢量圖,位圖放大失真而矢量圖放大不失真。因為位圖是建立像素基礎上的,所以放大失真。而矢量圖是通過特殊數學算法來實現放大不失真。
如圖所示,瓶子上麵的字體放大仍舊很清晰的就是矢量圖,模糊的就是位圖。
RaphaelJS,一個矢量圖包
RaphaelJS作為一個矢量圖包,它所以繪製的內容是一個真正的DOM節點。因此它可以被動態的訪問、操作、變化大小並敲打錘煉成為其它你想要的圖形。更有意思的是它們可以綁定比如點擊、懸停、移出等鼠標操作。RaphaelJS的功能簡直令人難以置信。
RaphaelJS的x,y坐標係
RaphaelJS使用x,y坐標係去繪製圖形。屏幕最左上角為0,0位置然後水平為x軸,垂直為y軸。
所以,如果代碼裏出現(15,20)意思就是這個點位於x軸15個點(水平),y軸20個點的位置(垂直)。
RaphaelJS作為一個矢量圖包,繪製時根據瀏覽器選擇使用SVG或者VML。因為繪製出來是一個實際存在的DOM節點,所以可以賦予點擊、懸停等操作。這些都是很有趣的地方。
【RaphaelJS和HTML5 Canvas是2個截然不同的東西。盡管它們所做的事情很相似:繪畫。但是它們的實現方式並不相同。RaphaelJS以矢量繪製為基礎,而HTML5 Canvas則是以光柵為基礎 】
安裝
安裝和搭建RaphaelJS非常簡單。你隻要下載最新的RaphaelJS的版本然後它引用到html中就行了。
第一步-下載RaphaelJS
打開官網 http://dmitrybaranovskiy.github.io/raphael/
下載 RaphaelJS https://raw.githubusercontent.com/DmitryBaranovskiy/raphael/master/raphael.js
RaphaelJS和別的包不同,它沒有其它任何依賴,就是這一個文件。
第二步-添加RaphaelJS到html中
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Raphael Test</title></head><body><div id="my-canvas" style="width:140px;padding:0px;margin:0px;"></div><!--some html doms--><!--some scripts--><script type="text/javascript" src="../js/lib/raphael.js"></script></body></html>
代碼和原書細節可能不同,是我自己測試用例,總體按照原書來寫的。
【注意這裏,此書裏麵講解了為什麼要把引用raphael.js 放置在html元素的最下方。因為瀏覽器執行html的代碼是順序執行的。你如果把引用放在前麵,瀏覽器會先加載它,導致中間有段時間頁麵會停滯,這裏推薦你先加載dom元素再導入這個js包。】
快速入門 - 創建你第一個圖形
第一步 – 建立一個用來繪製圖形的畫布
在瀏覽器視口創建畫布
創建raphael物體(對象),依賴於raphael的方法和function,如下所示:
var raphaelObj = Raphael(x,y,width,height);//Raphael function中4個參數分別是x坐標、y坐標、寬度、高度。
因為這個是在瀏覽器視口裏麵來創建的,所以畫布的位置是絕對定位。因此,它會在所有html元素下麵重疊。
比如:
// 在瀏覽器視口中創建畫布var paper = Raphael(20, 30, 650, 400);
這裏的Raphael對象被初始化並且分配一個變量稱為paper。這個變量被賦予RaphaelJS的所有權利。它從此以後成為Raphael畫布對象。
在元素中創建Raphael對象(推薦)
要在一個元素中初始化一個Raphael對象,我們必須把這個元素的ID或者這個元素本身加入到坐標係(x,y)中。
我們舉個例子:
//元素本身作為參數//This line creates a Raphael paper inside 'my-canvas', which is 650pxin width and 400px in heightvar elm= document.getElementById("my-canvas");var paper = Raphael(elm, 650, 400);//or// 直接傳遞元素的ID作為參數//This line also creates a Raphael paper inside 'my-canvas', which is 650px in width and 400px in heightvar paper = Raphael("my-canvas", 650, 400);
這樣我們就可以啟動引擎開始跑了!
第二步--繪製圓形
Raphael可以繪製的基本圖形有圓形、矩形和橢圓等。
圓形可以通過調用circle()來發來進行。我們使用剛才的paper對象來進行調用。
語法如下:
var cir = paper.circle(x,y,r);// x and y are the positioning co-ordinates, and "r" is the radius of the circle//Here the circle is assigned to a variable called cir.//Examplevar cir = paper.circle(35,25,20);// This will create a circle of 20 pixels in radius at 35,25 (X & Y axis).
circle()方法一共3個參數,分別是x,y坐標和半徑。我們在剛才的代碼中添加上麵的操作。
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Raphael Test</title></head><body><div id="my-canvas" style="width:140px;padding:0px;margin:0px;"></div><!--some html doms--><!--some scripts--><script type="text/javascript" src="../js/lib/raphael.js"></script><script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var cir = paper.circle(30, 30, 10);</script></body></html>
效果如上圖所示,由於沒有賦予paper和cir其它屬性,所以沒有顏色之類的屬性,我們看起來不直觀。下麵將介紹attr()方法。
attr() 方法
attr()方法,將我們要添加或者修改的屬性作為參數,屬性其實就是鍵值對。如果你熟悉jQuery的話,你就明白attr()的語法與jquery的完全一致。進一步講,就是JSON格式數據。做過js的童鞋們,或多或少應該接觸過JSON。沒接觸過的,建議去翻閱一下資料。比XML更快的數據傳遞方式,以後肯定會大放異彩。
語法如下:
element.attr({"Property1":value1,"Property2":value2})//加入如下屬性的鍵值對var coloredCircle = paper.circle(35,25,20).attr({"fill":"#17A9C6","stroke":"#2A6570","stroke-width":2});
剛才這個例子添加到我們的Script裏麵就行了,這裏不再截圖和添加代碼了。效果是:在坐標(35,25)位置出現一個半徑為20的顏色為深綠的圓(本人色弱,顏色認錯大家莫怪)。
當然,還有其它非常多的屬性可以添加。有興趣大家可以自己去查閱一下資料,這裏暫不提及。本書最後部分會有涉及。
下麵我們要講的部分是本書的重點,也是學習Raphael.js的重點。上麵講了那麼多其實不過是三兩行代碼就可以搞定的事情,前提是你正確的下載了包並引用了正確的路徑。接下來我們要講的是創建、操作、轉換、動畫、事件響應等內容,是Raphael的核心部分,也是工作涉及到的重要部分。
你需要了解的重要特性
創建一個Raphael的元素非常容易。為了更加方便,有些已經定義好的方法供生成一些基本的幾何圖形。
基本圖形
RaphaelJS有3個基本圖形,分別是 圓、橢圓和矩形。前麵已經講過圓形,這裏我就不再贅述圓形了。
矩形
我們可以使用rect()方法來創建一個矩形。這個方法一共有4個必須參數和一個可選參數。5個參數按順序分別是x坐標、y坐標、矩形寬度、矩形高度、圓角半徑。
圓角半徑默認為0,為可選參數。
語法如下:
paper.rect(X,Y,Width,Height,border-radius(optional));var rect = paper.rect(35,25,170,100).attr({"fill":"#17A9C6", //filling with background color "stroke":"#2A6570", // border color of the rectangle "stroke-width":2 // the width of the border});
圓角矩形:
var rect = paper.rect(35,25,170,100,20).attr({"fill":"#17A9C6", //filling with background color "stroke":"#2A6570", // border color of the rectangle "stroke-width":2 // the width of the border});
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Raphael Test</title></head><body><div id="my-canvas" style="width:140px;padding:0px;margin:0px;"></div><!--some html doms--><!--some scripts--><script type="text/javascript" src="../js/lib/raphael.js"></script><script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var cir = paper.circle(30, 30, 10);var coloredCircle = paper.circle(55, 45, 20).attr({"fill": "#17A9C6","stroke": "#2A6570","stroke-width": 2});var rect = paper.rect(65, 65, 120, 80).attr({"fill": "#17A9C6", //filling with background color "stroke": "#2A6570", // border color of the rectangle "stroke-width": 2 // the width of the border});var rect1 = paper.rect(190, 65, 120, 80,20).attr({"fill": "#17A9C6", //filling with background color "stroke": "#2A6570", // border color of the rectangle "stroke-width": 2 // the width of the border});</script></body></html>
橢圓
橢圓的方法是ellipse(),有4個必要參數。分別是x坐標、y坐標、水平半徑、垂直半徑。水平半徑和垂直半徑其實就是橢圓的寬和高除以2。語法如下:
//paper.ellipse(X,Y,rX,rY);var ellipse = paper.ellipse(195,125,170,100).attr({"fill":"#17A9C6", // background color of the ellipse "stroke":"#2A6570", // ellipse's border color "stroke-width":2 // border width});
由於和上麵矩形的代碼幾乎一致,除了參數意義。這裏不再重複代碼和截圖。
複雜圖形
建立上麵那種基本圖形可以說非常簡單。但是複雜圖形,比如五角星、八角形等怎麼辦?它們根本不是圓或者矩形或者橢圓。
這是Raphael的另外一個奇異之處。
複雜圖形的創建將使用path()方法。它隻有一個參數,我們稱它為pathString。看上去就是一串字母和數字的組合。其實它非常易於閱讀和理解。
Paper.path([pathString])
pathString SVG格式的路徑字符串。
路徑字符串由一個或多個命令組成。每個命令以一個字母開始,隨後是逗號(“,”)分隔的參數。例如:"M10,20L30,40"
我們看到兩個命令:“M”與參數(10, 20)和“L”與參數(30, 40)大寫字母的意思是命令是絕對的,小寫是相對的。
這裏是可用命令的簡表,詳細內容請參照: SVG路徑字符串格式。
命令 | 名稱 | 參數 |
---|---|---|
M | 移動到(moveTo) | (x y)+ |
Z | 閉合路徑(closepath) | (none) |
L | 直線(lineTo) | (x y)+ |
H | 水平直線 | x+ |
V | 垂直直線 | y+ |
C | 曲線(curveto) | (x1 y1 x2 y2 x y)+ |
S | 平滑曲線 | (x2 y2 x y)+ |
Q | 二次貝賽爾曲線 | (x1 y1 x y)+ |
T | 平滑二次貝塞爾曲線 | (x y)+ |
A | 橢圓弧 | (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ |
R | Catmull-Rom 曲線* | x1 y1 (x y)+ |
paper.path("pathString");var tri = paper.path("M0,0L26,0L13,18L0,0").attr({"fill":"#17A9C6", // filling the background color "stroke":"#2A6570", // the color of the border "stroke-width":2 // the size of the border});
具體例子的介紹稍後,這裏先說明一下,上麵的命令都是命令+參數這樣子的。但是命令有大小寫的區別,其效果也不同。大寫表示絕對,小寫為相對。比如(M20,20)表示從(0,0)位置來計算的,而(m20,20)則是相對畫筆的位置(dom位置)來計算。
在這裏你可能會感覺很頭大,因為這麼多命令,還有幾個挺難理解的曲線。再加上複雜圖形怎麼可能直接就寫一串字符出來。其實不必擔心,因為複雜圖形你可以使用工具來進行繪製。在矢量圖製作工具中製作完成然後導出svg格式就行了。推薦使用一個叫做inkscape的矢量圖製作工具。
(2014-01-07接上)
首先說下,昨天文章裏麵有個內容是js的引用在body底下位置,這裏其實如果你使用jquery或者dojo等框架,可以等document加載完成加入那些js就行了。比如$(document).ready(function(){})這樣子的。
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Raphael Test</title></head><body><div id="my-canvas" style="width:140px;padding:0px;margin:0px;"></div><!--some html doms--><!--some scripts--><script type="text/javascript" src="../js/lib/raphael.js"></script><script type="text/javascript">var paper = Raphael("my-canvas", 700, 600);//1、直線使用path命令Lpaper.path("M20,20L100,20z").attr({"fill": "#5DDEF4","stroke": "#2A6570","stroke-width": 2});//2、三角形使用Path命令Lpaper.path("M130,30 L200,30 L160,90 z").attr({"fill": "#5DDEF4","stroke": "#2A6570","stroke-width": 2});//3、T形使用Path命令H,Vpaper.path("M 40,40 H 90 V 60 H 70 V 110 H 60 V 60 H 40 z").attr({"fill": "#5DDEF4","stroke": "#2A6570","stroke-width": 2});//4、2次貝塞爾曲線形,使用path命令Qpaper.path("M240,40L300,40L300,100");paper.path("M240,40Q300,40 300,100").attr('stroke', 'red');//5、2次貝塞爾曲線形,使用path命令Q和T(第一個是正常繪製,第二個光滑連接) paper.path('M10,250 L90,130 L160,160 L250,190 L250,70');paper.path('M10,250 Q90,130 160,160 T250,70').attr('stroke', 'red');//6、繪製3次貝賽爾曲線,使用命令C,平滑畫線使用命令Spaper.path('M320,120 L350,180 L450,260 L480,140');paper.path('M320,120 C350,180 450,260 480,140').attr('stroke', 'red');paper.path('M320,120 S450,260 480,140').attr('stroke', 'yellow');</script></body></html>
代碼裏麵刪除了上麵文字裏麵的基本圖形的繪製代碼。為了節省空間,這裏就使用這些繪製path的代碼。上麵代碼中一共6個小模塊,使用了path命令中最後2個之外的其它所有命令。最後兩個待會兒解釋。先解釋上麵幾個。代碼運行效果圖如下:
這裏說明下,原書這裏是去inkscape裏麵裏麵用inkscape自帶的一些工具畫了一些星星之類的圖形,這裏我們是直接寫的path命令串,可以更加容易地加深對於path的理解。我們看運行效果圖,一共有6個圖形。我們上麵的代碼塊也有6小塊,分別對應瀏覽器上麵的6個圖形。
繪製直線,準確的說是繪製連線,我們代碼內容是這樣的:
paper.path("M20,20L100,20z")
這裏,pathString為M20,20L100,20z,我們查看上麵的path命令表。發現執行情況是這樣的:我們的筆移動到坐標(20,20)的位置,連一條線到(100,20),閉合path。也就是上麵6個圖形中的最左上角的那個線段。
接下來是那個三角形:
paper.path("M130,30 L200,30 L160,90 z")
三角形這個命令和上麵線段是一致的,隻是多連了一次,構成了一個三角形。這裏我們是可以明白,隻要你不斷的用L命令去連接,其實可以構成任意複雜的圖形。下麵我們繼續講解第三個代碼塊:
paper.path("M 40,40 H 90 V 60 H 70 V 110 H 60 V 60 H 40 z")
這裏我們用到了H和V命令,這倆命令我們通過查看path命令表可知道它們分別是橫著和豎著連線。所以剛才上麵那行代碼的執行情況是:我們的筆移動到(40,40),水平線連接到x坐標90,垂直線連接到60,水平線······如此反複,需要注意H和V後麵跟的不是線的長度而是坐標,最後我們閉合path得到一個T的圖形。這個其實使用上麵那個L命令也可以做到,但是水平和垂直命令名對於這種0角度或者90度的線處理起來更加容易。
第四塊代碼如下:
paper.path("M240,40L300,40L300,100");paper.path("M240,40Q300,40 300,100");
這裏我們第一行代碼是畫兩條線段,上麵講解到了,隻不過這裏沒有z結尾來閉合path。第二行代碼使用的坐標與第一個完全一致,隻不過兩個L命令換成了Q,我們查看命令表,Q表示二次貝塞爾曲線,效果大家可以看靠上位置的那個曲線和曲線附著的的線段。Q命令的後的坐標含義可以通過上麵線段的坐標來得知。
第五個代碼塊如下:
paper.path('M10,250 L90,130 L160,160 L250,190 L250,70');paper.path('M10,250 Q90,130 160,160 T250,70');
這裏的效果圖是上麵圖形中最大那個圖形。其實如果理解了第四個圖形,這個很好理解。唯一的不同就是這裏使用了T命令,這個T和接下來的S命令很相似,就是分別對應Q和S的平滑曲線畫法,生活他們中間的那個坐標,而直接使用後一(兩)個坐標即可。
第六個代碼塊如下:
paper.path('M320,120 L350,180 L450,260 L480,140');paper.path('M320,120 C350,180 450,260 480,140').attr('stroke', 'red');paper.path('M320,120 S450,260 480,140').attr('stroke', 'yellow');
看到最後一幅圖,我們看的到一條紅色曲線和一條黃色,兩個圖形的區別就是命令中缺省第一個坐標的區別。上麵第5個圖形也可以做成類似第六幅圖的曲線對比圖,大家可以稍微修改一下代碼就能搞定了。當然這裏其實曲線的每個附著的線段是不是必要存在,為了加深理解,我們將它畫了出來。
**************************************
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Raphael Test</title></head><body><div id="my-canvas" style="width:140px;padding:0px;margin:0px;"></div><!--some html doms--><!--some scripts--><script type="text/javascript" src="../js/lib/raphael.js"></script><script type="text/javascript">var paper = Raphael("my-canvas", 700, 600);//繪製左上的橢圓 paper.ellipse(130,40,60,30);paper.ellipse(70,70,60,30);paper.path('M70,40 A60,30 0 0,0 130,70').attr('stroke','yellow');paper.text(40,30,'start(70,40)').attr({'font-size':11,'fill':'blue' });paper.text(160,80,'end(130,70)').attr({'font-size':11,'fill':'blue' });paper.text(70,120,'large-arc-flag=0\nsweep-flag=0').attr({'font-size': 11,'fill': 'green','text-anchor': 'start' });//繪製右上的橢圓 paper.ellipse(330,40,60,30);paper.ellipse(270,70,60,30);paper.path('M270,40 A60,30 0 0,1 330,70').attr('stroke','yellow');paper.text(240,30,'start(270,40)').attr({'font-size':11,'fill':'blue' });paper.text(360,80,'end(330,70)').attr({'font-size':11,'fill':'blue' });paper.text(270,120,'large-arc-flag=0\nsweep-flag=1').attr({'font-size': 11,'fill': 'green','text-anchor': 'start' });//繪製左下的橢圓 paper.ellipse(130,240,60,30);paper.ellipse(70,270,60,30);paper.path('M70,240 A60,30 0 1,0 130,270').attr('stroke','yellow');paper.text(40,230,'start(70,240)').attr({'font-size':11,'fill':'blue' });paper.text(160,280,'end(130,270)').attr({'font-size':11,'fill':'blue' });paper.text(70,320,'large-arc-flag=1\nsweep-flag=0').attr({'font-size': 11,'fill': 'green','text-anchor': 'start' });//繪製右下的橢圓 paper.ellipse(330,240,60,30);paper.ellipse(270,270,60,30);paper.path('M270,240 A60,30 0 1,1 330,270').attr('stroke','yellow');paper.text(240,230,'start(270,240)').attr({'font-size':11,'fill':'blue' });paper.text(360,280,'end(330,270)').attr({'font-size':11,'fill':'blue' });paper.text(270,320,'large-arc-flag=1\nsweep-flag=1').attr({'font-size': 11,'fill': 'green','text-anchor': 'start' });</script></body></html>
其中有個paper.text方法正好我們解析來要講解的內容,這裏也算提前預覽一下。
A(a)elliptical arc(rx ry x-axis-rotation large-arc-flag sweep-flag x y) ;
參數含義:
rx:橫軸的長度;
ry:縱軸的長度;
x-axis-rotation:橢圓的橫軸與x軸的角度;
large-arc-flag:區分弧度的大小(0表示小角度弧度,1表示大角度弧度);
sweep-flag:繪製弧度圍繞橢圓中心的方向(0表示逆時針方向,1表示順時針方向);
x y:橢圓曲線終點坐標;
效果圖如下:
圖畫上麵有個A命令的參數含義,其實加上這位博主的參數含義的解釋還是很好理解的。注意圖中的黃色曲線,每個下麵有2個參數的解釋,忽略4組圖形的開始和結束坐標,其實他們的區別就是在larget-arc-flag和sweep-flag的參數設置不同。
large-arc-flag:區分弧度的大小(0表示小角度弧度,1表示大角度弧度);
sweep-flag:繪製弧度圍繞橢圓中心的方向(0表示逆時針方向,1表示順時針方向);
是不是一目了然?larget-arc-flag就是畫小弧還是大弧的區別、sweep-flag是逆時針還是順時針。
“Catmull-Rom 曲線”不是SVG標準命令,這裏不再贅述,有興趣的可以自己去研究測試一下。
接下來我們講解的方法是text方法。上例中出現了paper.text(),是用來顯示文字內容的方法。語法其實很簡單,就是x,y坐標和文字內容。
paper.text(X,Y,"Raphael JS Text");var text = paper.text(40,55,"Raphael Text").attr({"fill":"#17A9C6", // font-color"font-size":75, // font size in pixels"text-anchor":"start","font-family":"century gothic" // font family of the text});
//text-anchor屬性表明文字與坐標的關係,是從這個坐標開始、為中心還是為結尾。屬性值可以設置 "start", "middle" or "end" 默認"middle"。
操作Raphael元素的樣式
在上篇的內容中,我們已經提到了一個方法是attr()方法,是給一個Raphael圖形添加樣式及屬性定義的方法。我們要修改一個元素的樣式和屬性也可以使用attr方法來進行。上篇中我們聲明了一個rect對象,這裏我們可以來修改它的樣式:
rect.attr('fill','#ddd');
這行代碼將會把我們繪製好的矩形修改成為內部填充褐色。也就是說,attr()其實相當於mysql裏麵的insert into語句中的on duplicate key update,沒有我就添加有我就更新這樣子執行方式。
Raphael元素的變換
Raphael其實提供了好幾個方法供大家調用來變換元素,但是其中幾個的方法都是不推薦的。唯一推薦的元素變換方法是transform()方法。transform方法的參數與上篇最後的path命令串很相似,隻不過這是transform命令串。它有4個命令:
T 平移
S 縮放
R 按角度旋轉
M 變換矩陣
比如:"t100,100r30,100,100s2,2,100,100r45s1.5"
<!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Raphael Test</title></head><body><div id="my-canvas" style="width:140px;padding:0px;margin:0px;"></div><!--some html doms--><!--some scripts--><script type="text/javascript" src="../js/lib/raphael.js"></script><script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var rect = paper.rect(20, 20, 60, 40).attr({"stroke": "red"// border color of the rectangle });var rect1 = paper.rect(20, 70, 60, 40, 20).attr({"stroke": "yellow"// border color of the rectangle });var rect2 = paper.rect(20, 20, 60, 40).attr({"stroke": "red"// border color of the rectangle }).transform("r90t100,0");var rect3 = paper.rect(20, 70, 60, 40, 20).attr({"stroke": "yellow"// border color of the rectangle }).transform("r90T100,0");console.log("第一個矩形坐標是:(" + rect.attr('x') + "," + rect.attr('y') + ")");console.log("第三個矩形坐標是:(" + rect2.attr('x') + "," + rect2.attr('y') + ")");console.log("第三個矩形的轉換屬性是:"+rect2.transform());console.log("第四個矩形的轉換屬性是:"+rect3.transform());</script></body></html>
效果圖如下:
代碼裏麵我們一共畫了4個矩形,2個直角矩形,2個圓角矩形。我們發下代碼裏麵創建矩形的時候,2個直角矩形的坐標是相同的,2個圓角矩形也是相同的。但是我們在創建的時候,其中一個直角和圓角矩形都執行了transform方法。直角執行了
transform("r90t100,0");
圓角執行了:
transform("r90T100,0");
但是我們發現兩個參數出了大小寫不一致其餘相同的,但是執行結果卻大相徑庭。是因為T執行了絕對平移而t是相對平移。什麼意思?我想可能很多人會比較疑惑。絕對,就是無論其它什麼變換我都不管不顧隻會去執行我後麵緊跟的參數,所以T執行的是不管你前麵轉了90度還是沒轉,我都x軸平移100px。而相對,則是我轉了90度,我的頭部(假設元素都有一個頭部)本來朝右變成了朝下,x軸平移100px,好吧我向著x平移100px,但是實際是去y軸平移了100px,因為我本來指向x軸的頭部變成了y軸方向。我在瀏覽器控製台打印了4句話,分別是2個直角矩形的坐標和2個矩形轉換後的transform的值。我們發現不論是不是發生了變換,並不改變元素的本身其它屬性。那麼轉換屬性是怎麼在dom裏麵體現的?我們通過firebug的dom查看機製來查看一下:
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Raphael Test</title></head><body><div id="my-canvas" style="width:140px;padding:0px;margin:0px;"></div><!--some html doms--><!--some scripts--><script type="text/javascript" src="../js/lib/raphael.js"></script><script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var rect = paper.rect(20, 20, 60, 40).attr({"stroke": "red"// border color of the rectangle });rect.animate({transform: "r90t100,0s1.5"}, 1000);// rect.animate({// transform:"r90T100,0s1.5"// },1000);</script></body></html>
上麵的動畫能夠很清晰的看到變換的過程。講到這裏,我們引出了Raphael的動畫方法animate。下麵我們就開始講Raphael圖形的動畫效果。
Raphael圖形的動畫效果
Raphael的圖形動畫效果可以達到非常平滑的程度,並且任何屬性都可以,不論是顏色、透明度、寬度、高度還是其它細節。如果運行了上麵那段小程序的,你就能感覺到了。簡單的方法,簡單的參數,卻收獲了很好的效果。Raphael的動畫效果方法是animate(),語法如下:
Element.animate({動畫屬性的鍵值對},動畫時間,緩動類型,回調函數);
緩動類型其實就是動畫過渡公式,是哪種類型的。主要有以下這些類型:
“linear”(線性)
“<”或“easeIn”或“ease-in” (由慢到快)
“>”或“easeOut”或“ease-out”(又快到慢)
“<>”或“easeInOut”或“ease-in-out”(由慢到快再到慢)
“backIn”或“back-in”(開始時回彈)
“backOut”或“back-out”(結束時回彈)
“elastic”(橡皮筋)
“bounce”(彈跳)
具體類型大家其實根據上麵那個代碼就可以試驗一下。動畫時間是動畫的持續時間,毫秒為單位。回調函數是動畫執行完成後調用的函數。
rect.animate({transform: "r90t100,0s1.5"}, 1000,"bounce",function(){console.log("finish");});
大家可以將上麵我那段代碼的動畫效果改成上麵這句話來看一下效果,發現矩形在變換過去之後會像小球落地一樣跳幾下才停下來。並且動畫完成後會執行回調函數,在firebug控製台打印finish。其它的幾個效果,大家複製上麵類型的內容到bounce位置替換它就可以了。
這裏需要注意的是,開始那個動畫效果的鍵值對,裏麵除了transform還是可以其它屬性,高度、寬度、填充顏色、透明度等等都可以。裏麵的值是動畫完成後的最終屬性,比如我們在鍵值對裏麵有個"width":200,其意思就是動畫執行完時矩形的寬度變成200px。這個地方不要以為隻有transform屬性才可以,其實所有屬性都可以的。不過最後還是要強調一下,盡管動畫效果看上去很好玩,但是這個方法是使用代價還是很高的,它相比其它方法要消耗更多的瀏覽器等資源。所以要有選擇地去使用這個方法,不要濫用。
小結一下,本篇到現在位置已經講了attr()方法修改元素屬性,transform()方法執行元素變化,animate()方法製造動畫效果。重要的是,如果你親自去執行了那麼動畫和變換,你會發現Raphael在這個方麵做的非常好,動畫以及變換都非常平滑,沒有任何突兀的感覺。那麼動畫和元素變換到此為止,我們接下來就要講到元素的事件綁定了,也就是業務開發中的核心部分,與用戶的交互才是任何程序最迷人同時也是最為難開發者的地方。但是這本書,坦白地說,這個地方做的比較差。它就講了一個click()和鼠標懸停mouseover()方法,而且都是一個例子就帶過了。本書其實比較短就短在這個地方了。攤開了講,這裏講個百八十頁都沒問題,但是本書就講了一頁多點。我先照著書上麵的代碼寫個我們的測試代碼貼出來:
<script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var rect = paper.rect(20, 20, 60, 40).attr({"fill": "green","stroke": "red" // border color of the rectangle });rect.click(function(){alert("hahah!");});//rect.mouseover(function(){// alert("wow");//});</script>
大家把上麵任意貼出來的代碼的Script內的內容換成這個即可,我們點擊矩形會發生alert事件,跳出提示框:"hahah!"。其實後麵那個mouseover()和mouseout()等一樣處理方式。代碼是不是看上去很簡單?哪裏有我說的那麼複雜,還得百八十頁才能講明白?我這麼說,是因為我從開始學習Raphael開始就一直在鼓搗事件問題並且還沒搞定。我們想象一下,我可以對麵前的圖形做什麼操作:點擊、雙擊、左鍵單擊、右鍵單擊、鼠標懸停、鼠標移出、鼠標按下、鼠標摁住拖動、鼠標按下的鍵抬起······你也許會說這都是日常js開發中會碰到的鼠標事件,花點時間總是能搞定的?這是實話,隻要你仔細去研究一下,這些都不是什麼大問題。但是,如果我還要用鼠標拉長圖形怎麼辦?我拖動圖形的同時,如果覆蓋或者說碰撞了別的圖形怎麼辦?我怎麼確定我的點是不是點到一個複雜圖形裏麵了?我要點擊選擇一個元素然後鍵盤修改圖形的屬性能不能做到?當一個圖形被另外一個圖形包裹,我點擊操作怎麼確定是哪個元素?這裏究竟有多少個坑,我想想都頭大。最令人頭疼的是,我剛才問的所有問題,都是我現在的工作需要解決的問題。這段話,如果你隻是用Raphael去繪製圖形,並不對它做什麼複雜操作,那麼你可以無視它了。我們還是要先把書翻譯完成。為了不打斷本書的翻譯流程,我們的元素事件的話題將會另起一篇博文來進行講解和探討。
那些你必須知道的Raphael方法
Element 方法
這些方法由元素調用。比如我們前麵的rect.click()。
animate()
animate() 方法是用來在特定時間內通過動畫效果來變換元素的屬性的方法。語法如下:
rect.animate({ "width":"300", "height":"200" },500,'bounce',function(){ alert("animation complete") });
Raphael元素.animate(元素鍵值對,動畫持續時間,緩動類型(可選參數),回調函數(可選參數))。
attr()
attr()方法是Raphael方法中最常用的方法之一,它通過Raphael元素屬性鍵值對作為參數來對元素進行添加或者修改屬性。添加和修改可以使元素的樣式,也可以是其它物理屬性,比如坐標、寬高等等。語法如下:
rect.attr({"fill":"#17A9C6", // Adds a background color "stroke":"#2A6570", // the color of the border "stroke-width":2 // the width of the border})
元素名稱 | 類型 | 簡介 |
arrow-end | string | 路徑的末尾顯示箭頭。字符串格式是<type>[-<width>[-<length>]]。可能的類型:classic、block、open、oval、diamond、none,寬:wide、narrow、midium,長:long、short、midium。 |
clip-rect | string | 剪貼矩形。逗號或空格分隔的值:x,y,寬度和高度 |
cursor | string | 光標的CSS類型 |
cx | number | 圓或橢圓的圓心的x軸坐標 |
cy | number | 圓或橢圓的圓心的y軸坐標 |
fill | string | 填充。顏色、漸變或圖像 |
fill-opacity | number | 填充不透明度 |
font | string | 文本特性 |
font-family | string | 字體 |
font-size | number | 字體大小(像素) |
font-weight | string | 字體粗細 |
height | number | 高度 |
href | string | URL。指定時,元素表現為超鏈接 |
opacity | number | 透明度 |
path | string | SVG的路徑字符串格式 |
r | number | 圓、橢圓或圓角矩形的半徑 |
rx | number | 橢圓的橫向半徑 |
ry | number | 橢圓的垂直半徑 |
src | string | 圖像的URL,隻適用於Element.image元素 |
stroke | string | 筆觸顏色 |
stroke-dasharray | string | [“”,“-”,“.”,“-.”,“-..”,“.”,“-”,“--”,“-.”,“--.”,“--..”] |
stroke-linecap | string | [“butt”,“square”,“round”] |
stroke-linejoin | string | [“bevel”,“round”,“miter”] |
stroke-miterlimit | number | |
stroke-opacity | number | |
stroke-width | number | 筆觸寬度(像素,默認為1) |
target | string | 與href一起使用 |
text | string | 文本元素的內容。使用\n換行 |
text-anchor | string | [“start”,“middle”,“end”],默認為“middle” |
title | string | 工具提示內容 |
transform | string | 請參照:Element.transform |
width | number | |
x | number | |
y | number |
click()
click()方法是用來為Raphael元素綁定單擊事件的方法,語法如下:
rect.click(function(){//點擊之後的動作alert("clicked rectangle");})
dbclick()
dbclick()方法和click語法一樣,觸發為雙擊觸發。語法如下:
cir.dblclick(function(){alert("It's a double click !");})
mousedown()
mousedown()方法為Raphael元素綁定鼠標鍵按下的動作,任何鼠標鍵按下都是觸發事件。語法如下:
rect.mousedown(function(){rect.animate({'width':'200' },400)})
mouseup()
mouseup()方法與上麵mousedown()相反,觸發條件為鼠標按下的鍵被釋放,語法也和上麵完全一致。
mousemove()
mousemove()方法為鼠標在一個Raphael元素上麵移過時觸發。
mouseover()
mousemove()方法為鼠標進入元素時觸發。這裏需要說明,mouseover()和上麵mousemove()的區別,你從外麵移動到一個Raphael元素時,兩個方法都可以觸發事件。但是如果你把事件執行完成,繼續移動鼠標(假設此時鼠標光標還在元素中),mouseover()將不再繼續執行事件,而mousemove() 會持續觸發事件。
mouseout()
mouseout()方法觸發為鼠標移出一個Raphael元素時,它隻有在移出時觸發,進入不觸發。
clone()
clone()方法是克隆一個Raphael時調用的。有個同鞋可能會問,那我直接var newrect=rect;難道不行嗎?實際上是不行的,因為newrect實際隻是一個指向老rect的"快捷方式",它並不會創建一個新的Dom。
//下麵這個是我們想要的效果的代碼:var newrect=rect.clone().attr({"x":50,"y":50});//而不是這個:var newrect=rect.attr({"x":50,"y":50});
data()
data()方法是個不可思議的方法。你可以根據自己需要為Raphael元素賦予你想賦予的含義,並且在需要時取回來:
newrect.data({"name": "heihei","age":2});newrect.click(function(){alert(newrect.data("age"));});
我們可以隨意賦予元素它要裝載的內容,在需要的時候拿回來。這個功能非常靈活,你可以隨意設計關鍵詞,當你需要為用戶展現鼠標移過展現內容時就可以用這個。當然有了這個為元素添加data的方法就肯定有移出的方法。
removeData()
與上麵的為元素添加內容相反,removeData()方法是移出已經添加了的內容:
newrect.removeData("age");
這樣,我們上那個alert就會提示是undefined。也就是removeData(你添加的內容的key)。
getBBox()
getBBox()方法獲得一個Raphael元素的邊界框(我把它成為包圍盒)。其實應該就是能包圍元素的最小矩形。getBBox()有個參數isWithoutTransform,值是true或者false。參數含義是獲得的邊界框是在執行transform也就是變換之前的元素還是變換後的。默認是false,意思是轉換後的,也就是你不設置裏麵參數為true,它獲得的包圍盒就是轉換之後的。書上說它的返回值有6個值:
{
x: number 左上角 x
y: number 左上角y
x2: number 右下角 x
y2: number 右下角 y
width: number 寬
height: number 高
}
我測試時其實返回的包圍盒對象是8個值,其實它還會帶著元素的cx和cy值返回,也就是元素的創建坐標。它的效果我們通過一段代碼來看:
<script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var cir = paper.circle(50, 50, 50).attr({"fill": "green","stroke": "red" // border color of the rectangle });var newcir = cir.clone().attr({"fill": "yellow"}).transform("t100,100");var bbox = newcir.getBBox();var bboxOld = newcir.getBBox(true);//我們通過獲得的包圍盒來繪製包裹圓的矩形paper.rect(bbox.x, bbox.y, bbox.width, bbox.height).attr({"stroke": "yellow"});paper.rect(bboxOld.x, bboxOld.y, bboxOld.width, bboxOld.height).attr({"stroke": "purple"});</script>
由於其它html部分在前麵的博文裏麵已經多次貼出,這裏就不再重複了,隻是把js部分貼出來,其它都是一樣的。這段程序執行結果如下:
getPointAtLength()
getPointAtLength()方法在給定的path對象和指定的長度,返回那個位置點的坐標。語法如下:
path.getPointAtLength(length)
返回值:
X number 點的x坐標
Y number 點的y坐標
Alpha number 導數(切線)的角度
這裏需要注意,原書這裏是錯誤的語法,其實應path元素來調用這個方法,參數是一個長度,而原書成了path元素和長度2個對象作為參數。我已經試驗過代碼:
<script type="text/javascript">var paper = Raphael("my-canvas", 700, 600);var path = paper.path("M240,40L300,40L300,100");var pathQ = paper.path("M240,40Q300,40 300,100").attr('stroke', 'red');var pointObj = path.getPointAtLength(60);console.log("x:" + pointObj.x + ",y:" + pointObj.y + ",Alpha:" + pointObj.alpha);var pointObjQ = pathQ.getPointAtLength(60);console.log("x:" + pointObjQ.x + ",y:" + pointObjQ.y + ",Alpha:" + pointObjQ.alpha);</script>
toFront() 、toBack() 、hide() 、show() 、remove()
這裏我們有5個方法一起講解。這5個方法都沒有參數,也就是 元素.方法(),就行了。從字麵意思我們就能讀懂,toFront()到前麵來,toBack()與toFront()相反,到後麵去;hide()和show()相反,分別是隱藏和顯示;remove()刪除。大家如果了解Css的話,前麵4個應該很好理解。toFront()就相當於我把一個dom的z-index修改的很大,大到超過其它所有元素,所以它離用戶眼睛最近而覆蓋其它元素,toback與它相反。而hide()和show()就更不必說了,把一個元素隱藏和顯示出來。就好像我們操作Css時會用到display:none;display:block;一樣。remove(),刪除元素,調用這個方法的元素將會從畫布上移除。我們調用時候就知道hide()時dom是存在的,隻是你看不到而已;而remove()是直接把dom節點刪除掉了,也就是真正意義上的不存在了。
transform()
上一篇博文,transform()方法已經講解的很詳細,我們這裏就不再重複。這裏隻是再說一點,element.transform("some transfrom string")的作用其實用element.attr({"transform":"some transform string"})也可以達到。上一篇裏麵我們強調過,transform其實是Raphael元素的一個屬性,attr既然可以修改和添加屬性,那當然可以這樣子做。
到此為止,我們介紹了比較重要的Element方法。也就是供元素來調用的方法,下麵我們要講解的時候paper,也就是畫布可以調用的方法。
畫布的方法
畫布的方法隻能由畫布本身來調用,我們前麵的聲明的畫布是var paper=Raphael("my-canvas", 650, 400);也就是下麵講到的方法需要paper去調用。其實前麵我們已經碰到了很多個方法是由paper去調用的。還記得吧,我們去創建每個圖形都是paper來進行的。paper.circle()、paper.rect()、paper.ellipse()、paper.path()。這些我們前麵都舉了例子來描述,這裏就不再描述前麵已經出現的方法。我們繼續講解其它前麵沒講到的畫布方法。
paper.clear()
paper.clear()方法清空畫布,還記得上麵元素方法裏麵有個remove()方法吧。那個是單個去除一個元素,這裏是畫布來調用清除所有元素的方法。
paper.image()
這裏說明一下,RaphaelJS確實是個非常優秀的前台圖形繪製工具,但是不要以為它可以替代圖片。其它普通圖片和Raphael是互補的,而不是可以替代的關係。所以Raphael提供了引用圖片的方法,就是paper.image()。它有5個參數:
參數 | 說明 |
src | 圖片的路徑,對經常寫前台的童鞋們來說這個小菜一碟 |
X | 圖片擺放位置的x坐標 |
Y | 圖片擺放位置的y坐標 |
width | 圖片的寬度 |
height | 圖片的高度 |
例:paper.image("images/testimage.png",10,10,200,150);將在畫布的(10,10)位置擺放一個寬200,長150的圖片。
paper.setSize()
paper.setSize()用來重新設置畫布的大小。你以在發現畫布大小不合適時調整畫布的大小而不是需要從頭建立畫布然後重複原來的工作。方法有2個參數:寬和高
paper.setSize(600,800);
我們將畫布的大小修改為寬600px,高800px。
paper.set()
paper.set()方法是個很重要的方法。它幫助我們對Raphael元素進行分組然後進行批量管理。也就是我們放進一個set裏麵的Raphael元素如果用set來執行一些動作,那麼這些操作是所有在set裏麵的元素起作用的。但是set本身並不創建和複製、克隆Raphael元素,也就是你刪除一個set,不會刪除set裏麵的Raphael元素,但是你可以用set來幫助管理set內的元素。我們貼個代碼看看:
<script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var rect = paper.rect(20, 20, 60, 40).attr({"stroke": "red", // border color of the rectangle });var rect1 = paper.rect(20, 70, 60, 40, 20).attr({"stroke": "yellow", // border color of the rectangle });var cir = paper.circle(150, 100, 30);var raphaelSet = paper.set();raphaelSet.push(rect, rect1, cir);raphaelSet.attr({"fill": "red"});</script>
下麵是執行效果:
set的方法
接下來講解的方法是隻有set對象才能調用的方法,前麵我們已經提到了set對象的聲明:
var raphaelSet = paper.set();
set.clear()
我們想要清空set,可不要用remove()而是用clear(),remove()會把所有set裏麵的元素remove掉。clear()隻是清除set裏的內容,並不會對裏麵的內容本身有影響。那就會有同鞋問,我不想清空set,隻想刪除其中一個可以嗎?當然是可以的。那就是下個方法了。
set.exclude()
set.exclude(rect);還記得我前麵的代碼裏麵將rect添加進入raphaelSet裏麵嗎?現在你可以試一試在在執行raphaelSet.attr()之前使用raphaelSet.exclude(rect);試一試。效果和我們想象的一樣,第一個矩形沒有被填充紅色,因為它被從set裏麵剔除出來了。
set.forEach()
看到forEach關鍵詞,我們立即就能理解到這個方式是幹什麼用的。那就是日常開發中最常碰到的循環。set.forEach()就是去循環我們創建的set對象,然後通過遍曆對set內的元素進行操作。這個功能是差不多算是set裏麵最重要的方法了,我們使用set大部分業務都需要這個循環了。
raphaelSet.forEach(function(ele){ele.attr({"fill","red"});});
這句代碼的效果大家可以試一試,可以替換我們剛才那個raphaelSet.attr({"fill","red"});
set.pop()
彈出set中的最後一個元素,就是清除最後一個添加進去的元素。
raphaelSet.push(rect, rect1, cir);raphaelSet.pop();raphaelSet.attr({"fill": "red"});
我們上麵貼的那個代碼,修改成為這段,就會發現圓形並沒有被填充紅色,因為在執行填充之前,它已經被從set裏麵pop了出來。
set.splice()
set.splice()方法和普通的js或者java的數據的slice有點不同。它有3個參數index,count,element。什麼意思呢?set.splice(1,2,rect),我從set裏麵index為1的位置開始往後刪除2個元素,然後把rect添加進來。所以slice可以同時刪除和添加元素。注釋它是有返回值的,它將返回被刪除掉的元素。我們用代碼來看下:
<script type="text/javascript">var paper = Raphael("my-canvas", 650, 400);var rect = paper.rect(20, 20, 60, 40).attr({"stroke": "yellow", // border color of the rectangle });var rect1 = paper.rect(20, 90, 60, 40, 20).attr({"stroke": "yellow", // border color of the rectangle });var rect2 = paper.rect(90, 20, 60, 40, 5).attr({"stroke": "yellow", // border color of the rectangle });var rect3 = paper.rect(90, 90, 60, 40, 5).attr({"stroke": "yellow", // border color of the rectangle });var cir = paper.circle(200, 100, 30);var cir1 = paper.circle(250, 100, 20);var cir2 = paper.circle(280, 100, 10);var raphaelSet = paper.set();raphaelSet.push(rect, rect1, rect2, rect3);raphaelSet.splice(2, 1, cir, cir1, cir2);raphaelSet.forEach(function(ele){ele.attr({"fill": "red"});console.log(ele[0]);})</script>
執行效果如下:
為了方便查看我們元素的數序,我在控製台了打印了一下set裏麵的內容,我們執行了聲明set、push元素、從第二個index開始刪除一個rect然後插入3個circle、調用forEach()循環這個set然後執行填充紅色指令。
本篇博文差不多到此介紹了raphaelJS中最常用的方法。當然還有其它的一些方法沒講到。我們所翻譯的書到此主題內容已經翻譯完成了。後麵的內容其實大家可以略過。接下來的內容主要是介紹一些官方或者教程網站之類的東西。關心的童鞋可以去看一下那些網站。下篇博文的更新內容應該是翻譯另外一本稍微複雜點的書,那本書的後麵好像有一些有意思的例子,我們可以嚐試一下。