简体   繁体   中英

html5 canvas: auto font size for drawn wrapped rotated text

suppose that there is a text to be drawn inside a rotated bounding rectangle (not aligned to normal axes xy), and that text can be also rotated , given the max width of the bounding box, how to select the best font size to use to draw a wrapped text inside that bounding box in html5 canvas and javascript?

I know that method: measureText () can measure dimensions of give font size, but I need the inverse of that: using a known width to get the problem font size.

thanks

You do not have to find the font point size to make it fit. The font will smoothly scale up and down according to the current transformation scale.

All you do is measureText to find its textWidth , get the pointSize from the context.font attribute then if you have the width and height of the box you need to fit then find the minimum of the width / textWidth and height / pointSize and you have the scale that you need to render the font at.

As a function

var scale2FitCurrentFont = function(ctx, text, width, height){
    var points, fontWidth;
    points = Number(ctx.font.split("px")[0]); // get current point size
    points += points * 0.2; // As point size does not include hanging tails and
                            // other top and bottom extras add 20% to the height
                            // to accommodate the extra bits  
    var fontWidth = ctx.measureText(text).width;
    // get the max scale that will allow the text to fi the current font
    return Math.min(width / fontWidth, height / points);
}

The arguments are

  • ctx is current context to draw to
  • text the text to draw
  • width the width to fit the text to
  • height the height to fit the text to

Returns the scale to fit the text within the width and height.

The demo has it all integrated and it draws random boxes and fills with random text from your question. It keeps the font selection and point size separate from the font scaling so you can see it will work for any font and any point size.

 var demo = function(){ /** fullScreenCanvas.js begin **/ var canvas = (function(){ var canvas = document.getElementById("canv"); if(canvas !== null){ document.body.removeChild(canvas); } // creates a blank image with 2d context canvas = document.createElement("canvas"); canvas.id = "canv"; canvas.width = window.innerWidth; canvas.height = window.innerHeight; canvas.style.position = "absolute"; canvas.style.top = "0px"; canvas.style.left = "0px"; canvas.style.zIndex = 1000; canvas.ctx = canvas.getContext("2d"); document.body.appendChild(canvas); return canvas; })(); var ctx = canvas.ctx; /** fullScreenCanvas.js end **/ /** FrameUpdate.js begin **/ var w = canvas.width; var h = canvas.height; var cw = w / 2; var ch = h / 2; var PI2 = Math.PI * 2; // 360 to save typing var PIh = Math.PI / 2; // 90 // draws a rounded rectangle path function roundedRect(ctx,x, y, w, h, r){ ctx.beginPath(); ctx.arc(x + r, y + r, r, PIh * 2, PIh * 3); ctx.arc(x + w - r, y + r, r, PIh * 3, PI2); ctx.arc(x + w - r, y + h - r, r, 0, PIh); ctx.arc(x + r, y + h - r, r, PIh, PIh * 2); ctx.closePath(); } // random words var question = "Suppose that there is a text to be drawn inside a rotated bounding rectangle (not aligned to normal axes xy), and that text can be also rotated, given the max width of the bounding box, how to select the best font size to use to draw a wrapped text inside that bounding box in html5 canvas and javascript? I know that method: measureText() can measure dimensions of give font size, but I need the inverse of that: using a known width to get the problem font size. thanks."; question = question.split(" "); var getRandomWords= function(){ var wordCount, firstWord, s, i, text; wordCount = Math.floor(rand(4)+1); firstWord = Math.floor(rand(question.length - wordCount)); text = ""; s = ""; for(i = 0; i < wordCount; i++){ text += s + question[i + firstWord]; s = " "; } return text; } // fonts to use?? Not sure if these are all safe for all OS's var fonts = "Arial,Arial Black,Verdanna,Comic Sans MS,Courier New,Lucida Console,Times New Roman".split(","); // creates a random font with random points size in pixels var setRandomFont = function(ctx){ var size, font; size = Math.floor(rand(10, 40)); font = fonts[Math.floor(rand(fonts.length))]; ctx.font = size + "px " + font; } var scale2FitCurrentFont = function(ctx, text, width, height){ var points, fontWidth; var points = Number(ctx.font.split("px")[0]); // get current point size points += points * 0.2; var fontWidth = ctx.measureText(text).width; // get the max scale that will allow the text to fi the current font return Math.min(width / fontWidth, height / points); } var rand = function(min, max){ if(max === undefined){ max = min; min = 0; } return Math.random() * (max - min)+min; } var randomBox = function(ctx){ "use strict"; var width, height, rot, dist, x, y, xx, yy,cx, cy, text, fontScale; // get random box width = rand(40, 400); height = rand(10, width * 0.4); rot = rand(-PIh,PIh); dist = Math.sqrt(width * width + height * height) x = rand(0, ctx.canvas.width - dist); y = rand(0, ctx.canvas.height - dist); xx = Math.cos(rot); yy = Math.sin(rot); ctx.fillStyle = "white"; ctx.strokeStyle = "black"; ctx.lineWidth = 2; // rotate the box ctx.setTransform(xx, yy, -yy, xx, x, y); // draw the box roundedRect(ctx, 0, 0, width, height, Math.min(width / 3, height / 3)); ctx.fill(); ctx.stroke(); // get some random text text = getRandomWords(); // get the scale that will fit the font fontScale = scale2FitCurrentFont(ctx, text, width - textMarginLeftRigth * 2, height - textMarginTopBottom * 2); // get center of rotated box cx = x + width / 2 * xx + height / 2 * -yy; cy = y + width / 2 * yy + height / 2 * xx; // scale the transform xx *= fontScale; yy *= fontScale; // set the font transformation to fit the box ctx.setTransform(xx, yy, -yy, xx, cx, cy); // set up the font render ctx.fillStyle = "Black"; ctx.textAlign = "center"; ctx.textBaseline = "middle" // draw the text to fit the box ctx.fillText(text, 0, 0); } var textMarginLeftRigth = 8; // margin for fitted text in pixels var textMarginTopBottom = 4; // margin for fitted text in pixels var drawBoxEveryFrame = 60; // frames between drawing new box var countDown = 1; // update function will try 60fps but setting will slow this down. function update(){ // restore transform ctx.setTransform(1, 0, 0, 1, 0, 0); // fade clears the screen ctx.fillStyle = "white" ctx.globalAlpha = 1/ (drawBoxEveryFrame * 1.5); ctx.fillRect(0, 0, w, h); // reset the alpha ctx.globalAlpha = 1; // count frames countDown -= 1; if(countDown <= 0){ // if frame count 0 the draw another text box countDown = drawBoxEveryFrame; setRandomFont(ctx); randomBox(ctx); } if(!STOP){ // do until told to stop. requestAnimationFrame(update); }else{ STOP = false; } } update(); } // demo code to restart on resize var STOP = false; // flag to tell demo app to stop function resizeEvent(){ var waitForStopped = function(){ if(!STOP){ // wait for stop to return to false demo(); return; } setTimeout(waitForStopped,200); } STOP = true; setTimeout(waitForStopped,100); } window.addEventListener("resize",resizeEvent); demo(); /** FrameUpdate.js end **/ 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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