zarocknz/javascript-winwheel

responsive text or text wrapping

shidcordero opened this issue · 4 comments

Is there a way to make the text responsive to its canvas?

I currently have this and want to make the text fit on the segment

image

Hi @LorisZ no not really. You will need to set the font size for each of the segments so the text fits nicely, please see http://dougtesting.net/winwheel/docs/tut7_colours_lines_fonts for more details.

Also note you can include \n in the text to get it to break on to a new line.

Regards,
DouG.

@zarocknz but the text is dynamically added. Also, the container of the canvas is responsive. It would be very difficult to size the text inside the segment.

I agree, I actually came to the github to see if there is a way to dynamically edit font size or try my best to hack it.

For @shidcordero sake i'm including my idea on how one could possibly make it dynamic, by editing the fontSize when making the segments.

If anyone figures the math out, lemme know as I'm only working on this challenge in my free time.

let segmentsToBeAdded = [...];
let segments = [];
let angleSize = 360 / segmentsToBeAdded.length;
let margin = 15;
let angleWidth= 400;
let ctx = document.getElementById('canvas').getContext("2d");
function heightOfArc(radius,angle){
    return (((angle / 360) * 2) * Math.PI) * radius;
}
for(let i =0;i<segmentsToBeAdded.length;i++){
    let fontSize = angleWidth;
    let foundSize = false;
    while(!foundSize){
        ctx.font = fontSize + "px Arial";
        let metrics = ctx.measureText(segmentsToBeAdded[i]);
        let fontHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
        let fontWidth = metrics.width;

        /*
Here you have access to the width of the would be text[fontWidth] the height of the text[fontHeight]
and the angle of the arc the text will be in[angleWidth], and you can check whether the font will fit 
via heightOfArc to see if the height can fit inside the height of the arc. Tho you'd need to run heightOfArc 
several times to check each pixel,
        */
        if( ... MATH TO DETECT WHETHER SIZE FITS ...) fontSize--; //Still working on the math, haven't found a solution yet
        else foundSize = true;
    }
    segments.push({
        "text":segmentsToBeAdded[i],
        "textFontSize": fontSize,
    })
}
new winWheel({
    "outerRadius": angleWidth,
    "margin":  15,
     "textAlignment": "outer"
    "segments": segments,
    "canvasId": "canvas"
});

So I had an amazing idea when I wrote my comment, but wanted to test it before including it, and it works!!!
If you use

//Simple math function to get length of an arc.
function heightOfArc(radius,angle){
    return (((angle / 360) * 2) * Math.PI) * radius;
}
/**
 * NOTE: this only works when using the winWheel param: "textAlignment": "outer",
 * @param {number} props.fontSize               - the fontSize to test the text
 * @param {number} props.wheelWidth             - the width of the wheel
 * @param {number} props.margin                 - the margin on the wheel
 * @param {number} props.angle                  - the angle of the arc
 * @param {string} props.text                   - the text to test
 * @param {CanvasRenderingContext2D} props.ctx  - the 2d context
 * @return {boolean}
 */
function fitsInsideArc(props){
    let arcWidth = props.wheelWidth - props.margin;
    props.ctx.font = props.fontSize + "px Arial";
    let metrics = props.ctx.measureText(props.text);
    let fontHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
    let fontWidth = metrics.width;
    if(fontWidth > arcWidth) return false;
    for(let i = arcWidth - fontWidth;i<arcWidth;i++){
        if(fontHeight > heightOfArc(i,props.angle)) return false;
    }
    return true;
}

Then use the code in my previous comment and replace this line:

 if( ... MATH TO DETECT WHETHER SIZE FITS ...) fontSize--; //Still working on the math, haven't found a solution yet

with this new code:

if(!fitsInsideArc({fontSize,margin,angle:arcSize,wheelWidth:angleWidth,text:segmentsToBeAdded[i],ctx})) fontSize--;

Attached is the end result, all the text is dynamic, and the angle size is dynamic, yet they all fit even with different text sizes.
chrome_i1CsUCygZc
Note: I know some of the text overlaps just a TAD, but I cannot think of a way to fix this, as it's not really easy to get the text height. In fact, to get the text's height you need a modern browser as some browsers do not support advance text metrics