A GUI engine combining EaselJS and Backbone.js. Originally developed for use with Flash CC HTML5 output, but can be used with any EaselJS content.
- Navigatable views with keyboard, mouse and touch support (single-touch is enabled on the stage;
new easelbone.Views.Root({ ..., touch: false })opts out) - UI controls: sliders, checkboxes, selectboxes, and buttons
- Scrollable containers with list and float layouts, scrolled by wheel, scrollbar, keyboard focus or dragging the content (touch)
- Text rendering with auto-sizing and emoji support
- Alpha mask support
- Asset loading and management
- Built on top of AMD modules (RequireJS)
- Opt-in dirty rendering (
new easelbone.Views.Root({ ..., dirtyRendering: true })): the stage ticks every frame but is repainted only when the picture changed. After each tick the root view walks the display list as the painter would (hidden subtrees and the inside of cached objects are skipped) and compares a fingerprint of everything that decides the picture: transforms, alpha, child order, MovieClip and Sprite frames (nested at any depth, independent or synched), text, images, video time, masks, shadows, redrawn Graphics. Noinvalidate()calls are needed for changed display objects, also not for changes made outside a tick. See Dirty rendering below.
Visit the documentation and examples page for a full overview of the library and live demos.
npm install easelboneOr with Bower:
bower install easelboneLive examples are hosted on GitHub Pages:
- Controls — Sliders, checkboxes, selectboxes, and buttons
- Scrollable Areas — Scrollable container demo
- Float Layout — Float layout demo
- Alpha Mask — Alpha mask demo
- Background & Fill — Background fill and placeholder demo
- BigText — Auto-sizing text rendering demo
- Emoji Text — Text rendering with emoji support
- Benchmark — Performance stress test with paint/tick statistics
Pull request previews are automatically deployed to https://catlabinteractive.github.io/easelbone/pr/<PR_NUMBER>/examples/.
# Install dependencies
npm install
npx bower install
# Build
npm run build
# Watch for changes
npx grunt watch
# Headless tests (need `npx playwright install chromium` once)
npx http-server dist -p 8080 --silent & # serve the build first
npm run smoke # every example page renders cleanly
npm run test:baseline # BigText vertical centering (self-serving)
npm run test:touch # touch on the stage, drag scrolling (self-serving)
npm run test:dirty # dirty rendering, nested MovieClips (plain node, no browser)Note: BigText fits and centers text on the measured glyph bounds
(TextMetrics.actualBoundingBox*), so the drawn ink never leaves the
available space and manual BigText.setFontOffset(...) calls are no longer
needed in modern browsers — even for decorative fonts whose glyphs extend
beyond their em box or advance widths. Registering an offset still works and
takes precedence (restoring the fully legacy behavior for that font), as does
the legacy heuristic on browsers without extended TextMetrics.
Off by default. With dirtyRendering: true the root view paints a frame when one of these is true:
- the display-list fingerprint differs from the previous frame, plus the two frames after that;
- a view's
tick()returnedtrue(Views.Base.tick()returnsfalsewhen it is ticked by a dirty-rendering root view andtrueotherwise, so a view that does not override it leaves the decision to the fingerprint; a view that overrides it and returnstruekeeps painting every frame); rootView.invalidate()was called, the mouse was pressed or released over the stage, or the page became visible again (a mouse move does not paint: hover visuals are display properties, which the fingerprint sees);- an image the walk has met finished loading (a sprite sheet that was still loading, a
srcswapped for a picture of the same size), or an easelbone object asked for it: an emoji image that arrived after its text was painted, aFillthat is laid out at draw time or whose image finished loading (it listens for the load itself, and nothing is painted for it while the image loads), a pinned object that moved; - nothing was painted for
rootView.heartbeatIntervalms (default500): a change nothing noticed shows that late instead of not at all.
What the fingerprint cannot see, and needs rootView.invalidate(): pixels drawn into a canvas that a Bitmap shows, a display object with a custom draw() (also one that draws a hover state from the mouse position), a Graphics command whose fields are edited after it was appended. (A Graphics object that is cleared and redrawn, or appended to, is seen. A Text counts whether or not it was given a font.)
Other things to know:
- A MovieClip or tween that changes nothing visible (under a hidden or cached ancestor) no longer causes paints. A cached container shows what is below it only after
updateCache(), as always. rootView.render()(and sosetView()andresize()) ticks the stage before painting, asstage.update()does without the option, with no time passed (delta: 0: a clip with a framerate does not advance). It is one paint: the next frame is compared with what it painted.rootView.update()only paints.rootView.paintCountcounts stage paints in both modes; a host can show paints per second next to the ticker FPS.easelbone.DirtyRenderingexposesfingerprint(stage)andisActive(). Its presence also tells a host that this build decides on the display list. Up to 1.2.18 the option painted while any MovieClip advanced or any tween was active, visible or not, and missed changes made outside a tick.- Per root view:
isActive()answers for the root view whose views are ticking,rootView.invalidate()paints that root view only. What easelbone objects raise is seen once by every dirty-rendering root view. rootView.setDirtyRendering(false|true)switches the mode at run time;rootView.destroy()(also called byremove()) stops the tracker and removes the ticker, stage and page listeners.
npm run bump # or bump:minor — bumps package.json, commits, creates the vX.Y.Z tag
npm publishnpm publish wraps itself around that:
prepublishOnlyrunsbower install && npm run build, so a stale or missingdist/scripts/easelbone.jscan never be published.- The published tarball is limited to the built bundle and its source map (see
the
filesfield) — the examples, sources and tooling stay in the repo. postpublishpushes the branch and the new tag withgit push --follow-tags, then creates the matching GitHub Release with auto-generated notes (gh release create ... --generate-notes --verify-tag). This needs the GitHub CLI to be installed and authenticated (gh auth login);--verify-tagmakes it abort rather than invent a tag if you published without bumping first.