Writting a custom indicator in Javascript
In this short post we code a gauge indicator in JS and integrate it with Wolfram Language
JerryIApril 22, 2024
iojavascript
Let's create a gauge that can react to value changes. Define a gauge meter:
gauge[level_Real];
gauge /: MakeBoxes[g_gauge, StandardForm] := With[{},
ViewBox[g, g]
] Here we use identical display and input expressions, the last one has to be defined as a frontend symbol too:
.js
function setNeedlePosition(needle, value) {
value = Math.max(0, Math.min(1, value));
const angle = value * 180 - 90;
needle.style.transform = 'rotate('+Math.round(angle)+'deg)';
}
core.gauge = async (args, env) => {
const gauge = document.createElement('div');
gauge.style.width = '100px';
gauge.style.height = '50px';
gauge.style.border = '1px solid #000';
gauge.style.borderRadius = '50px 50px 0 0';
gauge.style.position = 'relative';
gauge.style.background = 'linear-gradient(to right, red 0%, yellow 50%, green 100%)';
const needle = document.createElement('div');
needle.style.width = '2px';
needle.style.height = '40px';
needle.style.background = '#000';
needle.style.position = 'absolute';
needle.style.bottom = '0';
needle.style.left = '50%';
needle.style.transformOrigin = 'bottom';
const pos = await interpretate(args[0], env);
setNeedlePosition(needle, pos);
gauge.appendChild(needle);
env.element.appendChild(gauge);
}
// for later
core.gauge.setNeedlePosition = setNeedlePosition; Let's test it:
gauge[0.3] (*VB[*)(gauge[0.3])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeK5BITyxNTy0yBoPL9gCxdwlI"*)(*]VB*) You can also place it with other wolfram expressions
gauge[0.3] // Framed (*BB[*)((*VB[*)(gauge[0.3])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeK5BITyxNTy0yBoPL9gCxdwlI"*)(*]VB*))(*,*)(*"1:eJxTTMoPSmNiYGAo5gMSwSWVOakuqcn5RYkl+UUQcRYgEVSak1rMA1ZQlFngn+eZV1BaUswKFHBLzClORVUYDBYvSsxNBQuFFJWmAgAQ7xjr"*)(*]BB*) For the next step, we need to define prototypes for our symbol to update the needle position and enable instancing:
.js
core.gauge.update = async (args, env) => {
const val = await interpretate(args[0], env);
core.gauge.setNeedlePosition(env.element.firstChild.firstChild, val);
}
core.gauge.destroy = () => {}
core.gauge.virtual = true; Now let's provide a symbol to gauge and manipulate it with a slider:
meterLevel = 0.5;
gauge[meterLevel // Offload]
EventHandler[InputRange[0,1,0.1], (meterLevel = #)&] (*VB[*)(gauge[Offload[meterLevel]])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeK5BITyxNT4Vw2YGEf1paTn5iSjEXkJ2bWpJa5JNalpoDAKdSDsA="*)(*]VB*) (*VB[*)(EventObject[<|"Id" -> "4e5f4f00-7f50-4d5d-af3d-9b132c9e78ac", "Initial" -> 0.5, "View" -> "13dbf70f-40fd-4e46-92dd-d49b41d545eb"|>])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKGxqnJKWZG6TpmhikpeiapJqY6VoapaTopphYJpkYppiamKYmAQCLVxYF"*)(*]VB*)