Material up-vote
We discovered a blog post by MaterialUp with a nice list of UX designs and felt like taking on the challenge of turning it into code. Here is an implementation of their material up-vote design.
Update: watch a video of Jake going through this example.
The structure
The structure can be split into three parts. The button, the background and the animation.
The inner circles
The actual button is composed of two filled circles. They each contain a Scaling
with Factor="0"
which gives them a size of 0. We want to animate them from the center and outwards and having a rest state at the center lets us describe our animations as deviation from size 0. Scaling
is also a lot faster than animating Width
and Height
. This is because Scaling
scales a texture instead of changing actual layout properties. Therefore, scaling an element with a Width
and Height
of 0 wouldn’t work.
We group the circles using a panel. This is so that we can change their ordering using BringToFront
on the individual circles without having them move all the way in front of the text. See the animation section further down.
<Panel ClipToBounds="True" Height="90%" Width="90%">
<Panel ux:Name="textPanel">
<Arrow ux:Name="arrow"/>
<DarkText ux:Name="unupvotedText" Value="22" Offset="0,5"/>
<LightText ux:Name="upvotedText" Value="23">
<Translation Y="1" RelativeTo="ParentSize"/>
</LightText>
</Panel>
</Panel>
<Panel>
<Circle ux:Name="pinkCircle" Color="#FF4081">
<Scaling ux:Name="pinkScaling" Factor="0"/>
</Circle>
<Circle ux:Name="grayCircle" Color="#D7D7D7">
<Scaling ux:Name="grayScaling" Factor="0"/>
</Circle>
</Panel>
The background
The background works in pretty much the same way as the button. The circles are placed in the bottom of the UX so that they appear behind everything else.
<Panel>
<Circle ux:Name="lightCircle" Color="#D7D7D7" MaxWidth="2000" Width="2000" MaxHeight="2000" Height="1200">
<Scaling ux:Name="lightScaling" Factor="0" />
</Circle>
<Circle ux:Name="darkCircle" Color="#363F45" MaxWidth="2000" Width="2000" MaxHeight="2000" Height="1200">
<Scaling ux:Name="darkScaling" Factor="0" />
</Circle>
</Panel>
The animation
A StateGroup
is used for animation. It has two states, “upvoted” and “unupvoted”. They each describe an animated deviation from the rest state. The “unupvoted” is assigned to the Rest
property of the StateGroup
and so becomes the default state when the app is launched.
Both states animates the Factor
property of the circles so that they animate back to their original size. We also use BringToFront
to make sure the circles being animated are on top.
<StateGroup ux:Name="sg" Rest="unupvoted">
<State ux:Name="unupvoted">
<Change grayScaling.Factor="1" Duration="0.4" DurationBack="0.2" DelayBack="0.4" Easing="CircularInOut"/>
<Change darkScaling.Factor="1" Duration="0.4" Delay="0.15" DelayBack="0.4" DurationBack="0.2" Easing="CircularInOut"/>
<Change statusBar.Style="Light" Duration="0.4" DurationBack="0"/>
<BringToFront Target="grayCircle"/>
<BringToFront Target="darkCircle"/>
</State>
<State ux:Name="upvoted">
<Change pinkScaling.Factor="1" Duration="0.4" DurationBack="0.2" DelayBack="0.4" Easing="CircularInOut"/>
<Change lightScaling.Factor="1" Duration="0.4" Delay="0.15" DurationBack="0.2" DelayBack="0.4" Easing="CircularInOut"/>
<Change statusBar.Style="Dark" Duration="0.4" DurationBack="0"/>
<Move Target="arrow" RelativeTo="Size" Y="-1" Duration="0.4" DelayBack="0.2" Easing="CircularInOut"/>
<Move Target="textPanel" RelativeTo="Size" Y="-1" Duration="0.4" Delay="0.2" Easing="CircularInOut"/>
<BringToFront Target="pinkCircle"/>
<BringToFront Target="lightCircle"/>
</State>
</StateGroup>