Creating custom UI components

My wife is probably my biggest critic. And I can't remember how many times she told me "man I don't like these standard UI elements. Why don't you make them a lot more -child friendly- ?"

Now I do understand what she means, because I also develop in unity3d and there have used in the past UI elements with a lot more appeal for kids (read, more animated etc.)

So before I make new apps, I first have another goal; Trying to create my own UI elements to maka my wife happy :slight_smile: . That can't be bad.

Today I started with something simple (I think) because I created my own buttons.
AFAIK they work ok , so now I was wondering if what I did was a good way to go and maybe some people have suggestions for me?

The source code is here

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Zim UI test</title>

</head>

<body>
    <h2>Zim.js UI test</h2>
    <script type="module">

        import "https://zimjs.org/cdn/019/zim";

        const frame = new Frame(FIT, 1920, 1080, white, "#eeeeee", ready, ["red_rect_button.png", "green_rect_button.png", "yellow_rect_button.png"], "./", new ProgressBar());
        function ready(frame, stage) {


            function createButton(color, caption, buttonWidth, buttonHeight) {

                const buttonContainer = new Container(buttonWidth, buttonHeight).reg(buttonWidth / 2, buttonHeight / 2, true);

                const bitmap = new SlicedBitmap(buttonWidth, buttonHeight, new Pic(color + "_rect_button.png"), [[0.498, 0.502], [0.498, 0.502]], [[0, 1, 0], [0, 1, 0]]).addTo(buttonContainer);
                bitmap.sha("rgba(0,0,0,.5)", 5, 5, 10);
                new Label({
                    text: caption,
                    color: white,
                    size: 30,
                    align: CENTER,
                    valign: CENTER
                }).addTo(buttonContainer).center();

                buttonContainer.on("rollover", function () {
                    buttonContainer.animate({
                        props: [
                            { props: { scaleX: 1.1 }, time: 0.07 },
                            { props: { scaleX: 1.0, scaleY: 1.1 }, time: 0.07 },
                            { props: { scaleX: 1.1, scaleY: 1.1 }, time: 0.07 }
                        ],
                        ease: "linear"
                    });
                });

                buttonContainer.on("rollout", function () {
                    buttonContainer.animate({
                        props: { scale: 1 },
                        time: .2,
                        ease: zimEase([1.2, 1.5, 1.3, 1])
                    });
                });

                buttonContainer.on("click", function () {
                    buttonContainer.animate({
                        props: [
                            { props: { scaleX: 1.085, scaleY: 0.8 }, time: 0.13 },
                            { props: { scaleX: 0.89, scaleY: 1.23 }, time: 0.07 },
                            { props: { scaleX: 1.10, scaleY: 0.94 }, time: 0.12 },
                            { props: { scaleX: 0.96, scaleY: 1.0 }, time: 0.10 },
                            { props: { scaleX: 1.0, scaleY: 1.0 }, time: 0.10 }
                        ],
                        ease: "linear"
                    });
                });
                return buttonContainer;
            }

            const button1 = createButton("green", "Button 1", 300, 120).pos(300, 600);
            const button2 = createButton("yellow", "Button 2", 300, 120).pos(830, 600);
            const button3 = createButton("red", "Button 3", 300, 120).pos(1360, 600);

            button1.on("click", function () {
                zog("button clicked");
            });

            button2.animate({
                props: { y: 300 },
                time: 2,
                ease: "easeInOut",
                rewind: true,
                loop: true
            });

            button3.animate({
                props: { rotation: 360 },
                time: 5,
                ease: "linear",
                loop: true
            });

        } // end ready

    </script>


</body>

</html>

The result can also be seen on Zim UI test

I plan on making some more parameters for the buttons, but I do like the overall look.

Is this a good way to make these components?

Next on the planning is checkboxes. Still figuring out how to do that.

If I do things terribly bad, then please tell me. I am not a coding genius or whatever. I try to make it work.

Ow and while you read this. If you go to the url above, then you see that I let my third button rotate. Do you see some "strange" behavior in the borders? I see some strange subtle deformations I'll prpbably have to learn to live with ?

Kind regards,
Bart

2 Likes

Cool - I do not see anything strange in the rotating border.

You can also do something like:

class FunButton extends Container {
    constructor(color=red, caption="click", buttonWidth=100, buttonHeight=50) {
        super(buttonWidth, buttonHeight); // call super class 
        this.backing = new SlicedBitmap(buttonWidth, buttonHeight, etc)
            .addTo(this);
    }
    doSomething() {
        // method
    }
}
new FunButton().center();

Thanks for the info Dan. I changed the code to

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Zim UI test</title>
</head>

<body>
    <h2>Zim.js UI test</h2>

    <script type="module">
        import "https://zimjs.org/cdn/019/zim";

        const frame = new Frame(FIT, 1920, 1080, white, "#eeeeee", ready, ["red_rect_button.png", "green_rect_button.png", "yellow_rect_button.png"], "./", new ProgressBar());

        function ready(frame, stage) {

            class FunButton extends Container {
                constructor(color = "red", caption = "click", buttonWidth = 262, buttonHeight = 102) {
                    super(buttonWidth, buttonHeight);

                    this.reg(buttonWidth / 2, buttonHeight / 2, true);

                    const bitmap = new SlicedBitmap(buttonWidth, buttonHeight, new Pic(color + "_rect_button.png"), [[0.498, 0.502], [0.498, 0.502]], [[0, 1, 0], [0, 1, 0]]).addTo(this);

                    bitmap.sha("rgba(0,0,0,.5)", 5, 5, 10);

                    new Label({
                        text: caption,
                        color: white,
                        size: 30,
                        align: CENTER,
                        valign: CENTER
                    }).addTo(this).center();

                    this.on("rollover", () => {
                        this.animate({
                            props: [
                                { props: { scaleX: 1.1 }, time: 0.07 },
                                { props: { scaleX: 1.0, scaleY: 1.1 }, time: 0.07 },
                                { props: { scaleX: 1.1, scaleY: 1.1 }, time: 0.07 }
                            ],
                            ease: "linear"
                        });
                    });

                    this.on("rollout", () => {
                        this.animate({
                            props: { scale: 1 },
                            time: 0.2,
                            ease: zimEase([1.2, 1.5, 1.3, 1])
                        });
                    });

                    this.on("click", () => {
                        this.animate({
                            props: [
                                { props: { scaleX: 1.085, scaleY: 0.8 }, time: 0.13 },
                                { props: { scaleX: 0.89, scaleY: 1.23 }, time: 0.07 },
                                { props: { scaleX: 1.10, scaleY: 0.94 }, time: 0.12 },
                                { props: { scaleX: 0.96, scaleY: 1.0 }, time: 0.10 },
                                { props: { scaleX: 1.0, scaleY: 1.0 }, time: 0.10 }
                            ],
                            ease: "linear"
                        });
                    });
                }
            }

            const button1 = new FunButton("green", "Button 1", 300, 120).pos(300, 600);
            const button2 = new FunButton("yellow", "Button 2", 300, 120).pos(830, 600);
            const button3 = new FunButton("red", "Button 3", 300, 120).pos(1360, 600);

            // extra click handler in addition to the built-in click animation
            button1.on("click", () => {
                zog("button clicked");
            });

            button2.animate({
                props: { y: 300 },
                time: 2,
                ease: "easeInOut",
                rewind: true,
                loop: true
            });

            button3.animate({
                props: { rotation: 360 },
                time: 5,
                ease: "linear",
                loop: true
            });
        }
    </script>
</body>

</html>

Now this is probably also the ideal way to go to for example create a checkbox with a "checked"-property and getter and setter functions with visual feedback. Nice thanks

2 Likes