前面一篇写了使用jointjs实现自动布局和拖拽缩放,这篇记录一下自定义图形。

首先jointjs内置的图形有很多,文档已经列出来了:

但是有时候这些图形满足不了我们的需求,就需要我们自己去绘制自己想要的图形。例如完成下面图的效果:

拆解一下每个基本图案,实际上是 一个圆形图片 + 一个圆角矩形 + 文字。

  1. 代码基本结构跟之前没有太大区别,原始节点数据新加了几个属性。
/** 原始数据:节点 */
nodes: [
{ id: 1, label: '阿巴', img: require('../../assets/img/pic-1.jpg') },
{ id: 2, label: '暴富', img: require('../../assets/img/pic-2.jpg') },
{ id: 3, label: '小熊', img: require('../../assets/img/pic-3.jpg') }
],
/** 原始数据:连线 */
links: [
{ from: 1, to: 2 },
{ from: 1, to: 3 }
]
  1. 在自己的某个目录创建一个js文件,在这个文件内写自定义图形的代码。我这里主要使用joint.shapes.basic.Generic.extend方法,这个方法返回一个创建好的图形的对象。

    文档内也有其他方法定义自定义图形的方法,例如:joint.dia.Element.define(),从头开始创建图元子类型,也可以继承某种标准类型,创建某标准类型的子类型,例如:joint.shapes.standard.Rectangle.define()。

    文档:https://resources.jointjs.com/tutorial/custom-elements
  • 首先,使用joint.shapes.basic.Generic.extend自定义图案的基本代码是下面的这些:

    let option = {
    /** 一些自定义配置:比如大小,样式等 */
    };
    joint.shapes.basic.Generic.extend({
    /** markup配置项:要渲染的svg结构的模板 */
    markup: '',
    /** 这是设置默认属性,其中的defaultsDeep我理解为类似于Object.assign的东西,后面我贴下专业解释 */
    defaults: joint.util.defaultsDeep(option, joint.shapes.basic.Generic.prototype.defaults),
    /** 初始化函数,on里面监听某些属性的变化,比如监听label的变化就是change:label, 监听attrs的变化就是change:attrs,可以多个,空格分隔即可,监听变化后调用update函数更新内容 */
    initialize: function() {
    this.on(
    'change:label',
    function() {
    this.updateRectangles();
    },
    this
    );
    this.updateRectangles();
    joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments);
    },
    updateRectangles() {
    /** 这写更新图形的代码 */
    }
    });
  • 第一个markup配置,我们要渲染 一个圆形图片 + 一个圆角矩形 + 文字;

    /**
    * 需要有一个这种外壳,看名字感觉是基于这个主体元素进行缩放和旋转(猜的)
    * <g class="rotatable">
    * <g class="scalable">
    * 这里放主体的一个承载元素,我放了一个rect作为最外层
    * </g>
    * 要渲染的内容放在这里
    * </g>
    * 如下:
    * 由于我选的图片是正方形的,所以做了一个圆边
    */
    markup: `<g class="rotatable">
    <g class="scalable">
    <rect class="body" />
    </g>
    <g>
    <rect id="image-box" />
    <clipPath id="clip">
    <use xlink:href="#image-box" />
    </clipPath>
    <image class="avatar" clip-path="url(#clip)" />
    </g>
    <g>
    <rect class="name-bg" />
    <text class="name"></text>
    </g>
    </g>`,
  • 其次搞一下option给他一个好看的样式。

    let option = {
    /** 这里必须有type,相当于你要在哪一个类型下创建一个叫什么的子类型 */
    type: 'basic.customShape',
    /** 整体的尺寸大小 */
    size: {
    width: 80,
    height: 114
    },
    /** attrs里面通过选择器给相应元素配置样式 */
    attrs: {
    '.body': {
    width: 80,
    height: 114,
    fill: 'none'
    },
    '#image-box': {
    width: 80,
    height: 80,
    rx: '50%',
    ry: '50%',
    },
    '.avatar': {
    width: 80,
    height: 80,
    },
    '.name-bg': {
    width: 50,
    height: 24,
    rx: 12,
    ry: 12,
    fill: '#ffcdcd',
    /** refX和refY是相对于父元素在x和y方向的偏移量 */
    refX: 15,
    refY: 90
    },
    '.name': {
    fill: '#000',
    /** ref可以指定继承于哪个元素,这决定了refX和refY基于谁进行偏移 */
    ref: '.name-bg',
    /** 文字居中的配置 */
    textVerticalAnchor: 'middle',
    textAnchor: 'middle',
    refX: '50%',
    refY: '50%'
    }
    }
    };
  • initialize里面监听内容不变,接着写updateRectangles函数

    updateRectangles() {
    /** 通过this.get(属性名)获取对应属性值 */
    const attrs = this.get('attrs');
    const rect = { label: this.get('label'), image: this.get('image') };
    /** 给attrs里面的属性赋值 */
    attrs['.name'].text = rect.label;
    attrs['.avatar']['xlink:href'] = rect.image;
    }

在接收joint.shapes.basic.Generic.extend这个方法的返回时,可以直接在joint.shapes里面创建一个新名称:

joint.shapes.basic.customShape = joint.shapes.basic.Generic.extend({});

也可以随便赋值给一个变量myShape,然后export出去;

在vue文件引入这个js后,在创建节点时,把图形函数名换成customShape就可以了,例如:

let node = new joint.shapes.basic.customShape({...});

或者

let node = new myShape({...});

  1. 最后完整的js代码:
import * as joint from 'jointjs';
let option = {
type: 'basic.customShape',
size: {
width: 80,
height: 114
},
attrs: {
'.body': {
width: 80,
height: 114,
fill: 'none'
},
'#image-box': {
width: 80,
height: 80,
rx: '50%',
ry: '50%',
},
'.avatar': {
width: 80,
height: 80,
},
'.name-bg': {
width: 50,
height: 24,
rx: 12,
ry: 12,
fill: '#ffcdcd',
refX: 15,
refY: 90
},
'.name': {
fill: '#000',
ref: '.name-bg',
textVerticalAnchor: 'middle',
textAnchor: 'middle',
refX: '50%',
refY: '50%'
}
}
};
let customShape = joint.shapes.basic.Generic.extend({
markup: `<g class="rotatable">
<g class="scalable">
<rect class="body" />
</g>
<g>
<rect id="image-box" />
<clipPath id="clip">
<use xlink:href="#image-box" />
</clipPath>
<image class="avatar" clip-path="url(#clip)" />
</g>
<g>
<rect class="name-bg" />
<text class="name"></text>
</g>
</g>`,
defaults: joint.util.defaultsDeep(option, joint.shapes.basic.Generic.prototype.defaults),
initialize: function() {
this.on(
'change:label',
function() {
this.updateRectangles();
},
this
);
this.updateRectangles();
joint.shapes.basic.Generic.prototype.initialize.apply(this, arguments);
},
updateRectangles() {
const attrs = this.get('attrs');
const rect = { label: this.get('label'), image: this.get('image') };
attrs['.name'].text = rect.label;
attrs['.avatar']['xlink:href'] = rect.image;
}
}); export default customShape;

对于配置中defaults: joint.util.defaultsDeep(option, joint.shapes.basic.Generic.prototype.defaults)的defaultsDeep方法

[jointjs] 自定义shape的更多相关文章

  1. Android自定义shape的使用

    MainActivity如下: package cn.testshape; import android.os.Bundle; import android.app.Activity; /** * D ...

  2. [UWP]不怎么实用的Shape指南:自定义Shape

    1. 前言 这篇文章介绍了继承并自定义Shape的方法,不过,恐怕,事实上,100个xaml的程序员99个都不会用到.写出来是因为反正都学了,当作写个笔记. 通过这篇文章,你可以学到如下知识点: 自定 ...

  3. 自定义shape文件

    1.shape文件 btn_bg.xml文件内容 <?xml version="1.0" encoding="utf-8"?> <shape ...

  4. Android 自定义shape圆形按钮

    Shape的属性: solid 描述:内部填充 属性:android:color 填充颜色 size 描述:大小 属性: android:width 宽 android:height 高 gradie ...

  5. android中自定义shape

    <shape> <!-- 实心 --> <solid android:color="#ff9d77"/> <!-- 渐变 --> & ...

  6. android 自定义shape 带阴影边框效果

    在drawable 里面 建立一个 xml 直接复制 看效果 自己调试就可以 <?xml version="1.0" encoding="utf-8"?& ...

  7. Progress 自定义(一)-shape

    需求:自定义ProgressBar,使用系统自定义shape; 效果图: 1.默认底色: 2.第一进度颜色: 3.第二进度颜色: 实现分析: 1.目录结构: 代码实现: 1.progress_styl ...

  8. Button 自定义(一)-shape

    需求:自定义Button,使用系统自定义Shape: 效果图: 1.默认状态 2.选中状态 实现分析: 1.目录结构: 代码实现: 1.button_normal.xml <?xml versi ...

  9. 人之初,性本动 - G2 2.1 发布

    前言 随着可视化进入深水区,G2面临了越来越多交互上的需求.动画是提升交互必不可少的一部分,也是之前G2的薄弱环节.这个版本里我们开发并替换了动画底层,统一了时间轴,使G2的动画性能大大提升,并提供了 ...

  10. G2 2.0 更灵活、更强大、更完备的可视化引擎!

    概述 G2作为一款技术产品,自诞生以来,服务于广大的Web工程师群体和一部分数据分析师.一直来,G2 因其易用的语法和扎实的可视化理论基础,广受使用者好评.G2 1.x 的可视化能力已经非常强大,使用 ...

随机推荐

  1. 面试视频知识点整理1-7(http协议)

    http协议类 1)http协议的主要特点             简单快速   统一资源符 灵活          通过http协议,可以修改http头,完成不同数据类型的传输 无连接        ...

  2. Window/linux(docker) 单台宿主机部署多个Jenkins节点

    Window/linux(docker) 单台宿主机部署多个Jenkins节点 在使用Jenkins过程中,增加了手动输入的步骤,会阻塞节点运行任务: 由于资源有限,需要在一台机器挂载了很多Jenki ...

  3. IDEA给【类】和【方法】设置作者和日期等注释

    https://blog.csdn.net/m0_61933976/article/details/127021176 一.在Java类的开头自动注释作者名字和日期等信息 这样以后只要我们创建一个类, ...

  4. jemeter批量测试

    一.使用badboy录制脚本 1.下载安装badboy(参看:https://blog.csdn.net/qq_36396763/article/details/78803381),成功标志如下: 2 ...

  5. 【SSO单点系列】(2):CAS4.0 之 跨域 Ajax 登录实践

    CAS4.0 之 跨域 Ajax 登录实践 一.问题描述 CAS实现单点 实现一处登录 可访问多个应用 . 但是原登录是CAS默认登录页面和登出页面是无法重定向到自定义页面的   此处使用Ajax+I ...

  6. 【Qt】汇总Qt坑若干

    1..pro里添加了类以后调用时还是报错? solution:注意添加或者修改以后要保存保存保存,否则Qt是不会自己更新的! 2.在ui界面修改了类的成员名,保存了以后.cpp里调用,成员名还是不更新 ...

  7. FreeSql 将 Saas 租户方案精简到极致[.NET ORM]

    什么是多租户 维基百科:"软件多租户是指一种软件架构,在这种软件架构中,软件的一个实例运行在服务器上并且为多个租户服务".一个租户是一组共享该软件实例特定权限的用户.有了多租户架构 ...

  8. Tomcat9启动闪退或者在windows服务中启动异常以及启动日志乱码问题

    首先虽然jdk6以后不需要在环境变量中额外配置jre了,但是我在tomcat的bin下startup.bat时启动时发现 可见用到了JRE_HOME,所以你需要去额外配置一下,否则会出现另外一种错误, ...

  9. ZSTUOJ刷题④:Problem B.--输出双层金字塔

    Problem B: 输出双层金字塔 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 7860  Solved: 5834 Description 输出双层 ...

  10. Day15-static、抽象类、接口、内部类

    static.抽象类.接口.内部类 一.static关键字详解 1.静态的变量/方法 package Demo02; //static public class Student { private s ...