├── design ├── css │ ├── select.area.css │ ├── resize.css │ └── style.css ├── images │ ├── grid.png │ ├── image.jpg │ ├── card_bg.png │ └── canvas_bg.jpg ├── fontawesome-free-5.6.3 │ ├── webfonts │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.ttf │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-solid-900.woff │ │ ├── fa-solid-900.woff2 │ │ └── fa-regular-400.woff2 │ └── css │ │ └── all.min.css ├── js │ ├── index.js │ ├── diamond.js │ ├── drag.js │ ├── select.area.js │ ├── components.js │ ├── resize.js │ └── frame.js ├── x0popup │ ├── x0popup.min.js │ └── x0popup.min.css └── vue │ └── vue.min.js ├── _config.yml ├── images └── preview.png ├── package.json ├── README.md ├── drag.md ├── selectArea.md ├── app.js ├── resize.md └── index.html /design/css/select.area.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-leap-day -------------------------------------------------------------------------------- /images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/images/preview.png -------------------------------------------------------------------------------- /design/images/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/images/grid.png -------------------------------------------------------------------------------- /design/images/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/images/image.jpg -------------------------------------------------------------------------------- /design/images/card_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/images/card_bg.png -------------------------------------------------------------------------------- /design/images/canvas_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/images/canvas_bg.jpg -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /design/fontawesome-free-5.6.3/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newpanjing/flow-chart/HEAD/design/fontawesome-free-5.6.3/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphic-design", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./app.js" 7 | }, 8 | "dependencies": { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /design/js/index.js: -------------------------------------------------------------------------------- 1 | //引入js 2 | 3 | var libs = ['/js/jsplumb.js','/js/select.area.js','js/resize.js','js/drag.js','vue/vue.js','/js/diamond.js','js/components.js','/x0popup/x0popup.min.js','/js/frame.js']; 4 | var base = './design/'; 5 | libs.forEach(lib => { 6 | var script = document.createElement('script'); 7 | script.type = 'text/javascript'; 8 | script.src = base + lib; 9 | document.body.appendChild(script); 10 | }); 11 | 12 | window.onload = function () { 13 | //初始化 14 | frameInit(); 15 | } 16 | -------------------------------------------------------------------------------- /design/js/diamond.js: -------------------------------------------------------------------------------- 1 | function Diamond(width, height) { 2 | 3 | var canvas = document.createElement('canvas'); 4 | canvas.width = width; 5 | canvas.height = height; 6 | var ctx = canvas.getContext("2d"); 7 | 8 | ctx.lineWidth = 3; 9 | ctx.strokeStyle = 'black'; 10 | 11 | ctx.moveTo(0, height / 2 - 1); 12 | ctx.lineTo(width / 2 - 1, 1); 13 | ctx.lineTo(width - 1, height / 2 - 1) 14 | ctx.lineTo(width / 2 - 1, height - 1) 15 | ctx.lineTo(1, height / 2 - 1); 16 | ctx.stroke(); 17 | ctx.closePath(); 18 | ctx.fillStyle = 'yellow'; 19 | ctx.fill(); 20 | 21 | 22 | return canvas.toDataURL('image/png'); 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js+vue flow chart 在线流程图 2 | 3 | 原生js+vue实现 图形画板,和windows图标一样,可以自由的拖动、拖拉改变大小。 4 | 以及和windows桌面一样 有选区,可以选中若干个元素一起拖动。 5 | 6 | 在线预览地址: 7 | [https://newpanjing.github.io/flow-chart/index.html](https://newpanjing.github.io/flow-chart/) 8 | ## 特色功能 9 | + 键盘位移 10 | + 双击编辑 11 | + 拖拽 12 | + 改变大小 13 | + 单选/多选 14 | 15 | ## 源码启动预览 16 | + 启动: 17 | ```shell 18 | npm run start 19 | ``` 20 | + 也可以直接运行app.js 21 | ````shell 22 | node app.js 23 | ```` 24 | 25 | ## 预览 26 |  27 | ## js 插件 28 | 29 | + drag.js 30 | > 实现了div的自由拖动 31 | 32 | + resize.js 33 | > 实现了div 自由改变大小 34 | + select.area.js 35 | > 实现了 div 上选区 36 | 37 | ## 文档 38 | 39 | + [drag.js](/drag.md) 40 | + [resize.js](/resize.md) 41 | + [select.area.js](/selectArea.md) 42 | -------------------------------------------------------------------------------- /drag.md: -------------------------------------------------------------------------------- 1 | # drag.js 2 | js div 实现推拽功能 3 | 4 | 用法: 5 | ```javascript 6 | new Drag(options).register(el) 7 | ``` 8 | 9 | options: 10 | 11 | | 名称 | 参数 | 说明 | 12 | |---|----|----| 13 | |onDrag|left=左边距离,top=顶部距离|推拽中| 14 | |onBegin|left=左边距离,top=顶部距离,x=移动的左边相对距离,y=移动的顶部相对距离|开始推拽| 15 | |onEnd|left=左边距离,top=顶部距离|结束推拽| 16 | 17 | ## 支持jquery 18 | ```javascript 19 | 20 | $.fn.drag=function() { 21 | var options={ 22 | onBegin:function(data) { 23 | 24 | }, 25 | onEnd:function(data) { 26 | 27 | }, 28 | onDrag:function(data) { 29 | 30 | } 31 | } 32 | new Drag(options).register(this); 33 | } 34 | ``` 35 | 36 | jquery使用: 37 | ```javascript 38 | $(".aa").drag(); 39 | ``` 40 | 41 | ## 支持vue 42 | ```javascript 43 | 44 | Vue.directive('drag', { 45 | inserted(el, binding) { 46 | var options={}; 47 | new Drag(options).register(el); 48 | }}); 49 | ``` 50 | 51 | vue使用: 52 | ```html 53 |
54 | ``` -------------------------------------------------------------------------------- /design/css/resize.css: -------------------------------------------------------------------------------- 1 | .border{ 2 | border: #0774d6 1px solid; 3 | position: absolute; 4 | left: -1px; 5 | right: -1px; 6 | top: -1px; 7 | bottom: -1px; 8 | display: none; 9 | z-index: 99; 10 | } 11 | .active .direction,.active .border{ 12 | display: block; 13 | } 14 | .direction{ 15 | position: absolute; 16 | width: 5px; 17 | height: 5px; 18 | border: 1px #0774d6 solid; 19 | background-color: #0792ff; 20 | display: none; 21 | z-index: 100; 22 | } 23 | 24 | .nw{left: -4px; top: -4px; cursor: nw-resize} 25 | .ws{left: -4px; bottom: -4px; cursor: sw-resize} 26 | .se{right: -4px; bottom: -4px; cursor: se-resize} 27 | .ne{right: -4px; top: -4px; cursor: ne-resize} 28 | 29 | 30 | .w{left: -4px; top: 50%; margin-top: -3px; cursor: crosshair;border-radius: 5px;} 31 | .s{left: 50%; bottom: -4px; margin-left: -3px; cursor: crosshair;border-radius: 5px;} 32 | .e{right: -4px; top: 50%; margin-top: -3px; cursor: crosshair;border-radius: 5px;} 33 | .n{top:-4px;left: 50%;margin-left: -3px;cursor: crosshair;border-radius: 5px;} 34 | -------------------------------------------------------------------------------- /selectArea.md: -------------------------------------------------------------------------------- 1 | # select.area.js 2 | js div 创建选区 3 | 4 | 用法: 5 | ```javascript 6 | new SelectArea(options).register(el) 7 | ``` 8 | 9 | options: 10 | 11 | | 名称 | 参数 | 说明 | 12 | |---|----|----| 13 | |onBegin|data|选区结束| 14 | |onHandler|data|选区拉伸中| 15 | |onEnd|data|选区开始| 16 | 17 | ## 支持jquery 18 | ```javascript 19 | 20 | $.fn.selectArea=function() { 21 | var options={ 22 | onBegin:function(data) { 23 | 24 | }, 25 | onEnd:function(data) { 26 | 27 | }, 28 | onHandler:function(data) { 29 | 30 | } 31 | } 32 | new SelectArea(options).register(this); 33 | } 34 | ``` 35 | 36 | jquery使用: 37 | ```javascript 38 | $(".aa").selectArea(); 39 | ``` 40 | 41 | ## 支持vue 42 | ```javascript 43 | 44 | Vue.directive('selectarea', { 45 | inserted(el, binding) { 46 | var options={}; 47 | new Resize(options).register(el); 48 | }}); 49 | ``` 50 | 51 | vue使用: 52 | ```html 53 | 54 | ``` 55 | 56 | ### PS: 为什么不适用驼峰大小写? 57 | > vue会报错,具体原因还没来得及去看文档。 -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 创建一个简单的http服务器 4 | */ 5 | 6 | var http = require('http'); 7 | var baseDir = ''; 8 | var path = require('path'); 9 | var fs = require('fs'); 10 | 11 | http.createServer(function (req, res, next) { 12 | var url = req.url; 13 | console.log(`${req.method} ${req.url}`) 14 | if (url == '/') { 15 | url = 'index.html'; 16 | } 17 | 18 | var index = url.indexOf('?'); 19 | if (index != -1) { 20 | url = url.substring(0, index); 21 | } 22 | 23 | //读取文件 24 | 25 | var ap = path.join(__dirname, baseDir, url); 26 | 27 | var exists = fs.existsSync(ap); 28 | if (!exists) { 29 | res.writeHead(404); 30 | res.end('not found.') 31 | return; 32 | } 33 | 34 | 35 | var readerStream = fs.createReadStream(ap); 36 | readerStream.pipe(res); 37 | // readerStream.on('data', function (data) { 38 | // 39 | // }); 40 | // 41 | // res.end(url); 42 | 43 | }).on('error', function (err) { 44 | console.error(err); 45 | }).listen(3000); 46 | 47 | console.log('listen http://127.0.0.1:3000'); 48 | -------------------------------------------------------------------------------- /resize.md: -------------------------------------------------------------------------------- 1 | # resize.js 2 | js div 拉伸改变大小 3 | 4 | 用法: 5 | ```javascript 6 | new Resize(options).register(el) 7 | ``` 8 | 9 | options: 10 | 11 | | 名称 | 参数 | 说明 | 12 | |---|----|----| 13 | |onBegin|data|开始拉伸| 14 | |onResize|data|拉伸中| 15 | |onEnd|data|结束拉伸| 16 | 17 | ## 支持jquery 18 | ```javascript 19 | 20 | $.fn.resize=function() { 21 | var options={ 22 | onBegin:function(data) { 23 | 24 | }, 25 | onEnd:function(data) { 26 | 27 | }, 28 | onResize:function(data) { 29 | 30 | } 31 | } 32 | new Resize(options).register(this); 33 | } 34 | ``` 35 | 36 | jquery使用: 37 | ```javascript 38 | $(".aa").resize(); 39 | ``` 40 | 41 | ## 支持vue 42 | ```javascript 43 | 44 | Vue.directive('resize', { 45 | inserted(el, binding) { 46 | var options={}; 47 | new Resize(options).register(el); 48 | }}); 49 | ``` 50 | 51 | vue使用: 52 | ```html 53 | 54 | ``` 55 | -------------------------------------------------------------------------------- /design/js/drag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 拖拽 3 | * @constructor 4 | */ 5 | function Drag(options) { 6 | 7 | /** 8 | * 9 | * @param el 节点 10 | */ 11 | this.register = function (el) { 12 | 13 | el.addEventListener('mousedown', function (e) { 14 | 15 | if (e.button != 0) { 16 | //屏蔽左键以外的按键 17 | return; 18 | } 19 | 20 | if(e.target.tagName=='TEXTAREA'){ 21 | return false; 22 | } 23 | 24 | //获取x坐标和y坐标 25 | var x = e.clientX; 26 | var y = e.clientY; 27 | 28 | //获取左部和顶部的偏移量 29 | var l = el.offsetLeft; 30 | var t = el.offsetTop; 31 | 32 | if (options && options.onBegin) { 33 | options.onBegin.call(el, { 34 | left: x - l, 35 | top: y - t 36 | }); 37 | } 38 | 39 | 40 | //开关打开 41 | var isDown = true; 42 | //设置样式 43 | el.style.cursor = 'move'; 44 | 45 | var nl = x, nt = y; 46 | 47 | 48 | window.onmousemove = function (e) { 49 | if (!isDown) { 50 | return; 51 | } 52 | //获取x和y 53 | var nx = e.clientX; 54 | var ny = e.clientY; 55 | 56 | 57 | //计算移动后的左偏移量和顶部的偏移量 58 | nl = nx - (x - l); 59 | nt = ny - (y - t); 60 | 61 | 62 | el.style.left = nl + 'px'; 63 | el.style.top = nt + 'px'; 64 | 65 | if (options && options.onDrag) { 66 | options.onDrag.call(el, { 67 | left: nl, 68 | top: nt, 69 | x: nx - x, 70 | y: ny - y 71 | }); 72 | } 73 | 74 | return false; 75 | } 76 | 77 | window.onmouseup = function (e) { 78 | //开关关闭 79 | isDown = false; 80 | el.style.cursor = 'default'; 81 | 82 | if (options && options.onEnd) { 83 | options.onEnd.call(el, {left: nl, top: nt}); 84 | } 85 | 86 | return false; 87 | } 88 | // e.stopPropagation(); 89 | if (e.stopPropagation) { 90 | e.stopPropagation(); 91 | } else if (e.preventDefault) { 92 | e.preventDefault(); 93 | } else { 94 | window.event.returnValue == false; 95 | } 96 | }); 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /design/js/select.area.js: -------------------------------------------------------------------------------- 1 | function SelectArea(options) { 2 | 3 | this.register = function (el) { 4 | 5 | el.addEventListener('mousedown', function (e) { 6 | 7 | //TODO 需要考虑x,y滚动条的距离 8 | 9 | if (e.button != 0 || e.target != this) { 10 | return; 11 | } 12 | 13 | 14 | var x = e.clientX; 15 | var y = e.clientY; 16 | 17 | var ox = x; 18 | var oy = y; 19 | 20 | var tempX = x - e.target.offsetLeft; 21 | var tempY = y - e.target.offsetTop; 22 | 23 | 24 | var isDown = true; 25 | 26 | x = tempX; 27 | y = tempY; 28 | 29 | 30 | //考虑滚动条 31 | //有两种情况 一种是自己的滚动条,一种是父容器的滚动条 32 | sx = e.target.parentElement.scrollLeft; 33 | sy = e.target.parentElement.scrollTop; 34 | 35 | x += sx; 36 | y += sy; 37 | 38 | if (options && options.onBegin) { 39 | options.onBegin.call(el, e); 40 | } 41 | var select = document.createElement('div'); 42 | 43 | select.style.position = 'absolute'; 44 | select.style.border = '1px #4a98be solid'; 45 | select.style.backgroundColor = 'rgba(127,214,255,0.3)'; 46 | select.style.zIndex = '99999999'; 47 | 48 | el.appendChild(select); 49 | 50 | window.onmousemove = function (ev) { 51 | if (!isDown) { 52 | return; 53 | } 54 | //计算差值 55 | var w = ev.clientX - ox; 56 | var h = ev.clientY - oy; 57 | 58 | 59 | if (w < 0) { 60 | x = tempX + w + sx; 61 | } 62 | if (h < 0) { 63 | y = tempY + h + sy; 64 | } 65 | 66 | var width = Math.abs(w); 67 | var height = Math.abs(h); 68 | 69 | 70 | //计算 选区覆盖的元素,并进行选中 71 | var x1 = x, y1 = y; 72 | var x2 = x + width, y2 = y + height; 73 | 74 | select.style.left = x + 'px'; 75 | select.style.top = y + 'px'; 76 | select.style.width = width + 'px'; 77 | select.style.height = height + 'px'; 78 | 79 | if (options && options.onHandler) { 80 | options.onHandler.call(el, { 81 | x1: x1, 82 | x2: x2, 83 | y1: y1, 84 | y2: y2, 85 | width: width, 86 | height: height 87 | }) 88 | } 89 | } 90 | window.onmouseup = function (ee) { 91 | isDown = false; 92 | select.remove(); 93 | if (options && options.onEnd) { 94 | options.onEnd.call(el, e); 95 | } 96 | return false; 97 | } 98 | 99 | return false; 100 | }); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /design/js/components.js: -------------------------------------------------------------------------------- 1 | function componentInit() { 2 | 3 | //注册组件 4 | Vue.component('shape', { 5 | props: ['shape'], 6 | data: function () { 7 | 8 | var shape = this.shape; 9 | 10 | 11 | var mappers = { 12 | numbers: ['left', 'top', 'width', 'height', 'borderWidth', 'fontSize', 'borderRadius', 'padding'], 13 | strings: ['active', 'borderColor', 'borderStyle', 'fontColor', 'background', 'fontStyle', 'index'] 14 | } 15 | var alias = { 16 | fontColor: 'color', 17 | index: 'z-index' 18 | }; 19 | 20 | return { 21 | width() { 22 | return shape.width 23 | }, 24 | height() { 25 | return shape.height 26 | }, 27 | data: shape, 28 | live() { 29 | return shape; 30 | }, 31 | value() { 32 | 33 | var value = shape.value; 34 | //换行改为br 35 | //空格改为占位符 36 | value = value.replace(/ /g, ' ').replace(/\r|\n/g, '