如何在FabricJS中正确对齐反转的弧形文本?
修复fabric.curvedText反转后的文本对齐问题
当使用fabric.curvedText库的reverse选项时,反转后的弧形文本无法保持和原文本一致的对齐效果(比如居中对齐),问题出在原库计算反转文本的起始角度和定位时没有做对齐补偿。以下是修复方案:
修复思路
通过覆盖fabric.CurvedText的核心渲染方法,在反转文本时重新计算起始角度,确保文本始终围绕中心点对齐,同时调整字符的绘制偏移,让反转后的文本位置和原文本对齐逻辑一致。
修改后的完整代码
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.9/fabric.js"></script> <script src="https://amz-rekognition.s3.amazonaws.com/json/fabric.curvedText.js"></script> <!-- 修复reverse对齐的补丁代码 --> <script> // 覆盖fabric.CurvedText的字符宽度计算 fabric.CurvedText.prototype._getCharWidth = function(char) { var ctx = this.canvas.getContext(); ctx.font = this.fontSize + 'px ' + this.fontFamily; return ctx.measureText(char).width; }; // 覆盖弧形文本渲染逻辑,修复反转对齐 fabric.CurvedText.prototype._renderCurved = function(ctx) { var centerX = this.left + this.width / 2; var centerY = this.top + this.height / 2; var radius = this.radius; var anglePerChar = []; var totalAngle = 0; var chars = this.text.split(''); var maxCharHeight = this.fontSize; // 简化处理,用字号作为字符高度 // 计算每个字符对应的弧度 for (var i = 0; i < chars.length; i++) { var charWidth = this._getCharWidth(chars[i]); var angle = (charWidth + this.spacing) / radius; anglePerChar.push(angle); totalAngle += angle; } // 计算起始弧度,确保文本居中 var startAngle = -totalAngle / 2; if (this.reverse) { startAngle = totalAngle / 2; anglePerChar = anglePerChar.map(a => -a); } var currentAngle = startAngle; ctx.save(); ctx.translate(centerX, centerY); ctx.rotate(-Math.PI / 2); for (var j = 0; j < chars.length; j++) { var char = chars[j]; var angle = anglePerChar[j]; var charWidth = this._getCharWidth(char); ctx.save(); ctx.rotate(currentAngle + angle / 2); if (this.reverse) { // 反转时调整平移和缩放,保证字符正确显示且对齐 ctx.translate(radius, -maxCharHeight / 2); ctx.scale(-1, 1); } else { ctx.translate(radius, -maxCharHeight / 2); } ctx.fillStyle = this.fill; ctx.strokeStyle = this.stroke; ctx.lineWidth = this.strokeWidth; ctx.font = this.fontSize + 'px ' + this.fontFamily; ctx.fillText(char, 0, 0); ctx.strokeText(char, 0, 0); ctx.restore(); currentAngle += angle; } ctx.restore(); }; </script> <canvas id="c" width="400" height="300"></canvas><br/> <input type="text" id="text" value="CurvedText" /><br> Reverse : <input type="checkbox" name="reverse" id="reverse" /><br> Radius : <input type="range" min="0" max="500" value="50" id="radius" /><br> Spacing : <input type="range" min="5" max="40" value="20" id="spacing" /><br> Color : <input type="color" value="blue" id="fill" /><br> Effect : <select name="effect" id="effect" > <option value="curved">Curved</option> <option value="arc">Arc</option> <option value="STRAIGHT">STRAIGHT</option> <option value="smallToLarge">smallToLarge</option> <option value="largeToSmallTop">largeToSmallToped</option> <option value="largeToSmallBottom">largeToSmallBottom</option> <option value="bulge">bulge</option> </select><br> <br> <button id="convert">Convert Text/Curved</button>
CSS
canvas { border:1px solid #000; }
JavaScript
$(function () { canvas = new fabric.Canvas('c'); canvas.on('selection:cleared', onDeSelected); canvas.on('object:selected', onSelected); canvas.on('selection:created', onSelected); var CurvedText = new fabric.CurvedText('CurvedText',{ left: 100, top: 5, textAlign: 'center', fill: '#0000FF', radius: 100, fontSize: 20, spacing: 20, stroke:"#FFFF00", strokeWidth:1 }); var CurvedText1 = new fabric.CurvedText('CurvedText',{ left: 100, top: 100, textAlign: 'center', fill: '#0000FF', radius: 100, fontSize: 20, spacing: 20, stroke:"#FFFF00", strokeWidth:1, reverse:true }); canvas.add(CurvedText).renderAll(); canvas.add(CurvedText1).renderAll(); $('#text').keyup(function(){ var obj = canvas.getActiveObject(); if(obj){ obj.setText(this.value); canvas.renderAll(); } }); $('#reverse').click(function(){ var obj = canvas.getActiveObject(); if(obj){ obj.set('reverse',$(this).is(':checked')); canvas.renderAll(); } }); $('#radius, #spacing').change(function(){ var obj = canvas.getActiveObject(); if(obj){ obj.set($(this).attr('id'),$(this).val()); } canvas.renderAll(); }); $('#effect').change(function(){ var obj = canvas.getActiveObject(); if(obj){ obj.set($(this).attr('id'),$(this).val()); } canvas.renderAll(); }); $('#fill').change(function(){ var obj = canvas.getActiveObject(); if(obj){ obj.setFill($(this).val()); } canvas.renderAll(); }); $('#convert').click(function(){ var props = {}; var obj = canvas.getActiveObject(); if(obj){ if(/curvedText/.test(obj.type)) { default_text = obj.getText(); props = obj.toObject(); delete props['type']; var textSample = new fabric.Text(default_text, props); }else if(/text/.test(obj.type)) { default_text = obj.getText(); props = obj.toObject(); delete props['type']; props['textAlign'] = 'center'; props['radius'] = 50; props['spacing'] = 20; var textSample = new fabric.CurvedText(default_text, props); } canvas.remove(obj); canvas.add(textSample).renderAll(); canvas.setActiveObject(canvas.item(canvas.getObjects().length-1)); } }); }); function onSelected(){ var obj = canvas.getActiveObject(); $('#text').val(obj.getText()); $('#reverse').prop('checked', obj.get('reverse')); $('#radius').val(obj.get('radius')); $('#spacing').val(obj.get('spacing')); $('#fill').val(obj.getFill()); $('#effect').val(obj.getEffect()); } function onDeSelected(){ $('#text').val(''); $('#reverse').prop('checked', false); $('#radius').val(50); $('#spacing').val(20); $('#fill').val('#0000FF'); $('#effect').val('curved'); }
说明
- 补丁代码通过覆盖
fabric.CurvedText的_renderCurved方法,重新计算反转文本的起始弧度,确保文本始终围绕中心点居中对齐。 - 调整了反转时字符的绘制偏移和缩放,保证字符显示方向正确的同时,对齐效果和非反转文本一致。
- 无需修改原库文件,只需在引入原库后添加补丁代码即可生效。
内容的提问来源于stack exchange,提问作者NekoLopez
相关产品推荐
相关产品推荐

