Skip to main content

Tutorial - My First Clip

This tutorial doesn’t assume any existing MotorCortex knowledge.

Before We Start the Tutorial

We will build a small clip that we can use to promote a website. During this tutorial we will learn the basics of MotorCortex Clips and Effects and how can we use them to create the following Clip:

It is important to understand that the previous clip is not rendered. What you see is actual HTML elements animating at a sequence thus providing the video experience.

Setup for the tutorial

In order to follow through this tutorial you will need internet access and a code editor. You will also need to have installed node and npm to your machine. Of course you can just read this tutorial if that is what you want or download the code from the GitHub repository. Throughout this tutorial we will use all tools provided by donkeyclip including

  • CLI: a command line interface that sets up a new donkeyclip project
  • DCode: an online developer tool that will help you during the development process

At the end of this tutorial we will publish our clip at donkeyclip.com: an online community space for publishing your donkeyclips or explore clips from other developers

Now bear with the next 10 steps and by the end we will have a complete, working example of the previously shown clip.

Step 1: Download the boilerplate

Donkeyclip newclip is an environment for creating donkeyclips. We took care of the setup so you can focus on being a creative developer and start writing code right away! To create a new donkeyclip project, run:

npx donkeyclip@latest newclip my-first-clip
cd my-first-clip
npm start
Note
npx on the first line is not a typo - it’s a package runner tool that comes with npm 5.2+.

This will install the latest version of the clip-starter-boilerplate and all the dependencies leaving you with a ready to use clip so you don't have to start from zero. By running npm start you will be automatically navigated to (DCode) the online developer tool for donkeyclips.

Step 2: Code Structure

Before we jump into writing code let's take a look at the code structure. There are two root level folders the clip and the server. The server folder includes all the code that is mandatory for your project to communicate with code.donkeyclip.com. We will not edit or create any file in the server folder. The clip folder contains all the code of your donkeyclip. It contains two folders library and scenes and the host clip files (clip.html,clip.css,clip.js). In addition the initiParams.js and initParamsValidationRules.js are files that contain the dynamic values of your clip and the rules that defines the type of these parameters accordingly.

All Clips should be separated into scenes. You will find this come in handy as your donkeyclip grows in code and complexity. Each scene is a MotorCortex Clip as Incident, which means that it has its own context and effects. All effects that our clip will use lie in the the library folder.

Step 3: Define the scenes and effects

Let's first define the scenes we want our donkeyclip to have. In this tutorial we have 6 scenes that present:

  • the website name
  • some text related to the website
  • a first presentation slide with some context
  • a second presentation slide with some other context
  • some text related to the website
  • the website name

we can see that the two last scenes are the same as the first two so, in this tutorial we will see how we can use the same scene multiple times in our code. Also, we will use the following effects to animate the scenes or create transitions from scene to scene:

  • fadeIn
  • fadeOut
  • slideIn
  • slideOut
  • scaleBig
  • widthIn

Having decided about our scenes, we can change the context of the clip/clip.html file to the following:

<!-- clip/clip.html -->
<div id="root">
<div class="scene" id="scene-1"></div>
<div class="scene" id="scene-2"></div>
<div class="scene" id="scene-3"></div>
<div class="scene" id="scene-4"></div>
<div class="scene" id="scene-5"></div>
<div class="scene" id="scene-6"></div>
</div>

and the css file to:

/* clip/clip.css */
#root {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: 100%;
width: 100%;
background-color: black;
font-family: "Arial";
}
.scene {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
}

We have now 6 scenes the one on top of the other. The next step is to create a single clip for each scene, with its own context and add transitions from scene to scene.

By defining our scenes and context we can now change the initParams.js file to match our needs just for the first scene:

// clip/initParams.js
export default [
{
name: "Default",
value: {
scene1: {
website: "donkeyclip.com",
backgroundColor: "#1e1e1e",
fontColor: "white",
},
},
},
];

Now the first scene will use three effects (fadeIn, fadeOut and scaleBig) so let's go ahead and create the following files in the library folder (see more about effects here):

// clip/library/fadeIn.js
import { CSSEffect } from "@donkeyclip/motorcortex";
export const fadeIn = (selector, duration, easing = "linear") =>
new CSSEffect(
{
animatedAttrs: {
opacity: 1,
},
},
{
selector,
duration,
easing,
}
);
// clip/library/fadeOut.js
import { CSSEffect } from "@donkeyclip/motorcortex";
export const fadeOut = (selector, duration, easing = "linear") =>
new CSSEffect(
{
animatedAttrs: {
opacity: 0,
},
},
{
selector,
duration,
easing,
}
);
// clip/library/scaleBig.js
import { CSSEffect } from "@donkeyclip/motorcortex";
export const scaleBig = (selector, duration, easing = "linear", scaleX = 1.1) =>
new CSSEffect(
{
animatedAttrs: {
transform: {
scaleX,
},
},
},
{
selector,
duration,
easing,
}
);

Step 4: Create the first scene

To create our first scene we can edit the scene-1 folder files (html,css,js) to match the following.

<!-- clip/scenes/scene-1/index.html -->
<div class="container">
<p>{{initParams.scene1.website}}</p>
</div>
/* clip/scenes/scene-1/index.css */
*{
--font-color:{{initParams.scene1.fontColor}};
--background-color:{{initParams.scene1.backgroundColor}};
}

.container {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: 100%;
font-size: 38px;
color: var(--font-color);
background-color: var(--background-color);
opacity: 0;
}

// clip/scenes/scene-1/index.js
import { HTMLClip } from "@donkeyclip/motorcortex";
import html from "./index.html";
import css from "./index.css";
import { fadeIn } from "../../library/fadeIn";
import { fadeOut } from "../../library/fadeOut";
import { scaleBig } from "../../library/scaleBig";

export default (selector, addFadeOut = false) => {
const clip = new HTMLClip({
html,
css,
selector,
containerParams: {
width: "800px",
height: "450px",
},
initParams: {
scene1: "@initParams.scene1",
},
});
clip.addIncident(fadeIn(".container", 1), 0);
clip.addIncident(scaleBig(".container", 3000), 0);
if (addFadeOut) clip.addIncident(fadeOut(".container", 1), 3000);
return clip;
};

We can see that the html and css files are making use of the initial parameters. This means that we can change the context of our clip by simply changing the initParams.js values.

Step 5: Add the first scene into the clip

Finally we can add the first scene in our root clip by editing the clip/clip.js file to match the following:

// clip/clip.js
import { HTMLClip } from "@donkeyclip/motorcortex";
import html from "./clip.html";
import css from "./clip.css";
import initParams from "./initParams";
import scene1 from "./scenes/scene-1";

export const clip = new HTMLClip({
html,
css,
host: document.getElementById("clip"),
initParams: initParams[0].value,
containerParams: {
width: "800px",
height: "450px",
},
});
// starts at 0 ends at 3000
clip.addIncident(scene1("#scene-1"), 0);

Step 6: Create the rest of the effects

Let's go ahead now and create the rest of the effects we will use for the other scenes

// clip/library/slideIn.js
import { CSSEffect } from "@donkeyclip/motorcortex";
export const slideIn = (selector, duration, easing = "easeInOutCubic") =>
new CSSEffect(
{
animatedAttrs: {
top: "0%",
},
initialValues: {
top: "100%",
},
},
{
selector,
duration,
easing,
delay: "@stagger(0,100)",
}
);
// clip/library/slideOut.js
import { CSSEffect } from "@donkeyclip/motorcortex";
export const slideOut = (selector, duration, easing = "easeInOutCubic") =>
new CSSEffect(
{
animatedAttrs: {
top: "-100%",
},
},
{
selector,
duration,
easing,
delay: "@stagger(0,100)",
}
);
// clip/library/widthIn.js
import { CSSEffect } from "@donkeyclip/motorcortex";
export const widthIn = (selector, duration, easing = "easeInOutCubic") =>
new CSSEffect(
{
animatedAttrs: {
width: "65%",
},
},
{
selector,
duration,
easing,
}
);

Step 7: Create the second scene

For the second scene let's create the files index.html, index.css and index js in the folder clip/scenes/scene-2 and add the initial parameters in the initParams.js file.

// add the scene2 initial parameters in clip/initParams.js file
export default [
{
name: "Default",
value: {
scene1: {...},
scene2: {
title: "Programmatic Video System for the Web",
header: "Read the full documentation at",
footer: "motorcortexjs.com",
backgroundColor: "#1e1e1e",
fontColor: "white",
},

},
},
];

<!-- clip/scene-2/index.html -->
<div class="container">
<p>{{initParams.scene2.title}}</p>
<h3>{{initParams.scene2.header}}</h3>
<p>{{initParams.scene2.footer}}</p>
</div>
/* clip/scenes/scene-2/index.css */
* {
--font-color:{{initParams.scene2.fontColor}};
--background-color:{{initParams.scene2.backgroundColor}};
}

.container {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: 100%;
color: var(--font-color);
background-color: var(--background-color);
font-size: 22px;
opacity: 0;
}
// clip/scenes/scene-2/index.js
import { HTMLClip } from "@donkeyclip/motorcortex";
import html from "./index.html";
import css from "./index.css";
import { fadeIn } from "../../library/fadeIn";
import { scaleBig } from "../../library/scaleBig";

export default (selector) => {
const clip = new HTMLClip({
html,
css,
selector,
containerParams: {
width: "800px",
height: "450px",
},
initParams: {
scene2: "@initParams.scene2",
},
});
clip.addIncident(fadeIn(".container", 1), 0);
clip.addIncident(scaleBig(".container", 3000), 0);
return clip;
};

Step 8: Create the third scene

For the third scene let's create the folder clip/scenes/scene-3 and its files index.html, index.css and index js and add the initial parameters in the initParams.js file.

// add the scene3 initial parameters in clip/initParams.js file
export default [
{
name: "Default",
value: {
scene1: {...},
scene2: {...},
scene3: {
image:
"https://donkey-spaces.ams3.cdn.digitaloceanspaces.com/assets/dc-presentation/man-on-desk.png",
title: "Dynamic Videos",
header: "Create your video template as if you were creating a website",
paragraph:
"Using technologies you already know like HTML, CSS and JavaScript",
footer: "Don't miss it",
backgroundColor: "whitesmoke",
fontColor: "#1e1e1e",
},
},
},
];

<!-- clip/scene-3/index.html -->
<div class="container">
<div class="slide-top fill"><img src="{{initParams.scene3.image}}" /></div>
<div class="slide-top column width-out">
<h1>{{initParams.scene3.title}}</h1>
<h3>{{initParams.scene3.header}}</h3>
<p>{{initParams.scene3.paragraph}}</p>
<p>{{initParams.scene3.footer}}</p>
</div>
</div>
/* clip/scenes/scene-3/index.css */
* {
--font-color:{{initParams.scene3.fontColor}};
--background-color:{{initParams.scene3.backgroundColor}};
}

.container {
display: flex;
justify-content: center;
flex-direction: row;
align-items: center;
height: 100%;
flex: 1;
font-size: 12px;
color: var(--font-color);
background-color: var(--background-color);
opacity: 0;
position: relative;
top: 100%;
overflow: hidden;
}

.container div {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}

.container div.width-out {
width: 100%;
}
.container div.fill {
flex: 1;
}
.container div.column {
flex-direction: column;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}

.slide-top {
position: relative;
top: 100%;
}

// clip/scenes/scene-3/index.js
import { HTMLClip } from "@donkeyclip/motorcortex";
import html from "./index.html";
import css from "./index.css";
import { fadeIn } from "../../library/fadeIn";
import { widthIn } from "../../library/widthIn";
import { slideIn } from "../../library/slideIn";
import { slideOut } from "../../library/slideOut";
import { scaleBig } from "../../library/scaleBig";

export default (selector) => {
const clip = new HTMLClip({
html,
css,
selector,
containerParams: {
width: "800px",
height: "450px",
},
initParams: {
scene3: "@initParams.scene3",
},
});

clip.addIncident(fadeIn(".container", 1), 0);
clip.addIncident(scaleBig(".slide-top", 6000), 0);
clip.addIncident(slideIn(".container, .slide-top", 1000), 0);
clip.addIncident(widthIn(".width-out", 500), 4000);
clip.addIncident(slideOut(".slide-top", 1000), 6000);

return clip;
};

Step 9: Create the fourth scene

Finally, for the fourth scene let's create the folder clip/scenes/scene-4 and its files index.html, index.css and index js and add the initial parameters in the initParams.js file.

// add the scene4 initial parameters in clip/initParams.js file
export default [
{
name: "Default",
value: {
scene1: {...},
scene2: {...},
scene3: {...},
scene4: {
image:
"https://donkey-spaces.ams3.cdn.digitaloceanspaces.com/assets/dc-presentation/woman-on-desk.webp",
title: "Become part of the community",
header: "and experience the fun of being a video developer",
paragraph:
"Explore, create, like and share creative ideas, or use it for your own purpose",
footer: "another-website.com",
backgroundColor: "whitesmoke",
fontColor: "#1e1e1e",
},
},
},
];

<!-- clip/scene-4/index.html -->
<div class="container">
<div class="slide-top column">
<h1>{{initParams.scene4.title}}</h1>
<h3>{{initParams.scene4.header}}</h3>
<p>{{initParams.scene4.paragraph}}</p>
<p>{{initParams.scene4.footer}}</p>
</div>
<div class="slide-top"><img src="{{initParams.scene4.image}}" /></div>
</div>
/* clip/scenes/scene-4/index.css */
* {
--font-color:{{initParams.scene4.fontColor}};
--background-color:{{initParams.scene4.backgroundColor}};
}

.container {
display: flex;
justify-content: center;
flex-direction: row;
align-items: center;
height: 100%;
flex: 1;
font-size: 12px;
position: relative;
top: 101%;
color: var(--font-color);
background-color: var(--background-color);
}

.container div {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
flex: 1;
}
.container div.column {
flex-direction: column;
min-width: 65% !important;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}

.slide-top {
position: relative;
top: 100%;
}
// clip/scenes/scene-4/index.js
import { HTMLClip } from "@donkeyclip/motorcortex";
import html from "./index.html";
import css from "./index.css";
import { fadeIn } from "../../library/fadeIn";
import { slideIn } from "../../library/slideIn";
import { slideOut } from "../../library/slideOut";
import { scaleBig } from "../../library/scaleBig";

export default (selector) => {
const clip = new HTMLClip({
html,
css,
selector,
containerParams: {
width: "800px",
height: "450px",
},
initParams: {
scene4: "@initParams.scene4",
},
});

clip.addIncident(fadeIn(".container", 1), 0);
clip.addIncident(slideIn(".container,.slide-top", 1000), 0);
clip.addIncident(scaleBig(".container,.slide-top", 4000, undefined, 1.05), 0);
clip.addIncident(slideOut(".slide-top", 1000), 4000);

return clip;
};

Step 10: add the scenes into the root clip

Finally we need to add all the scenes in the root clip. To do so you can edit the clip/clip.js file to match the following:

import { HTMLClip } from "@donkeyclip/motorcortex";
import html from "./clip.html";
import css from "./clip.css";
import initParams from "./initParams";
import scene1 from "./scenes/scene-1";
import scene2 from "./scenes/scene-2";
import scene3 from "./scenes/scene-3";
import scene4 from "./scenes/scene-4";
import initParamsValidationRules from "./initParamsValidationRules";

export const clip = new HTMLClip({
html,
css,
host: document.getElementById("clip"),
initParamsValidationRules,
initParams: initParams[0].value,
containerParams: {
width: "800px",
height: "450px",
},
});
// starts at 0 ends at 3000
clip.addIncident(scene1("#scene-1"), 0);
// starts at 3000 ends at 6000
clip.addIncident(scene2("#scene-2"), 3000);
// starts at 6000 ends at 12000
clip.addIncident(scene3("#scene-3"), 6000);
// starts at 12000 ends at 17000
clip.addIncident(scene4("#scene-4"), 12000);
// starts at 17000 ends at 20000
clip.addIncident(scene2("#scene-5"), 17000);
// starts at 17000 ends at 23000
clip.addIncident(scene1("#scene-6", false), 20000);

we can see that we import the file initParamsValidationRules.js. This file informs donkeyclip about the type of your parameters defined in the initParams.js file, and prevents the clip from initializing if you provide invalid type. Based on our initParams.js file we have the following initParamsValidationRules.js file:

export default {
scene1: {
label: "Intro",
type: "object",
props: {
website: "string",
backgroundColor: "color",
fontColor: "color",
},
},
scene2: {
label: "Welcome",
type: "object",
props: {
title: "string",
header: "string",
footer: "string",
backgroundColor: "color",
fontColor: "color",
},
},
scene3: {
label: "Presentation 1",
type: "object",
props: {
image: "string",
header: "string",
paragraph: "string",
footer: "string",
backgroundColor: "color",
fontColor: "color",
},
},
scene4: {
label: "Presentation 2",
type: "object",
props: {
image: "string",
header: "string",
paragraph: "string",
footer: "string",
backgroundColor: "color",
fontColor: "color",
},
},
};

Step 11: Publish and Use

Option 1: DonkeyClip way

The DonkeyClip way, is simple. You first have to connect with the DonkeyClip community. Once you have created a profile the option publish will be available at code.donkeyclip.com. Once you click publish you will be redirected to donkeyclip.com where you can add a thumbnail and other details regarding your clip. Then, when you are ready, click Publish. The clip will now be available at User -> View Profile -> My DonkeyClips. From there you can click use this clip and use it as a React Component or an iframe. If you want to change something in your clip, you can perform the changes at your code locally, click publish again and repeat the process. This will create a new version of your clip, which will automatically propagate the change to any site you used the donkeyclip (read this for more).

Option 2: Code your way through

In order to code your way through, you should include the donkeyclip project as is in your code. For that you will need to copy the clip folder and install the proper dependencies in your project. Also you need to change the host of the root clip to the element you want to host your clip.