简体   繁体   English

如何在圆形html画布中居中放置字母?

[英]How can I center a letter in circle html canvas?

So far, to do the centering, I am using the following two lines of code: 到目前为止,要进行居中,我正在使用以下两行代码:

ctx.textAlign="center"; 
ctx.textBaseline = "middle";

This almost does the job, but some characters like "g" and "y" are not completely centered. 几乎可以完成工作,但是某些字符(例如“ g”和“ y”)并未完全居中。 How can I make sure that all of them are supported? 如何确保所有这些功能均受支持? Here is a JSbin that shows that the majority of characters like "g" is below the center line. 是一个JSbin,表明大多数字符(例如“ g”)都在中心线下方。

Expectation: 期望: 在此处输入图片说明

Reality: 现实: 在此处输入图片说明

To make my "expectation" work, I subtract 15px from the y value of the letter, but this messes up small letters like "a" and makes them go outside of the bounds on the top. 为了使我的“期望”起作用,我从字母的y值中减去了15px,但这使“ a”这样的小字母弄乱了,并使它们超出了顶部的范围。

Measuring text. 测量文字。

One way is to render the character and then scan the pixels to find the extent, top, bottom, left, and right, to find the real center of the character. 一种方法是渲染角色,然后扫描像素以找到范围,顶部,底部,左侧和右侧,以找到角色的真实中心。

This is an expensive process so added to that you would store the results of previous measurements in a map and return those results for the same character and font. 这是一个非常昂贵的过程,因此您需要将以前的测量结果存储在地图中,并以相同的字符和字体返回这些结果。

The example below creates the object charSizer . 下面的示例创建对象charSizer You set a font charSizer.font = "28px A font" then you can get the information regarding any character. 设置字体charSizer.font = "28px A font"即可获取有关任何字符的信息。 charSizer.measure(char) which returns an object containing information regarding the characters dimensions. charSizer.measure(char)返回包含有关字符尺寸信息的对象。

You can measure characters in production and serve the information to the page to reduce client side processing but you will need to target each browser as they all render text differently. 您可以测量生产中的字符并将信息提供给页面,以减少客户端的处理,但是您将需要针对每个浏览器,因为它们均以不同的方式呈现文本。

Example

The example has instructions. 该示例包含说明。 The left canvas show char render to normal center using ctx.textAlign = "center" and ctx.textBaseline = "middle" . 左画布使用ctx.textAlign = "center"ctx.textBaseline = "middle" char渲染到正常中心。 Also included are color codded lines to show extent, center, bounds center, and weighted center. 还包括彩色拼合线,以显示范围,中心,边界中心和加权中心。 The middle canvas draw the char in circle using bounds center and the right canvas uses weighted center. 中间的画布使用边界中心画圆,而右画布使用加权中心。

This is an example only, untested and not up to production quality. 这仅是示例,未经测试且未达到生产质量。

 const charSizer = (() => { const known = new Map(); var w,h,wc,hc; const workCan = document.createElement("canvas"); const ctx = workCan.getContext("2d"); var currentFont; var fontHeight = 0; var fontId = ""; function resizeCanvas(){ wc = (w = workCan.width = fontHeight * 2.5 | 0) / 2; hc = (h = workCan.height = fontHeight * 2.5 | 0) / 2; ctx.font = currentFont; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillStyle = "black"; } function measure(char){ const info = { char, width : ctx.measureText(char).width, top : null, left : w, right : 0, bottom : 0, weightCenter : { x : 0, y : 0 }, center : { x : 0, y : 0 }, offset : { x : 0, y : 0 }, wOffset : { x : 0, y : 0 }, area : 0, width : 0, height : 0, } ctx.clearRect(0,0,w,h); ctx.fillText(char,wc,hc); const pixels8 = ctx.getImageData(0,0,w,h).data; const pixels = new Uint32Array(pixels8.buffer); var x,y,i; i = 0; for(y = 0; y < h; y ++){ for(x = 0; x < w; x ++){ const pix = pixels[i++]; if(pix){ const alpha = pixels8[(i<<2)+3]; info.bottom = y; info.right = Math.max(info.right, x); info.left = Math.min(info.left, x); info.top = info.top === null ? y : info.top; info.area += alpha; info.weightCenter.x += (x - wc) * (alpha/255); info.weightCenter.y += (y - hc) * (alpha/255); } } } if(info.area === 0){ return {empty : true}; } info.area /= 255; info.weightCenter.x /= info.area; info.weightCenter.y /= info.area; info.height = info.bottom - info.top + 1; info.width = info.right - info.left + 1; info.center.x = info.left + info.width / 2; info.center.y = info.top + info.height / 2; info.offset.x = wc - info.center.x; info.offset.y = hc - info.center.y; info.wOffset.x = -info.weightCenter.x; info.wOffset.y = -info.weightCenter.y; info.top -= hc; info.bottom -= hc; info.left -= wc; info.right -= wc; info.center.x -= wc; info.center.y -= hc; return info; } const API = { set font(font){ currentFont = font; fontHeight = Number(font.split("px")[0]); resizeCanvas(); fontId = font; }, measure(char){ var info = known.get(char + fontId); if(info) { return {...info} } // copy so it is save from change info = measure(char); known.set(char + fontId,info); return info; } } return API; })() //============================================================================== //============================================================================== // Demo code from here down not part of answer code. const size = 160; const sizeh = 80; const fontSize = 120; function line(x,y,w,h){ ctx.fillRect(x,y,w,h); } function hLine(y){ line(0,y,size,1) } function vLine(x){ line(x,0,1,size) } function circle(ctx,col = "red",x= sizeh,y = sizeh,r = sizeh*0.8,lineWidth = 2) { ctx.lineWidth = lineWidth; ctx.strokeStyle = col; ctx.beginPath(); ctx.arc(x,y,r,0,Math.PI * 2); ctx.stroke(); } const ctx = canvas.getContext("2d"); const ctx1 = canvas1.getContext("2d"); const ctx2 = canvas2.getContext("2d"); canvas.width = size; canvas.height = size; canvas1.width = size; canvas1.height = size; canvas2.width = size; canvas2.height = size; canvas.addEventListener("click",nextChar); canvas1.addEventListener("click",nextFont); ctx.font = "20px Arial"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText("Click this canvas", sizeh,sizeh-30); ctx.fillText("cycle", sizeh,sizeh); ctx.fillText("characters", sizeh,sizeh + 30); ctx1.font = "20px Arial"; ctx1.textAlign = "center"; ctx1.textBaseline = "middle"; ctx1.fillText("Click this canvas", sizeh,sizeh - 30); ctx1.fillText("cycle", sizeh,sizeh); ctx1.fillText("fonts", sizeh,sizeh + 30); charSizer.font = "128px Arial"; ctx1.textAlign = "center"; ctx1.textBaseline = "middle"; ctx2.textAlign = "center"; ctx2.textBaseline = "middle"; const chars = "\\"ABCDQWZ{@pqjgw|/*"; const fonts = [ fontSize+"px Arial", fontSize+"px Arial Black", fontSize+"px Georgia", fontSize+"px Impact, Brush Script MT", fontSize+"px Rockwell Extra Bold", fontSize+"px Franklin Gothic Medium", fontSize+"px Brush Script MT", fontSize+"px Comic Sans MS", fontSize+"px Impact", fontSize+"px Lucida Sans Unicode", fontSize+"px Tahoma", fontSize+"px Trebuchet MS", fontSize+"px Verdana", fontSize+"px Courier New", fontSize+"px Lucida Console", fontSize+"px Georgia", fontSize+"px Times New Roman", fontSize+"px Webdings", fontSize+"px Symbol",]; var currentChar = 0; var currentFont = 0; var firstClick = true; function nextChar(){ if(firstClick){ setCurrentFont(); firstClick = false; } ctx.clearRect(0,0,size,size); ctx1.clearRect(0,0,size,size); ctx2.clearRect(0,0,size,size); var c = chars[(currentChar++) % chars.length]; var info = charSizer.measure(c); if(!info.empty){ ctx.fillStyle = "red"; hLine(sizeh + info.top); hLine(sizeh + info.bottom); vLine(sizeh + info.left); vLine(sizeh + info.right); ctx.fillStyle = "black"; hLine(sizeh); vLine(sizeh); ctx.fillStyle = "red"; hLine(sizeh + info.center.y); vLine(sizeh + info.center.x); ctx.fillStyle = "blue"; hLine(sizeh + info.weightCenter.y); vLine(sizeh + info.weightCenter.x); ctx.fillStyle = "black"; circle(ctx,"black"); ctx.fillText(c,sizeh,sizeh); ctx1.fillStyle = "black"; circle(ctx1); ctx1.fillText(c,sizeh + info.offset.x,sizeh+ info.offset.y); ctx2.fillStyle = "black"; circle(ctx2,"blue"); ctx2.fillText(c,sizeh + info.wOffset.x, sizeh + info.wOffset.y); } } function setCurrentFont(){ fontUsed.textContent = fonts[currentFont % fonts.length]; charSizer.font = fonts[currentFont % fonts.length]; ctx.font = fonts[currentFont % fonts.length]; ctx2.font = fonts[currentFont % fonts.length]; ctx1.font = fonts[(currentFont ++) % fonts.length]; } function nextFont(){ setCurrentFont(); currentChar = 0; nextChar(); } 
 canvas { border : 2px solid black; } .red {color :red;} .blue {color :blue;} 
 <canvas id="canvas"></canvas><canvas id="canvas1"></canvas><canvas id="canvas2"></canvas><br> Font <span id="fontUsed">not set</span> [center,middle] <span class=red>[Spacial center]</span> <span class=blue> [Weighted center]</span><br> Click left canvas cycles char, click center to cycle font. Not not all browsers support all fonts 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM