我們已經(jīng)在前幾個系列涵蓋了很多話題烤礁,從基礎(chǔ)對象操作到動畫讼积,事件,濾鏡脚仔,組合和子類勤众。但還有幾件非常有趣和有用的事情要討論!
自由繪畫
如果說有什么功能能讓<canvas>
在眼前一亮鲤脏,那一定是它能夠很好的支持自由繪圖们颜!由于畫布只是一個2D位圖, Fabric為我們提供了一張紙:可以自由繪畫猎醇,而且非常自然窥突。
只需將Fabric canvas的isDrawingMode
屬性設(shè)置為true
即可實(shí)現(xiàn)自由繪制模式。這樣畫布上的點(diǎn)擊和移動就會被立刻解釋為鉛筆或刷子硫嘶。
當(dāng)isDrawingMode
為true
時波岛,您可以隨意在畫布上多次繪制,但是一旦你在畫布上做任何動作音半,然后觸發(fā)“mouseup”事件则拷,F(xiàn)abric就會觸發(fā)"path:created" 事件,并且將剛剛的繪畫轉(zhuǎn)變?yōu)?code>fabric.Path的實(shí)例曹鸠,
如果在任何時刻,將isDrawingMode
設(shè)置為false
彻桃,你創(chuàng)建的路徑對象會仍然存在于畫布上坛善。而且由于它們是fabric.Path
對象,所以可以以任何方式修改它們: 移動邻眷,旋轉(zhuǎn)眠屎,縮放等。
還有兩個屬性可以定制自由繪圖: freeDrawingBrush.color
and freeDrawingBrush.width
肆饶,兩者都可以通過freeDrawingBrush
實(shí)例在Fabric畫布實(shí)例上使用改衩。freeDrawingBrush.color
可以是任何常規(guī)的顏色值,代表畫筆的顏色驯镊。freeDrawingBrush.width
是一個像素葫督,代表畫筆的寬度。
在不久的將來板惑,我們計(jì)劃添加更多的自由繪圖選項(xiàng):各種版本的畫筆(如噴霧式或粉筆式)橄镜。還可以自定義畫筆模式,還有一個可以自己擴(kuò)展的選項(xiàng)冯乘,類似于Fabric圖像濾鏡洽胶。
自定義
一件有趣的事情是如何去自定義Fabric,您可以在canvas
或canvas
對象上調(diào)整數(shù)十種各樣的參數(shù)裆馒,以使事情按照所需的方式運(yùn)行姊氓。我們來看看其中的一些丐怯。
鎖定對象
畫布上的每個對象都可以以幾種方式鎖定∷牛“l(fā)ockMovementX”响逢,“l(fā)ockMovementY”绒窑,“l(fā)ockRotation”棕孙,“l(fā)ockScalingX”,“l(fā)ockScalingY”是鎖定相應(yīng)對象動作的屬性些膨。所以將某個對象的lockMovementX
屬性設(shè)置為true
會阻止對象被水平移動蟀俊,此時你仍然可以垂直移動它。類似的订雾,lockRotation
可以阻止旋轉(zhuǎn)肢预,lockScalingX
/ lockScalingY
可以阻止水平或者垂直縮放。所有這些都是可以疊加的洼哎,你可以將它們組合在一起使用烫映。
改變邊框和角
您可以通過“hasControls”和“hasBorders”屬性來控制對象的邊框和角的可見性。只是將它們設(shè)置為false
噩峦。
object.hasBorders = false;
object.hasControls = false;
您還可以通過某些自定義屬性來調(diào)整它的外觀:“cornerDashArray”锭沟,“borderDashArray”,“borderColor”识补,“transparentCorners”“cornerColor”族淮,“cornerStrokeColor”,“cornerStyle”凭涂,“selectionBackgroundColor”祝辣,“padding”和“cornerSize”。
object.set({
borderColor: 'red',
cornerColor: 'green',
cornerSize: 6
});
object.set({
transparentCorners: false,
cornerColor: 'blue',
cornerStrokeColor: 'red',
borderColor: 'red',
cornerSize: 12,
padding: 10,
cornerStyle: 'circle',
borderDashArray: [3, 3]
});
禁用選擇
您可以通過將canvas的“selection”屬性設(shè)置為false
來禁用畫布上的對象選擇切油,這可以防止在畫布上選中任何對象蝙斜。如果只需要使某些對象不可選,則可以更改某個對象的“selectable”屬性澎胡,只是將其設(shè)置為false
乍炉,并且對象失去其交互性。
自定義選擇
現(xiàn)在滤馍,如果你不想禁用選擇岛琼,而是要改變它的外觀呢?沒問題巢株。
canvas畫布上有4個屬性來控制它的表現(xiàn): "selectionColor", "selectionBorderColor", "selectionLikeWidth", and "selectionDashArray"槐瑞。這些都非常的語義化,讓我們來看一個例子:
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.selectionColor = 'rgba(0,255,0,0.3)';
canvas.selectionBorderColor = 'red';
canvas.selectionLineWidth = 5;
最后一個屬性“selectionDashArray”可不是那么簡單阁苞,他允許我們使用虛線作為選擇線困檩。定義虛線模式的方式是通過數(shù)組指定間隔祠挫。因此,要創(chuàng)建一個模式悼沿,其中有一個大點(diǎn)的數(shù)字等舔,后面跟著一個小一點(diǎn)的數(shù)字來代表虛線的間隔,我們可以使用[10糟趾,5]
慌植,作為“selectionDashArray”。這將畫出10px長的線义郑,然后跳過5px蝶柿,再畫一個10px長度的線,以此類推非驮。如果我們使用[2, 4, 6]
的數(shù)組交汤,這樣將通過繪制2px長的線,然后跳過4px劫笙,然后繪制6px線芙扎,然后跳過2px,然后繪制4px線填大,然后跳過6px戒洼,以此類推。例如栋盹,這就是[5,10]
規(guī)則的樣子:
虛線描邊
與canvas上的“selectionDashArray”類似施逾,所有Fabric對象都有“strokeDashArray”屬性,負(fù)責(zé)在對象上執(zhí)行虛線的描邊操作例获。
var rect = new fabric.Rect({
fill: '#06538e',
width: 125,
height: 125,
stroke: 'red',
strokeDashArray: [5, 5]
});
canvas.add(rect);
可點(diǎn)擊區(qū)域
如你所知汉额,所有的Fabric對象都有邊界邊框,用于拖動榨汤、旋轉(zhuǎn)蠕搜、縮放。您可能已經(jīng)注意到收壕,當(dāng)用于控制的角和邊框存在時妓灌,即使在單擊沒有繪制內(nèi)容的對象邊框內(nèi)的空白時也可以拖動該對象。
看看這個圖像:
默認(rèn)情況下蜜宪,畫布上的所有Fabric對象都可以通過邊框拖動虫埂。如果你想要不同的行為,例如根據(jù)Fabric對象的實(shí)際內(nèi)容進(jìn)行點(diǎn)擊或拖拽圃验,您可以在對象上使用“perPixelTargetFind”屬性掉伏。只需將其設(shè)置為true
即可獲得所需的行為。
旋轉(zhuǎn)控件
由于版本1.0 Fabric在默認(rèn)情況下使用默認(rèn)UI,對象不能同時縮放和旋轉(zhuǎn)斧散。作為代替的是每個對象都有一個單獨(dú)的旋轉(zhuǎn)控件供常。該控件的相應(yīng)屬性為“hasRotatingPoint”,默認(rèn)是true
鸡捐,將其設(shè)置為false
可以禁用旋轉(zhuǎn)控制栈暇。您也可以通過“rotatePointOffset”數(shù)字屬性自定義其相對于對象的偏移量。
Fabric對象變形
自1.0版以來箍镜,F(xiàn)abric中還提供了許多其他與轉(zhuǎn)換相關(guān)的屬性源祈,其中一個是畫布實(shí)例上的“uniScaleTransform”。默認(rèn)為false
鹿寨,可用于啟用對象的非均勻縮放;換句話說新博,它允許在通過角拖動時改變對象的比例薪夕。
有“centeredScaling”和“centeredRotation”屬性(在v1.3.4之前是一個屬性“centerTransform”)脚草。他們指定對象的中心是否應(yīng)該用作轉(zhuǎn)換的起點(diǎn)。當(dāng)它們都設(shè)置為true
原献,對象總是從中心縮放/旋轉(zhuǎn)時馏慨,它會和1.0之前的行為相同。由于1.0變形原點(diǎn)是動態(tài)的姑隅,這樣可以在縮放對象時進(jìn)行更精細(xì)的控制写隶。
最后一對新屬性是“originX”和“originY”。默認(rèn)設(shè)置為“l(fā)eft”和“top”讲仰,它們允許以編程的方式更改變形的原點(diǎn)慕趴。當(dāng)你拖動對象的角時,就是這些屬性正在動態(tài)變化鄙陡。
那么什么時候需要我們手動更改它們冕房?舉個例子,當(dāng)我們處理文本對象時趁矾,當(dāng)您動態(tài)的改變文本并且文本框尺寸變大耙册,“originX”和“originY”指出了增長的位置。所以如果你需要文本對象居中時毫捣,你可以將originX設(shè)置為“center”详拙。如果讓文本移到右邊,你需要將“originX”設(shè)置為“right”等等蔓同。這種行為類似于CSS中的“position:absolute”饶辙。
畫布的背景和疊加
從第一部分可以記得,您可以指定一個顏色來填充整個畫布背景斑粱。只需將任意常規(guī)顏色值設(shè)置為canvas的“backgroundColor”屬性弃揽。
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.backgroundColor = 'rgba(0,0,255,0.3)';
canvas.renderAll();
你可以進(jìn)一步去分配圖像作為背景。您需要使用setBackgroundImage
方法,傳遞url和一個可選的完成回調(diào)蹋宦。
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.setBackgroundImage('../assets/pug.jpg', canvas.renderAll.bind(canvas));
最后披粟,您還可以設(shè)置疊加圖像,在這種情況下冷冗,它將始終顯示在畫布上呈現(xiàn)的任何對象之上守屉。只需使用setOverlayImage,傳遞url和一個可選的完成回調(diào)蒿辙。
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.setOverlayImage('../assets/jail_cell_bars.png', canvas.renderAll.bind(canvas));
Node.js上的Fabric
Fabric的獨(dú)特之處在于它不僅可以在客戶端拇泛,瀏覽器中也可以在服務(wù)器上工作!當(dāng)您要從客戶端發(fā)送數(shù)據(jù)并在服務(wù)器上直接創(chuàng)建該數(shù)據(jù)的映像時思灌,這可能很有用俺叭。或者如果您為了速度泰偿,方便性或其他原因熄守,只是想從控制臺使用Fabric API。
讓我們來看看如何設(shè)置在Node環(huán)境下運(yùn)行Fabric耗跛。
首先裕照,您需要安裝Node.js,如果還沒有调塌。有幾種方法來安裝Node晋南,具體取決于你的操作系統(tǒng)。您可以按照這些說明或這些說明羔砾。
Node安裝成功之后负间,我們需要安裝node-canvas庫,node-canvas是NodeJS的Canvas實(shí)現(xiàn)姜凄。它依賴Cairo- 可以在Mac政溃,Linux或Windows上運(yùn)行的2D圖形庫。node-canvas具有專門的安裝說明檀葛,具體取決于您的操作系統(tǒng)玩祟。
由于Fabric運(yùn)行在Node上,它作為一個NPM包屿聋。所以下一步是安裝NPM空扎。您可以在其github repo中找到安裝說明,不過一般情況下當(dāng)您安裝Node的時候會自動安裝了NPM润讥。
最后一步是安裝Fabric包转锈,使用NPM。這通過運(yùn)行npm install fabric
(或npm install -g fabric
來全局安裝包)完成楚殿。
我們現(xiàn)在運(yùn)行Node控制臺撮慨,應(yīng)該就使用node-canvas和Fabric了:
> node
...
> typeof require('canvas'); // "function"
> typeof require('fabric'); // "object"
現(xiàn)在一切都準(zhǔn)備好了,我們可以嘗試一個簡單的“helloworld”的測試。讓我們創(chuàng)建一個helloworld.js文件:
var fs = require('fs'),
fabric = require('fabric').fabric,
out = fs.createWriteStream(__dirname + '/helloworld.png');
var canvas = fabric.createCanvasForNode(200, 200);
var text = new fabric.Text('Hello world', {
left: 100,
top: 100,
fill: '#f55',
angle: 15
});
canvas.add(text);
var stream = canvas.createPNGStream();
stream.on('data', function(chunk) {
out.write(chunk);
});
然后運(yùn)行它作為node helloworld.js
砌溺,打開helloworld.png:
那么這里發(fā)生了什么影涉?我們來看看這段代碼的重要部分。
首先规伐,我們引入了Fabricfabric = require('fabric').fabric
蟹倾,然后我們用fabric.createCanvasForNode()
替代了平時使用的new fabric.Canvas()
來創(chuàng)建了Fabric的canvas畫布,此方法將width和height作為參數(shù)猖闪,并創(chuàng)建該大小的畫布(在這種不傳參的情況下為200x200)鲜棠。
然后有熟悉的對象創(chuàng)建(new fabric.Text()
)和添加到畫布(canvas.add(text)
)。
到目前做的一切都只是創(chuàng)建了一個Fabric畫布培慌,然后渲染了一個text文本豁陆,現(xiàn)在,如何創(chuàng)建在畫布上呈現(xiàn)的任何圖像吵护?在canvas實(shí)例上使用可用的createPNGStream
方法盒音。createPNGStream
返回Node的流,然后可以使用on('data')
輸出到圖像文件中何址,并將其寫入與圖像文件(fs.createWriteStream()
)對應(yīng)的流中里逆。
fabric.createCanvasForNode
和canvas.createPNGStream
幾乎是Node特有的唯一2種方法进胯,其他一切都是一樣的用爪。您仍然可以按照通常的方式創(chuàng)建對象,將它們添加到畫布上胁镐,修改偎血,渲染等。值得一提的是盯漂,當(dāng)您通過createCanvasForNode
創(chuàng)建畫布時颇玷,它將使用nodeCanvas
屬性進(jìn)行擴(kuò)展,該屬性是對原始節(jié)點(diǎn)畫布實(shí)例的引用就缆。
Node服務(wù)器與Fabric
例如帖渠,讓我們創(chuàng)建一個簡單的Node服務(wù)器,它將使用JSON格式的Fabric數(shù)據(jù)收聽傳入的請求竭宰,并輸出該數(shù)據(jù)的圖像空郊。整個腳本只有25行長!
var fabric = require('fabric').fabric,
http = require('http'),
url = require('url'),
PORT = 8124;
var server = http.createServer(function (request, response) {
var params = url.parse(request.url, true);
var canvas = fabric.createCanvasForNode(200, 200);
response.writeHead(200, { 'Content-Type': 'image/png' });
canvas.loadFromJSON(params.query.data, function() {
canvas.renderAll();
var stream = canvas.createPNGStream();
stream.on('data', function(chunk) {
response.write(chunk);
});
stream.on('end', function() {
response.end();
});
});
});
server.listen(PORT);
此片段中的大部分代碼應(yīng)該已經(jīng)很熟悉了切揭。它的要點(diǎn)在于服務(wù)器響應(yīng)狞甚。我們正在創(chuàng)建Fabric畫布,將JSON數(shù)據(jù)加載到其中廓旬,渲染它哼审,并將最終結(jié)果作為服務(wù)器響應(yīng)進(jìn)行流式傳輸。
要測試它,我們來獲取一個綠色涩盾,并且稍微旋轉(zhuǎn)的矩形:
{"objects":[{"type":"rect","left":103.85,"top":98.85,"width":50,"height":50,"fill":"#9ae759","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1.39,"scaleY":1.39,"angle":30,"flipX":false,"flipY":false,"opacity":0.8,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}
URL編碼后:
%7B"objects"%3A%5B%7B"type"%3A"rect"%2C"left"%3A103.85%2C"top"%3A98.85%2C"width"%3A50%2C"height"%3A50%2C"fill"%3A"%239ae759"%2C"overlayFill"%3Anull%2C"stroke"%3Anull%2C"strokeWidth"%3A1%2C"strokeDashArray"%3Anull%2C"scaleX"%3A1.39%2C"scaleY"%3A1.39%2C"angle"%3A30%2C"flipX"%3Afalse%2C"flipY"%3Afalse%2C"opacity"%3A0.8%2C"selectable"%3Atrue%2C"hasControls"%3Atrue%2C"hasBorders"%3Atrue%2C"hasRotatingPoint"%3Afalse%2C"transparentCorners"%3Atrue%2C"perPixelTargetFind"%3Afalse%2C"rx"%3A0%2C"ry"%3A0%7D%5D%2C"background"%3A"rgba(0%2C%200%2C%200%2C%200)"%7D
并通過“data”查詢參數(shù)傳遞給服務(wù)器十气。立即回應(yīng)“image / png”的Content-type,如下所示:
正如你所看到的春霍,在服務(wù)器上使用Fabric是非常簡單直接的桦踊。隨意試驗(yàn)這個片段≈粘可以從URL參數(shù)中更改畫布尺寸籍胯,或者在返回圖像作為響應(yīng)之前修改客戶端數(shù)據(jù)。
在Node環(huán)境自定義Fabric的字體
在我們可以在Fabric中使用自定義字體之前离福,我們需要先加載它們杖狼。在瀏覽器(客戶端)中,加載字體的最常用方法是使用CSS3 @font-face規(guī)則妖爷。 在Node(服務(wù)器端)的Fabric中蝶涩,我們可以使用node-canvas的Font API加載字體。
下面的示例演示如何加載和使用自定義字體絮识。將其保存到customfont.js并確保字體文件的路徑正確绿聘。在這個例子中,我們使用Ubuntu作為自定義字體次舌。
var fs = require('fs'),
fabric = require('fabric').fabric,
canvas = fabric.createCanvasForNode(300, 250);
var font = new canvas.Font('Ubuntu', __dirname + '/fonts/Ubuntu-Regular.ttf');
font.addFace(__dirname + '/fonts/Ubuntu-Bold.ttf', 'bold');
font.addFace(__dirname + '/fonts/Ubuntu-Italic.ttf', 'normal', 'italic');
font.addFace(__dirname + '/fonts/Ubuntu-BoldItalic.ttf', 'bold', 'italic');
canvas.contextContainer.addFont(font); // 使用 createPNGStream 或者 createJPEGStream 的時候
canvas.contextTop.addFont(font); // 使用 toDataURL 或者 toDataURLWithMultiplier的時候
var text = new fabric.Text('regular', {
left: 150,
top: 50,
fontFamily: 'Ubuntu'
});
canvas.add(text);
text = new fabric.Text('bold', {
left: 150,
top: 100,
fontFamily: 'Ubuntu',
fontWeight: 'bold'
});
canvas.add(text);
text = new fabric.Text('italic', {
left: 150,
top: 150,
fontFamily: 'Ubuntu',
fontStyle: 'italic'
});
canvas.add(text);
text = new fabric.Text('bold italic', {
left: 150,
top: 200,
fontFamily: 'Ubuntu',
fontWeight: 'bold',
fontStyle: 'italic'
});
canvas.add(text);
var out = fs.createWriteStream(__dirname + '/customfont.png');
var stream = canvas.createPNGStream();
stream.on('data', function(chunk) {
out.write(chunk);
});
運(yùn)行node customfont.js
創(chuàng)建一個圖像(customfont.png)熄攘,如下所示:
讓我們仔細(xì)看看發(fā)生了什么。首先彼念,通過將字體名稱和路徑(作為常規(guī)字體文件)作為參數(shù)傳遞挪圾,創(chuàng)建一個字體對象new canvas.Font()
。然后通過將字體path逐沙,weight和style作為參數(shù)傳遞哲思,使用font.addFace()
添加其他字體。最后吩案,使用canvas.contextContainer.addFont()
(當(dāng)使用createPNGStream或createJPEGStream時)或canvas.contextTop.addFont()
(使用toDataURL或toDataURLWithMultiplier時)可以將字體添加到所需的上下文中棚赔。
現(xiàn)在我們可以通過將fabric.Text
對象的fontFamily屬性設(shè)置為字體名稱來使用我們的字體。結(jié)合fontWeight和fontStyle屬性徘郭,我們可以應(yīng)用我們添加的字體靠益。有關(guān)這些屬性的更多信息,請參閱第2部分崎岂。請注意捆毫,該示例顯示了如何在創(chuàng)建新文本對象時使用自定義字體,但這也適用于通過JSON加載的文本對象冲甘。
這就到了Fabric的4部分系列的結(jié)尾绩卤。我希望你現(xiàn)在擁有足夠的知識途样,創(chuàng)造有趣,酷濒憋,有用何暇,有趣,具有挑戰(zhàn)性凛驮,令人興奮的東西裆站!
下一章:Fabric.js介紹 第五部分