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([[9946],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var a=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},m="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=c(n),u=i,h=m["".concat(s,".").concat(u)]||m[u]||d[u]||r;return n?a.createElement(h,o(o({ref:t},p),{},{components:n})):a.createElement(h,o({ref:t},p))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:i,o[1]=l;for(var c=2;c<r;c++)o[c]=n[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},2665:(e,t,n)=>{n.d(t,{Z:()=>f});var a=n(2784);const i="container_lQCo",r="small_As57",o="banner_A4QS",l="player_tELG",s="link_BTzN",c="icon_wSGd";var p=n(7896);function m(e){let{width:t=24,height:n=24,...i}=e;return a.createElement("svg",(0,p.Z)({viewBox:"0 0 24 24",width:t,height:n,"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 d=n(9817),u=n(6277);function h(e){let{name:t}=e;const n=`https://github.com/motion-canvas/motion-canvas/blob/main/packages/examples/src/scenes/${t}.tsx`;return a.createElement(d.Z,{to:n,className:(0,u.Z)("padding--sm",s)},a.createElement("span",null,"View source code"),a.createElement(m,{className:c}))}function f(e){let{name:t,banner:n,small:s}=e;return a.createElement("div",{className:(0,u.Z)(i,n&&o,s&&r)},a.createElement("motion-canvas-player",{class:l,src:`/examples/${t}.js`,auto:n}),a.createElement(h,{name:t}))}n(1263).Z.canUseDOM&&n.e(5483).then(n.bind(n,5483))},3641:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>m,contentTitle:()=>c,default:()=>h,frontMatter:()=>s,metadata:()=>p,toc:()=>d});var a=n(7896),i=(n(2784),n(876)),r=n(2665),o=n(8128);const l="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 {all} from '@motion-canvas/core/lib/flow';\n\nexport default makeScene2D(function* (view) {\n const myCircle = createRef<Circle>();\n\n view.add(\n <Circle\n //highlight-start\n ref={myCircle}\n x={-300}\n width={240}\n height={240}\n fill=\"#e13238\"\n />,\n );\n\n yield* all(\n myCircle().position.x(300, 1).to(-300, 1),\n myCircle().fill('#e6a700', 1).to('#e13238', 1),\n );\n});\n",s={sidebar_position:1,slug:"/quickstart"},c="Quickstart",p={unversionedId:"getting-started/quickstart",id:"getting-started/quickstart",title:"Quickstart",description:"In this guide, we'll create a simple animation using Motion Canvas.",source:"@site/docs/getting-started/quickstart.mdx",sourceDirName:"getting-started",slug:"/quickstart",permalink:"/docs/quickstart",draft:!1,editUrl:"https://github.com/motion-canvas/motion-canvas/blob/main/packages/docs/docs/getting-started/quickstart.mdx",tags:[],version:"current",lastUpdatedBy:"Jacob",sidebarPosition:1,frontMatter:{
10sidebar_position:1,slug:"/quickstart"},sidebar:"docs",previous:{title:"Getting Started",permalink:"/docs/category/getting-started"},next:{title:"Animation flow",permalink:"/docs/flow"}},m={},d=[{value:"Prerequisites",id:"prerequisites",level:3},{value:"Creating a new project",id:"creating-a-new-project",level:3},{value:"Starting the editor",id:"starting-the-editor",level:3},{value:"Programming an animation",id:"programming-an-animation",level:3},{value:"Explanation",id:"explanation",level:3}],u={toc:d};function h(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"quickstart"},"Quickstart"),(0,i.kt)("p",null,"In this guide, we'll create a simple animation using Motion Canvas."),(0,i.kt)("h3",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("p",null,"Make sure that ",(0,i.kt)("a",{parentName:"p",href:"https://nodejs.org/"},"Node.js")," version 16 or greater is installed\non your machine."),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"You can run the following command to check if Node.js is already installed:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"node -v\n"))),(0,i.kt)("h3",{id:"creating-a-new-project"},"Creating a new project"),(0,i.kt)("p",null,"Run the following command in order to scaffold a new Motion Canvas project:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"npm init @motion-canvas\n")),(0,i.kt)("p",null,"Answer the prompts to name your project and select which language you would like\nto use; either TypeScript or plain JavaScript."),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"We recommend using TypeScript in your first project, since that's the language\nwe're using throughout this documentation.")),(0,i.kt)("p",null,"To complete the scaffolding process, run the following command in your newly\ncreated project directory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell"},"npm install\n")),(0,i.kt)("h3",{id:"starting-the-editor"},"Starting the editor"),(0,i.kt)("p",null,"To start the editor, run:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell"},"npm run serve\n")),(0,i.kt)("p",null,"The editor can be accessed by visiting\n",(0,i.kt)("a",{parentName:"p",href:"http://localhost:9000/"},"http://localhost:9000/"),". We'll use it to preview our\nanimation, but for now there's not much to see."),(0,i.kt)("h3",{id:"programming-an-animation"},"Programming an animation"),(0,i.kt)("p",null,"The scaffolding command will create several files for you, but for now we're\ngoing to focus on ",(0,i.kt)("inlineCode",{parentName:"p"},"src/scenes/example.tsx"),", which is where we can add our\nanimations. Open ",(0,i.kt)("inlineCode",{parentName:"p"},"example.tsx")," in a text editor, and replace all code in the\nfile with the following snippet."),(0,i.kt)(o.Z,{language:"tsx",title:"src/scenes/example.tsx",mdxType:"CodeBlock"},l),(0,i.kt)("p",null,"Now save the file. Any changes you make are automatically picked up and\nreflected in the preview."),(0,i.kt)("admonition",{title:"Vite on Windows Subsystem for Linux (WSL) 2",type:"caution"},(0,i.kt)("p",{parentName:"admonition"},"When running Vite on WSL2, file system watching does not work if a file is\nedited by Windows applications."),(0,i.kt)("p",{parentName:"admonition"},"To fix this, move the project folder into the WSL2 file system and use WSL2\napplications to edit files.",(0,i.kt)("br",{parentName:"p"}),"\n","Accessing the Windows file system from WSL2 is slow, so this will improve\nperformance."),(0,i.kt)("p",{parentName:"admonition"},"For more information view the\n",(0,i.kt)("a",{parentName:"p",href:"https://vitejs.dev/config/server-options.html#server-watch"},(0,i.kt)("strong",{parentName:"a"},"Vite Docs")),".")),(0,i.kt)("p",null,"You should see a gray circle in the preview pane at the top right of the web\napplication. Press the play button to see the circle animate across the screen."),(0,i.kt)(r.Z,{small:!0,name:"quickstart",mdxType:"AnimationPlayer"}),(0,i.kt)("p",null,"Depending on the size of your browser, you may want to zoom the preview out by\nscrolling down on your mouse wheel while hovering over the preview panel."),(0,i.kt)("h3",{id:"explanation"},"Explanation"),(0,i.kt)("p",null,"Each video in Motion Canvas is represented by an instance of the ",(0,i.kt)("inlineCode",{parentName:"p"},"Project"),"\nclass. In our example, the project is declared in ",(0,i.kt)("inlineCode",{parentName:"p"},"src/project.ts"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="src/project.ts"',title:'"src/project.ts"'},"import {makeProject} from '@motion-canvas/core/lib';\n\nimport example from './scenes/example?scene';
10\n\nexport default makeProject({\n scenes: [example],\n background: 'white',\n});\n")),(0,i.kt)("p",null,"When creating a project, we need to provide it with an array of scenes to\ndisplay. In this case, we use only one scene imported from\n",(0,i.kt)("inlineCode",{parentName:"p"},"src/scenes/example.tsx"),"."),(0,i.kt)("p",null,"A scene is a set of elements displayed on the screen and an animation that\ngoverns them. The most basic scene looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-tsx"},"import {makeScene2D} from '@motion-canvas/2d/lib/scenes';\n\nexport default makeScene2D(function* (view) {\n // animation\n});\n")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"makeScene2D()")," takes a function generator and turns it into a scene which we\nthen import in our project file. The function generator describes the flow of\nthe animation, while the provided ",(0,i.kt)("inlineCode",{parentName:"p"},"view")," argument is used to add elements to the\nscene."),(0,i.kt)("p",null,"You can learn more about scenes, nodes, and this XML-like syntax in the\n",(0,i.kt)("a",{parentName:"p",href:"/docs/hierarchy"},"scene hierarchy")," section. For now, what's important is that,\nin our example, we add an individual ",(0,i.kt)("a",{parentName:"p",href:"/api/2d/components/Circle"},(0,i.kt)("inlineCode",{parentName:"a"},"<Circle/>")),"\nnode to our scene. We make it red, set its width and height as ",(0,i.kt)("inlineCode",{parentName:"p"},"240")," pixels and\nposition it ",(0,i.kt)("inlineCode",{parentName:"p"},"300")," pixels left from the center:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-tsx"},'view.add(\n <Circle\n // highlight-start\n ref={myCircle}\n x={-300}\n width={240}\n height={240}\n fill="#e13238"\n />,\n);\n')),(0,i.kt)("p",null,"To animate our circle we first need to\n",(0,i.kt)("a",{parentName:"p",href:"/docs/references"},"grab a reference to it"),". That's the purpose of the\n",(0,i.kt)("a",{parentName:"p",href:"/api/core/utils#createRef"},(0,i.kt)("inlineCode",{parentName:"a"},"createRef"))," function. We use it to create a\nreference and pass it to our circle using the\n",(0,i.kt)("a",{parentName:"p",href:"/api/2d/components/NodeProps#ref"},(0,i.kt)("inlineCode",{parentName:"a"},"ref"))," attribute:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-tsx"},'// highlight-next-line\nconst myCircle = createRef<Circle>();\n\nview.add(\n <Circle\n // highlight-next-line\n ref={myCircle}\n x={-300}\n width={240}\n height={240}\n fill="#e13238"\n />,\n);\n')),(0,i.kt)("p",null,"We then access the circle through ",(0,i.kt)("inlineCode",{parentName:"p"},"myCircle()")," and animate its properties:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-tsx"},"yield *\n all(\n myCircle().fill('#e6a700', 1).to('#e13238', 1),\n myCircle().position.x(300, 1).to(-300, 1),\n );\n")),(0,i.kt)("p",null,"This snippet may seem a bit confusing so let's break it down."),(0,i.kt)("p",null,"Each property of a node can be read and updated throughout the animation. For\nexample, in the circle above we defined its ",(0,i.kt)("inlineCode",{parentName:"p"},"fill")," property as ",(0,i.kt)("inlineCode",{parentName:"p"},"'#e13238'"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-tsx"},'<Circle\n ref={myCircle}\n x={-300}\n width={240}\n height={240}\n // highlight-next-line\n fill="#e13238"\n/>\n')),(0,i.kt)("p",null,"Using our reference we can now retrieve this property's value:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"const fill = myCircle().fill(); // '#e13238'\n")),(0,i.kt)("p",null,"We can also update it by passing the new value as the first argument:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"myCircle().fill('#e6a700');\n")),(0,i.kt)("p",null,"This will immediately update the color of our circle. If we want to transition\nto a new value over some time, we can pass the transition duration (in seconds)\nas the second argument:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"myCircle().fill('#e6a700', 1);\n")),(0,i.kt)("p",null,"This ",(0,i.kt)("a",{parentName:"p",href:"/docs/tweening"},"creates a tween animation")," that smoothly changes the fill\ncolor over one second."),(0,i.kt)("p",null,"Animations in Motion Canvas don't play on their own, we need to explicitly tell\nthem to. This is why scenes are declared using generator functions - they serve\nas a description of how the animation should play out. By yielding different\ninstructions we can tell the scene animation to do different things."),(0,i.kt)("p",null,"For example, to play the tween we created, we can do:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * myCircle().fill('#e6a700', 1);\n")),(0,i.kt)("p",null,"This will pause the generator, play out the animation we yielded, and then\ncontinue."),(0,i.kt)("p",null,"To play another animation, right after the first one, we can simply write\nanother ",(0,i.kt)("inlineCode",{parentName:"p"},"yield*")," statement:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * myCircle().fill('#e6a700', 1);\nyield * myCircle().fill('#e13238', 1);\n")),(0,i.kt)("p",null,"But since we're animating the same property, we can write it in a more compact\nway:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * myCircle().fill('#e6a700', 1).to('#e13238', 1);\n")),(0,i.kt)("p",null,"In our example, aside from changing the color, we also move our circle around.\nWe can try doing it the same way we animated the color:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield * myCircle().fill('#e6a700', 1).to('#e13238', 1);\nyield * myCircle().position.x(300, 1).to(-300, 1);\n")),(0,i.kt)("p",null,"This works, but the position will start animating ",(0,i.kt)("strong",{parentName:"p"},"after")," the fill color. To\nmake them happen at the same time, we use the ",(0,i.kt)("a",{parentName:"p",href:"/api/core/flow#all"},(0,i.kt)("inlineCode",{parentName:"a"},"all"))," function:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"yield *\n all(\n myCircle().fill('#e6a700', 1).to('#e13238', 1),\n myCircle().position.x(300, 1).to(-300, 1),\n );\n")),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"/api/core/flow#all"},(0,i.kt)("inlineCode",{parentName:"a"},"all"))," takes one or more animations and merges them together. Now they'll\nhappen at the same time. The ",(0,i.kt)("a",{parentName:"p",href:"/docs/flow"},"animation flow")," section goes into more\ndepth about generators and flow functions such as ",(0,i.kt)("a",{parentName:"p",href:"/api/core/flow#all"},(0,i.kt)("inlineCode",{parentName:"a"},"all")),"."),(0,i.kt)("p",null,"This brings us back to our initial example:"),(0,i.kt)(o.Z,{language:"tsx",title:"src/scenes/example.tsx",mdxType:"C
10odeBlock"},l))}h.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.