Closed farzher closed 6 months ago
Hi @farzher,
Thanks for reaching out! Do you have some code to illustrate the problem that you're encountering? If possible, I can try to see how it can be fixed.
I think you are looking for something like this
const {a, div, li, p, ul} = van.tags
const mainObj = {
i: 0,
text: 'Hello'
}
const stateObj = Object.fromEntries(Object.entries(mainObj).map(([k,v]) => [k, van.state(v)]));
function update() {
for (const key of Object.keys(mainObj)) {
if (stateObj[key].val !== mainObj[key]) {
stateObj[key].val = mainObj[key]
}
}
}
function recursive() {
if (mainObj.text.length > 15) {
return
}
const rand = Math.round(Math.random() * 26)
mainObj.text += String.fromCharCode(65 + rand)
mainObj.i += rand
recursive()
}
van.add(document.body, div(stateObj.i), div(stateObj.text))
recursive()
update()
Hi @farzher,
Thanks for reaching out! Do you have some code to illustrate the problem that you're encountering? If possible, I can try to see how it can be fixed.
sure, one use case i have is rendering UI on top of a webgl game canvas.
i have a game state. i have a render function. i simply want to redraw my UI every frame, like a game would.
here's some example code i have of rendering usernames. it mostly works but it's very hairy
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/vanjs-org/van/public/van-1.2.8.nomodule.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vanjs-ext@0.2.2/dist/van-x.nomodule.min.js"></script>
<style>
html,body,* {margin:0;padding:0;overflow:hidden;}
body {background: rgb(26,26,26);}
</style>
<body></body>
<script>
// some example state
const me = {user_id:1, x: 0, y: 0}
const lobby_state = {}
const chat_bubbles = vanX.reactive({})
chat_bubbles[1] = {username: 'farzher', msg: 'sup', user_id: 1}
chat_bubbles[2] = {username: 'Guest', msg: 'hello', user_id: 2}
lobby_state[2] = {x: 50, y: 50, user_id: 2}
window.requestAnimationFrame(update)
function update() {
window.requestAnimationFrame(update)
// simulate player walking around
for(const user_id in lobby_state) {
const player = lobby_state[user_id]
player.x += 1
if(player.x > 200) player.x = -200
}
}
// called many times per second
window.requestAnimationFrame(render)
function render() {
window.requestAnimationFrame(render)
render_chat_bubbles()
}
const manualrender = van.state(0)
function render_chat_bubbles() {
manualrender.val++
}
function chat_bubbles_vanjs() {
const {div, a, code, img, input, hr, span} = van.tags
const van_if = (bool, val) => bool ? val : div()
return (
div({style: 'pointer-events: none'},
vanX.list(div, chat_bubbles, ({val: msginfo}) => {
const w = window.innerWidth
const h = window.innerHeight
const is_me = msginfo.user_id == me.user_id
if(is_me) return div({class: 'speech-bubble', style: () => `manualrender:${manualrender.val}; max-width:240px; max-height:100px; padding:10px; text-align:center; position:absolute; left:${w/2}px; top:${h/2 - 80}px; transform: translate(-50%, -50%); color: white;`}, msginfo.msg)
// the chat bubble is not from me, we need to render it on the player
const entity = lobby_state[msginfo.user_id]
return div({class: 'speech-bubble', style: () => `manualrender:${manualrender.val}; max-width:240px; max-height:100px; padding:10px; text-align:center; position:absolute; left:${entity.x + w/2 - me.x}px; top:${entity.y + h/2 - 80 - me.y}px; transform: translate(-50%, -50%); color: white;`}, msginfo.msg)
})
)
)
}
van.add(document.body, chat_bubbles_vanjs())
</script>
If you want to avoid updating the variable immediately after editing, you need to use additional state
here is an example https://jsfiddle.net/Sirenko/suzw85o3/
Hi @farzher,
Normally it might not be a good idea to re-render the entire UI. Making the re-rendering as localized as possible is usually better for performance and user experience.
But in case you do want global re-rendering for the entire UI, you can try this pattern:
const frameNum = van.state(0)
const fps = 60 // You can set your desired fps here
setInterval(() => ++frameNum.val, 1000 / fps)
van.add(document.body, () => {
frameNum.val
return <The entire DOM tree for your UI>
})
You shouldn't use states or binding functions like vanX.list
for the rest of your app, as using states elsewhere might trigger the re-rendering some local parts of the UI.
@sirenkovladd your examples are much better than what i had. but they're still too clumsy to use, for example they still have the problem that window.innerWidth is somehow cached and never recalculated when the window is resized.
@Tao-VanJS this works great, except, yeah, it constantly rewrites the dom which can be a problem. i guess i'm looking for a js library that will let me generate a full virtual dom every frame like that, but it will do a diff on its end and only update on the dom what's necessary. would you happen to know of any lightweight options for this?
@Tao-VanJS this works great, except, yeah, it constantly rewrites the dom which can be a problem. i guess i'm looking for a js library that will let me generate a full virtual dom every frame like that, but it will do a diff on its end and only update on the dom what's necessary. would you happen to know of any lightweight options for this?
I guess React is a good diff-based re-rendering? In VanJS, you're encouraged to bind states to local scopes to prevent the entire DOM tree from being re-rendered.
maybe i'm just doing it wrong. but all these reactive frameworks are so frustrating.
i don't want to turn some of my state variables into weird
van.state
proxies. i don't want it to randomly re-render, switching out input elements while typing and causing weirdness.i just want to render stuff to the screen. i know when my state changed and don't mind manually calling a render/update function.
i tried doing
const manualrender = van.state(0)
then i can saymanualrender.val++
to trigger a redraw. except it only redraws the parts where i'm usingmanualrender
and has stale data for other parts of the UI