PageSourceSearch

https://motioncanvas.io/assets/js/defc87c6.9b283378.js

js motioncanvas.io collected 2026-10-04 00:27:45 UTC 23,135 bytes, 10 lines download raw bytes

1var _____WB$wombat$assign$function_____=function(name){return (globalThis._wb_wombat && globalThis._wb_wombat.local_init && globalThis._wb_wombat.local_init(name))||globalThis[name];};if(!globalThis.__WB_pmw){globalThis.__WB_pmw=function(obj){this.__WB_source=obj;return this;}}{
2let window = _____WB$wombat$assign$function_____("window");
3let self = _____WB$wombat$assign$function_____("self");
4let document = _____WB$wombat$assign$function_____("document");
5let location = _____WB$wombat$assign$function_____("location");
6let top = _____WB$wombat$assign$function_____("top");
7let parent = _____WB$wombat$assign$function_____("parent");
8let frames = _____WB$wombat$assign$function_____("frames");
9let opener = _____WB$wombat$assign$function_____("opener");
10"use strict";(self.webpackChunk_motion_canvas_docs=self.webpackChunk_motion_canvas_docs||[]).push([[5468],{876:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>h});var a=t(2784);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function r(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?o(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):o(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,a,i=function(e,n){if(null==e)return{};var t,a,i={},o=Object.keys(e);for(a=0;a<o.length;a++)t=o[a],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a<o.length;a++)t=o[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=a.createContext({}),p=function(e){var n=a.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(s.Provider,{value:n},e.children)},m="mdxType",u={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),m=p(t),d=i,h=m["".concat(s,".").concat(d)]||m[d]||u[d]||o;return t?a.createElement(h,r(r({ref:n},c),{},{components:t})):a.createElement(h,r({ref:n},c))}));function h(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,r=new Array(o);r[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l[m]="string"==typeof e?e:i,r[1]=l;for(var p=2;p<o;p++)r[p]=t[p];return a.createElement.apply(null,r)}return a.createElement.apply(null,t)}d.displayName="MDXCreateElement"},2665:(e,n,t)=>{t.d(n,{Z:()=>g});var a=t(2784);const i="container_lQCo",o="small_As57",r="banner_A4QS",l="player_tELG",s="link_BTzN",p="icon_wSGd";var c=t(7896);function m(e){let{width:n=24,height:t=24,...i}=e;return a.createElement("svg",(0,c.Z)({viewBox:"0 0 24 24",width:n,height:t,"aria-hidden":!0},i),a.createElement("path",{d:"M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z",fill:"currentColor"}))}var u=t(9817),d=t(6277);function h(e){let{name:n}=e;const t=`http://web.archive.org/web/20230315084656/https://github.com/motion-canvas/motion-canvas/blob/main/packages/examples/src/scenes/${n}.tsx`;return a.createElement(u.Z,{to:t,className:(0,d.Z)("padding--sm",s)},a.createElement("span",null,"View source code"),a.createElement(m,{className:p}))}function g(e){let{name:n,banner:t,small:s,link:p}=e;return a.createElement("div",{className:(0,d.Z)(i,t&&r,s&&o)},a.createElement("motion-canvas-player",{class:l,src:`/examples/${n}.js`,auto:t}),a.createElement(h,{name:p||n}))}t(1263).Z.canUseDOM&&Promise.all([t.e(5392),t.e(9106)]).then(t.bind(t,9106))},6646:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>s,default:()=>d,frontMatter:()=>l,metadata:()=>p,toc:()=>m});var a=t(7896),i=(t(2784),t(876)),o=t(2665),r=t(8128);const l={sidebar_position:7,slug:"/tweening"},s="Tweening",p={unversionedId:"getting-started/tweening",id:"getting-started/tweening",title:"Tweening",description:"Tweens are one of the fundamental building blocks of animation. They are a",source:"@site/docs/getting-started/tweening.mdx",sourceDirName:"getting-started",slug:"/tweening",permalink:"/docs/tweening",draft:!1,editUrl:"http://web.archive.org/web/20230315084656/https://github.com/motion-canvas/motion-canvas/blob/main/packages/docs/docs/getting-started/tweening.mdx",tags:[],version:"current",lastUpdatedBy:"Jacob",sidebarPosition:7,frontMatter:{
10sidebar_position:7,slug:"/tweening"},sidebar:"docs",previous:{title:"References",permalink:"/docs/references"},next:{title:"Time Events",permalink:"/docs/time-events"}},c={},m=[{value:"<code>tween</code> function",id:"tween-function",level:2},{value:"Timing functions",id:"timing-functions",level:3},{value:"Interpolation functions",id:"interpolation-functions",level:3},{value:"Tweening properties",id:"tweening-properties",level:3},{value:"<code>spring</code> function",id:"spring-function",level:2},{value:"Spring description",id:"spring-description",level:3},{value:"Settle tolerance",id:"settle-tolerance",level:3},{value:"Saving and restoring states",id:"saving-and-restoring-states",level:2}],u={toc:m};function d(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"tweening"},"Tweening"),(0,i.kt)("p",null,"Tweens are one of the fundamental building blocks of animation. They are a\nspecial type of generators that animate between two values over given time."),(0,i.kt)("h2",{id:"tween-function"},(0,i.kt)("inlineCode",{parentName:"h2"},"tween")," function"),(0,i.kt)("p",null,"The simplest way to create a tween is via the\n",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#tween"},(0,i.kt)("inlineCode",{parentName:"a"},"tween"))," function:"),(0,i.kt)(r.Z,{language:"tsx",mdxType:"CodeBlock"},"import {makeScene2D} from '@motion-canvas/2d/lib/scenes';\nimport {Circle} from '@motion-canvas/2d/lib/components';\nimport {createRef} from '@motion-canvas/core/lib/utils';\nimport {tween, map} from '@motion-canvas/core/lib/tweening';\n\nexport default makeScene2D(function* (view) {\n  const circle = createRef<Circle>();\n\n  view.add(\n    <Circle\n      //highlight-start\n      ref={circle}\n      x={-300}\n      width={240}\n      height={240}\n      fill=\"#e13238\"\n    />,\n  );\n  //highlight-start\n  yield* tween(2, value => {\n    circle().position.x(map(-300, 300, value));\n  });\n  //highlight-end\n});\n"),(0,i.kt)("p",null,"In the example above, we animate the x coordinate of our circle from ",(0,i.kt)("inlineCode",{parentName:"p"},"-300")," to\n",(0,i.kt)("inlineCode",{parentName:"p"},"300")," over a span of ",(0,i.kt)("inlineCode",{parentName:"p"},"2")," second."),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#tween"},(0,i.kt)("inlineCode",{parentName:"a"},"tween"))," function takes two parameters. The first\none specifies the tween duration in seconds. The second is a callback function\nthat will be called each frame the tween takes place. The ",(0,i.kt)("inlineCode",{parentName:"p"},"value")," parameter it\nreceives is a number ranging from ",(0,i.kt)("inlineCode",{parentName:"p"},"0")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"1"),", informing us about the progress of\nthe tween. We can use it to calculate the values that our tween animates. In the\ncase of our circle, we use the ",(0,i.kt)("inlineCode",{parentName:"p"},"map")," function to map the ",(0,i.kt)("inlineCode",{parentName:"p"},"value")," range from\n",(0,i.kt)("inlineCode",{parentName:"p"},"[0, 1]")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"[-300, 300]")," and set it as the ",(0,i.kt)("inlineCode",{parentName:"p"},"x")," coordinate:"),(0,i.kt)(o.Z,{small:!0,name:"tweening-linear",mdxType:"AnimationPlayer"}),(0,i.kt)("h3",{id:"timing-functions"},"Timing functions"),(0,i.kt)("p",null,"At the moment, our animation feels a bit unnatural. The speed with which the\n",(0,i.kt)("inlineCode",{parentName:"p"},"value")," parameter changes is constant, which in turn makes the circle move with\nconstant speed. In real life, however, objects have inertia - they take time to\nspeed up and slow down. We can simulate this behavior with\n",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening"},"timing functions"),"."),(0,i.kt)("p",null,"A timing function takes a number in the range ",(0,i.kt)("inlineCode",{parentName:"p"},"[0, 1]")," and returns another\nnumber in the same range but with a modified rate of change. Motion Canvas\nprovides all ",(0,i.kt)("a",{parentName:"p",href:"http://web.archive.org/web/20230315084656/https://easings.net/"},"the most popular timing functions"),"\n(sometimes called easing functions) but since it's a normal JavaScript function\nyou can create your own."),(0,i.kt)("p",null,"Let's use the ",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#easeInOutCubic"},(0,i.kt)("inlineCode",{parentName:"a"},"easeInOutCubic"))," function to\nfix our animation:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield *\n  tween(2, value => {\n    circle().position.x(map(-300, 300, easeInOutCubic(value)));\n  });\n")),(0,i.kt)(o.Z,{small:!0,name:"tweening-cubic",mdxType:"AnimationPlayer"}),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"easeInOut")," means that the object will speed up at the start (",(0,i.kt)("inlineCode",{parentName:"p"},"in"),") and slow\ndown at the end (",(0,i.kt)("inlineCode",{parentName:"p"},"Out"),"). ",(0,i.kt)("inlineCode",{parentName:"p"},"Cubic")," denotes the mathematical function used - in\nthis case it's a cubic equation. Knowing that, a function called ",(0,i.kt)("inlineCode",{parentName:"p"},"easeOutQuad"),"\nwould make the object start with full speed and then slow down at the end using\na quadratic equation."),(0,i.kt)("p",null,"The effects of a particular easing function can be visualised by animating the\n",(0,i.kt)("inlineCode",{parentName:"p"},"y")," coordinate of an object as time changes with a constant rate, such as\nbouncing it in and out."),(0,i.kt)(o.Z,{small:!0,name:"tweening-visualiser",mdxType:"AnimationPlayer"}),(0,i.kt)("p",null,"Because using timing functions to map a range of values is a really common\npattern, it's possible to skip ",(0,i.kt)("inlineCode",{parentName:"p"},"map")," entirely and pass the range to the timing\nfunction itself:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// This:\nmap(-300, 300, easeInOutCubic(value));\n// Can be simplified to:\neaseInOutCubic(value, -300, 300);\n")),(0,i.kt)("h3",{id:"interpolation-functions"},"Interpolation functions"),(0,i.kt)("p",null,"So far, we've only animated a single, numeric value. The\n",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#map"},(0,i.kt)("inlineCode",{parentName:"a"},"map"))," function can be used to interpolate between two\nnumbers but to animate more complex types we'll need to use interpolation\nfunctions. Consider the following example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// import { Color } from \"@motion-canvas/core/lib/types\";\nyield *\n  tween(2, value => {\n    circle().fill(\n      Color.lerp(\n        new Color('#e6a700'),\n        new Color('#e13238'),\n        easeInOutCubic(value),\n      ),\n    );\n  });\n")),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"/api/core/types/Color#lerp"},(0,i.kt)("inlineCode",{parentName:"a"},"Color.lerp"))," is a static function that\ninterpolates between two colors:"),(0,i.kt)(o.Z,{small:!0,name:"tweening-color",mdxType:"AnimationPlayer"}),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"All ",(0,i.kt)("a",{parentName:"p",href:"/api/core/types"},"complex types")," in Motion Canvas provide a static method\ncalled ",(0,i.kt)("inlineCode",{parentName:"p"},"lerp")," that interpolates between two instances of said type.")),(0,i.kt)("p",null,"Aside from the default linear interpolation, some types offer more advanced\nfunctions such as the ",(0,i.kt)("a",{parentName:"p",href:"/api/core/types/Vector2#arcLerp"}
10,(0,i.kt)("inlineCode",{parentName:"a"},"Vector2.arcLerp")),". It\nmakes the object follow a curved path from point a to b:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield *\n  tween(2, value => {\n    circle().position(\n      // highlight-next-line\n      Vector2.arcLerp(\n        new Vector2(-300, 200),\n        new Vector2(300, -200),\n        easeInOutCubic(value),\n      ),\n    );\n  });\n")),(0,i.kt)(o.Z,{small:!0,name:"tweening-vector",mdxType:"AnimationPlayer"}),(0,i.kt)("h3",{id:"tweening-properties"},"Tweening properties"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#tween"},(0,i.kt)("inlineCode",{parentName:"a"},"tween"))," function is useful when we need to\norchestrate complex animations. However, there's a better way of tweening\nindividual properties. You may recall from the ",(0,i.kt)("a",{parentName:"p",href:"/docs/quickstart"},"quickstart"),"\nsection that the following tween:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield *\n  tween(2, value => {\n    circle().color(\n      Color.lerp(\n        new Color('#e6a700'),\n        new Color('#e13238'),\n        easeInOutCubic(value),\n      ),\n    );\n  });\n")),(0,i.kt)("p",null,"Can be written as:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * circle().color('#e13238', 2);\n")),(0,i.kt)("p",null,"Here, we use a ",(0,i.kt)("a",{parentName:"p",href:"/api/core/signals/SignalTween"},(0,i.kt)("inlineCode",{parentName:"a"},"SignalTween"))," signature that\nlooks similar to a setter, except it accepts the transition duration as its\nsecond argument. Under the hood, this will also create a tween - one that starts\nwith the current value and ends with the newly provided one."),(0,i.kt)("p",null,"We can chain multiple tweens together by calling the ",(0,i.kt)("inlineCode",{parentName:"p"},"to()")," method on the\nreturned object:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * circle().color('#e13238', 2).to('#e6a700', 2);\n")),(0,i.kt)("p",null,"By default, property tweens use ",(0,i.kt)("inlineCode",{parentName:"p"},"easeInOutCubic")," as the timing function. We can\noverride that by providing a third argument:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield *\n  circle().color(\n    '#e13238',\n    2,\n    // highlight-next-line\n    easeOutQuad,\n  );\n")),(0,i.kt)("p",null,"Similarly, we can pass a custom interpolation function as the fourth argument:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield *\n  circle().position(\n    new Vector2(300, -200),\n    2,\n    easeInOutCubic,\n    // highlight-next-line\n    Vector2.arcLerp,\n  );\n")),(0,i.kt)("h2",{id:"spring-function"},(0,i.kt)("inlineCode",{parentName:"h2"},"spring")," function"),(0,i.kt)("p",null,"The ",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#spring"},(0,i.kt)("inlineCode",{parentName:"a"},"spring"))," function allows us to interpolate\nbetween two values using Hooke's law. We need to provide it with the description\nof our spring and the ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"to")," values. You can think of it as having a\nspring in resting position (the ",(0,i.kt)("inlineCode",{parentName:"p"},"to")," value), stretching it all the way to the\nstarting position (the ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," value), and then letting it go. The movement of\nthe spring as it tries to reach the equilibrium is what we can use to dive our\nanimations."),(0,i.kt)("p",null,"In the example below, we use springs to animate a position of a circle, but this\nmethod can be used for more things than just position."),(0,i.kt)(r.Z,{language:"tsx",mdxType:"CodeBlock"},"import {makeScene2D} from '@motion-canvas/2d';\nimport {Circle} from '@motion-canvas/2d/lib/components';\nimport {createRef} from '@motion-canvas/core/lib/utils';\nimport {\n  PlopSpring,\n  SmoothSpring,\n  spring,\n} from '@motion-canvas/core/lib/tweening';
10\n\nexport default makeScene2D(function* (view) {\n  const circle = createRef<Circle>();\n\n  view.add(\n    <Circle\n      // highlight-start\n      ref={circle}\n      x={-400}\n      size={240}\n      fill={'#e13238'}\n    />,\n  );\n\n  yield* spring(PlopSpring, -400, 400, 1, value => {\n    circle().position.x(value);\n  });\n  yield* spring(SmoothSpring, 400, -400, value => {\n    circle().position.x(value);\n  });\n});\n"),(0,i.kt)(o.Z,{small:!0,name:"tweening-spring",mdxType:"AnimationPlayer"}),(0,i.kt)("h3",{id:"spring-description"},"Spring description"),(0,i.kt)("p",null,"The first argument of the ",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#spring"},(0,i.kt)("inlineCode",{parentName:"a"},"spring"))," function expects\nan object that describes the physical properties of our spring. Motion Canvas\nships with a few useful presets that you can use, such as ",(0,i.kt)("inlineCode",{parentName:"p"},"PlopSpring")," and\n",(0,i.kt)("inlineCode",{parentName:"p"},"SmoothSpring"),". But it's possible to define your own spring:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"const MySpring = {\n  mass: 0.04,\n  stiffness: 10.0,\n  damping: 0.7,\n  initialVelocity: 8.0,\n};\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"mass")," - Describes the inertia of the spring. How much force is required to\naccelerate and decelerate it."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stiffness")," - The coefficient of the spring. Usually represented by ",(0,i.kt)("inlineCode",{parentName:"li"},"k")," in\nHooke's equation. It describes how stiff the spring is."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"damping")," - Over time, damping causes the spring to lose energy and eventually\nsettle in equilibrium. You can set it to ",(0,i.kt)("inlineCode",{parentName:"li"},"0")," to create a spring that\noscillates indefinitely."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"initialVelocity")," - The initial velocity of the spring. You can set the ",(0,i.kt)("inlineCode",{parentName:"li"},"from"),"\nand ",(0,i.kt)("inlineCode",{parentName:"li"},"to")," positions to the same value and give the spring some initial velocity\nto make it oscillate in place.")),(0,i.kt)("h3",{id:"settle-tolerance"},"Settle tolerance"),(0,i.kt)("p",null,"Notice how in our spring example, we provided the first spring with an\nadditional value:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * spring(PlopSpring, -400, 400, 1 /*...*/);\n//                               here ^\n")),(0,i.kt)("p",null,"This optional argument is called ",(0,i.kt)("inlineCode",{parentName:"p"},"settleTolerance")," and is used to define the\nminimal distance from the ",(0,i.kt)("inlineCode",{parentName:"p"},"to")," value the spring should reach to be considered\nsettled. The generator created by the ",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening#spring"},(0,i.kt)("inlineCode",{parentName:"a"},"spring")),"\nfunction finishes ",(0,i.kt)("strong",{parentName:"p"},"only")," when the spring settles. By adjusting the tolerance\nwe can make the animation finish faster, depending on our needs. In our example\nwe animate the position so a tolerance of ",(0,i.kt)("inlineCode",{parentName:"p"},"1")," means that the spring needs to be\nat most ",(0,i.kt)("inlineCode",{parentName:"p"},"1")," pixel away from the ",(0,i.kt)("inlineCode",{parentName:"p"},"to")," value."),(0,i.kt)("h2",{id:"saving-and-restoring-states"},"Saving and restoring states"),(0,i.kt)("p",null,"All nodes provide a ",(0,i.kt)("a",{parentName:"p",href:"/api/2d/components/Node#save"},(0,i.kt)("inlineCode",{parentName:"a"},"save"))," method which allows\nus to save a snapshot of the node's current state. We can then use the\n",(0,i.kt)("a",{parentName:"p",href:"/api/2d/components/Node#restore"},(0,i.kt)("inlineCode",{parentName:"a"},"restore"))," method at a later point in our\nanimation to restore the node to the previously saved state."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// highlight-next-line\ncircle().save();\nyield * circle().position(new Vector2(300, -200), 2);\n// highlight-next-line\nyield * circle().restore(1);\n")),(0,i.kt)("p",null,"It is also possible to provide a custom ",(0,i.kt)("a",{parentName:"p",href:"/api/core/tweening"},"timing function")," to\nthe ",(0,i.kt)("a",{parentName:"p",href:"/api/2d/components/Node#restore"},(0,i.kt)("inlineCode",{parentName:"a"}
10,"restore"))," method."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * circle().restore(1, linear);\n")),(0,i.kt)("p",null,"Node states get stored on a stack. This makes it possible to save more than one\nstate by invoking the ",(0,i.kt)("a",{parentName:"p",href:"/api/2d/components/Node#save"},(0,i.kt)("inlineCode",{parentName:"a"},"save"))," method multiple\ntimes. When calling ",(0,i.kt)("a",{parentName:"p",href:"/api/2d/components/Node#restore"},(0,i.kt)("inlineCode",{parentName:"a"},"restore")),", the node will\nbe restored to the most recently saved state by popping the top entry in the\nstate stack. If there is no saved state, this method does nothing."),(0,i.kt)("p",null,"The example below shows a more complete example of how we can store and restore\nmultiple states across an animation."),(0,i.kt)(r.Z,{language:"tsx",mdxType:"CodeBlock"},"import {makeScene2D} from '@motion-canvas/2d';\nimport {Circle} from '@motion-canvas/2d/lib/components';\nimport {createRef} from '@motion-canvas/core/lib/utils';\nimport {all} from '@motion-canvas/core/lib/flow';\n\nexport default makeScene2D(function* (view) {\n  const circle = createRef<Circle>();\n\n  view.add(\n    <Circle\n      // highlight-start\n      ref={circle}\n      size={150}\n      position={[-300, -300]}\n      fill={'#e13238'}\n    />,\n  );\n\n  circle().save();\n  yield* all(circle().position.x(0, 1), circle().scale(1.5, 1));\n\n  circle().save();\n  yield* all(circle().position.y(0, 1), circle().scale(0.5, 1));\n\n  circle().save();\n  yield* all(circle().position.x(300, 1), circle().scale(1, 1));\n\n  yield* circle().restore(1);\n  yield* circle().restore(1);\n  yield* circle().restore(1);\n});\n"),(0,i.kt)(o.Z,{small:!0,name:"tweening-save-restore",mdxType:"AnimationPlayer"}))}d.isMDXComponent=!0}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.