texteditor.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <!-- 必须强制指定页面编码为 UTF-8 -->
  5. <meta charset="utf-8">
  6. <!-- Demo 的简要说明,必须要填写 -->
  7. <meta name="description" content="Kity 实现模拟光标输入">
  8. <!-- Demo 的作者,建议填写 -->
  9. <meta name="author" content="kity@baidu.com">
  10. <!-- Demo 的标题,必须填写 -->
  11. <title>模拟光标</title>
  12. <!-- Demo 开发过程中使用 CMD 引入 Kity 源码,方便调试 -->
  13. <!-- dev start -->
  14. <!-- 目录型的 Demo 注意修正源码引用路径 -->
  15. <script src="../dev-lib/sea.js"></script>
  16. <script>
  17. // 设置好 kity 源代码目录
  18. seajs.config( { base: '../src'} );
  19. // 定义 Demo 模块
  20. define('demo', function(require) { require('kity'); });
  21. </script>
  22. <script>
  23. // 启动 Demo 模块
  24. seajs.use('demo');
  25. </script>
  26. <!-- dev 版本 示例所需脚本文件 end -->
  27. <script src="public/jquery.js"></script>
  28. <style>
  29. .editor-container{
  30. }
  31. .receiver{
  32. position:absolute;
  33. padding:0;
  34. margin:0;
  35. word-wrap:break-word;
  36. clip:rect(1em 1em 1em 1em);
  37. }
  38. </style>
  39. </head>
  40. <body>
  41. <script>
  42. var Cursor = kity.createClass('Cursor',{
  43. base: kity.Line,
  44. constructor: function(height, color, width) {
  45. this.callBase();
  46. this.height = height || 20;
  47. this.stroke(color || 'blue', width || 1);
  48. this.setHide();
  49. this.timer = null;
  50. },
  51. setPosition: function(offset) {
  52. try{
  53. this.setPoint1(offset.x,offset.y);
  54. this.setPoint2(offset.x,offset.y + this.height);
  55. }catch(e){
  56. debugger
  57. }
  58. return this;
  59. },
  60. setHeight:function(height){
  61. this.height = height;
  62. },
  63. setHide:function(){
  64. clearInterval(this.timer);
  65. this.setStyle('display','none');
  66. return this;
  67. },
  68. setShowHold : function(){
  69. clearInterval(this.timer);
  70. this.setStyle('display','');
  71. return this;
  72. },
  73. setShow:function(){
  74. clearInterval(this.timer);
  75. var me = this,
  76. state = '';
  77. this.timer = setInterval(function(){
  78. me.setStyle('display',state);
  79. state = state ? '' : 'none';
  80. },300);
  81. return this;
  82. },
  83. setTextShape:function(text){
  84. if(!text){
  85. this.text = new kity.Text();
  86. }else{
  87. this.text = text;
  88. }
  89. return this
  90. },
  91. getTextShape:function(){
  92. return this.text
  93. },
  94. setTxtContent : function(text){
  95. this.text.setContent(text)
  96. },
  97. updatePosition:function(index){
  98. }
  99. });
  100. var Receiver = kity.createClass('Receiver',{
  101. clear : function(){
  102. this.$container.html('');
  103. this.index = 0;
  104. return this;
  105. },
  106. constructor : function(){
  107. this.$container = $('<div contenteditable="true" class="receiver"></div>');
  108. this.$container.appendTo(document.body);
  109. this.$container.on('keydown keypress keyup', $.proxy(this.keyboardEvents,this));
  110. this.timer = null;
  111. this.index = 0;
  112. },
  113. setPosition : function(textShapeOffset){
  114. this.$container.css({
  115. top:textShapeOffset.x,
  116. left:textShapeOffset.y
  117. });
  118. this.textShape.setPosition(textShapeOffset.x, textShapeOffset.y);
  119. return this;
  120. },
  121. setRange : function(range,index){
  122. this.index = index || this.index;
  123. var text = this.$container[0].firstChild;
  124. console.log(text)
  125. console.log(this.index)
  126. range.setStart(text || this.$container[0], this.index).collapse(true);
  127. setTimeout(function(){
  128. range.select()
  129. });
  130. return this;
  131. },
  132. setTextShape:function(textShape){
  133. if(!textShape){
  134. textShape = new kity.Text();
  135. }
  136. this.textShape = textShape;
  137. return this;
  138. },
  139. setTextShapeSize:function(size){
  140. this.textShape.setSize(size);
  141. return this;
  142. },
  143. getTextShapeHeight:function(){
  144. return this.textShape.getRenderBox().height;
  145. },
  146. appendTextShapeToPaper:function(paper){
  147. paper.addShape(this.textShape);
  148. return this;
  149. },
  150. keyboardEvents : function(e){
  151. clearTimeout(this.timer);
  152. var me = this;
  153. switch(e.type){
  154. case 'keyup':
  155. this.textShape.setContent(this.$container.text().replace(/\u200b/g,''));
  156. this.updateTextData();
  157. this.updateCursor();
  158. this.timer = setTimeout(function(){
  159. me.cursor.setShow()
  160. },500);
  161. break;
  162. case 'keypress':
  163. case 'keyup':
  164. }
  165. },
  166. updateTextData : function(){
  167. this.textShape.textData = this.getTextOffsetData();
  168. this.index = this.index + 1;
  169. },
  170. setCursor : function(cursor){
  171. this.cursor = cursor;
  172. return this;
  173. },
  174. updateCursor : function(){
  175. this.cursor.setShowHold();
  176. if(this.index == this.textData.length){
  177. this.cursor.setPosition({
  178. x : this.textData[this.index-1].x + this.textData[this.index-1].width,
  179. y : this.textData[this.index-1].y
  180. })
  181. }else if(this.index == 0){
  182. this.cursor.setPosition({
  183. x : this.textShape.getX(),
  184. y : this.textData.getY()
  185. })
  186. }else{
  187. if(this.index + 1 == this.textData.length){
  188. var lastChar = this.textData[this.index];
  189. this.cursor.setPosition({
  190. x : lastChar.x + lastChar.width,
  191. y : lastChar.y
  192. })
  193. }else{
  194. this.cursor.setPosition(this.textData[this.index])
  195. }
  196. }
  197. return this;
  198. },
  199. getTextOffsetData:function(){
  200. var text = this.textShape.getContent();
  201. this.textData = [];
  202. // this.textShape.clearContent();
  203. for(var i= 0,l = text.length;i<l;i++){
  204. var box = this.textShape.getExtentOfChar(i);
  205. this.textData.push({
  206. x:box.x,
  207. y:box.y,
  208. width:box.width,
  209. height:box.height
  210. })
  211. }
  212. return this;
  213. },
  214. setCurrentIndex:function(offset){
  215. var me = this;
  216. this.getTextOffsetData();
  217. var hadChanged = false;
  218. $.each(this.textData,function(i,v){
  219. if(offset.x >= v.x && offset.x <= v.x + v.width){
  220. if(offset.x - v.x > v.width/2){
  221. me.index = i + 1;
  222. }else {
  223. me.index = i
  224. }
  225. hadChanged = true;
  226. return false;
  227. }
  228. })
  229. return this;
  230. },
  231. setCursorHeight:function(){
  232. this.cursor.setHeight(this.getTextShapeHeight())
  233. return this;
  234. }
  235. });
  236. var Range = kity.createClass('Range',{
  237. constructor : function(){
  238. this.nativeRange = document.createRange();
  239. this.nativeSel = window.getSelection();
  240. },
  241. select:function(){
  242. var start = this.nativeRange.startContainer;
  243. if(start.nodeType == 1 && start.childNodes.length == 0){
  244. var char = document.createTextNode('\u200b');
  245. start.appendChild(char);
  246. this.nativeRange.setStart(char,1);
  247. this.nativeRange.collapse(true);
  248. }
  249. this.nativeSel.removeAllRanges();
  250. this.nativeSel.addRange(this.nativeRange);
  251. return this;
  252. },
  253. setStart:function(node,index){
  254. this.nativeRange.setStart(node,index);
  255. return this;
  256. },
  257. setEnd:function(node,index){
  258. this.nativeRange.setEnd(node,index);
  259. return this;
  260. },
  261. collapse:function(toStart){
  262. this.nativeRange.collapse(toStart === true);
  263. return this;
  264. },
  265. insertNode:function(node){
  266. this.nativeRange.insertNode(node);
  267. return this;
  268. }
  269. });
  270. var cursor = new Cursor();
  271. var receiver = new Receiver();
  272. var range = new Range();
  273. var paper = new kity.Paper(document.body).setHeight(500);
  274. paper.addShape(cursor)
  275. .setStyle('border','1px solid #ccc')
  276. .setStyle('cursor','text')
  277. .on('click',function(e){
  278. var originEvent = e.originEvent;
  279. var position = e.getPosition('top');
  280. if(e.targetShape.getType() != 'Text'){
  281. var offset = e.getPosition('top');
  282. cursor.setShow().setPosition(offset);
  283. receiver.clear()
  284. .setTextShape()
  285. .setTextShapeSize(cursor.height)
  286. .appendTextShapeToPaper(paper)
  287. .setPosition(position)
  288. .setRange(range,0)
  289. .setCursor(cursor)
  290. }else{
  291. receiver.setCursor(cursor)
  292. .setTextShape(e.targetShape)
  293. .setCursorHeight()
  294. .setCurrentIndex(position)
  295. .updateCursor()
  296. .setRange(range)
  297. }
  298. })
  299. </script>
  300. </body>
  301. </html>