Mechanical counter display

Even with just new, I see same issue. But works with demo - ODD ??
Will see if I can firure out why :crazy_face:

PS I see it wasn't easy - will take some time to understand your script !

Thing is - it is rapid development in public and it is not a node to rely on any quality. Specially there is nothing done in backward compatible manner. So if something added or removed and it makes old versions not working it is as is without any warnings.

If you have done some modifications previously to make it look and behave as you wish - too bad. You'll need to do it again or find the missing bits. Yes I can help if you share your modifications and describe what you are trying to achieve.

1 Like

I figured it out, the old version worked with numbers longer than the display, the new version doesn't.

My meter readings had a lot of digits after the decimal point, so I added this to your script to trim it to fit the number of digits

let data = msg.payload.toString().slice(0,count).split('')

What should be done to make the digits scroll from top to bottom or bottom to top depending on the digits are incrementing or decrementing ?

time_temp2

Well, this looks just too frickin' awesome!

I've never done transitions with Vue, so I had to try. I did a FlexDash custom widget for one digit to get started. Here's how it looks:

recording

I commented out the "visibility: hidden" so one can see what is happening. Also, at the bottom you can see the values for "current" and "next" as well as when the animation is active.

HotNipi's version has some nifty queuing when changes come in faster than the animation, but I noticed that without that extra complexity the next digit that scrolls in simply changes on-the-fly, which looks perfectly fine to me. You can see that when it counts up past 5.

Now what I need is to be able to easily give this one-digit component a name so I can plop it down N times for a whole number...

The flow to produce this:

[{"id":"4ba2dad94e5ffd6f","type":"flexdash custom","z":"451d9acba8e52c2f","fd_container":"28d1859702cdf729","fd_cols":"2","fd_rows":"2","fd_array":false,"fd_array_max":10,"fd_output_topic":"","fd_loopback":false,"name":"HotNipi Meter Digit","sfc_source":"<template>\n    <div class=\"slot\" v-bind=\"$attrs\">\n        <Transition @after-enter=\"animation_end\">\n            <!-- transition causes these elements to animate when enabled/disabled -->\n            <div v-if=\"moving\" :class=\"cc+' next'\">{{num_next}}</div>\n            <div v-else        :class=\"cc+' current'\">{{num_current}}</div>\n        </Transition>\n    </div>\n    <!-- information below digit for testing -->\n    <div>\n        <span v-if=\"moving\">moving</span>\n        cur={{num_current}} -> next={{num_next}}\n    </div>\n</template>\n\n<script>\nexport default {\n    props: {\n        payload: { type: Number, default: 0 },\n        color: { type: String, default: \"white\" },\n    },\n\n    data() { return {\n        size: 4, // font size multiplier\n        speed: 0.95, // speed in seconds\n        num_next: 0, // digit to scroll in\n        num_current: this.payload, // current digit\n        moving: false, // transition is happening\n    }},\n\n    watch: {\n        payload(pl) {\n            this.num_next = pl // put the new digit into 'next'\n            this.moving = true // start scrolling 'next' into view\n        },\n    },\n\n    computed: {\n        cc() { return `text-${this.color} num` }, // material design color class\n    },\n\n    methods: {\n        // at end of animation swap next/current back, ready for next time\n        animation_end() {\n            this.num_current = this.num_next\n            this.moving = false\n        },\n    },\n}\n</script>\n\n<style scoped>\n.slot {\n    height: calc(v-bind(size) * 1em);\n    width: calc(v-bind(size) * .8em);\n    text-align: center;\n    line-height: calc(v-bind(size) * 1em);\n    outline: 1px solid #4d4d4d;\n    outline-offset: -3px;\n    border: 3px solid #000000a3;\n    border-radius: 6px;\n    position: relative;\n    overflow: visible; /* should be hidden */\n    background: linear-gradient(0deg, #161616, #7e7e7e, #161616);\n}\n\n.num {\n    position: absolute;\n    left: 0;\n    right: 0;\n    user-select: none;\n    font-size: calc(v-bind(size) *.7em);\n}\n\n/* scale to the left of the digit */\n.num:after {\n    content: \"\";\n    position: absolute;\n    left: -2%;\n    width: 20%;\n    height: 100%;\n    background: linear-gradient(to bottom,\n            #00000050,\n            #00000050 50%,\n            transparent 50%,\n            transparent);\n    background-size: 100% 7%;\n}\n\n/* scale to the right of the digit */\n.num:before {\n    content: \"\";\n    position: absolute;\n    right: -2%;\n    width: 20%;\n    height: 100%;\n    background: linear-gradient(to bottom,\n            #00000050,\n            #00000050 50%,\n            transparent 50%,\n            transparent);\n    background-size: 100% 7%;\n}\n\n/* animation stuff */\n\n/* define the transitions: transition \"top\" for this.speed seconds */\n.v-enter-active,\n.v-leave-active {\n    transition: top calc(v-bind(speed) *1s) ease-in;\n}\n/* current is normally at 0% (i.e. in the box) and when leaving goes to 100% (below the box) */\n.current { top: 0%; } \n.current.v-leave-to { top: 100%; }\n/* next is normally at 0% (but hidden) and when entering comes from above the box */\n.next { top: 0%; }\n.next.v-enter-from { top: -100%; }\n\n</style>","x":490,"y":1240,"wires":[[]]},{"id":"82a4294aeb925edc","type":"inject","z":"451d9acba8e52c2f","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"next","payloadType":"str","x":110,"y":1240,"wires":[["4f654ea12604e2c5"]]},{"id":"4f654ea12604e2c5","type":"function","z":"451d9acba8e52c2f","name":"up/down","func":"let v = context.get('v')||0\nv = (v+1) % 18\ncontext.set('v', v)\nif (v < 10) return { payload: v, color: \"amber\"}\nelse return { payload: 18-v, color: \"green-darken-1\" }\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":1240,"wires":[["4ba2dad94e5ffd6f"]]},{"id":"28d1859702cdf729","type":"flexdash container","name":"charts","kind":"StdGrid","fd_children":",d2dbe97c44f6fb5a,d2b3883a74adc563,ab904c40d22f83e1,527401ee602f4386,4f31b9c704e0b947,a8dfa00ae270ac88,b27685878ffe3711,8713c375979525c8,5021eeaf697e64c2,02a8193ebdb37b8f,4ba2dad94e5ffd6f","title":"","tab":"9f23b0158f8280e3","min_cols":"1","max_cols":"20","unicast":"","parent":"","solid":false,"cols":"1","rows":"1"},{"id":"9f23b0158f8280e3","type":"flexdash tab","name":"charts","icon":"mdi-chart-line","title":"charts","fd_children":",28d1859702cdf729","fd":"e8f5aea52ab49500"}]

And since some of you have not yet installed FlexDash :roll_eyes: here's the code for the custom widget:

<template>
    <div class="slot" v-bind="$attrs">
        <Transition @after-enter="animation_end">
            <!-- transition causes these elements to animate when enabled/disabled -->
            <div v-if="moving" :class="cc+' next'">{{num_next}}</div>
            <div v-else        :class="cc+' current'">{{num_current}}</div>
        </Transition>
    </div>
    <!-- information below digit for testing -->
    <div>
        <span v-if="moving">moving</span>
        cur={{num_current}} -> next={{num_next}}
    </div>
</template>

<script>
export default {
    props: {
        payload: { type: Number, default: 0 },
        color: { type: String, default: "white" },
    },

    data() { return {
        size: 4, // font size multiplier
        speed: 0.95, // speed in seconds
        num_next: 0, // digit to scroll in
        num_current: this.payload, // current digit
        moving: false, // transition is happening
    }},

    watch: {
        payload(pl) {
            this.num_next = pl // put the new digit into 'next'
            this.moving = true // start scrolling 'next' into view
        },
    },

    computed: {
        cc() { return `text-${this.color} num` }, // material design color class
    },

    methods: {
        // at end of animation swap next/current back, ready for next time
        animation_end() {
            this.num_current = this.num_next
            this.moving = false
        },
    },
}
</script>

<style scoped>
.slot {
    height: calc(v-bind(size) * 1em);
    width: calc(v-bind(size) * .8em);
    text-align: center;
    line-height: calc(v-bind(size) * 1em);
    outline: 1px solid #4d4d4d;
    outline-offset: -3px;
    border: 3px solid #000000a3;
    border-radius: 6px;
    position: relative;
    overflow: visible; /* should be hidden */
    background: linear-gradient(0deg, #161616, #7e7e7e, #161616);
}

.num {
    position: absolute;
    left: 0;
    right: 0;
    user-select: none;
    font-size: calc(v-bind(size) *.7em);
}

/* scale to the left of the digit */
.num:after {
    content: "";
    position: absolute;
    left: -2%;
    width: 20%;
    height: 100%;
    background: linear-gradient(to bottom,
            #00000050,
            #00000050 50%,
            transparent 50%,
            transparent);
    background-size: 100% 7%;
}

/* scale to the right of the digit */
.num:before {
    content: "";
    position: absolute;
    right: -2%;
    width: 20%;
    height: 100%;
    background: linear-gradient(to bottom,
            #00000050,
            #00000050 50%,
            transparent 50%,
            transparent);
    background-size: 100% 7%;
}

/* animation stuff */

/* define the transitions: transition "top" for this.speed seconds */
.v-enter-active,
.v-leave-active {
    transition: top calc(v-bind(speed) *1s) ease-in;
}
/* current is normally at 0% (i.e. in the box) and when leaving goes to 100% (below the box) */
.current { top: 0%; } 
.current.v-leave-to { top: 100%; }
/* next is normally at 0% (but hidden) and when entering comes from above the box */
.next { top: 0%; }
.next.v-enter-from { top: -100%; }

</style>

Hey, @hotNipi, what would it take to get you to install FlexDash and produce some nifty components? :firecracker: :firecracker: :firecracker: :joy:

3 Likes

The widget is dummy. You can freely display alphabet or what ever signs the browser can render as text. The bigger/smaller concept isn't always natural.
For numbers for sure it would be possible but I think it takes to introduce another rule - the incoming payload can't be changed more than smallest display unit. And that's unreasonable requirement for general purpose widget.

1 Like

Well, you have done most of it already. You asked nicely and you pointed out how my ideas do look like in the Vue world. Remaining problem is just the distance between me and Vue. (Earth - Mars ?) And there you can't help much cos it can be only me to start going.

To bring any quality it takes to know the environment well, but as I don't ...

4 Likes

Hmmm, I already had asked @hotnipi yesterday (behind the scenes) if he could also do some cool stuff with Flexdash... Now you asking the same. Hopefully he is aware that we asked it completely independent from each other, otherwise yet another conspiracy theory might get started on Twitter :joy:

And of course we need to take into account his allergic reactions for web frameworks like Vue :wink:. Which I can understand a bit, since he is a true artist without them...

2 Likes

Don't drag me into the Twitter please :smiley:

Even if my Vue learning curve is not even started yet, I'm not completely useless. Most of the fanciness of my content is actually CSS. That part I can do no matter the framework. My art skills of course are not high level but I can recognize when the art is missing. @BartButenaers already has some experiences working with me in those terms and must admit, it turned out nice. (multi-state switch if you remember)
So you know ..

5 Likes