From 9b260d7f7a58d1179d6128be01f1bbd92847d47a Mon Sep 17 00:00:00 2001 From: kekee000 Date: Thu, 5 Feb 2015 02:09:07 +0800 Subject: [PATCH] addpath support drag curve --- README.md | 2 + src/editor/command/editor.js | 6 +- src/editor/controller/initRender.js | 17 ++- src/editor/mode/addpath.js | 155 +++++++++++++++++++++++----- src/editor/mode/shapes.js | 2 +- src/render/shape/BezierCurve.js | 73 +++++++++++++ src/render/shape/support.js | 3 +- 7 files changed, 225 insertions(+), 33 deletions(-) create mode 100644 src/render/shape/BezierCurve.js diff --git a/README.md b/README.md index bbe67cf..81c5a16 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,5 @@ fonteditor 在线ttf字体编辑器 11. 2015-1-26 改变项目存储方式为IndexedDB. 12. 2015-1-29 增加移动方向键改变glyf顺序. + +13. 2015-2-5 添加轮廓支持拖拽曲线,支持`ctrl+Z`回退. diff --git a/src/editor/command/editor.js b/src/editor/command/editor.js index 965a0af..baf7797 100644 --- a/src/editor/command/editor.js +++ b/src/editor/command/editor.js @@ -94,7 +94,11 @@ define( * 添加path */ addpath: function () { - this.setMode('addpath'); + var me = this; + // 此处由于监听down事件,需要延迟处理 + setTimeout(function () { + me.setMode('addpath'); + }, 20); }, /** diff --git a/src/editor/controller/initRender.js b/src/editor/controller/initRender.js index 606028d..74548a3 100644 --- a/src/editor/controller/initRender.js +++ b/src/editor/controller/initRender.js @@ -51,6 +51,8 @@ define( } } + + /* eslint-disable max-depth */ /** * 初始化渲染器 */ @@ -219,11 +221,21 @@ define( } // 撤销 else if (e.keyCode === 90 && e.ctrlKey) { - me.execCommand('undo'); + if (me.mode.undo) { + me.mode.undo.call(me, e); + } + else { + me.execCommand('undo'); + } } // 恢复 else if (e.keyCode === 89 && e.ctrlKey) { - me.execCommand('redo'); + if (me.mode.redo) { + me.mode.redo.call(me, e); + } + else { + me.execCommand('redo'); + } } else { @@ -249,6 +261,7 @@ define( me.render.refresh(); }); } + /* eslint-enable max-depth */ return initRender; } diff --git a/src/editor/mode/addpath.js b/src/editor/mode/addpath.js index 9cb5af3..3aa0783 100644 --- a/src/editor/mode/addpath.js +++ b/src/editor/mode/addpath.js @@ -12,7 +12,7 @@ define( var mode = { - click: function (e) { + down: function (e) { var coverLayer = this.coverLayer; var result = coverLayer.getShapeIn(e); @@ -21,11 +21,16 @@ define( if (result[0] && this.points.length > 1 && result[0] === this.points[0]) { var points = this.points.map(function (p) { - return { + var ret = { x: p.x, - y: p.y, - onCurve: true + y: p.y }; + + if (p.onCurve) { + ret.onCurve = true; + } + + return ret; }); var shape = this.fontLayer.addShape('path', { @@ -33,7 +38,9 @@ define( }); this.fontLayer.refresh(); this.fire('change'); - this.setMode('shapes', [shape]); + + this.setMode('shapes', [shape], 'addpath'); + } // 添加控制点 else { @@ -55,34 +62,61 @@ define( var point = coverLayer.addShape({ type: 'point', x: x, - y: y + y: y, + onCurve: true }); - if (this.points.length === 0) { + this.points.push(point); + + if (this.points.length === 1) { point.style = { strokeColor: 'red' }; } else { - var p0 = this.points[this.points.length - 1]; - coverLayer.addShape({ - type: 'line', - p0: { - x: p0.x, - y: p0.y - }, - p1: { - x: point.x, - y: point.y - } - }); + var p0 = this.points[this.points.length - 2]; + + if (p0.onCurve) { + coverLayer.addShape({ + type: 'line', + p0: { + x: p0.x, + y: p0.y + }, + p1: { + x: point.x, + y: point.y + } + }); + } + else { + coverLayer.addShape({ + type: 'beziercurve', + points: this.points.slice(this.points.length - 3) + }); + } } this.dashedLine.p0.x = point.x; this.dashedLine.p0.y = point.y; - this.points.push(point); coverLayer.refresh(); } + + // 标记鼠标按住 + this.downMouse = true; + }, + + up: function (e) { + this.downMouse = false; + if (this.curCurve) { + this.curCurve = null; + // 增加一个悬空的点,用来创建平滑曲线 + this.points.push({ + x: e.x, + y: e.y, + onCurve: false + }); + } }, move: function (e) { @@ -96,11 +130,13 @@ define( this.render.setCursor('crosshair'); } + var points = this.points; var x = e.x; var y = e.y; + var last = points[points.length - 1]; // 最后一个点 - if (this.points.length) { - var last = this.points[this.points.length - 1]; + // 配合shift和alt键设置水平或者垂直拖拽 + if ((e.shiftKey || e.altKey) && points.length) { if (e.shiftKey) { y = last.y; } @@ -110,15 +146,78 @@ define( } } + // 如果鼠标被按住,则可以拖出bezier曲线 + if (this.downMouse && points.length > 2) { + // 设置倒数第二个点 + var last2 = points[points.length - 2]; + // 如果已经创建了曲线,则需要改变曲线形状 + if (!this.curCurve && last2.onCurve) { - // 更新dashLine - this.dashedLine.p1.x = x; - this.dashedLine.p1.y = y; - this.dashedLine.disabled = false; + points.splice(points.length - 1, 0, { + x: 2 * last.x - x, + y: 2 * last.y - y, + onCurve: false + }); - this.coverLayer.refresh(); + this.coverLayer.shapes.splice(this.coverLayer.shapes.length - 1, 1); + + this.curCurve = this.coverLayer.addShape({ + type: 'beziercurve', + points: points.slice(points.length - 3) + }); + } + else { + last2.x = 2 * last.x - x; + last2.y = 2 * last.y - y; + } + + this.dashedLine.disabled = true; + this.coverLayer.refresh(); + } + else if (points.length) { + // 更新dashLine + this.dashedLine.p1.x = x; + this.dashedLine.p1.y = y; + this.dashedLine.disabled = false; + this.coverLayer.refresh(); + } }, + undo: function (e) { + // 移除上一个控制点 + if (this.points.length) { + var points = this.points; + var shapes = this.coverLayer.shapes; + var last = points[points.length - 1]; + var last2 = points[points.length - 2]; + + // 如果本段是曲线,并且上一个是悬空点 + if (last && !last.onCurve) { + points.splice(points.length - 3, 3); + shapes.splice(shapes.length - 2, 2); + } + // 如果本段是曲线,并且上一个不是悬空点 + else if (last2 && !last2.onCurve) { + points.splice(points.length - 2, 2); + shapes.splice(shapes.length - 2, 2); + } + else { + points.splice(points.length - 1, 1); + shapes.splice(shapes.length - 2, 2); + } + + if (points.length) { + var point = points[points.length - 1]; + this.dashedLine.p0.x = point.x; + this.dashedLine.p0.y = point.y; + } + else { + this.dashedLine.disabled = true; + } + + this.coverLayer.refresh(); + } + }, begin: function () { this.coverLayer.clearShapes(); @@ -136,7 +235,7 @@ define( end: function () { - this.points = this.dashedLine = null; + this.points = this.dashedLine = this.downMouse = this.curCurve = null; this.coverLayer.clearShapes(); this.coverLayer.refresh(); this.render.setCursor('default'); diff --git a/src/editor/mode/shapes.js b/src/editor/mode/shapes.js index ae928e7..4dfaaa3 100644 --- a/src/editor/mode/shapes.js +++ b/src/editor/mode/shapes.js @@ -264,7 +264,7 @@ define( this.currentGroup.refresh(); this.currentGroup.setMode('scale'); - if (prevMode === 'bound') { + if (prevMode === 'bound' || prevMode === 'addpath') { this.clicked = false; } else { diff --git a/src/render/shape/BezierCurve.js b/src/render/shape/BezierCurve.js new file mode 100644 index 0000000..b8507bb --- /dev/null +++ b/src/render/shape/BezierCurve.js @@ -0,0 +1,73 @@ +/** + * @file 绘制bezier曲线 + * @author mengke01(kekee000@gmail.com) + */ + +define( + function (require) { + + var isInsidePath = require('../../graphics/isInsidePath'); + var pathAdjust = require('graphics/pathAdjust'); + var computeBoundingBox = require('graphics/computeBoundingBox'); + + var proto = { + + type: 'beziercurve', + + adjust: function (shape, camera) { + var ratio = camera.ratio; + var x = camera.center.x; + var y = camera.center.y; + pathAdjust(shape.points, ratio, ratio, -x, -y); + pathAdjust(shape.points, 1, 1, x, y); + }, + + move: function (shape, mx, my) { + pathAdjust(shape.points, 1, 1, mx, my); + return shape; + }, + + getRect: function (shape) { + return computeBoundingBox.computePath(shape.points); + }, + + isIn: function (shape, x, y) { + var bound = computeBoundingBox.computePath(shape.points); + if ( + x <= bound.x + bound.width + && x >= bound.x + && y <= bound.y + bound.height + && y >= bound.y + ) { + + return isInsidePath(shape.points, { + x: x, + y: y + }); + } + return false; + }, + + draw: function (ctx, shape) { + var points = shape.points; + // 二次bezier曲线 + if (points.length === 3) { + ctx.moveTo(points[0].x, points[0].y); + ctx.quadraticCurveTo(points[1].x, points[1].y, points[2].x, points[2].y); + } + // 三次bezier曲线 + else if (points.length === 4) { + ctx.moveTo(points[0].x, points[0].y); + ctx.bezierCurveTo( + points[1].x, points[1].y, + points[2].x, points[2].y, + points[3].x, points[3].y + ); + } + } + }; + + + return require('./Shape').derive(proto); + } +); diff --git a/src/render/shape/support.js b/src/render/shape/support.js index 90f3813..117eddb 100644 --- a/src/render/shape/support.js +++ b/src/render/shape/support.js @@ -19,7 +19,8 @@ define( path: require('./Path'), axis: require('./Axis'), graduation: require('./Graduation'), - line: require('./Line') + line: require('./Line'), + beziercurve: require('./BezierCurve') }; return support;