最近很想做一个编辑器. 那么, 需要electron. 然后需要参考code mirror, 和monaco, 不过在这之前, 我们可以看看模拟光标是怎么搞的: https://www.zhihu.com/question/36292298/answer/102418523

学习微软好榜样

  • VS Code 的核心代码,也就是 Microsoft/monaco-editor
  • mock光标大家都在干:
    • Ace
    • CodeMirror
    • Atom
最开始这个光标的跳动,是通过 JavaScript 来控制光标的 opacity。后来社区给我们贡献了一个 pull request,使用 CSS animation 来调整 opacity。实现上来说肯定是比 JavaScript 版本更优雅,同时也提供了四五种不同的光标跳动的选项。

过程

参考: https://stackoverflow.com/questions/2678264/how-to-make-an-html-js-wysiwyg-editor

  1. 先用把页面变得可编辑, 可用两种方法之一:
    1. designMode
    2. contentEditable
  2. 然后就可以使用document.execCommand
  3. 接下来就考虑保存innerHTML在一个textarea里面

iframe or div?

  • 其实两个都可以
  • 用iframe更多的是从浏览器兼容, css外泄等等方面考虑.
  • iframe + designMode有更好的效果在很多浏览器上面.
  • div + contentEditable有可能有兼容性问题, 以及某些特定浏览器bug.

######

参考: http://www.developphp.com/video/JavaScript/Building-a-WYSIWYG-Rich-Text-Editor-Textarea-Replacement-Part-2

iframe.contentWindow.document.body.contentEditable= "true";
textarea.value=iframe.contentWindow.document.body.innerHTML;
iframe.document.designMode='on'//这个属性很关键, 是说可编辑
iframe.document.execCommand('bold', false, null)
var linkURL = prompt("Enter the URL for this link:", "http://"); 
richTextField.document.execCommand("CreateLink", false, linkURL);

参考: https://code.tutsplus.com/tutorials/create-a-wysiwyg-editor-with-the-contenteditable-attribute–cms-25657

Document.execCommand()//可以控制contentEditable的元素
document.execCommand(CommandName, ShowDefaultUI, ValueArgument);
// commandname是要执行的命令, 一个字符串
// showdefaultui是一个bool, 是否展示, 这个最好保持false
// valueargument 一个字符串, 作为参数使用, 比如颜色值, url啥的, 如果不用设为null.
https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand

  1. display: none的元素可以得到焦点吗?

    • 结论: 不可以, 但是, 高度宽度定为1像素是可以的.
  2. 如何模拟光标

    @keyframes monaco-cursor-blink {
    	50% {
    		opacity: 0;
    	}
    	100% {
    		opacity: 1;
    	}
    }
    
    .cursor-blink {
    	animation: monaco-cursor-blink 1s step-start 0s infinite;
    }
    .cursor-blink {
    	will-change: opacity; /*用gpu替代cpu*/
    }
    参考: https://github.com/Microsoft/vscode/issues/22900#issuecomment-288529120
    

  3. 如何在textarea中移动光标

    var start = txtarea.selectionStart;
    var end = txtarea.selectionEnd;
    var sel = txtarea.value.substring(start, end);
    txtarea.focus()
    txtarea.selectionEnd= end + 7;
    
    

css animations css动画

参考:

  • https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Animations/Using_CSS_animations
  • https://developer.mozilla.org/zh-CN/docs/Web/CSS/animation

四座大山

  1. monaco
  2. Ace
  3. CodeMirror
  4. Atom

搜索关键字

  • cursor
  • caret
  • mock
  • custom
  • opacity
  • CSS animation

https://news.ycombinator.com/item?id=13941293

如何模拟光标, 看看原文:

google说:

Powerful* text editors built on the web stack cannot rely on the OS text caret and have to provide their own.

In this case, VSCode is probably using the most reasonable approach to blinking a cursor: a step timing-function with a CSS keyframe animation. This tells the browser to only change the opacity every 500ms. Meanwhile, Chrome hasn’t yet optimised this completely yet, hence http://crbug.com/361587.

So currently, Chrome is doing the full rendering lifecycle (style, paint, layers) every 16ms when it should be only doing that work at a 500ms interval. I’m confident that the engineers working on Chrome’s style components can sort this out, but it’ll take a little bit of work. I think the added visibility on this topic will likely escalate the priority of the fix. :)

* Simple text editors, and basic ones built on [contenteditable] can, but those rarely scale to the feature set most want.

(I work on the Chrome team, though not on the rendering engine)

到现在都没关: https://bugs.chromium.org/p/chromium/issues/detail?id=361587

微软说:

原文在这个issue里面:https://github.com/Microsoft/vscode/issues/22900#issuecomment-288529120, 这里是最后的js: https://github.com/Microsoft/vscode/blob/master/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts

Thank you all for looking into this issue. Your investigations and suggestions helped us find the root cause and possible solutions! We compared JavaScript setInterval, animated gif and several other techniques, and we settled on the following solution:

  • If editor.cursorBlinking is set to blink or terminal.integrated.cursorBlinking is set to true, the blinking logic is now implemented in JavaScript. Our testing shows the CPU usage drops to less than 1%.
  • If editor.cursorBlinking is set to smooth, expand or phase, which use CSS easing functions, we’ll stop the blinking animation after the cursor is idle for 10 seconds.

This fix is already in our Insider build and it will be available in our April Stable build which will be released in the next few days. It would be great if some of you could verify our test results. If you see problems, please create a new issue.

gif方法

<img src="http://i.imgur.com/t1MAWCR.gif" width=2 height=10 /> <br>
<img src="http://i.imgur.com/t1MAWCR.gif" width=3 height=30 /> <br>
<img src="http://i.imgur.com/t1MAWCR.gif" width=4 height=40 /> <br>
<style>
img {
  border-radius: 2px;
}
</style>

原文: https://jsfiddle.net/mrkev/stxq613s/1/

更多方法

  • 一个chrome only的方法: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback

css animations方法

参考: https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108

参考

  • execCommand: https://quirksmode.org/dom/execCommand.html

引申出了另一个问题, 如何处理copy paste

<script>
function handlePaste (e) {
    var clipboardData, pastedData;

    // Stop data actually being pasted into div
    e.stopPropagation();
    e.preventDefault();

    // Get pasted data via clipboard API
    clipboardData = e.clipboardData || window.clipboardData;
    pastedData = clipboardData.getData('Text');

    // Do whatever with pasteddata
    alert(pastedData);
}

document.getElementById('editableDiv').addEventListener('paste', handlePaste);
</script>
<div id='editableDiv' contenteditable='true'>Paste</div>

处理undo chain

保持选中状态