From 86dbd1a82f090455f7f564159e50ecd52435abb7 Mon Sep 17 00:00:00 2001 From: kekee000 Date: Sat, 2 Jul 2016 23:41:23 +0800 Subject: [PATCH] add editor --- build.sh | 5 + css/editor.css | 333 ++++++++++++++++++++++++++++ css/editor.less | 18 ++ demo/editor-iframe.html | 37 ++++ demo/js/editor-iframe.js | 155 +++++++++++++ editor.html | 33 +++ edp-build-config.js | 5 +- module.conf | 3 +- src/editor/command/editor.js | 22 +- src/editor/command/transform.js | 6 +- src/editor/controller/initRender.js | 2 +- src/editor/mode/shapes.js | 2 +- src/fonteditor/editor.js | 88 ++++++++ 13 files changed, 693 insertions(+), 16 deletions(-) create mode 100644 css/editor.css create mode 100644 css/editor.less create mode 100644 demo/editor-iframe.html create mode 100644 demo/js/editor-iframe.js create mode 100644 editor.html create mode 100644 src/fonteditor/editor.js diff --git a/build.sh b/build.sh index d0f901d..935627d 100644 --- a/build.sh +++ b/build.sh @@ -22,10 +22,15 @@ build_asset() { # 移动文件到指定目录 move_asset() { mv ./release/src ./release/$version + cat ./release/index.html | sed -e "s#'\.\/src'#'./$version'#g" > ./release/index.html.tmp mv ./release/index.html.tmp ./release/index.html + cat ./release/index-en.html | sed -e "s#'\.\/src'#'./$version'#g" > ./release/index-en.html.tmp mv ./release/index-en.html.tmp ./release/index-en.html + + cat ./release/editor.html | sed -e "s#'\.\/src'#'./$version'#g" > ./release/editor.html.tmp + mv ./release/editor.html.tmp ./release/editor.html } build_index diff --git a/css/editor.css b/css/editor.css new file mode 100644 index 0000000..9f35bcc --- /dev/null +++ b/css/editor.css @@ -0,0 +1,333 @@ +html, +body { + height: 100%; +} +/** + * @file 重置样式 + * @author mengke01(kekee000@gmail.com) + */ +* { + margin: 0; + padding: 0; + list-style: none; +} +blockquote, +body, +button, +dd, +dl, +dt, +fieldset, +form, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +input, +legend, +li, +ol, +p, +pre, +td, +textarea, +th, +ul { + margin: 0; + padding: 0; +} +body, +button, +input, +select, +textarea { + font: 12px/1.5 tahoma, arial, sans-serif; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 100%; +} +address, +cite, +dfn, +em, +var { + font-style: normal; +} +code, +kbd, +pre, +samp { + font-family: courier new, courier, monospace; +} +small { + font-size: 12px; +} +ol, +ul { + list-style: none; +} +a { + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +legend { + color: #000; +} +fieldset, +img { + border: 0; +} +button, +input, +select, +textarea { + font-size: 100%; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +@font-face { + font-family: 'fonteditor'; + src: url('../../font/fonteditor.ttf') format('truetype'); +} +.ico { + display: inline-block; +} +.ico:before, +.ico:after { + font-family: 'fonteditor'; + font-style: normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.1px; +} +.i-leave:before { + content: '\e00c'; +} +.i-edit, +.i-del, +.i-leave { + font-size: 12px; +} +.i-edit:hover, +.i-del:hover, +.i-leave:hover { + cursor: pointer; + color: #4A90E2; +} +.i-edit, +.i-del { + color: #76abe9; +} +.i-edit:before { + content: '\e021'; +} +.i-del:before { + content: '\e020'; +} +.i-github:before { + content: '\e01e'; +} +.i-help:before { + content: '\e016'; +} +.i-new:before { + content: '\e019'; +} +.i-open:before { + content: '\e01A'; +} +.i-add:before { + content: '\e003'; +} +.i-undo:before { + content: '\e001'; +} +.i-redo:before { + content: '\e002'; +} +.i-down:before { + content: '\e00e'; +} +.i-left:before { + content: '\e00a'; +} +.i-ttf:before { + content: '\e00f'; +} +.i-woff:before { + content: '\e010'; +} +.i-zip:before { + content: '\e011'; +} +.i-save:before { + content: '\e022'; +} +.i-upshape:before { + content: '\e014'; +} +.i-downshape:before { + content: '\e00b'; +} +.i-reversepoints:before { + content: '\e00d'; +} +.i-alignleft:before { + content: '\e006'; +} +.i-aligncenter:before { + content: '\e004'; +} +.i-alignright:before { + content: '\e007'; +} +.i-aligntop:before { + content: '\e008'; +} +.i-alignmiddle:before { + content: '\e005'; +} +.i-aligndescent:before { + content: '\e009'; +} +.i-alignbaseline:before { + content: '\e009'; +} +.i-rotateleft:before { + content: '\e01c'; +} +.i-rotateright:before { + content: '\e01d'; +} +.i-flip:before { + content: '\e013'; +} +.i-mirror:before { + content: '\e012'; +} +.i-splitshapes:before { + content: '\e024'; +} +.i-joinshapes:before { + content: '\e025'; +} +.i-intersectshapes:before { + content: '\e026'; +} +.i-tangencyshapes:before { + content: '\e027'; +} +.i-rangemode:before { + content: '\e029'; +} +.i-pointmode:before { + content: '\e028'; +} +.glyf-editor { + width: 100%; + height: 100%; + font-size: 12px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + display: none; +} +.glyf-editor .marker-x, +.glyf-editor .marker-y { + position: absolute; + width: 0; + height: 0; + z-index: 60; + pointer-events: none; +} +.glyf-editor .marker-x { + width: 20px; + border-top: 1px dashed #000; +} +.glyf-editor .marker-y { + height: 20px; + border-left: 1px dashed #000; +} +.editor-contextmenu { + color: #333; + width: 150px; + border: 1px solid #999; + background: #FEFEFE; + line-height: 24px; + padding: 0 4px; + box-shadow: 1px 1px 1px #CCC; + position: absolute; +} +.editor-contextmenu li { + padding-left: 4px; + cursor: pointer; + border-bottom: 1px solid #CCC; +} +.editor-contextmenu li > ul { + display: none; + color: #333; + width: 150px; + border: 1px solid #999; + background: #FEFEFE; + line-height: 24px; + padding: 0 4px; + box-shadow: 1px 1px 1px #CCC; + position: absolute; + margin-left: 130px; +} +.editor-contextmenu > li[data-sub] { + color: #4A90E2; +} +.editor-contextmenu > li[data-sub]:after { + content: '>'; + margin-right: 10px; + float: right; + font-family: 'Simsun'; +} +.editor-contextmenu li[data-tag="selected"] { + display: inline-block; + display: block; +} +.editor-contextmenu li[data-tag="selected"]:before, +.editor-contextmenu li[data-tag="selected"]:after { + font-family: 'fonteditor'; + font-style: normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.1px; +} +.editor-contextmenu li[data-tag="selected"]:after { + content: '\e01f'; +} +.editor-contextmenu li[data-tag="selected"]:after { + float: right; + margin-right: 4px; + color: #4A90E2; +} +.editor-contextmenu li:hover { + background: #EEE; +} +.editor-contextmenu li:hover > ul { + display: block; +} +.editor-contextmenu li:last-child { + border-bottom: none; +} +.editor-panel { + width: 100%; + height: 100%; + font-size: 12px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} diff --git a/css/editor.less b/css/editor.less new file mode 100644 index 0000000..5e901e1 --- /dev/null +++ b/css/editor.less @@ -0,0 +1,18 @@ +html, +body { + height: 100%; +} + +@import './widget/reset.less'; +@import './widget/ico.less'; +@import './widget/glyf-editor.less'; + + +.editor-panel { + width: 100%; + height: 100%; + font-size: 12px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} diff --git a/demo/editor-iframe.html b/demo/editor-iframe.html new file mode 100644 index 0000000..3340837 --- /dev/null +++ b/demo/editor-iframe.html @@ -0,0 +1,37 @@ + + + + + 测试editor远程调用 + + + + + + + + + + + + + + diff --git a/demo/js/editor-iframe.js b/demo/js/editor-iframe.js new file mode 100644 index 0000000..4e3fd39 --- /dev/null +++ b/demo/js/editor-iframe.js @@ -0,0 +1,155 @@ +/** + * @file iframe 调用编辑器 + * @author mengke01(kekee000@gmail.com) + */ + + +define(function (require) { + + /** + * 用于远程 editor 编辑器接口,由于使用postMessage进行跨域调用, + * 这里接口都采用异步化 + * + * @param {Object} options 参数 + * @param {jQuery|HTMLElement} options.main 主元素,iframe对象或者jquery对象 + * @param {string=} options.url editor接口 + */ + function Editor(options) { + this.main = $(options.main); + this.url = options.url || '../editor.html'; + this.key = 'editor-' + Math.random(); + // 给 editor页面传递 key id,以便于通信的时候根据key来进行通信 + this.onMessage = $.proxy(this.onMessage, this); + this.readyList = []; + + // 注册默认监听的事件 + this.events = { + load: function () { + var me = this; + this.readyList.forEach(function (promise) { + promise.resolve(me); + }); + this.readyList = null; + this.loaded = true; + } + }; + + var url = this.url + (this.url.indexOf('?') >= 0 ? '&' : '?') + 'key=' + this.key; + var frame = this.main[0]; + frame.src = url; + window.addEventListener('message', this.onMessage); + } + + /** + * 编辑器准备就绪时的事件 + * + * @return {Promise} + */ + Editor.prototype.ready = function () { + var promise = $.Deferred(); + if (this.loaded) { + promise.resolve(this); + } + else { + this.readyList.push(promise); + } + return promise; + }; + + /** + * 设置字体格式 + * + * @param {Object} font 字体格式 + * @return {Promise} + */ + Editor.prototype.setFont = function (font) { + return this.postMessage('setFont', [font]); + }; + + /** + * 获取字体格式 + * + * @return {Promise} + */ + Editor.prototype.getFont = function () { + return this.postMessage('getFont'); + }; + + /** + * 执行命令 + * + * @param {string} name 命令 + * @param {Array?} args 命令参数列表 + * @return {Promise} + */ + Editor.prototype.execCommand = function () { + return this.postMessage('execCommand', [].slice.call(arguments)); + }; + + /** + * 获取editor对象,用来进行同域时候的精细化调用 + * + * @return {Editor} + */ + Editor.prototype.getEditor = function () { + return this.main[0].contentWindow.editor; + }; + + Editor.prototype.postMessage = function (name, args) { + var stamp = Date.now(); + if (this.events[stamp]) { + throw new Error('call proxy function to quickly:' + name); + } + + this.events[stamp] = $.Deferred(); + this.main[0].contentWindow.postMessage({ + key: this.key, + name: name, + stamp: stamp, + data: args || null + }, '*'); + return this.events[stamp]; + }; + + Editor.prototype.onMessage = function (e) { + if (e.data.key === this.key) { + var name = e.data.name; + var data = e.data.data; + var stamp = e.data.stamp; + if (stamp && this.events[stamp]) { + this.events[stamp].resolve(data); + delete this.events[stamp]; + } + else if (this.events[name]) { + this.events[name].call(this, data) + } + } + }; + + Editor.prototype.dispose = function () { + this.main.remove(); + this.events = this.main = null; + }; + + + + var shape_baidu = require('demo/../data/contours-5'); + var editor = new Editor({ + main: '.editor-frame' + }) + + + editor.ready().then(function (editor) { + editor.setFont(shape_baidu).then(function () { + editor.execCommand('rescale', 0.5) + }) + }) + + $('#get-font').on('click', function () { + editor.getFont().then(function (data) { + console.log(data) + }) + }) + + return {}; +}); diff --git a/editor.html b/editor.html new file mode 100644 index 0000000..0971c71 --- /dev/null +++ b/editor.html @@ -0,0 +1,33 @@ + + + + + Editor + + + + +
+ + + + + + + + diff --git a/edp-build-config.js b/edp-build-config.js index 9515a23..40f558d 100644 --- a/edp-build-config.js +++ b/edp-build-config.js @@ -11,6 +11,7 @@ exports.getProcessors = function () { new LessCompiler({ files: [ 'css/main.less', + 'css/editor.less', 'css/preview.less' ], entryFiles: [] @@ -30,7 +31,8 @@ exports.getProcessors = function () { new ModuleCompiler( { files: [ - 'src/fonteditor/main.js' + 'src/fonteditor/main.js', + 'src/fonteditor/editor.js' ], configFile: './module.conf' }), @@ -39,6 +41,7 @@ exports.getProcessors = function () { new JsCompressor({ files: [ 'src/fonteditor/main.js', + 'src/fonteditor/editor.js', 'dep/**/*.js', '!dep/**/*.min.js', ] diff --git a/module.conf b/module.conf index 6e2b49d..df8f590 100644 --- a/module.conf +++ b/module.conf @@ -13,6 +13,7 @@ } ], "combine": { - "fonteditor/main": 1 + "fonteditor/main": 1, + "fonteditor/editor": 1 } } diff --git a/src/editor/command/editor.js b/src/editor/command/editor.js index dcb8844..e9b286c 100644 --- a/src/editor/command/editor.js +++ b/src/editor/command/editor.js @@ -17,10 +17,10 @@ define( /** * 重置缩放 */ - rescale: function () { + rescale: function (scale) { this.coverLayer.clearShapes(); var size = this.render.getSize(); - var scale = 512 / this.options.unitsPerEm; + scale = scale || (512 / this.options.unitsPerEm); this.render.scaleTo(scale, { x: size.width / 2, @@ -130,14 +130,16 @@ define( rightSideBearing = (this.rightSideBearing.p0.x - box.x - box.width) / scale; } - this.fire('setting:font', { - setting: { - leftSideBearing: Math.round(leftSideBearing), - rightSideBearing: Math.round(rightSideBearing || 0), - unicode: this.font.unicode, - name: this.font.name - } - }); + if (this.font) { + this.fire('setting:font', { + setting: { + leftSideBearing: Math.round(leftSideBearing), + rightSideBearing: Math.round(rightSideBearing || 0), + unicode: this.font.unicode, + name: this.font.name + } + }); + } } }; } diff --git a/src/editor/command/transform.js b/src/editor/command/transform.js index 4ee1f04..b08dc96 100644 --- a/src/editor/command/transform.js +++ b/src/editor/command/transform.js @@ -58,6 +58,7 @@ define( * @return {boolean} `false`或者`undefined` */ rotateleft: function (shapes) { + shapes = shapes || (this.currentGroup && this.currentGroup.shapes); return support.rotate.call(this, shapes, -Math.PI / 2); }, @@ -68,6 +69,7 @@ define( * @return {boolean} `false`或者`undefined` */ rotateright: function (shapes) { + shapes = shapes || (this.currentGroup && this.currentGroup.shapes); return support.rotate.call(this, shapes, Math.PI / 2); }, @@ -78,7 +80,7 @@ define( * @return {boolean} `false`或者`undefined` */ flipshapes: function (shapes) { - + shapes = shapes || (this.currentGroup && this.currentGroup.shapes); if (!shapes || !shapes.length) { return false; } @@ -97,7 +99,7 @@ define( * @return {boolean} `false`或者`undefined` */ mirrorshapes: function (shapes) { - + shapes = shapes || (this.currentGroup && this.currentGroup.shapes); if (!shapes || !shapes.length) { return false; } diff --git a/src/editor/controller/initRender.js b/src/editor/controller/initRender.js index f491ed7..760d759 100644 --- a/src/editor/controller/initRender.js +++ b/src/editor/controller/initRender.js @@ -43,7 +43,7 @@ define( this.fire('save'); break; default: - this.execCommand(e.command, e); + this.execCommand(e.command); } } diff --git a/src/editor/mode/shapes.js b/src/editor/mode/shapes.js index 5d76d8c..ae94201 100644 --- a/src/editor/mode/shapes.js +++ b/src/editor/mode/shapes.js @@ -69,7 +69,7 @@ define( default: // 是否编辑器支持 if (this.supportCommand(command)) { - this.execCommand(command, e); + this.execCommand(command); } } } diff --git a/src/fonteditor/editor.js b/src/fonteditor/editor.js new file mode 100644 index 0000000..03e2708 --- /dev/null +++ b/src/fonteditor/editor.js @@ -0,0 +1,88 @@ +/** + * @file 编辑器导出接口,用于编辑字形相关的矢量图形 + * @author mengke01(kekee000@gmail.com) + */ + +define(function (require) { + var editor = require('../editor/main'); + var options = require('../editor/options'); + var commandList = require('../editor/menu/commandList'); + + // 用来做跨域通信的密钥 + var EDITOR_KEY = require('../common/lang').parseQuery(location.search.slice(1)).key || 'fonteditor'; + + /** + * 向引用方外部发送事件 + * @param {string} name 名称 + * @param {Object} data 数据 + * @param {number} stamp 某次通信的信标,用以区分多次事件 + */ + function fireEvent(name, data, stamp) { + parent.postMessage({ + key: EDITOR_KEY, + name: name, + stamp: stamp, + data: data || null + }, '*'); + return this; + } + + function onMessage() { + window.addEventListener('message', function (e) { + if (!e.data.key === EDITOR_KEY) { + return; + } + + var name = e.data.name; + var args = e.data.data; + if (typeof window.editor[name] === 'function') { + var data = window.editor[name].apply(window.editor, args); + if (e.data.stamp) { + fireEvent(name, data === window.editor ? null : data, e.data.stamp); + } + } + }); + } + + function removeCommandMenu(name, menu) { + for (var i = menu.length - 1; i >= 0; i--) { + if (menu[i].name === name) { + menu.splice(i, 1); + return true; + } + else if (menu[i].items) { + if (removeCommandMenu(name, menu[i].items)) { + return true + } + } + } + return false; + } + + function initEditor() { + removeCommandMenu('fontsetting', commandList.editor) + removeCommandMenu('save', commandList.editor) + removeCommandMenu('moresetting', commandList.editor) + + options.editor.unitsPerEm = 1024; + options.editor.axis.metrics = { + ascent: 812, // 上升 + descent: -212, // 下降 + xHeight: 400, // x高度 + capHeight: 700 // 大写字母高度 + }; + var context = editor.create($('#editor-panel').get(0), options); + return context; + } + + function main() { + onMessage(); + var context = initEditor(); + context.focus(); + window.editor = context; + fireEvent('load'); + } + main(); + + return {}; +});