iconic_morph 1.3.0
iconic_morph: ^1.3.0 copied to clipboard
Path-level icon animation for Flutter — morph one SVG icon into another, plus 3D constant-stroke spins, draw-on, trace, and idle loops, all drawn from your own SVGs. No Rive, no Lottie, no new assets, [...]
Changelog #
1.3.0 #
Weight is never modulated; the exit vanishes by alpha. Thinning ink is per-contour, and a staggered exit therefore paints neighbouring contours at different weights — the glyph stops reading as one balanced drawing. So the taper is off by default and an alpha dissolve over the last quarter of the exit is the whole vanish. It also solves the terminal dot properly: a round-capped stroke shorter than its own width is a dot, and no length curve can hide that, but a transparent one is simply not there.
Changed #
IconMorphPlan.taperFloornow defaults to1— stroke weight untouched at both ends.exitTaper/assembleTaperremain, inert until the floor is lowered.StrokeTaper.kFlooris1, and the newStrokeTaper.exitWeight/entryWeightfold in the rule that a floor of 1 bypasses the taper and the no-dot clamp together. There is no middle setting where weight is modulated only for degenerate geometry.IconTrimDrawandIconDetailSpinare back to constant weight (1.1.0 had put the no-dot clamp on them; same balance argument applies).IconMorphPlan.exitFade(0.75) is now the documented vanish, not a finisher for the taper.
1.2.0 #
The lift is a thin-then-dissolve, not a thin-to-nothing. 1.1.0 removed the terminal dot by tapering ink to zero width, but a stroke that keeps thinning reads as starving rather than as a pen lifting — and it still ends on a hard cut at whatever width the last visible frame had. The taper now stops at half weight and an alpha dissolve over the end of the exit does the removing.
Added #
StrokeTaper.fade(prog, start)— the dissolve: alpha 1 → 0 over the end of a trim-out.StrokeTaper.kFloor(0.5) and afloorparameter onout/into.IconMorphPlan.exitFade(default0.75) — where the dissolve starts.1disables it (removal by length alone).IconMorphPlan.taperFloor(default0.5) — how thin a taper may ever take the stroke, at either end.1= never thin.StrokeTaper.weightedtakes analpha:argument and returnsnullonce fully faded, so a dissolved stroke skips its draw entirely.
Changed #
StrokeTaper.outnow eases tofloorinstead of to 0;StrokeTaper.intoramps fromfloorinstead of from 0.MorphExit.trim's length now tracks its (already smoothstepped) window progress directly. Both front-loaded curves tried on top of it — 1.0.x'seaseOutCubicand 1.1.0'seaseOutQuad— turned the ink into a stub while it was still fully opaque, which is the very thing that reads as a dot.
1.1.0 #
The ink now runs out. A trim-path that animates length alone cannot vanish cleanly: a round-capped stroke shorter than its own width is a dot, so every un-draw used to end on a fixed-size dot that then blinked out of existence, and every draw-on began by stamping one. Ink is now weighted by the length it has, so a stroke starts and finishes as a stroke.
Added #
StrokeTaper— the weight law for a trim-path end, and the one home for it:out(the pen lift),into(the nib pressing down),lengthClamp(the unconditional no-dot safety net — ink is never wider than a third of the length it has left), andweighted, which returns thePaintto stroke with ornullonce the ink is spent. That null matters:strokeWidth = 0is Skia's hairline mode (a 1px line), not invisibility, so a width animated to zero would otherwise end on a flash. Below a minimum renderable width it holds the floor and pays the remainder in alpha, so the vanish stays smooth instead of aliasing into sub-pixel shimmer.IconMorphPlan.exitTaper(default0.5) — where in a leaving contour's exit its weight starts running out.1restores constant weight.IconMorphPlan.assembleTaper(default0.18) — how much of an arriving contour's draw-on the nib takes to reach full weight.0disables it.
Changed #
MorphExit.trimno longer double-eases. The per-contour window is already a Hermite smoothstep; aneaseOutCubicon top of it spent 99% of the length in the first 70% of the exit and then parked the remaining stub on screen for the rest — which is what read as "a dot that hangs, then cuts". OneeaseOutQuadnow shapes it: still decisive (three quarters of the ink gone by the midpoint, clearing well before the target assembles), without the park.MorphAssemble.trim,IconTrimDraw(both directions, continuous and bloom) andIconDetailSpin's accent trim all carry the no-dot law. Genuine authored dots — contours whose whole length is a hair — keep full weight and shrink out, rather than being erased for being short.IconMorphPlan.hashCodemoved toObject.hashAll: the field list passedObject.hash's 20-argument ceiling, where a further field would have been silently dropped from the hash.
1.0.2 #
- Packaging & docs only — no API or behaviour changes. Rename the vendored
path_parsingMIT notice fromLICENSE_path_parsingtoTHIRD_PARTY_NOTICES.mdso hosts report a single project license (MIT) instead of two. Dan Field's notice is retained in full as MIT requires.
1.0.1 #
- Packaging & docs only — no API or behaviour changes. Use the canonical MIT license text so GitHub and pub.dev detect the license cleanly, and point the author link at the pub.dev publisher page.
1.0.0 #
Initial public release on pub.dev.
Animations #
IconicMorph— path-level cross-icon morph: a travelling "worm" rides a quintic-Hermite flight curve from one glyph to the next, with decoupled assemble (trim / scale / 3D-flip) and exit (un-draw / fade / scale) modes.IconicAnimatedIcon+ effect catalog —IconSpin3D(constant-stroke 3D spin/unlock/flip),IconTrimDraw,IconTrace,IconBreathe,IconBlink,IconConverge,IconWeightPulse,IconDetailSpin,IconGridPop,IconLineShrink,IconShuffle,IconInboxRiffle,IconLockEngage.IconicMorphHero/IconicMorphSequence— declarative multi-state morph (one element, no remounts → no flicker).IconImage— dependency-free static icon; strokes the parsed SVG path at a constant width, pixel-consistent with every animated effect at rest.
Foundations #
- Zero runtime dependencies — the SVG path parser and the 4×4 matrix math are
vendored in-tree; a consumer's
flutter pub getpulls in nothing beyond the Flutter SDK. IconGeometry.useManifest()/IconGeometry.resolver— read geometry from a baked JSON manifest instead of parsing SVG at runtime (cheaper, and you can ship no SVGs).dart run iconic_morph:bake <svg-dir> <out.json>— generate that manifest from a folder of SVGs.- Reduced-motion aware (
IconMotion.reduced): effects snap to their end state. - Bundled demo glyphs —
MorphIcons.user/.face/.lock.