PageSourceSearch

https://motioncanvas.io/assets/js/29eed114.a77a3436.js

js motioncanvas.io collected 2026-10-04 00:27:35 UTC 13,776 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([[3626],{876:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>m});var a=t(2784);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(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 c(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?i(Object(t),!0).forEach((function(n){r(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):i(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,r=function(e,n){if(null==e)return{};var t,a,r={},i=Object.keys(e);for(a=0;a<i.length;a++)t=i[a],n.indexOf(t)>=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)t=i[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var o=a.createContext({}),s=function(e){var n=a.useContext(o),t=n;return e&&(t="function"==typeof e?e(n):c(c({},n),e)),t},p=function(e){var n=s(e.components);return a.createElement(o.Provider,{value:n},e.children)},u="mdxType",f={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,r=e.mdxType,i=e.originalType,o=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(t),d=r,m=u["".concat(o,".").concat(d)]||u[d]||f[d]||i;return t?a.createElement(m,c(c({ref:n},p),{},{components:t})):a.createElement(m,c({ref:n},p))}));function m(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var i=t.length,c=new Array(i);c[0]=d;var l={};for(var o in n)hasOwnProperty.call(n,o)&&(l[o]=n[o]);l.originalType=e,l[u]="string"==typeof e?e:r,c[1]=l;for(var s=2;s<i;s++)c[s]=t[s];return a.createElement.apply(null,c)}return a.createElement.apply(null,t)}d.displayName="MDXCreateElement"},7217:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>o,contentTitle:()=>c,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var a=t(7896),r=(t(2784),t(876));const i={sidebar_position:7,slug:"/references"},c="References",l={unversionedId:"getting-started/references",id:"getting-started/references",title:"References",description:"Usually, when creating a node, we want to store a reference to it, so we can",source:"@site/docs/getting-started/references.mdx",sourceDirName:"getting-started",slug:"/references",permalink:"/docs/references",draft:!1,editUrl:"http://web.archive.org/web/20230315084657/https://github.com/motion-canvas/motion-canvas/blob/main/packages/docs/docs/getting-started/references.mdx",tags:[],version:"current",lastUpdatedBy:"Jacob",sidebarPosition:7,frontMatter:{sidebar_position:7,slug:"/references"},sidebar:"docs",previous:{title:"Signals",permalink:"/docs/signals"},next:{title:"Tweening",permalink:"/docs/tweening"}},o={},s=[{value:"<code>ref</code> property",id:"ref-property",level:2},{value:"<code>createRef()</code> function",id:"createref-function",level:2},{value:"<code>makeRef()</code> function",id:"makeref-function",level:2},{value:"Array of references",id:"array-of-references",level:3},{value:"Custom functions",id:"custom-functions",level:3},{value:"<code>makeRefs()</code> function",id:"makerefs-function",level:2}],p={toc:s};
10function u(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"references"},"References"),(0,r.kt)("p",null,"Usually, when creating a node, we want to store a reference to it, so we can\nanimate it later. One way to do that is by assigning it to a variable first, and\nthen adding it to the scene:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"const circle = <Circle />;\nview.add(circle);\n\n// we can now animate our circle:\nyield * circle.scale(2, 0.3);\n")),(0,r.kt)("admonition",{type:"info"},(0,r.kt)("p",{parentName:"admonition"},"If you're used to libraries such as React, the above example may seem strange.\nIn Motion Canvas, the JSX components immediately create and return an instance\nof the given class. It's completely valid to store it as a reference and use it\nthroughout the animation.")),(0,r.kt)("p",null,"But this approach doesn't scale well. The more nodes we add, the harder it gets\nto see the overall structure of our scene. Consider the following example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"const rectA = <Rect />;\nconst rectB = <Rect />;\nconst circle = <Circle>{rectA}</Circle>;\nview.add(\n  <Layout>\n    {circle}\n    {rectB}\n  </Layout>,\n);\n")),(0,r.kt)("p",null,"And now compare it to a version that doesn't store any references:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"view.add(\n  <Layout>\n    <Circle>\n      <Rect />\n    </Circle>\n    <Rect />\n  </Layout>,\n);\n")),(0,r.kt)("p",null,"If you find the latter example more readable, this guide is for you."),(0,r.kt)("h2",{id:"ref-property"},(0,r.kt)("inlineCode",{parentName:"h2"},"ref")," property"),(0,r.kt)("p",null,"Each node in Motion Canvas has a property called ",(0,r.kt)("inlineCode",{parentName:"p"},"ref")," that allows you to create\na reference to said node. It accepts a callback that will be invoked right after\nthe node has been created, with the first argument being the newly created\ninstance."),(0,r.kt)("p",null,"With this in mind, we can rewrite the initial example as:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"let circle: Circle;\nview.add(\n  <Circle\n    ref={instance => {\n      circle = instance;\n    }}\n  />,\n);\n\nyield * circle.scale(2, 0.3);\n")),(0,r.kt)("p",null,"Using the ",(0,r.kt)("inlineCode",{parentName:"p"},"ref")," property in this way is not really practical, and we wouldn't\nrecommend it. But it's crucial to understand how it works because all the\nupcoming methods use this property as a base."),(0,r.kt)("h2",{id:"createref-function"},(0,r.kt)("inlineCode",{parentName:"h2"},"createRef()")," function"),(0,r.kt)("p",null,"The preferred way of using the ",(0,r.kt)("inlineCode",{parentName:"p"},"ref")," property is in conjunction with the\n",(0,r.kt)("a",{parentName:"p",href:"/api/core/utils#createRef"},(0,r.kt)("inlineCode",{parentName:"a"},"createRef()"))," function. Continuing with our\nexample, we can rewrite it as:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"import {createRef} from '@motion-canvas/core/lib/utils';\n\n// ...\n\nconst circle = createRef<Circle>();\nview.add(<Circle ref={circle} />);\n\nyield * circle().scale(2, 0.3);\n")),(0,r.kt)("p",null,"Notice that ",(0,r.kt)("inlineCode",{parentName:"p"},"circle")," is no longer just a variable that points to our circle.\nInstead, it's a ",(0,r.kt)("a",{parentName:"p",href:"/docs/signals"},"signal-like")," function that can be used to access\nit. Invoking it without any arguments (",(0,r.kt)("inlineCode",{parentName:"p"},"circle()"),") returns our instance."),(0,r.kt)("p",null,"Going back to the example with the more complex scene, we can now rewrite it as:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"const rectA = createRef<Rect>();\nconst rectB = createRef<Rect>();\nconst circle = createRef<Circle>();\nview.add(\n  <Layout>\n    <Circle ref={circle}>\n      <Rect ref={rectA} />\n    </Circle>\n    <Rect ref={rectB} />\n  </Layout>,\n);\n")),(0,r.kt)("h2",{id:"makeref-function"},(0,r.kt)("inlineCode",{parentName:"h2"},"makeRef()")," function"),(0,r.kt)("p",null,"Another common use case of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ref")," property is to assign the newly created\ninstance to a property of some object. In the following example, we assign our\ncircle to ",(0,r.kt)("inlineCode",{parentName:"p"}
10,"circle.instance")," (We'll talk about why this may be useful in a bit):"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"const circle = {instance: null as Circle};\nview.add(\n  <Circle\n    // highlight-start\n    ref={instance => {\n      circle.instance = instance;\n    }}\n    // highlight-end\n  />,\n);\n")),(0,r.kt)("p",null,"We can use the ",(0,r.kt)("a",{parentName:"p",href:"/api/core/utils#makeRef"},(0,r.kt)("inlineCode",{parentName:"a"},"makeRef()"))," function to simplify this\nprocess:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"import {makeRef} from '@motion-canvas/core/lib/utils';\n\n// ...\n\nconst circle = {instance: null as Circle};\nview.add(\n  // highlight-next-line\n  <Circle ref={makeRef(circle, 'instance')} />,\n);\n")),(0,r.kt)("h3",{id:"array-of-references"},"Array of references"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"makeRef()")," can be particularly useful when we create an array of nodes and want\nto grab references to all of them:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"const circles: Circle[] = [];\nview.add(\n  <Layout>\n    {range(10).map(index => (\n      // highlight-next-line\n      <Circle ref={makeRef(circles, index)} />\n    ))}\n  </Layout>,\n);\n")),(0,r.kt)("p",null,"In JavaScript, arrays are objects whose properties are their indices. So\n",(0,r.kt)("inlineCode",{parentName:"p"},"makeRef(circles, index)")," will set the nth element of our array to the created\ncircle. As a result, we end up with an array of size ",(0,r.kt)("inlineCode",{parentName:"p"},"10")," filled with circles\nthat we can use to animate all of them."),(0,r.kt)("h3",{id:"custom-functions"},"Custom functions"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"makeRef()")," can also be used to return more than one reference from a custom\nfunction component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"function Label({\n  refs,\n  children,\n}: {\n  refs: {rect: Rect; text: Txt};\n  children: string;\n}) {\n  return (\n    // highlight-next-line\n    <Rect ref={makeRef(refs, 'rect')}>\n      // highlight-next-line\n      <Txt ref={makeRef(refs, 'text')}>{children}</Txt>\n    </Rect>\n  );\n}\n\nconst label = {rect: null as Rect, text: null as Text};\nview.add(<Label refs={label}>HELLO</Label>);\n\n// we can now animate both the Rect and the Text of our label:\nyield * label.rect.opacity(2, 0.3);\nyield * label.text.fontSize(24, 0.3);\n")),(0,r.kt)("p",null,"In this example, we define a function component called ",(0,r.kt)("inlineCode",{parentName:"p"},"Label")," consisting of a\nrectangle with some text inside. When using the component, we use the ",(0,r.kt)("inlineCode",{parentName:"p"},"refs"),"\nproperty to pass the ",(0,r.kt)("inlineCode",{parentName:"p"},"label")," object created by us. ",(0,r.kt)("inlineCode",{parentName:"p"},"makeRef()")," is then used to\nfill this object with all the necessary references."),(0,r.kt)("h2",{id:"makerefs-function"},(0,r.kt)("inlineCode",{parentName:"h2"},"makeRefs()")," function"),(0,r.kt)("p",null,"Looking at the previous example, you may notice that we had to define the ",(0,r.kt)("inlineCode",{parentName:"p"},"refs"),"\ntype twice. First in the ",(0,r.kt)("inlineCode",{parentName:"p"},"Label")," declaration and then again when creating the\n",(0,r.kt)("inlineCode",{parentName:"p"},"label")," object:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"function Label({\n  refs,\n  children,\n}: {\n  // highlight-next-line\n  refs: {rect: Rect; text: Txt};\n  children: string;\n}) {\n  return (\n    <Rect ref={makeRef(refs, 'rect')}>\n      <Txt ref={makeRef(refs, 'text')}>{children}</Txt>\n    </Rect>\n  );\n}\n\n// highlight-next-line\nconst label = {rect: null as Rect, text: null as Text};\nview.add(<Label refs={label}>HELLO</Label>);\n")),(0,r.kt)("p",null,"We can use ",(0,r.kt)("a",{parentName:"p",href:"/api/core/utils#makeRefs"},(0,r.kt)("inlineCode",{parentName:"a"}
10,"makeRefs()"))," to eliminate this\nredundancy. It can extract the type from the ",(0,r.kt)("inlineCode",{parentName:"p"},"Label")," declaration and create an\nempty object matching it:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-tsx"},"import {makeRef, makeRefs} from '@motion-canvas/core/lib/utils';\n\n// ...\n\nfunction Label({\n  refs,\n  children,\n}: {\n  // highlight-next-line\n  refs: {rect: Rect; text: Txt};\n  children: string;\n}) {\n  return (\n    <Rect ref={makeRef(refs, 'rect')}>\n      <Txt ref={makeRef(refs, 'text')}>{children}</Txt>\n    </Rect>\n  );\n}\n\n// highlight-next-line\nconst label = makeRefs<typeof Label>();\nview.add(<Label refs={label}>HELLO</Label>);\n")))}u.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.