Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

responsive text or text wrapping #97

Open
shidcordero opened this issue May 8, 2020 · 4 comments
Open

responsive text or text wrapping #97

shidcordero opened this issue May 8, 2020 · 4 comments
Assignees
Labels

Comments

@shidcordero
Copy link

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

@zarocknz
Copy link
Owner

zarocknz commented May 10, 2020

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 zarocknz self-assigned this May 10, 2020
@shidcordero
Copy link
Author

shidcordero commented May 10, 2020

@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.

@Frustrated-Programmer
Copy link

Frustrated-Programmer commented Dec 3, 2020

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"
});

@Frustrated-Programmer
Copy link

Frustrated-Programmer commented Dec 3, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants