Anki Forums

Random font for multiple fields, code clean-up help

Hey all, I’ve been adding different snippets of code to my card design and ended up with a lot of different randomizers. Everything is random, font, color, size, position, background. The font is randomized with an extension which invokes QFontDatabase and associates it to a < class >. The rest is all code which I obtained over months of testing. The thing is the code is very inefficient and sometimes doesn’t work correctly. In fact, I want to make the code work for multiple fields but it isn’t. If anyone can help me with this, I’m linking the pastebin with the full code so nothing gets lost here. The extension I’m using for random fonts is this one: 1603486068

The main three fields which need to be randomized are Expression, Simplified and Traditional.
I made three addons so the QFontDatabase fetches three different font sets and associates them to three different classes being that Expression is a Japanese field and the others are Traditional and Simplified Chinese respectively (QFontDatabase.Japanese, etc).

I’m not a coding expert and I don’t know how to reorganize the parts so they work correctly.
The code: random all anki - Pastebin.com

If anyone’s willing to help poor me to make this work I’ll make an honorable effigy in my memory to them haha
Thanks!

I think you might need to be more specific about what you want the code to do that it isn’t doing. You “want to make the code work for multiple fields but it isn’t”—what is the code supposed to do, and what is it doing instead?

You’re completely right! I’m very sorry. The problem I’m having the most issues with is that the font randomization of each of the fields works, but not together with the color randomization.
What I mean is, it randomizes the font but the color stays black, in fact it never changes to any other color. It’s supposed to change each letter to a different random color. What also doesn’t work correctly is the field shuffling (order of fields should be random in each note).
Sorry for not explaining this. What I think is that the < div > elements or maybe smth else are badly coded or not expressed correctly. I don’t know code but I can edit to some extent, maybe the problem lies there.
Thanks for your help

I’m not 100% sure how you’re using this card (I assume it’s a “back” template, since it has all three fields on it?), but I cleaned up the HTML and JavaScript a bit and came up with this:

<div id="yomis" hidden>
    <p>
        <div class="randomfont shuffle colorize">{{Expression}}</div>
        <div class="randomfont shuffle colorize">{{Simplified}}</div>
        <div class="randomfont shuffle colorize">{{Traditional}}</div>
        <div id="container"></div>
    </p>
</div>

<script>
function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}

function setRandomBackgroundColor() {
    const x = Math.floor(Math.random() * 256);
    const y = Math.floor(Math.random() * 256);
    const z = Math.floor(Math.random() * 256);
    const bgColor = "rgb(" + x + "," + y + "," + z + ")";
    document.body.style.background = bgColor;
}

function randomHsl(isNightMode) {
    const hue = (Math.random() * 361) | 0;
    const saturation = (Math.random() * 101) | 0;
    let lightness = (Math.random() * 51) | 0;
    if (isNightMode) {
        lightness += 50;
    }
    return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}

function colorizeEachChar(element) {
    const text = element.textContent;
    const nightMode = document.body.classList.contains("night_mode");
    const resultArray = [];
    for (const char of text) {
        if (/\S/.test(char)) {
            resultArray.push(
                `<span style="color: ${randomHsl(nightMode)};">${char}</span>`
            );
        } else {
            resultArray.push(" ");
        }
    }
    element.innerHTML = resultArray.join("");
}

setTimeout(() => {
    // Randomize the alignment and size of the text

    const alignments = ["justify","left","center","right"];
    const alignmentindex = Math.floor(Math.random()*alignments.length);

    const size = Math.floor(Math.random()*20)+Math.floor(Math.random()*60)+Math.floor(Math.random()*70)+10;

    const yomis = document.getElementById("yomis");
    yomis.style.fontSize = size+"px";
    yomis.style.textAlign = alignments[alignmentindex];

    // Randomize the color of each character

    document.querySelectorAll(".colorize").forEach(colorizeEachChar);

    // Randomize the order of the elements with the "shuffle" class

    const fields = [...document.querySelectorAll(".shuffle")];
    shuffleArray(fields);
    const container = document.getElementById("container");
    fields.forEach((fld, index) => {
        if (index !== 0) {
            container.appendChild(document.createElement("br"));
        }
        container.appendChild(fld);
    });

    // Randomize the background color

    setRandomBackgroundColor();

    // Un-hide the main container

    yomis.hidden = false;
}, 0);
</script>

This seemed to work well in some quick testing in Firefox. The color of each character is randomized… the result is truly garish :wink: Hope this helps!

2 Likes

THANK YOU! It’s working splendidly. The code also looks really professional now. Thank you so so much…

Great, glad I could help!

Was wondering, is it possible to make the field text choose from brighter colors if the background has a darker color and viceversa? So they contrast more? It’s sometimes difficult to read the text due to the colors being too similar between the field and the background color. Thanks

Try replacing the setRandomBackgroundColor and randomHsl functions with these versions:

function setRandomBackgroundColor() {
    const nightMode = document.body.classList.contains("night_mode");
    document.body.style.background = randomHsl(!nightMode);
}

// If lightColor is true, the resulting color will be "light" (L value above
// 65). Otherwise, it will be "dark" (L value below 35).
function randomHsl(lightColor) {
    const hue = (Math.random() * 361) | 0;
    const saturation = (50 + (Math.random() * 50)) | 0;
    let lightness = (Math.random() * 35) | 0;
    if (lightColor) {
        lightness += 65;
    }
    return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}

This will always use a dark background under night mode and a light background otherwise. The text will be light if the background is dark, and vice versa. It isn’t perfect—human perception of color is pretty complex to model, and I’m not at all an expert. The text should blend into the background a lot less often, though.

Sorry I didn’t respond earlier. This works splendidly! Thanks a lot. Was wondering, is it possible to have the text shuffle also between vertical and horizontal?
const alignments = [“justify”,“left”,“center”,“right”];

Which option would allow me to have vertical text as an option? Or an even better option, having it randomly choose if it appears horizontal or vertical and in one of those positions. For example
Horizontal+left, center+vertical, etc.
Is this doable?
Thanks a lot for your help!

I’m not sure, I don’t have any experience with vertically-oriented text. If it’s possible to do it with CSS, though, I’m sure you can adapt what you already have to make it happen!