本系列教程是用Vue.js + Nuxt.js + Element + Vuex + 开源js绘图库,打造一个属于本身的在线绘图软件,最终效果:http://topology.le5le.com 。若是你以为好,欢迎给文章和开源库点赞,让咱们更有动力去作好!css
本系列教程源码地址:Github前端
yarn create nuxt-app topology-vue // 注意在后面提示中,上移下移,按空格选中 Element
选择Element后,在plugins文件夹下会自带添加Element的插件配置vue
完成后,在nuxt.config.js中配置head相关信息,主要有两个阿里字体文件:
左侧工具栏字体文件:
//at.alicdn.com/t/font_1113798_0532l8oa6jqp.css node
右侧属性字体图标:
//at.alicdn.com/t/font_1331132_5lvbai88wkb.cssgit
head: { title: '乐吾乐 Topology - 开源免费绘图工具', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: '一个基于typescript + canvas的好用开源绘图工具和绘图引擎。易集成到本身的前端项目、还能够方便自定义图形库,支持微服务架构图、流程图、时序图、活动图、类图等' } ], link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, { rel: 'stylesheet', href: '//at.alicdn.com/t/font_1113798_0532l8oa6jqp.css' }, { rel: 'stylesheet', href: '//at.alicdn.com/t/font_1331132_5lvbai88wkb.css' } ] },
2.1 安装scss的依赖包github
yarn add node-sass sass-loader -D
2.2 给style标签加上lang="scss"标记vuex
<style lang="scss"> .page { width: 100%; height: 100%; } </style>
3.1 在asstes/css文件夹下新建一个base.scss公用全局样式文件typescript
3.2 导入
在layouts/default.vue的script脚本中导入:canvas
import '~/assets/css/base.scss'
修改layouts/default.vue为导航栏 + body两部分sass
其中:<nuxt /> 为Nuxt.js框架中对应页面路由的视图部分。
修改pages/index.vue为左中右三栏布局
yarn add topology-core topology-class-diagram topology-activity-diagram topology-flow-diagram topology-sequence-diagram -D
其中,topology-core为核心库引擎,其余的为图形库。具体参考:开发文档
咱们单独写个pages/canvas.server.js服务,用来提供topology相关服务
这里主要提供注册和左侧工具栏数据。
3.1 准备canvas相关数据
data() { return { // 左侧工具栏 tools: Tools, // 图形库 canvas: {}, // 图形库选项:https://www.yuque.com/alsmile/topology/canvas#hOupV canvasOptions: { rotateCursor: '/img/rotate.cur' }, // 右侧属性栏数据 props: { node: null, line: null, multi: false } } }
3.2 注册图形库
created() { canvasRegister() }
3.3 在父节点已经渲染后,new建立画布
mounted() { this.canvasOptions.on = this.onMessage this.canvas = new Topology('topology-canvas', this.canvasOptions) }
其中,onMessage 表示接受画布的消息回调函数
3.4 左侧工具栏支持鼠标拖放
<a v-for="(btn, i) in item.children" :key="i" :title="btn.name" :draggable="btn.data" @dragstart="onDrag($event, btn)" > <i :class="`iconfont ${btn.icon}`"></i> </a>
methods: { onDrag(event, node) { event.dataTransfer.setData('Text', JSON.stringify(node.data)) } }
只须要给拖放数据设置节点格式的字符串便可(画布自带支持拖放接收处理),节点数据格式文档:https://www.yuque.com/alsmile...
3.5 右侧属性栏
在components下建立CanvasProps.vue
<template> <div> <!-- 选中为空 --> <div v-if="!props.node && !props.line && !props.multi"> <div class="title">欢迎使用le5le-topology!</div> <div class="group"> <a class="star" href="https://github.com/le5le-com/topology" target="_blank">喜欢,点击这里打个star吧</a> <a href="https://www.yuque.com/alsmile/topology" target="_blank">使用教程</a> <br /> <a href="http://topology.le5le.com/assets/img/topology_wechat.jpg?t=1" target="_blank" >微信交流群(大群)</a> <br /> <a href="http://topology.le5le.com/assets/img/topology_wechat2.jpg" target="_blank">微信交流群2</a> <br /> <a href="https://www.yuque.com/alsmile/topology/faq#EVbCgt" target="_blank">联系咱们</a> </div> <div class="title">[Todo] 将来规划</div> <ul class="group"> <li>Github issues</li> <li>React demo</li> <li>Vue3 demo</li> <li>系列教程</li> </ul> <div class="bottom"> <div class="title">小提示</div> <ul class="group"> <li>方向键:控制节点移动5个像素</li> <li>Ctrl + 方向键:控制节点移动1个像素</li> <li>Ctrl + 鼠标移动:移动整个画布</li> <li>Ctrl + 鼠标滚轮:缩放</li> <li>添加或选中节点,右侧属性支持上传各类图片哦</li> </ul> </div> </div> <!-- 选中节点 --> <div v-if="props.node"> <div class="title">位置和大小</div> <div class="items"> <div class="flex grid"> <div>X(px)</div> <div class="ml5">Y(px)</div> </div> <div class="flex grid"> <div> <el-input-number v-model="props.node.rect.x" controls-position="right" @change="onChange" ></el-input-number> </div> <div class="ml5"> <el-input-number v-model="props.node.rect.y" controls-position="right" @change="onChange" ></el-input-number> </div> </div> </div> <div class="items"> <div class="flex grid"> <div>宽(px)</div> <div class="ml5">高(px)</div> </div> <div class="flex grid"> <div> <el-input-number v-model="props.node.rect.width" controls-position="right" @change="onChange" ></el-input-number> </div> <div class="ml5"> <el-input-number v-model="props.node.rect.height" controls-position="right" @change="onChange" ></el-input-number> </div> </div> </div> </div> </div> </template> <script > export default { data() { return {} }, props: { props: { type: Object, require: true } }, methods: { onChange(value) { this.$emit('change', this.props.node) } } } </script>
其中,props.node、line、multi分别表示是否选中节点、连线、多个对象。
这里咱们暂时没有用到vuex(后面教程介绍),直接使用原生的双向绑定更简单。用$emit通知父组件属性改变事件。
相关属性值,参数API文档:https://www.yuque.com/alsmile...
<div class="props"> <CanvasProps :props.sync="props" @change="onUpdateProps"></CanvasProps> </div>
一样,咱们利用.sync关键字使用双向绑定,并接收chang事件,反馈给画布组件:
onUpdateProps(node) { // 若是是node属性改变,须要传入node,从新计算node相关属性值 // 若是是line属性改变,无需传参 this.canvas.updateProps(node) }
自此,一个简单的绘图项目就完成了,后续功能完善待续。
但,但,但...右侧属性栏,但愿你们根据开发文档去参与完善,展现本身舞台的机会到了,可加入贡献者名单哦!不清楚的,欢迎联系管理员:(微信)alsmile123,或加群:
经过GitHub的pr方式:
开源项目不易,欢迎你们一块儿参与,给【文章、GitHub开源库】点星点赞,或资助服务器: