HTML5画布:自动字体大小绘制包裹旋转的文本
问题描述:
假设有一个旋转的边界矩形(未对准,以正常轴XY)内的被绘制的文本,并且文本也可以被旋转, 给出的最大宽度的边框,如何选择最佳的字体大小用来画内一个换行文本,在HTML5画布和JavaScript边框的?HTML5画布:自动字体大小绘制包裹旋转的文本
我知道方法:measureText()可以测量给出的字体大小的尺寸,但我需要的是相反的:使用已知宽度来获取问题字体大小。
谢谢
答
您不必查找字体大小以使其合适。根据当前的转换规模,字体将平滑地放大和缩小。
你要做的就是measureText
找到它textWidth
,从context.font
属性得到pointSize
那么如果你有width
,你需要适应,然后找出最小的width/textWidth
和height/pointSize
的盒子height
,你有规模你需要渲染字体。
作为功能
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);
}
的参数是
- CTX是当前上下文提请
- 文本文本绘制
- 宽度宽度使文字符合
- 高度高度与文本适合
返回规模以适应宽度和高度中的文本。
该演示将所有内容整合在一起,并绘制随机框,并用随机文本填充问题。它使字体选择和点大小与字体缩放分开,所以你可以看到它可以适用于任何字体和任何点的大小。
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 x-y), 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 **/
奈斯利高效! :-) – markE
谢谢Blindman67,你的回答非常有帮助,我会在我的代码上试试。非常感谢 –