html控制图的宽,用JointJS做一个简单的功能控制图

继上一篇介绍了GoJS之后,继续研究JS的绘图工具,毕竟GoJS有些小贵。这次选择了JointJS,完全开源,它还有一个商业版本叫Raddit,功能更强大。不过就我的需求场景,开源的Joint就足够了。接下来,我们看看它是怎么使用的。

JointJS是基于Backbone开发的,所以使用Joint之前,要先引入Backbone的相关依赖,所以我们的HTML文件是这样的:

JointJS Sample

这里我都用CDN,也可以将相应的库载到本地,但需要注意的是JS的加载顺序不能改。接下来,我们写一个官方的HelloWorld:

window.οnlοad=function() {

var graph = new joint.dia.Graph; // 创建画板,所有图上的元素都在画板里

var paper = new joint.dia.Paper({ // 创建画板上的画布,画布是用来渲染画板

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)'

},

});

// 创建一个矩形

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加入到画板中

// 创建一条连线

var link = new joint.shapes.standard.Link();

link.source(rect); // 连线头为矩形1

link.target(rect2); // 连线尾为矩形2

link.addTo(graph); // 将上面定义的连线加入到画板中

}

从上例里,我们可以看出,使用JointJS开发主要的步骤就是创建画板、创建画布、创建图形,然后将图形置于画板中。上面的例子执行后会得到下面的图像。

HelloWorld!

JointJS中的图形主要有两类,一是”元素”(Element),由构造函数创建。上例中的矩形就是JointJS库提供的标准元素,其构造函数为joint.shapes.standard.Rectangle。元素创建后,可以设置各种参数,比如位置,大小,风格等。JointJS提供了丰富的内置元素如矩形,圆形,椭圆等,可以参考API文档。同时,我们可以通过扩展joint.dia.Element类来自定义元素。

另一类图形是”连接”(Link),用来将两个”元素”连起来,一般显示为一条连线。上例中的连线是通过JointJS库提供的标准构造函数joint.shapes.standard.Link来创建的,创建后设置其”头”(Source)和”尾”(Target),即可将两个”元素”连起来。我们也可以设置”连接”的各种参数,如颜色,箭头,标签文字等。

HelloWorld之后我们来做一个简单的自动化功能控制图吧,HTML部分不变,我们来写JS部分。

第一步定义画板和画布

var graph = new joint.dia.Graph; // 创建画板,所有图上的元素都在画板里

var paper = new joint.dia.Paper({ // 创建画板上的画布,画布是用来渲染画板

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;

}

},

});

上面的属性有些多,但大部分都好理解,主要是验证函数validateConnection(),其返回true或false,用来决定连线是否被允许。传入的参数viewSource和viewTarget分别是画线过程中鼠标按钮释放时,当前连线头和尾的元素;而参数magnetSource和magnetTarget分别是当前连线头和尾的”连接点”(port)。”连接点”的概念下面会讲到。

第二步定义基本图例元素

这里,我们创建了与、或、非,三个基本元素,因为是图例元素,所以不允许连入连出。我们使用标准库提供的”joint.shapes.devs.Model”元素,因为它很方便设置”连接点”(port)。

// 创建基础元件模板

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));

// 添加一个分割栏将图例和绘图区域分开

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主要是后面的事件判断用。

最后,我们定义鼠标事件,来支持将图例元素拖入绘图区域

paper.on({ // JointJS事件都定义在画布上

// 当鼠标左键按下时

'element:pointerdown': function(elementView, evt) {

// 当图例元素被拖走时,在原来的位置创建一个新的图例元素

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));

}

// 被拖动的元素挪到图层的最上层,这样可以遮盖现有元素

elementView.model.toFront();

} else if (elementView.model.attr('.label/type') == 'instance') {

// 对于绘图元素,记住其被拖动时的起始点,当拖动位置超出绘图区域时,可以回到原点

evt.data = elementView.model.position();

}

},

// 当鼠标左键抬起时

'element:pointerup': function(elementView, evt, x, y) {

if (elementView.model.attr('.label/type') == 'primary') {

// 对于图例元素,当其被拖入绘图区域时,则在该位置创建一个新的绘图元素,并删除被拖动的图例元素

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 {

// 对于绘图元素,当其被拖出绘图区域时,则将其移回原点

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上,可以参考这里的说明。事件的种类很多,可以在”元素”、”连线”或”空白处”上监听,可以是各种鼠标事件,这里不赘述了。大部分事件都接受4个参数:

“cellView”(或叫”elementView”) – 事件监听的主体,可以通过elementView.model来获得元素对象,并对其做各种设置

“evt” – 保存信息用于在事件间传数据

“x”和”y” – 记录事件发生时鼠标的位置

上例中的事件函数,定义了将图例元素拖入绘图区域,并创建一个新的绘图元素的过程。

保存上面的代码并在浏览器里打开,大家应该可以看到如下内容。

49260b03f0dd0d7a17df7bf5704c512d.png

JointJS源码托管在Github中。更详细的开发API文档可以在官方API文档中找到。 本文中的示例代码,可以在这里下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/486596.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kax格式怎么导入不了pr_pr如何导入.mogrt文件 pr模板导入教程

Mogrt 文件是什么&#xff0c;Mogrt格式的模板文件&#xff0c;又称之为Pr动态图形模板&#xff0c;是一种新型的模板格式。.mogrt文件如何使用&#xff0c;pr如何导入.mogrt文件呢&#xff1f;Macw小编为大家带来了pr模板导入教程&#xff0c;一起来看看吧&#xff01;pr如何导…

《科学》:作弊者并非赢家,合作的物种表现得更好

本文系生物谷原创编译&#xff0c;欢迎分享&#xff0c;转载须授权&#xff01;健康的人际关系的标志是你付出多少&#xff0c;得到多少。自然界有它自己的健康关系。互利共生&#xff08;mutualism&#xff09;是指物种之间的相互作用&#xff0c;对每个物种都有利。一个例子是…

plt.savefig 不保存边框_Matlab科研绘图 amp; 保存

为方便自己绘图&#xff0c;记录一下画图常用命令。会不定期完善代码&#xff0c;欢迎评论区交流好用的画图技巧。部分绘图效果展示绘图代码详解以随机噪声为例x代码绘图结果&#xff1a;保存图片方式①复制无损矢量图&#xff1a;在figure中使用菜单edit---->copy figure&a…

servlet源码查看

1&#xff0c;下载源码&#xff0c;点击此处可下载 2&#xff0c;创建web项目 我这里以jdbc这个web项目为例讲解 在javaee libraries中有个javaee.jar包&#xff0c;选中它-->右击--》Properties 找到你下载的jar包 打开---apply 即可】 在任意一个.java文件中&#xff0c;…

自动驾驶出行,进入下半场

来源&#xff1a;脑极体近两年&#xff0c;出行领域最值得人们期待的创新恐怕就是自动驾驶技术了。经过长达十年的技术积累和两年的商业试验&#xff0c;自动驾驶出行已经正在开放商用的道路上逐渐加速。就在这个月初&#xff0c;百度Apollo自动驾驶出租车在北京海淀、亦庄等区…

python安装百度aip_Python3.6安装aip

AIP的安装&#xff08;此aip非百度AIP&#xff09; 如同以往安装库的方法一样&#xff0c;进入目录&#xff1a;C:\Users\admin\AppData\Local\Programs\Python\Python36\Scripts 执行&#xff1a;pip install aip我们的aip目录如下&#xff1a;如果正常导入AipOcr的话会报错的…

python缩进格式错误的是_Python 中常见错误总结

IndentationError: unexpected indent Python 中强制缩进&#xff0c;&#xff0c; IndentationError: unexpected indent 缩进错误 这类错误非常常见&#xff0c;一般都是由于tab在不同的平台上占用长度不同导致&#xff0c;有些事程序员自己直接使用空格或其他来顶替tab。 解…

史上曾被认为不可能的十大科学难题全被实现

来源&#xff1a;科学解码对于科学家来说&#xff0c;好像没有什么事情是不可能做到的。纵观科学发展史&#xff0c;我们便会发现&#xff0c;一个又一个看似“不可能的任务”最终都成为可能&#xff0c;例如利用核能、上演太空飞行、创建力场以及远距离传物。几个世纪前&#…

广东省一本计算机学校排名,南方科技大学排名2021 广东排名第16全国排名第276...

南方科技大学排名2019 广东排名第16全国排名第276每年的大学排名位次争议很大&#xff0c;虽然各大学校长口头上称不在乎、不看重&#xff0c;但实际上却卯足干劲&#xff0c;希望学校排名靠前&#xff0c;因为大学排名一定程度上体现了高校的办学成就。高考升学网本文介绍的主…

webpack轻松入门教程

webpack之傻瓜式教程及前端自动化入门 接触webpack也有挺长一段时间了&#xff0c;公司的项目也是一直用着webpack在打包处理&#xff0c;但前几天在教新人的情况下&#xff0c;遇到了一个问题&#xff0c;那就是&#xff1a;尽管网上的webpack教程满天飞&#xff0c;但是却很难…

首个单设备模拟神经元出现 可有效解决传统计算机所面临的问题

来源&#xff1a;IEEE电气电子工程师Photo: Research Group of R. Stanley Williams对于工程师们来说&#xff0c;无法实现人类大脑效率和超强计算性能的其中一个原因在于&#xff0c;一直以来我们缺少一种可以独立发挥神经元作用的电子设备。要做到这一一点&#xff0c;需要我…

电子科大沙河校区有计算机专业,电子科大沙河校区学费

依据电子科大沙河校区最新收费标准&#xff0c;电子科大沙河校区的学费每人每年需要4400元-60000元不等。其中中外合作办学项目每人每年需要60000元&#xff1b;理科专业每人每年需要4900元&#xff1b;文科专业每人每年需要4400元&#xff1b;软件工程专业每人每年需要9800元。…

SMB服务简介

1、Samba简介 Samba是种自由软件&#xff0c;用来让UNIX系列的操作系统与微软Windows操作系统的SMB/CIFS(Server Message Block/Common Internet File System)网络协定做连结。在目前的版本(v3)&#xff0c;不仅可存取及分享SMB的资料夹及打印机&#xff0c;本身还可以整合入Wi…

学云计算能干什么_陌陌主播等级计算?陌陌主播升级明细表?

有很多主播对陌陌直播规则都不太清楚&#xff0c;比如就有人问&#xff0c;陌陌主播等级计算&#xff1f;陌陌主播升级明细表&#xff1f;今天跟大家简单的说一下&#xff0c;一起来看看吧。加入海星公会&#xff0c;扶持奖励全都有&#xff0c;主播可获得热门推荐&#xff0c;…

建造自己的「天空之城」,密歇根大学博士后的这项研究可以虚空造物、偷天换日...

来源&#xff1a;机器之心 哈尔的移动城堡&#xff1f;天空之城&#xff1f;这幅图是否让你想起了这两部电影中的场景……上&#xff1a;《天空之城》剧照&#xff1b;下&#xff1a;《哈尔的移动城堡》剧照。是电影场景变为现实了吗&#xff1f;真的有人建造了一座空中楼阁&am…

个人计算机有控制器和运算器吗,cpu是由控制器和运算器组成的对还是错

对的。CPU即中央处理器&#xff0c;是计算机中负责读取指令&#xff0c;对指令译码并执行指令的核心部件。中央处理器主要包括两个部分&#xff0c;即控制器、运算器&#xff0c;其中还包括高速缓冲存储器及实现它们之间联系的数据、控制的总线。一、控制器是整个计算机系统的指…

又一壮举!GPT-3首次完成剧本创作,AI解决创造性问题的能力正迅速提升

来源&#xff1a;中国智慧城市导刊文章原载于 学术头条自今年 6 月份发布以来&#xff0c;OpenAI 的文本生成人工智能工具 GPT-3 获得了极大的关注。它被用来在论坛上发表评论、写诗、甚至在《卫报》中发表文章。当 GPT-3 没有经过专门培训就学会自动完成某项任务时&#xff0c…

Hinton构思下一代神经网络:属于无监督对比学习

本文由机器之心报道Geoffrey Hinton 是谷歌副总裁、工程研究员&#xff0c;也是 Vector Institute 的首席科学顾问、多伦多大学 Emeritus 荣誉教授。2018 年&#xff0c;他与 Yoshua Bengio、Yann LeCun 因对深度学习领域做出的巨大贡献而共同获得图灵奖。自 20 世纪 80 年代开…

windows 获取命令执行后的结果_法院判决以后,老赖欠钱不还,递交强制执行申请多久后有结果?...

网友提问&#xff1a;老赖欠钱不还&#xff0c;已向法院递交了强制执行申请一个月了&#xff0c;老赖仍逍遥法外&#xff0c;该怎么办&#xff1f;这个阶段你称之为老赖&#xff0c;也无不可。但还不是法律上所认可的老赖&#xff0c;法律上的老赖也只是个俗称&#xff0c;学名…

研究揭示动物社交欲望的神经机制

来源&#xff1a;中国科学院生物物理研究所10月22日&#xff0c;中国科学院生物物理研究所朱岩课题组在Nature Communications上发表题为Social attraction in Drosophila is regulated by the mushroom body and serotonergic system的研究论文&#xff0c;研究以果蝇为模型&a…