javascript
html控制图的宽,用JointJS做一个简单的功能控制图
繼上一篇介紹了GoJS之后,繼續(xù)研究JS的繪圖工具,畢竟GoJS有些小貴。這次選擇了JointJS,完全開源,它還有一個商業(yè)版本叫Raddit,功能更強大。不過就我的需求場景,開源的Joint就足夠了。接下來,我們看看它是怎么使用的。
JointJS是基于Backbone開發(fā)的,所以使用Joint之前,要先引入Backbone的相關依賴,所以我們的HTML文件是這樣的:
JointJS Sample這里我都用CDN,也可以將相應的庫載到本地,但需要注意的是JS的加載順序不能改。接下來,我們寫一個官方的HelloWorld:
window.οnlοad=function() {
var graph = new joint.dia.Graph; // 創(chuàng)建畫板,所有圖上的元素都在畫板里
var paper = new joint.dia.Paper({ // 創(chuàng)建畫板上的畫布,畫布是用來渲染畫板
el: document.getElementById('myGraph'), // 指向HTML里ID為"myGraph"的元素
model: graph, // 指定畫板
width: 600, // 畫布寬600像素
height: 100, // 畫布高100像素
gridSize: 1, // 畫布上元素拖動時步進的像素,默認1,設的高方便對齊
background: { // 畫布背景色
color: 'rgba(0, 0, 0, 0.1)'
},
});
// 創(chuàng)建一個矩形
var rect = new joint.shapes.standard.Rectangle();
rect.position(100, 30); // 矩形左上角的位置,x:100,y:30,單位像素
rect.resize(100, 40); // 矩形大小,寬100,高40,單位像素
rect.attr({
body: {
fill: 'blue' // 填充色
},
label: {
text: 'Hello', // 矩形上顯示的文字
fill: 'white' // 文字顏色
}
});
rect.addTo(graph); // 將上面定義的矩形加入到畫板中
var rect2 = rect.clone(); // 復制一個相同的矩形
rect2.translate(300, 0); // 將矩形在水平方向上向右移動300像素
rect2.attr('label/text', 'World!'); // 設置矩形2上的文字
rect2.addTo(graph); // 將矩形2加入到畫板中
// 創(chuàng)建一條連線
var link = new joint.shapes.standard.Link();
link.source(rect); // 連線頭為矩形1
link.target(rect2); // 連線尾為矩形2
link.addTo(graph); // 將上面定義的連線加入到畫板中
}
從上例里,我們可以看出,使用JointJS開發(fā)主要的步驟就是創(chuàng)建畫板、創(chuàng)建畫布、創(chuàng)建圖形,然后將圖形置于畫板中。上面的例子執(zhí)行后會得到下面的圖像。
HelloWorld!
JointJS中的圖形主要有兩類,一是”元素”(Element),由構造函數(shù)創(chuàng)建。上例中的矩形就是JointJS庫提供的標準元素,其構造函數(shù)為joint.shapes.standard.Rectangle。元素創(chuàng)建后,可以設置各種參數(shù),比如位置,大小,風格等。JointJS提供了豐富的內置元素如矩形,圓形,橢圓等,可以參考API文檔。同時,我們可以通過擴展joint.dia.Element類來自定義元素。
另一類圖形是”連接”(Link),用來將兩個”元素”連起來,一般顯示為一條連線。上例中的連線是通過JointJS庫提供的標準構造函數(shù)joint.shapes.standard.Link來創(chuàng)建的,創(chuàng)建后設置其”頭”(Source)和”尾”(Target),即可將兩個”元素”連起來。我們也可以設置”連接”的各種參數(shù),如顏色,箭頭,標簽文字等。
HelloWorld之后我們來做一個簡單的自動化功能控制圖吧,HTML部分不變,我們來寫JS部分。
第一步定義畫板和畫布
var graph = new joint.dia.Graph; // 創(chuàng)建畫板,所有圖上的元素都在畫板里
var paper = new joint.dia.Paper({ // 創(chuàng)建畫板上的畫布,畫布是用來渲染畫板
el: $('#myGraph'), // 指向HTML里ID為"myGraph"的元素
model: graph, // 指定畫板
width: 600, // 畫布寬600像素
height: 300, // 畫布高300像素
gridSize: 5, // 畫布上元素拖動時步進的為5像素,默認1
drawGrid: true, // 顯示步進點,方便對齊
background: { // 畫布背景色
color: 'rgba(0, 0, 0, 0.1)'
},
// 連接線風格
defaultLink: new joint.shapes.logic.Wire({
connector: { name: 'jumpover' }, // 當兩根連線交叉時,其中一根跳過
}),
linkPinning: false, // 連線必須連到某個元素,即不允許連到空白處
snapLinks: {
radius: 25 // 距離元素連接點25像素時自動連接上
},
// 驗證連線是否允許,
validateConnection: function(viewSource, magnetSource, viewTarget, magnetTarget, end, viewLink) {
if (end === 'target') {
// 連線目標必須時一個"in"類型連接點
if (!magnetTarget || !magnetTarget.getAttribute('port-group') || magnetTarget.getAttribute('port-group').indexOf('in') < 0) {
return false;
}
// 檢查連接點是否已經有連線接入,不允許多重接入
var portUsed = this.model.getLinks().some(function(link) {
return (link.id !== viewLink.model.id &&
link.get('target').id === viewTarget.model.id &&
link.get('target').port === magnetTarget.getAttribute('port'));
});
return !portUsed;
} else { // end === 'source'
// 連線起始點必須時一個"out"類型連接點
return magnetSource && magnetSource.getAttribute('port-group') && magnetSource.getAttribute('port-group').indexOf('out') >= 0;
}
},
});
上面的屬性有些多,但大部分都好理解,主要是驗證函數(shù)validateConnection(),其返回true或false,用來決定連線是否被允許。傳入的參數(shù)viewSource和viewTarget分別是畫線過程中鼠標按鈕釋放時,當前連線頭和尾的元素;而參數(shù)magnetSource和magnetTarget分別是當前連線頭和尾的”連接點”(port)。”連接點”的概念下面會講到。
第二步定義基本圖例元素
這里,我們創(chuàng)建了與、或、非,三個基本元素,因為是圖例元素,所以不允許連入連出。我們使用標準庫提供的”joint.shapes.devs.Model”元素,因為它很方便設置”連接點”(port)。
// 創(chuàng)建基礎元件模板
var gateTemplate = new joint.shapes.devs.Model({
position: { // 默認位置
x: 0,
y: 0
},
size: { // 默認大小
width: 50,
height: 60
},
// "連接點"(port)的風格
portMarkup: '',
// "連接點"(port)標簽文字的顯示風格
portLabelMarkup: ' ',
ports: { // 定義連接點
groups: {
'in': { // "入"連接點的屬性和風格
attrs: {
'.port-body': { // 這是JointJS類庫預定義的連接點屬性類
magnet: 'passive', // 該連接點只入不出
},
'.joint-port-body': { // 這是JointJS類庫預定義的連接點風格類
x:-10 // "入"連接點左移10個像素,這樣可以顯示在元素外部
}
},
label: {
position: {
args: {x:18}, // 標簽右移,這樣可以顯示在元素內部
},
},
},
'out': {
label: { // "出"連接點的屬性和風格
position: {
args: {x:-23}, // 標簽左移,這樣可以顯示在元素內部
},
},
}
}
},
attrs: {
'.label': {
'type': 'primary', // 自定義一個圖例屬性,后面事件操作時判斷用
fontSize: 12, // 標簽字體
'ref-x': .5, // 標簽相對于元素的水平位置
'ref-y': .05 // 標簽相對于元素的垂直位置
},
}
});
// 生成"與"元素,兩個"入"連接點,一個"出"連接點,顯示"And"字樣標簽
function genAndPr() {
return gateTemplate.clone().set('inPorts', ['IN1', 'IN2']).set('outPorts', ['OUT']).attr('.label/text', 'And');
}
// 生成"或"元素,兩個"入"連接點,一個"出"連接點,顯示"Or"字樣標簽
function genOrPr() {
return gateTemplate.clone().set('inPorts', ['IN1', 'IN2']).set('outPorts', ['OUT']).attr('.label/text', 'Or');
}
// 生成"非"元素,一個"入"連接點,一個"出"連接點,顯示"Not"字樣標簽
function genNotPr() {
return gateTemplate.clone().set('inPorts', ['IN ']).set('outPorts', ['OUT']).attr('.label/text', 'Not');;
}
// 圖例加入到畫板左側
graph.addCell(genAndPr().translate(20, 20));
graph.addCell(genOrPr().translate(20, 120));
graph.addCell(genNotPr().translate(20, 220));
// 添加一個分割欄將圖例和繪圖區(qū)域分開
var separator = new joint.shapes.standard.Polyline();
separator.resize(5, 400);
separator.position(95, 0);
separator.addTo(graph);
上面代碼寫得挺粗燥的,高手見笑。其實可以更好的抽象,只是懶得弄了。上面關鍵的概念就是”連接點”(port),JointJS庫中joint.shapes.devs.Model元素默認支持”入”(in)和”出”(out)兩種連接點,分別顯示在元素圖形的左邊和右邊,多個連接點會自動排列。通過前一段代碼中paper的linkPinning屬性設置,可以要求”連線”只允許接在”連接點”上。上面我們定義了”連接點”風格為一個長10px寬3px的水平線。上例中,因為我重寫了”連接點”的標記portMarkup,去掉了允許連線的屬性,所以這些連接點目前都無法被連線。
第三步定義繪圖元素
function genAnd() {
return genAndPr().set('portMarkup', '').attr('.label/type', 'instance');
}
function genOr() {
return genOrPr().set('portMarkup', '').attr('.label/type', 'instance');
}
function genNot() {
return genNotPr().set('portMarkup', '').attr('.label/type', 'instance');
}
上面的代碼還是很好理解的,我將圖例元素中的portMarkup改了,其實就是增加了css類port-body,這是JointJS庫中預定義的。該類中設置了”元素”的magnet屬性,設為true時可入可出;passive時只入不出;false時不能連線。這樣,我們的繪圖元素就可以接上連線了。另外,這里將自定義屬性.label/type改為instance主要是后面的事件判斷用。
最后,我們定義鼠標事件,來支持將圖例元素拖入繪圖區(qū)域
paper.on({ // JointJS事件都定義在畫布上
// 當鼠標左鍵按下時
'element:pointerdown': function(elementView, evt) {
// 當圖例元素被拖走時,在原來的位置創(chuàng)建一個新的圖例元素
if (elementView.model.attr('.label/type') == 'primary') {
var type = elementView.model.attr('.label/text');
if (type == 'And') {
graph.addCell(genAndPr().translate(20, 20));
} else if (type == 'Or') {
graph.addCell(genOrPr().translate(20, 120));
} else if (type == 'Not') {
graph.addCell(genNotPr().translate(20, 220));
}
// 被拖動的元素挪到圖層的最上層,這樣可以遮蓋現(xiàn)有元素
elementView.model.toFront();
} else if (elementView.model.attr('.label/type') == 'instance') {
// 對于繪圖元素,記住其被拖動時的起始點,當拖動位置超出繪圖區(qū)域時,可以回到原點
evt.data = elementView.model.position();
}
},
// 當鼠標左鍵抬起時
'element:pointerup': function(elementView, evt, x, y) {
if (elementView.model.attr('.label/type') == 'primary') {
// 對于圖例元素,當其被拖入繪圖區(qū)域時,則在該位置創(chuàng)建一個新的繪圖元素,并刪除被拖動的圖例元素
if (elementView.model.position().x > 105) {
var type = elementView.model.attr('.label/text');
if (type == 'And') {
graph.addCell(genAnd().translate(elementView.model.position().x, elementView.model.position().y));
} else if (type == 'Or') {
graph.addCell(genOr().translate(elementView.model.position().x, elementView.model.position().y));
} else if (type == 'Not') {
graph.addCell(genNot().translate(elementView.model.position().x, elementView.model.position().y));
}
}
// 刪除當前被拖動的元素
graph.removeCells(elementView.model);
} else {
// 對于繪圖元素,當其被拖出繪圖區(qū)域時,則將其移回原點
if (elementView.model.position().x < 110) {
elementView.model.position(evt.data.x, evt.data.y);
}
}
},
// 當鼠標左鍵雙擊時
'element:pointerdblclick': function(elementView, evt) {
// 雙擊繪圖元素則刪除該元素,相應的連線也會被自動刪除
if (elementView.model.attr('.label/type') == 'instance') {
elementView.model.remove();
}
},
})
JointJS的事件都定義在畫布paper上,可以參考這里的說明。事件的種類很多,可以在”元素”、”連線”或”空白處”上監(jiān)聽,可以是各種鼠標事件,這里不贅述了。大部分事件都接受4個參數(shù):
“cellView”(或叫”elementView”) – 事件監(jiān)聽的主體,可以通過elementView.model來獲得元素對象,并對其做各種設置
“evt” – 保存信息用于在事件間傳數(shù)據
“x”和”y” – 記錄事件發(fā)生時鼠標的位置
上例中的事件函數(shù),定義了將圖例元素拖入繪圖區(qū)域,并創(chuàng)建一個新的繪圖元素的過程。
保存上面的代碼并在瀏覽器里打開,大家應該可以看到如下內容。
JointJS源碼托管在Github中。更詳細的開發(fā)API文檔可以在官方API文檔中找到。 本文中的示例代碼,可以在這里下載
總結
以上是生活随笔為你收集整理的html控制图的宽,用JointJS做一个简单的功能控制图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux修改永久ip地址,centos
- 下一篇: python求和函数从1到m_pytho