Drag the handle, or click anywhere on the track.
const renderLayers = {
world: 1 << 0,
ui: 1 << 1,
};
async function createBackdrop(
world: EcsWorld,
renderContext: RenderContext,
): Promise<void> {
const whiteImage = await renderContext.imageCache.getOrLoad(
'img/White.png',
);
const backdropSprite = createImageSprite(whiteImage, renderContext, {
pixelsPerUnit: 1,
layer: renderLayers.world,
});
backdropSprite.tintColor = new Color(0.09, 0.11, 0.16, 1);
const backdrop = world.createEntity();
addPositionComponent(world, backdrop);
addSpriteComponent(world, backdrop, backdropSprite);
}
function createPointerInput(
world: EcsWorld,
time: Time,
game: Game,
): MouseInputSource {
const inputManager = registerInputs(world, time, {});
return new MouseInputSource(inputManager, game.container);
}
/**
* Builds the slider demo: a single `createSlider` track with a handle and
* fill, driving a live value label. The whole track is the drag surface -
* clicking anywhere on it, not just the handle, jumps the handle there.
* @param fontAtlasUrl - The URL of the font atlas JSON to load.
* @returns The created game.
*/
export const createSliderGame = async (fontAtlasUrl: string): Promise<Game> => {
const { game, world, renderContext, time } = createGame('demo-game');
createCamera(world, {
isStatic: true,
cullingMask: renderLayers.world,
});
await createBackdrop(world, renderContext);
const fontAtlasCache = new FontAtlasCache(renderContext.imageCache);
const fontAtlas: FontAtlas = await fontAtlasCache.getOrLoad(fontAtlasUrl);
const mouseInputSource = createPointerInput(world, time, game);
registerUiSystems(world, renderContext, time, {
pointerSource: mouseInputSource,
});
const canvas = createUiCanvas(world, renderContext, {
cullingMask: renderLayers.ui,
referenceResolution: { x: 1920, y: 1080 },
});
const whiteImage = await renderContext.imageCache.getOrLoad(
'img/White.png',
);
const handleImage = await renderContext.imageCache.getOrLoad(
'img/kenney_ui-pack/PNG/Blue/Default/button_round_gloss.png',
);
const trackColor = new Color(0.85, 0.85, 0.88, 1);
const accentColor = new Color(0.75, 0.75, 0.15, 1);
const trackSprite = createImageSprite(whiteImage, renderContext, {
pixelsPerUnit: 1,
layer: renderLayers.ui,
});
trackSprite.tintColor = trackColor;
const fillSprite = createImageSprite(whiteImage, renderContext, {
pixelsPerUnit: 1,
layer: renderLayers.ui,
});
fillSprite.tintColor = accentColor;
const trackTransition = {
normalColor: trackColor,
hoverColor: new Color(0.78, 0.78, 0.83, 1),
pressedColor: new Color(0.68, 0.68, 0.75, 1),
disabledColor: new Color(0.6, 0.6, 0.6, 0.6),
};
createLabel(world, canvas, {
text: 'Volume',
fontAtlas,
size: 28,
anchor: UiAnchor.center(),
anchoredPosition: { x: -260, y: 60 },
verticalAlign: textVerticalAlignments.middle,
color: Color.white,
category: renderLayers.ui,
});
const valueLabel = createLabel(world, canvas, {
text: '75',
fontAtlas,
size: 28,
anchor: UiAnchor.center(),
anchoredPosition: { x: 230, y: 60 },
verticalAlign: textVerticalAlignments.middle,
color: Color.white,
category: renderLayers.ui,
});
const valueText = world.getComponent(valueLabel, textId)!;
const slider = createSlider(world, canvas, {
trackSprite,
handleSprite: createImageSprite(handleImage, renderContext, {
pixelsPerUnit: 1,
layer: renderLayers.ui,
}),
handleSize: { x: 56, y: 56 },
fillSprite,
anchor: UiAnchor.center({ x: 500, y: 28 }),
anchoredPosition: { x: 0, y: 0 },
minValue: 0,
maxValue: 100,
value: 75,
wholeNumbers: true,
transition: trackTransition,
});
slider.onValueChanged.registerListener((value) => {
valueText.text = `${value}`;
});
world.addSystem(createCameraEcsSystem(time));
world.addSystem(createTransformEcsSystem());
world.addSystem(createTextShapingEcsSystem(renderContext));
world.addSystem(createRenderEcsSystem(renderContext));
world.addSystem(createPresentEcsSystem(renderContext));
return game;
};
createSlider builds a track with a draggable handle and a fill sprite that tracks the current value. The whole track is the drag surface - clicking anywhere on it, not just the handle, jumps the handle there - and dragging keeps tracking even if the pointer strays outside the track's vertical bounds. The value label updates live via onValueChanged.