邢台pc网站开发,电子政务门户网站建设汇报,兼职网站制作,惠州网站建设文章目录 概要效果技术细节代码 概要
Canvas上面提供输入#xff1a;
一、最简单可能是用dom渲染一个input,覆盖在图形上面进行文本编辑#xff0c;编辑完再把内容更新到图形.这样简单#xff0c;但是缺点也明显#xff0c;就是它不是真正绘制在canvas上面#xff0c;没… 文章目录 概要效果技术细节代码 概要
Canvas上面提供输入
一、最简单可能是用dom渲染一个input,覆盖在图形上面进行文本编辑编辑完再把内容更新到图形.这样简单但是缺点也明显就是它不是真正绘制在canvas上面没有层级。体验感较差
Fabric框架的思路大概是这样的。
二、如果要用自己是完全实现键盘响应、撤消/重做、文本样式/布局、光标/选中区。那也有点难度。
三、还有一种就是利用contentEditable和textarea元素在这些元素上面进行一些事件监听和文本内容处理。最重要的是保证canvas的字体样式要与元素的字体样式一样这样才能利用textarea的兴标和选中区体系。不然的话就自己完完全全实现。
我下面实现纯canvas绘制的就是利用textarea的键盘响应和光标体系包括选中块。大楖就是保证canvas与元素之间这种些重要属性做好同步。
下面看一下效果因为也是花了二三个小时弄了一个比较简单的,输入选中光标指定位置生入性能也是可以输入响应很快
效果 。 技术细节
技术细节有一个地方我是这样做的。因为我也没有借鉴别人的就昨天突然闲随手写了一下。
就是文本选中高亮检测到selectionStart和selectionEnd不相等的情况下就证明是选中区。
选中区的文本颜色和背景要高亮如果做文本计算的话那有点麻烦而且性能也不好/。
我是这样做的。
文本先以默认颜色绘制一遍然后把选中区作为剪切区域。再清空剪切区域的旧文本。再以高亮的颜色背景文本再绘制一遍。就行了这样很简单不需要做额外文本处理了.
其它的像光标位置做到以下几点
输入时保持textarea的光标位置与canvas的同步
单点canvas文本框的时候做一下坐标计算算出光标位置然后同步给textarea元素。 选中时候也需要同步给textarea元素并且textarea也要选中这个区域。这样保证选中区删除文本或插入文本保持一致 代码
不用管 canvasShapeRender这wh 这是之前写一个类似figma的渐变调节器小小的封装了一下没有别的功能 var renderer new CanvasShapeRender(container, {width: 500,height: 500,background: #efefef})renderer.add(new RichInputEditor({owner: renderer,x: 100,y: 100}))renderer.requestDraw() class RichInputEditor2 extends CanvasShape {constructor(opts {}) {super({type: group,...opts})this.minWidth Math.max(200, this.width)this.minHeight Math.max(30, this.height)this.width this.minWidththis.height this.minHeightlet scope this;this.selectionStart 0this.selectionEnd 0;this.curLine 0;// 光标所在行this.curX 0// this.curX0 // 光标x轴位置this.lineHeight 20this._focus false;// 光标x轴位置let getCursorX () {let texts this.text.texts;if (this.curLine texts.length) {return 0}if (this.selectionStart this.selectionEnd) {return this.text.getPositionFromOffsetAndLine(this.selectionStart,this.curLine)}return 0}let getCursorLine () {let line this.text.getLineFromPosition(this.selectionStart)return line}let run false;let updateCursor () {if (run) {return}run true;Promise.resolve().then(() {this.selectionStart this._textarea.selectionStartthis.selectionEnd this._textarea.selectionEndthis.widththis._textarea.scrollWidththis.heightthis._textarea.scrollHeightthis.curLine getCursorLine()this.curX getCursorX()run false})}let border this.border this.addShape({type: rect,x: 0,y: 0,fillStyle: #fff,strokeStyle: #000,cursor:text,beforeUpdate() {// scope.widthMath.max(minWidth,text.getTextMaxWidth())this.width scope.widththis.height scope.height},mousedown(e) {let [x,y]this.transformLocalCoord(e.downPoint.x, e.downPoint.y)this.__selectionStartnullsetTimeout(() {scope._textarea.focus()scope._focus truelet selectionStartscope.text.getSelectionFromPosition(x,y)scope.selectionStartselectionStartscope.selectionEndselectionStartscope._textarea.selectionStartselectionStartscope._textarea.selectionEndselectionStartthis._selectionStartselectionStartupdateCursor()this.owner.requestDraw()})e.stop()},drag(e){if(this._selectionStartnull){return}let [x,y]this.transformLocalCoord(e.point.x, e.point.y)let _selectionEndscope.text.getSelectionFromPosition(x,y)let _selectionStartthis._selectionStartlet selectionStartMath.min(_selectionStart,_selectionEnd)let selectionEndMath.max(_selectionStart,_selectionEnd)console.log(scope.selectionStart,selectionStart,selectionEnd)scope.selectionStartselectionStartscope.selectionEndselectionEndthis.owner.requestDraw()},mouseup(){if(scope.selectionStart!scope.selectionEnd){scope._textarea.setSelectionRange(scope.selectionStart,scope.selectionEnd)}this.owner.requestDraw()}})let text this.text this.addShape({silent:true,type: text,x: 2,// ignore:true,fillStyle: #000,textBaseline: middle,font: normal normal normal normal 14px sans-serif,beforeUpdate() {//this.lineHeight80this.textOffset[0,scope.lineHeight * 0.6]},})let selectionAreanew CanvasShapePath2D({silent:true,visible:false,fillStyle:#0000ff,beforeUpdate(){this.visiblescope.selectionStart!scope.selectionEndscope._focuslet pointstext.getSelectAreaFromSelection(scope.selectionStart,scope.selectionEnd)console.log(selectionArea,this.visible)this.fromMultiPolygon(points)}})this.add(selectionArea)let lightText this.addShape({type: text,x: 2,silent:true,visible:false,clipClearCanvas:true,fillStyle: #fff,textBaseline: middle,drawClip(ctx){if(this.clipPath){ctx.beginPath()ctx.clip(this.clipPath)ctx.fillStyleselectionArea.fillStylectx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height)}},beforeUpdate() {this.fonttext.font//this.lineHeight80this.textOffset[0,scope.lineHeight * 0.6]this.textstext.textsthis.setFontProperties(text)if(selectionArea.visible){this.clipPathselectionArea.path2dthis.visibletrue;}else{this.clipPathnulllightText.visiblefalse}// this.heightscope.height},})text.setTextContent(fdasfdsaffdsfadasfdafdas\nabcdefg)let cursor this.cursor this.addShape({type: line,x0: 0,y0: 0,x0: 0,y1: 30,x: 2,strokeStyle: #000,beforeUpdate() {this.visiblescope.selectionStartscope.selectionEndscope._focusthis.x 2 scope.curXthis.y scope.curLine * scope.lineHeightthis.y1 scope.lineHeightthis.lineHeight scope.lineHeight},})this._textarea document.createElement(textarea)this._textarea.style.position absolutethis._textarea.style.top 0pxthis._textarea.style.left -1000pxthis._textarea.style.boxSizing border-boxthis._textarea.style.width this.width pxthis._textarea.style.height this.height pxthis._textarea.style.userSelectnonethis._textarea.valuetext.getTextContent()text.bindDomTextStyle(this._textarea)// this._textarea.style.opacity0this._textarea.addEventListener(input, (e) {let texts e.target.value.split(/\n/)this.text.setTextContent(texts) updateCursor()this.owner.requestDraw()})document.body.appendChild(this._textarea)}ownerCreate() {this.owner.onMousedown () {if (this._focus) {this._focus falsethis.owner.requestDraw()}}let loop () {if (this._focus) {// this.syncTextareaToCanvas()this.cursor.ignore !this.cursor.ignorethis.owner.requestDraw()}setTimeout(loop, 800)}loop()}}