# Creating Keyframe Animations

We can create keyframe animations by adding an animations() attibute to the component passed into bytepath.createAsset

AnimatedBalloon.vue

<script>
    import Bytepath from "bytepath";
    export default Bytepath.CreateAsset({
        data() {
            return { balloonPos: new Bytepath.Position(), curFrame: 0 };
        },
        components: {
            balloon: Bytepath.samples.assets.tinyBalloon.tinyBalloon,
        },
        animations(){
            return {
                default: [
                    {
                        start: 0,
                        end: 100,
                        handler({ context, keyframe }) {
                            /**
                             * Balloon will scroll on the X axis to the value of the :keyframe prop
                             * This effectively means we scroll between X=0 to X=100 depending on keyframe
                             */
                            context.balloonPos.x = keyframe;
                        },
                    },
                ],
            };
        },
    });
</script>
<template>
    <balloon :color="color" :position="balloonPos" />
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

The Balloon asset should move to X=100 and never go past that point no matter how large :keyframe becomes

# Repeating animations

Assets have a :repeat prop that if true, will make the animation repeat from 0 if keyframe > end frame of the animation. Lets toggle repeat on the simple animation we made in the previous example

<script>
    import Bytepath from "bytepath";
    export default Bytepath.CreateAsset({
        data() {
            return { balloonPos: new Bytepath.Position(), curFrame: 0 };
        },
        components: {
            balloon: Bytepath.samples.assets.tinyBalloon.tinyBalloon,
        },
        animations(){
            return {
                default: [
                    {
                        start: 0,
                        end: 100,
                        handler({ context, keyframe }) {
                            /**
                             * Balloon will scroll on the X axis to the value of the :keyframe prop
                             * This effectively means we scroll between X=0 to X=100 depending on keyframe
                             */
                            context.balloonPos.x = keyframe;
                        },
                    },
                ],
            };
        },
    });
</script>
<template>
    <balloon :color="color" :position="balloonPos" />
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# Animations are reactive

Context is just a vue component so if you change any reactive data inside of it (such as position), your art asset will automatically update and/or re render if required.

# Using Create Keyframe function

You can create your animations by writing objects manually like we did in the previous example, and everything will work fine. However, writing objects manually

  • is a bit of a chore
  • prone to errors and typos
  • can be messy in the single file component paradigm if you have a lot of animation frames

Instead its suggested you use the bytepath CreateKeyframe function to create animations in a functional style. The following example is equivalent to the animation we made in the previous example

<script>
    import Bytepath from "bytepath";
    let _k = Bytepath.CreateKeyframe;
    export default Bytepath.CreateAsset({
        data() {
            return {
                balloonPos: new Bytepath.Position(),
            };
        },
        components: {
            balloon: Bytepath.samples.assets.balloon,
        },
        animations() {
            return {
                default: [
                    _k(0, 100, ({context, keyframe}) => {
                        context.balloonPos.x = keyframe;
                    }),
                ],
            };
        },
    });
</script>
<template>
    <balloon color="orange" :position="balloonPos"/>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  • create keyframe can accept
    • a function
    • start frame, a function,
    • start frame, end frame, a function

# Adding Multiple Keyframes

You can add as many keyframes as you want to an animation. In the example below we make the balloon perform the following steps

  • Frame 0 to 100 -- Scroll X position
  • Frame 100 to 200 -- Rotate
  • Frame 200 to 300 -- Scroll Y Position
<script>
    import Bytepath from "bytepath";
    let _k = Bytepath.CreateKeyframe;
    export default Bytepath.CreateAsset({
        data() {
            return {
                balloonPos: new Bytepath.Position(),
            };
        },
        components: {
            balloon: Bytepath.samples.assets.balloon,
        },
        animations() {
            return {
                default: [
                    /** From frame 0 to frame 100 scroll on the x axis **/
                    _k(0, 100, ({context, keyframe}) => {
                        context.balloonPos.x = keyframe * 3;
                    }),
                    /** From frame 100 to frame 200 perform a rotation **/
                    _k(100, 200, ({context, keyframe}) => {
                        context.balloonPos.angle = 100 - keyframe;
                    }),
                    /** From frame 200 to frame 300 scroll on the y axis **/
                    _k(200, 300, ({context, keyframe}) => {
                        context.balloonPos.y = 200 - keyframe;
                    }),
                ],
            };
        },
    });
</script>
<template>
    <balloon :position="balloonPos"/>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# Storing Animations In Separate Files

As you can see animations quickly turn your single file components into spaghetti. Luckily there is an easy solution to this. Just store your animations in separate files then import them into your component

<script>
    import Bytepath from "bytepath";
    import MyAnimation from "./MyAnimation";
    export default Bytepath.CreateAsset({
        data() {
            return {
                balloonPos: new Bytepath.Position(),
                curFrame: 0,
            };
        },
        components: {
            balloon: Bytepath.samples.assets.balloon,
        },
        animations(){
            return {
                default: MyAnimation,
            };
        },
    });
</script>
<template>
    <div>
        <input type="range" min="0" max="500" v-model.number="curFrame"/>Keyframe = {{ curFrame }}<br/>
        <balloon :position="balloonPos" />
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 * MyAnimation.js
 * The animation from the previous example, now stored in its own file to keep things tidy
 */
import Bytepath from "bytepath";
let _k = Bytepath.CreateKeyframe;
export default [
    _k(0, 100, ({context, keyframe}) => {
        context.balloonPos.x = keyframe * 3;
    }),
    _k(100, 200, ({context, keyframe}) => {
        context.balloonPos.angle = 100 - keyframe;
    }),
    /**
     * You can also just write manual objects here, if that's what you prefer.
     */
    {
        start: 200,
        end: 300,
        handler({context, keyframe}) {
            context.balloonPos.y = 200 - keyframe;
        },
    },
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# Assets can have an unlimited number of animations

Assets can have multiple animations, just add more keys to the object returned by animations() func. The human asset we have been using all along has a bunch of premade animations you can use in your projects.

<script>
    import CreateAsset from '../../../Factories/CreateAsset';
    import src from "./Human.svg";
    import kick from "./Kick";
    import punch from "./Punch";
    import firstAnimation from "./FirstAnimation";
    import FaceDance from "./FaceDance";
    export default CreateAsset({
        name: "human",
        src,
        animations() {
            return {
                flap: FaceDance,
                punch,
                kick,
                dance: firstAnimation,
                default: [
                    {
                        start: 0,
                        end: 500,
                        handler({context, tween}) {
                            context.layers.rightarm.angle = tween.number(0, 50);
                            context.layers.leftarm.y = tween.integer(0, 100);
                            context.layers.leftleg.x = tween.number(0, 20);
                            context.layers.rightleg.x = tween.number(0, 30);
                            context.defaultColor = tween.hex("#000000", "#FF0000");
                        },
                    },
                    {
                        start: 500,
                        end: 1000,
                        handler({context, tween}) {
                            context.layers.rightarm.angle = tween.number(50, 0);
                            context.layers.leftarm.y = tween.integer(100, 0);
                            context.layers.leftleg.x = tween.number(20, 0);
                            context.layers.rightleg.x = tween.number(30, 0);
                            context.defaultColor = tween.hex("#FF0000", "#000000");
                        },
                    },
                ],
            };
        },
    });
</script>
<template>
    <vector  :style="{fill: defaultColor }" v-bind="$props" v-slot="">
        <g id="humans">
            <layer v-for="(layer, i) in filteredLayers" :key="i" :position="layer" :layer="layer" />
        </g>
    </vector>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

So human has 5 premade animations

  • default: does nothing
  • dance: makes the man perform a dance
  • chicken: performs a chicken dance
  • punch: punches in the air kick: kicks in the air
<!-- MultipleAnimations.vue -->
<script>
    import Bytepath from "bytepath";
    export default Bytepath.CreateAsset({
        components: {
            human: Bytepath.samples.assets.human,
        }
    });
</script>
<template>
    <svg width="100" viewBox="-160 177 917 1024" overflow="visible">
        <human anim="flap"  color="red"  :keyframe="keyframe" :repeat="true"/>
        <human anim="dance" color="aqua" :keyframe="keyframe" :repeat="true" :x="1500"/>
        <human anim="punch" color="blue" :keyframe="keyframe" :repeat="true" :x="3000"/>
        <human anim="kick"  color="plum" :keyframe="keyframe" :repeat="true" :x="4500"/>
    </svg>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Last Updated: 10/15/2020, 8:50:20 AM