Open EmmanuelVinas opened 7 years ago
Thanks for filing! Will definitely add this as an option! :)
For future reference, here is a Twitter thread that has some good information: https://twitter.com/airnanan/status/862137455096594432
just found out about shapeshifter...so awesome !!! Currently I use AfterEffects + Lottie but AfterEffects are overkill for my purposes. export to bodymovin would truly be a win since you immediately could use shapeshifter with lottie. any plans to add this in the future?
Before AVD:
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="heartbreak"
android:width="56dp"
android:height="56dp"
android:viewportWidth="56"
android:viewportHeight="56">
<group
android:name="broken_heart_left_group"
android:pivotX="28"
android:pivotY="37.3">
<path
android:name="broken_heart_left"
android:pathData="M 28.031 21.054 C 28.02 21.066 28.01 21.078 28 21.09 C 26.91 19.81 25.24 19 23.5 19 C 20.42 19 18 21.42 18 24.5 C 18 28.28 21.4 31.36 26.55 36.03 L 28 37.35 L 28.002 37.348 L 27.781 36.988 L 28.489 36.073 L 27.506 34.764 L 28.782 33.027 L 26.944 31.008 L 29.149 28.725 L 27.117 27.143 L 29.149 25.018 L 26.488 22.977 L 28.031 21.054 L 28.031 21.054 Z"
android:fillColor="#000"/>
</group>
<group
android:name="broken_heart_right_group"
android:pivotX="28"
android:pivotY="37.3">
<path
android:name="broken_heart_right"
android:pathData="M 28.031 21.054 C 28.169 20.895 28.316 20.743 28.471 20.599 L 28.915 20.226 C 29.926 19.457 31.193 19 32.5 19 C 35.58 19 38 21.42 38 24.5 C 38 28.28 34.6 31.36 29.45 36.04 L 28.002 37.348 L 27.781 36.988 L 28.489 36.073 L 27.506 34.764 L 28.782 33.027 L 26.944 31.008 L 29.149 28.725 L 27.117 27.143 L 29.149 25.018 L 26.488 22.977 L 28.031 21.054 L 28.031 21.054 Z"
android:fillColor="#000"/>
</group>
<path
android:name="heart_stroke_left"
android:pathData="M 28.719 38.296 L 25.669 35.552 C 21.621 31.793 18.016 28.891 18.016 24.845 C 18.016 21.588 20.631 19.965 23.634 19.965 C 24.999 19.965 26.799 21.181 28.644 23.13"
android:strokeColor="#000"
android:strokeWidth="2"
android:trimPathEnd="0"/>
<path
android:name="heart_stroke_right"
android:pathData="M 27.231 38.294 L 30.765 35.2 C 34.834 31.235 37.752 29.118 38.004 25.084 C 38.168 22.459 35.773 20.035 33.379 20.035 C 30.432 20.035 29.672 21.047 27.231 23.133"
android:strokeColor="#000"
android:strokeWidth="2"
android:trimPathEnd="0"/>
<group android:name="filled">
<clip-path
android:name="clip"
android:pathData="M 18 37 L 38 37 L 38 37 L 18 37 Z"/>
<path
android:name="fill"
android:pathData="M 28 39 L 26.405 37.567 C 20.74 32.471 17 29.109 17 24.995 C 17 21.632 19.657 19 23.05 19 C 24.964 19 26.801 19.883 28 21.272 C 29.199 19.883 31.036 19 32.95 19 C 36.343 19 39 21.632 39 24.995 C 39 29.109 35.26 32.471 29.595 37.567 L 28 39 L 28 39 Z"
android:fillColor="#000000"/>
</group>
</vector>
</aapt:attr>
<target android:name="broken_heart_left_group">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="rotation"
android:duration="400"
android:valueFrom="0"
android:valueTo="-20"
android:valueType="floatType"
android:interpolator="@android:interpolator/linear_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="broken_heart_right_group">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="rotation"
android:duration="400"
android:valueFrom="0"
android:valueTo="20"
android:valueType="floatType"
android:interpolator="@android:interpolator/linear_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="broken_heart_left">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="100"
android:duration="300"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/linear_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="broken_heart_right">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="100"
android:duration="300"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/linear_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="heart_stroke_left">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="trimPathEnd"
android:startOffset="500"
android:duration="400"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="heart_stroke_right">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="trimPathEnd"
android:startOffset="500"
android:duration="400"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="clip">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="pathData"
android:startOffset="1160"
android:duration="120"
android:valueFrom="M 18 26 C 18 26 21 28 24 28 C 27 28 29 25 32 25 C 35 25 38 26 38 26 L 38 38 L 18 38 L 18 26 Z"
android:valueTo="M 18 18 C 18 18 24 18 24 18 C 24 18 32 18 32 18 C 32 18 38 18 38 18 L 38 38 L 18 38 L 18 18 Z"
android:valueType="pathType"
android:interpolator="@android:interpolator/fast_out_linear_in"/>
<objectAnimator
android:propertyName="pathData"
android:startOffset="1000"
android:duration="160"
android:valueFrom="M 18 38 C 18 38 24 38 24 38 C 24 38 32 38 32 38 C 32 38 38 38 38 38 L 38 38 L 18 38 L 18 38 Z"
android:valueTo="M 18 26 C 18 26 21 28 24 28 C 27 28 29 25 32 25 C 35 25 38 26 38 26 L 38 38 L 18 38 L 18 26 Z"
android:valueType="pathType"
android:interpolator="@android:interpolator/fast_out_linear_in"/>
</set>
</aapt:attr>
</target>
<target android:name="heartbreak">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="alpha"
android:startOffset="500"
android:duration="400"
android:valueFrom="0.4"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
</animated-vector>
After AVD exported in bodymovin
format:
{
"assets": [], //List of rasterized assets. Probably wouldn't exist with shapeshifter.
"fr": 30, // Framerate. I would use 60 as a default.
"h": 56, // Height of the canvas
"w": 56, // Width of the canvas
"ip": 0, // In point of the animation. It means at what frame to start animationg. It would always be 0 with Shapeshifter.
"op": 37.5, // Out point of the animation in frames. It would be always the duration of the animation in Sh.Sh. 1250 * 30 / 1000
"nm": "heart", // Name of the animation
"v": "5.1.16", // Version of the exported ffile
"layers": [ // This is the list of layers of the composition. Sh.Sh. could use only one and animate all in it in subgroups. Or set one layer per element.
{
"ty": 4, // Type of layer. In this case Shape Layer. With Sh.Sh. always 4. It could also be an image layer, text layer, etc.
"ind": 1, // Layer index. An incremental number per layer would be fine.
"st": 0, // Start value of the layer. In Sh.Sh. it will probably be 0.
"ip": 0, // In point of the layer. In Sh.Sh. it will probably be 0.
"op": 70, // Out point of the layer expressed in frames. With Sh.Sh. it should be [duration of the full animation in milliseconds * framerate / 1000]
"ks": { // "ks" is an object with all transformations for the layer: Anchor Point(Or pivot), Opacity, Position, Rotation, Scale. With Sh.Sh. it will probably make sense to keep this values as the default values.
"a": { // Anchor point property
"k": [0,0,0] // "k" is the property that holds the animation information. It can be an array of numbers or keyframes. For the Anchor Point, default is 0,0,0
},
"o": { // Opacity
"k": 100 // Default values. Ranges from 0 - 100
},
"p": { // Position
"k": [0,0,0] // default 0,0,0
},
"r": { // Rotation in degrees
"k": 0 // default 0
},
"s": { // Scale
"k": [100,100,100] // default 100 (no scaling)
}
},
"shapes": [ // This array contains all shapes and shape properties of the layer. It can hold nested groups as well.
{
"ty": "gr", // "ty" specifies the type of element. In this case "gr" it's a nested groupd.
"it": [ // "it" are the nested elements of a group.
{
"ty": "sh", // "sh" type is a shape (shape or path are the same)
"ks": { // Shape data.
"k": { // Shape data keyframes or single keyframe. In this case it's a single keyframe since it doesn't interpolate the shape.
"c": true, // Is shape closed or open
"v": [[28.031, 21.054],[28, 21.09],[23.5, 19],[18, 24.5],[26.55, 36.03],[28, 37.35],[28.002, 37.348],[27.781, 36.988],[28.489, 36.073],[27.506, 34.764],[28.782, 33.027],[26.944, 31.008],[29.149, 28.725],[27.117, 27.143],[29.149, 25.018],[26.488, 22.977],[28.031, 21.054]],
"i": [[0, 0],[0, 0.01],[1.74, 0],[0, -3.08],[-5.15, -4.67],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]],
"o": [[0, -0.01],[-1.09, -1.28],[-3.08, 0],[0, 3.78],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0],[0, 0]]
} // "v" are vertices, "o" outpoints and "i" inpoints. In bodymovin i and o are relative to v. All shapes in bodymovin are bezier curves.
}
},
{
"ty": "fl", // This is the fill type
"c": { // Color property
"k": [0,0,0,1] // Color property value RGB ranging from 0 to 1. A is always 1.
},
"o": { // Fill Opacity. This one is animated.
"k": [ // Fill Opacity property keyframes.
{
"e": [0], // End value of segment. "valueTo" in AVD
"i": { // interpolation curve inpoint
"x": [0.2], // x component of inpoint
"y": [1] // y component of inpoint
},
"o": { // interpolation curve outpoint
"x": [0], // x component of outpoint
"y": [0] // y component of outpoint
},
"s": [100], // Starting value of segment. "valueFrom" in AVD
"t": 3, // Starting time in frames of the animation in frames. "startOffset". 100 * framerate / 1000
},
{
"t": 12, // End time in frames of the animation in frames. "startOffset + duration". (100+300) * framerate / 1000
}
]
},
"r": 1 // Fill-rule of the fill. 1 is Non-zero, 2 is Even-Odd
},
{
"ty": "tr", // Transform type.
"a": { // Anchor point (or Pivot)
"k": [28,37.3]
},
"o": { // Opacity
"k": 100
},
"p": { // Position. Needs to compensate the pivot value.
"k": [28,37.3]
},
"r": { // Rotation. This is animated. so the "k" values is an array of segments (or keyframes).
"k": [
{
"e": [-20], // End value of the rotation. "valueTo" in AVD
"i": { // interpolation curve inpoint
"x": [0.2], // x component of inpoint
"y": [1] // y component of inpoint
},
"o": { // interpolation curve outpoint
"x": [0], // x component of outpoint
"y": [0] // x component of outpoint
},
"s": [0], // Starting value of segment. "valueFrom" in AVD
"t": 3 // Starting time in frames of the animation in frames. "startOffset". 100 * framerate / 1000
},
{
"t": 12 // End time in frames of the animation in frames. "startOffset + duration". (100+300) * framerate / 1000
}
]
},
"s": { // Scale value
"k": [100,100]
},
"sa": { // Skew Angle
"k": 0
},
"sk": { // Skew
"k": 0
}
}
]
}
]
}
]
}
@alexjlockwood Any update on this?
Bodymovin format is used by https://github.com/airbnb/lottie-android
Also see : https://github.com/romannurik/AndroidIconAnimator/issues/99