[{"data":1,"prerenderedAt":559},["ShallowReactive",2],{"navigation_docs":3,"i-nanime:logo":163,"-examples-road-trip":170,"-examples-road-trip-surround":528,"preview-examples\u002Froad-trip\u002FRoadTripDemo.vue-false":533,"mdc-n2254f-key":533},[4,35,95,115,129,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":6},"Getting Started",false,"\u002Fgetting-started","1.getting-started",[10,15,20,25,30],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-ph-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-ph-download-simple",{"title":21,"path":22,"stem":23,"icon":24},"Configuration","\u002Fgetting-started\u002Fconfiguration","1.getting-started\u002F3.configuration","i-ph-sliders-horizontal",{"title":26,"path":27,"stem":28,"icon":29},"Performance","\u002Fgetting-started\u002Fperformance","1.getting-started\u002F4.performance","i-ph-gauge",{"title":31,"path":32,"stem":33,"icon":34},"Accessibility","\u002Fgetting-started\u002Faccessibility","1.getting-started\u002F5.accessibility","i-ph-person-simple-circle",{"title":36,"icon":6,"path":37,"stem":38,"children":39,"page":6},"Composables","\u002Fcomposables","2.composables",[40,44,49,54,59,64,69,74,79,84,90],{"title":11,"path":41,"stem":42,"icon":43},"\u002Fcomposables\u002Fintroduction","2.composables\u002F0.introduction","i-ph-stack",{"title":45,"path":46,"stem":47,"icon":48},"useAnimate","\u002Fcomposables\u002Fuse-animate","2.composables\u002F1.use-animate","i-ph-play-circle",{"title":50,"path":51,"stem":52,"icon":53},"useWaapiAnimate","\u002Fcomposables\u002Fuse-waapi-animate","2.composables\u002F2.use-waapi-animate","i-ph-lightning",{"title":55,"path":56,"stem":57,"icon":58},"useAnimatable","\u002Fcomposables\u002Fuse-animatable","2.composables\u002F3.use-animatable","i-ph-cube",{"title":60,"path":61,"stem":62,"icon":63},"useSplitText","\u002Fcomposables\u002Fuse-split-text","2.composables\u002F4.use-split-text","i-ph-text-t",{"title":65,"path":66,"stem":67,"icon":68},"useScrambleText","\u002Fcomposables\u002Fuse-scramble-text","2.composables\u002F5.use-scramble-text","i-ph-shuffle",{"title":70,"path":71,"stem":72,"icon":73},"useDraggable","\u002Fcomposables\u002Fuse-draggable","2.composables\u002F6.use-draggable","i-ph-hand-grabbing",{"title":75,"path":76,"stem":77,"icon":78},"useAnimeTimeline","\u002Fcomposables\u002Fuse-anime-timeline","2.composables\u002F7.use-anime-timeline","i-ph-list-numbers",{"title":80,"path":81,"stem":82,"icon":83},"useAnimeScroll","\u002Fcomposables\u002Fuse-anime-scroll","2.composables\u002F8.use-anime-scroll","i-ph-mouse-scroll",{"title":85,"path":86,"stem":87,"icon":88,"badge":89},"useAnimeLayout","\u002Fcomposables\u002Fuse-anime-layout","2.composables\u002F9.use-anime-layout","i-ph-layout","New",{"title":91,"path":92,"stem":93,"icon":94},"Utilities","\u002Fcomposables\u002Futilities","2.composables\u002F99.utilities","i-ph-puzzle-piece",{"title":96,"icon":6,"path":97,"stem":98,"children":99,"page":6},"Components","\u002Fcomponents","3.components",[100,105,110],{"title":101,"path":102,"stem":103,"icon":104,"badge":89},"Transitions","\u002Fcomponents\u002Ftransitions","3.components\u002F1.transitions","i-ph-arrows-left-right",{"title":106,"path":107,"stem":108,"icon":109,"badge":89},"Transition styles","\u002Fcomponents\u002Ftransition-styles","3.components\u002F2.transition-styles","i-ph-palette",{"title":111,"path":112,"stem":113,"icon":114,"badge":89},"Layout group","\u002Fcomponents\u002Flayout-group","3.components\u002F3.layout-group","i-ph-frame-corners",{"title":116,"icon":6,"path":117,"stem":118,"children":119,"page":6},"Comparisons","\u002Fcomparisons","4.comparisons",[120,125],{"title":121,"path":122,"stem":123,"icon":124,"badge":89},"Compared with Nuxt modules","\u002Fcomparisons\u002Fvs-nuxt-modules","4.comparisons\u002F1.vs-nuxt-modules","i-ph-scales",{"title":126,"path":127,"stem":128,"icon":124,"badge":89},"Compared with Vue packages","\u002Fcomparisons\u002Fvs-vue-packages","4.comparisons\u002F2.vs-vue-packages",{"title":130,"icon":6,"path":131,"stem":132,"children":133,"page":6},"Examples","\u002Fexamples","5.examples",[134,138,143,148],{"title":11,"path":135,"stem":136,"icon":137},"\u002Fexamples\u002Fintroduction","5.examples\u002F0.introduction","i-ph-shapes",{"title":139,"path":140,"stem":141,"icon":142},"Drive","\u002Fexamples\u002Fdrive","5.examples\u002F1.drive","i-ph-hard-drives",{"title":144,"path":145,"stem":146,"icon":147},"Road trip","\u002Fexamples\u002Froad-trip","5.examples\u002F2.road-trip","i-ph-car-profile",{"title":149,"path":150,"stem":151,"icon":152},"Player","\u002Fexamples\u002Fplayer","5.examples\u002F3.player","i-ph-music-notes",{"title":154,"icon":6,"path":155,"stem":156,"children":157,"page":6},"Updates","\u002Fchanges","6.changes",[158],{"title":159,"path":160,"stem":161,"icon":162},"Changelog","\u002Fchanges\u002Fchangelog","6.changes\u002F1.changelog","i-ph-clock-counter-clockwise",{"left":164,"top":165,"width":166,"height":167,"rotate":168,"vFlip":6,"hFlip":6,"body":169},84,74,344,367,0,"\u003Cpath d=\"M124 401 V230 C124 144 186 114 256 114 C326 114 388 144 388 230 V304\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"80\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\u002F>\n  \u003Crect x=\"348\" y=\"358\" width=\"80\" height=\"80\" rx=\"12\" fill=\"#ff4b4b\"\u002F>",{"id":171,"title":144,"body":172,"description":519,"extension":520,"links":521,"meta":522,"navigation":523,"path":145,"seo":524,"stem":146,"__hash__":527},"docs\u002F5.examples\u002F2.road-trip.md",{"type":173,"value":174,"toc":514},"minimark",[175,180,185,189,212,215,219,312,501],[176,177],"render-code-block-preview",{":code":178,"src":179},"false","examples\u002Froad-trip\u002FRoadTripDemo.vue",[181,182,184],"h2",{"id":183},"try-it","Try it",[186,187,188],"p",{},"The numbered controls work. The greyed ones fill out the page and do\nnothing.",[190,191,192,200,206],"ol",{},[193,194,195,199],"li",{},[196,197,198],"strong",{},"Scroll the route panel"," sideways. The car follows the road and turns\nwith every bend, and a trail draws in behind it. It reverses when you\nscroll back. Each stop's marker fills in as the car reaches it.",[193,201,202,205],{},[196,203,204],{},"Click a stop card"," to scroll the panel to that stop, and the car\ndrives there. The card of the last stop reached is highlighted, and a\ncheck pops in on every stop the car has passed.",[193,207,208,211],{},[196,209,210],{},"Book trip"," runs the booking. The status line scrambles into each\nstep's name next to a spinner, and finished steps slide up into the log.\nPause stops the spinner and the step timer, and Resume carries on from\nthe same point. A check pops in when the booking is done.",[186,213,214],{},"Reset in the title bar scrolls the route back to the start, cancels the\nbooking and plays the title in again.",[181,216,218],{"id":217},"built-with","Built with",[220,221,222,231,238,255,262,269,276,284,292],"ul",{},[193,223,224,230],{},[225,226,227],"a",{"href":81},[228,229,80],"code",{},": the panel's\nhorizontal scroll, driving the car and the trail",[193,232,233,237],{},[225,234,235],{"href":46},[228,236,45],{},": the car on its motion path, the\ntrail, and the title's letters rising into place",[193,239,240,243,244,247,248,254],{},[228,241,242],{},"createMotionPath"," and ",[228,245,246],{},"createDrawable"," from\n",[225,249,251],{"href":250},"\u002Fcomposables\u002Futilities#proxies",[228,252,253],{},"#nanime\u002Fproxies\u002Fsvg",": the car's path\nand heading, and the trail's line drawing",[193,256,257,261],{},[225,258,259],{"href":61},[228,260,60],{},": the trip title, split into\nletters",[193,263,264,268],{},[225,265,266],{"href":66},[228,267,65],{},": the booking status\nline",[193,270,271,275],{},[225,272,273],{"href":51},[228,274,50],{},": the spinner",[193,277,278,283],{},[225,279,280],{"href":102},[228,281,282],{},"\u003CAnimeTransitionGroup>",": the booking log",[193,285,286,291],{},[225,287,288],{"href":102},[228,289,290],{},"\u003CAnimeTransition>",": the stop checks, the\nspinner swapping to a check, and the paused badge",[193,293,294,297,298,243,303,306,307],{},[228,295,296],{},"spring"," from ",[225,299,300],{"href":92},[228,301,302],{},"#nanime\u002Feasings",[228,304,305],{},"stagger","\nfrom ",[225,308,309],{"href":92},[228,310,311],{},"#nanime\u002Futils",[313,314,316,322,356,387,424,456,475],"collapsible",{"name":315},"How it's built",[186,317,318,321],{},[196,319,320],{},"Layout."," The window is split into a header, the route panel, the stop\ncards and the booking summary. The route panel reports its scroll progress\nto the parent, and the stop cards read it. The parent also calls the route\npanel to scroll to a stop, and resets the booking and the header.",[186,323,324,327,328,332,333,336,337,340,341,344,345,348,349,351,352,355],{},[196,325,326],{},"Route."," The road is one SVG path, drawn four times: a wide edge, the\nasphalt, a dashed centre line and a trail. The car is an SVG group moved\nalong the asphalt path by ",[225,329,330],{"href":46},[228,331,45],{}," with\n",[228,334,335],{},"createMotionPath(path)",". The proxy returns ",[228,338,339],{},"translateX",", ",[228,342,343],{},"translateY"," and\n",[228,346,347],{},"rotate"," values that follow the path, so the car turns with each bend. The\ntrail is the same path wrapped in ",[228,350,246],{},", animated with\n",[228,353,354],{},"draw: ['0 0', '0 1']",".",[186,357,358,359,363,364,340,367,370,371,374,375,378,379,382,383,386],{},"Neither animation plays on a clock.\n",[225,360,361],{"href":81},[228,362,80],{}," creates a scroll\nobserver on the panel with ",[228,365,366],{},"axis: 'x'",[228,368,369],{},"container"," set to the panel and\n",[228,372,373],{},"target"," set to the wide scene inside it. Passing that observer as each\nanimation's ",[228,376,377],{},"autoplay"," ties the animation's progress to the scroll\nposition. ",[228,380,381],{},"sync: true"," keeps it locked there with no smoothing, and the\n",[228,384,385],{},"ease: 'linear'"," on the car keeps its position proportional to the scroll.\nEach animation gets its own observer, since an observer drives one\nanimation.",[186,388,389,392,393,396,397,400,401,404,405,407,408,410,411,414,415,419,420,423],{},[196,390,391],{},"Stops."," Each stop has an ",[228,394,395],{},"at"," value, the fraction of the road where it\nsits. On mount, ",[228,398,399],{},"getPointAtLength"," on the road path places a marker at\neach fraction. The car is at the same fraction of the road as the panel is\nof its scroll, so a stop is reached when the observer's ",[228,402,403],{},"progress"," passes\nits ",[228,406,395],{},". Clicking a card scrolls the panel to ",[228,409,395],{}," times its scroll range\nwith ",[228,412,413],{},"scrollTo({ behavior: 'smooth' })",", and the scroll observer moves the\ncar there. The check on each card sits in an\n",[225,416,417],{"href":102},[228,418,290],{}," with a small ",[228,421,422],{},"spring()","\npop, so it appears as the car passes the stop and leaves when it reverses\npast it.",[186,425,426,429,430,434,435,438,439,443,444,447,448,451,452,455],{},[196,427,428],{},"Title."," ",[225,431,432],{"href":61},[228,433,60],{}," splits the title\ninto letters with ",[228,436,437],{},"wrap: 'clip'",", so each letter sits in a clipped box.\n",[225,440,441],{"href":46},[228,442,45],{}," moves each letter from ",[228,445,446],{},"y: '100%'","\nto ",[228,449,450],{},"0"," with ",[228,453,454],{},"stagger(18)",", so they rise into place one after another. The\nheader is keyed on a counter that Reset increments, which remounts it and\nplays the title again.",[186,457,458,461,462,465,466,469,470,474],{},[196,459,460],{},"Booking."," A ",[228,463,464],{},"useBooking"," composable steps through a list of steps on a\n",[228,467,468],{},"setTimeout",". Each step writes its text to the status line, which\n",[225,471,472],{"href":66},[228,473,65],{}," watches, so a new text\nstarts a new scramble. The wait for each step is the scramble's own length\nplus the step's delay, so the text always finishes resolving before the\nnext one starts. Pause clears the timer and Resume schedules the next step\nagain.",[186,476,477,478,482,483,485,486,243,489,492,493,497,498,355],{},"The spinner turns with\n",[225,479,480],{"href":51},[228,481,50],{},", a looping ",[228,484,347],{}," on\nthe compositor thread, so it keeps turning while the main thread works.\nPause and Resume call its ",[228,487,488],{},"pause()",[228,490,491],{},"play()",", so it stops at its\ncurrent angle and continues from there. The last three finished steps show\nin an ",[225,494,495],{"href":102},[228,496,282],{},". New steps fade\nup, and the oldest fades out as the rest move up. The spinner, the final\ncheck and the idle caret swap inside an ",[228,499,500],{},"\u003CAnimeTransition mode=\"out-in\">",[313,502,504],{"name":503},"AI build prompt",[505,506,512],"pre",{"className":507,"code":509,"language":510,"meta":511},[508],"language-text","Using the nanime Nuxt module (AnimeJS v4 for Vue, with Tailwind CSS v4 and\nNuxt UI), build a trip planner page in Vue 3 single-file components.\nuseAnimate, useAnimeScroll, useSplitText, useScrambleText,\nuseWaapiAnimate, \u003CAnimeTransition> and \u003CAnimeTransitionGroup> are\nauto-imported by nanime. Import createMotionPath and createDrawable from\n'#nanime\u002Fproxies\u002Fsvg', spring from '#nanime\u002Feasings', stagger from\n'#nanime\u002Futils', and the types AnimationParams, DrawableSVGGeometry,\nScrollObserver and ScrollObserverParams from '#nanime\u002Ftypes'. Use strict\nTypeScript with no `any` and no `as` casts. Ease names are AnimeJS v4\nstrings such as 'out(3)', not 'easeOutCubic'. Keep motion subtle: ease out\nfor things already on screen, and a small spring overshoot only for things\nthat pop in.\n\nLayout: a window with a title bar (Reset button), a header, and below it a\ntwo-column grid: the route panel and stop cards on the left, a booking\nsummary on the right (18rem wide on large screens, stacked on small ones).\n\nHeader: an \u003Ch2> \"Coast to Canyon\" and a line \"4 stops · 620 km · 5 days\",\nplus three disabled buttons (dates, travellers, share). Split the title\nwith useSplitText(title, { chars: { wrap: 'clip' } }) and animate the\nreturned chars with useAnimate(chars, { y: ['100%', '0%'], duration: 600,\ndelay: stagger(18), ease: 'out(3)' }). Fade the line in with\nuseAnimate(meta, { opacity: [0, 1], y: [6, 0], duration: 400, delay: 350,\nease: 'out(3)' }). Key the header on a counter that Reset increments, so\nReset replays the title.\n\nRoute panel: a div (template ref \"viewport\") with \"overflow-x-auto\noverscroll-x-contain\", holding a \"w-max\" div (ref \"scene\") with an SVG\n2600 x 300. The road path is:\n  M 100 160 C 380 40 620 40 900 150 S 1340 262 1620 152 S 2040 48 2300 142 S 2470 186 2520 168\nDraw it four times with fill=\"none\" and stroke-linecap=\"round\": a 52-wide\nedge, a 44-wide asphalt (ref \"road\"), a 2-wide dashed centre line\n(stroke-dasharray \"18 22\") and a 2.5-wide trail (ref \"trail\"). Add a car\nas an SVG \u003Cg ref=\"car\"> with CSS transform-box: fill-box and\ntransform-origin: 50% 50%, drawn pointing along +x.\n\nScroll: write a helper that returns useAnimeScroll({ container: viewport,\ntarget: scene, axis: 'x', enter: 'left left', leave: 'right right', sync:\ntrue, ...extra }), passing the template refs themselves. Call it\ntwice, since each observer drives one animation: once for the car with\nonUpdate reading observer.progress, once for the trail.\n- Car: useAnimate(car, computed(() => road.value ? {\n  ...createMotionPath(road.value), ease: 'linear', autoplay: carScroll } :\n  {})).\n- Trail: a computed createDrawable(trail.value)[0] ?? null, animated with\n  useAnimate([trailDrawable], { draw: ['0 0', '0 1'], ease: 'linear',\n  autoplay: trailScroll }).\nEmit the progress to the parent.\n\nStops: four stops { name, day, note, at }, with at = 0, 0.34, 0.66 and 1,\nthe fraction of the road where each sits. On mount, place an SVG marker\ncircle and label at road.getPointAtLength(road.getTotalLength() * at).\nA stop is reached when progress >= at - 0.01. Fill its marker when\nreached. Below the panel, a grid of four stop cards (2 columns on\nphones). The card of the last reached stop gets a primary border. Each\ncard has a check icon inside \u003CAnimeTransition :enter-animation=\"{ opacity:\n[0, 1], scale: [0.6, 1], ease: spring({ bounce: 0.2, duration: 300 }) }\">\nshown with v-if when the stop is reached. Clicking a card calls\nviewport.scrollTo({ left: at * (scrollWidth - clientWidth), behavior:\n'smooth' }), and the scroll observer moves the car.\n\nBooking summary: price lines and a total, a disabled \"Change car\" button,\na fixed-height log box and the controls. Keep the booking in a\nuseBooking() composable: state ('idle' | 'running' | 'done'), paused,\nstepIndex, statusText and six steps { label, delay }. Each step sets\nstatusText to \"[n\u002F6] \u003Clabel>...\" and schedules the next one after\n(text.length - 1) * (1000 \u002F 50) + 300 ms of scramble plus the step's\ndelay. Pause clears the timer, Resume schedules the next step again. The\nlast step sets statusText to \"Booked · ref RT-4821\" and state to 'done'.\n- Status line: useScrambleText(statusLine, {}, () => ({ text:\n  statusText.value, chars: '0123456789ABCDEF', settleDuration: 300,\n  revealRate: 50 })).\n- Spinner: useWaapiAnimate(spinner, { rotate: { to: 360 }, duration: 800,\n  ease: 'linear', loop: true }). Watch paused and call spin.pause() or\n  spin.play().\n- Log: the last three finished steps in \u003CAnimeTransitionGroup tag=\"ul\">\n  with enter { opacity: [0, 1], y: [8, 0], duration: 250, ease: 'out(3)' },\n  leave { opacity: 0, duration: 150, ease: 'out(2)' } and move { duration:\n  250, ease: 'out(3)' }.\n- The spinner, a final check and an idle caret swap inside \u003CAnimeTransition\n  mode=\"out-in\"> with a small pop on enter.\n- Buttons: \"Book trip\" when idle, Pause\u002FResume and Cancel while running,\n  \"Book again\" when done.\n\nReset scrolls the panel back to 0, resets the booking and replays the\ntitle.\n","text","",[228,513,509],{"__ignoreMap":511},{"title":511,"searchDepth":515,"depth":515,"links":516},2,[517,518],{"id":183,"depth":515,"text":184},{"id":217,"depth":515,"text":218},"A trip planner whose route is driven by scroll, with a booking log that runs step by step.","md",null,{},{"icon":147},{"title":525,"description":526},"Road trip example: scroll-driven animation in Nuxt","A trip planner built with useAnimeScroll, motion paths, useSplitText, useScrambleText and useWaapiAnimate in Nuxt. Scroll the route, jump to stops and book the trip.","rcFz_eDKKatQxOgxmOW3zQq9NJtOHTVooSnyc8l_XkU",[529,531],{"title":139,"path":140,"stem":141,"description":530,"icon":142,"children":-1},"A file manager whose tiles change layout, filter, sort and drag into the trash.",{"title":149,"path":150,"stem":151,"description":532,"icon":152,"children":-1},"A music player with a timeline for track changes, draggable seek and volume sliders, and a playlist that shuffles.",{"data":534,"body":535,"toc":557},{"title":511,"description":511},{"type":536,"children":537},"root",[538,543],{"type":539,"tag":540,"props":541,"children":542},"element","road-trip-demo",{},[],{"type":539,"tag":544,"props":545,"children":554},"u-button",{"className":546,"color":549,"icon":550,"target":551,"to":552,"variant":553},[547,548],"mt-2.5","pl-0.5","neutral","i-simple-icons-github","_blank","https:\u002F\u002Fgithub.com\u002Fastraldev\u002Fnanime\u002Fblob\u002Fmain\u002Fdocs\u002Fapp\u002Fcomponents\u002Fcontent\u002Fexamples\u002Froad-trip\u002FRoadTripDemo.vue","link",[555],{"type":510,"value":556},"View on GitHub",{"title":511,"searchDepth":515,"depth":515,"links":558},[],1792591718337]