博客园的小伙伴们,新年好啊。
春节刚过,元宵又至。不知大家所在之处是否还有看花灯,猜灯谜的传统呢?应此情此景,我们依托微软亚洲研究院自然语言计算组的研究成果,为大家带来了UAP版本的微软字谜APP:
Windows Phone Store地址 : 微软字谜
就算没有时间去看灯,也可以在手机上体验一把文艺范。
在微软字谜中,你可以闯过323关,一展聪明才智;可以用APP帮你出字谜给小伙伴猜,让他们也死几个脑细胞;还能上传原创字谜,与大家同乐。
与之前一样,接下来和大家分享下开发中的有意思的地方:
主页沿用了微软对联的形式,加入了3个小动画:
灯笼翻转:
<Storyboard x:Name="sb_LanternRotate" Completed="sb_LanternRotate_Completed">
<DoubleAnimation Storyboard.TargetName="img_Lantern" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)"
From="0" To="360" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="img_Lantern" Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:1"/>
</Storyboard>
菜单转动:
<Storyboard x:Name="sb_Menu" Completed="sb_Menu_Completed">
<DoubleAnimation Storyboard.TargetName="menu_Guess" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
From="90" To="0" Duration="0:0:0.20" BeginTime="0:0:0.20"/>
<DoubleAnimation Storyboard.TargetName="menu_Produce" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
From="90" To="0" Duration="0:0:0.20" BeginTime="0:0:0.40"/>
<DoubleAnimation Storyboard.TargetName="menu_Intro" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
From="90" To="0" Duration="0:0:0.20" BeginTime="0:0:0.60"/>
<DoubleAnimation Storyboard.TargetName="menu_My" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
From="90" To="0" Duration="0:0:0.20" BeginTime="0:0:0.60"/>
<DoubleAnimation Storyboard.TargetName="menu_Cloud" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
From="90" To="0" Duration="0:0:0.20" BeginTime="0:0:0.80"/>
</Storyboard>
灯笼移动:
<Storyboard x:Name="sb_LanternMove" Completed="sb_LanternMove_Completed">
<DoubleAnimation Storyboard.TargetName="img_Lantern" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)"
From="1" To="0.9" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="img_Lantern" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)"
From="1" To="0.9" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="img_Lantern" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
From="0" To="150" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="img_Lantern" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
From="0" To="-10" Duration="0:0:1"/>
</Storyboard>
关于飞入:
<Storyboard x:Name="sb_About">
<DoubleAnimation Storyboard.TargetName="tb_About" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
From="50" To="0" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="tb_About" Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:1"/>
</Storyboard>
然后利用 storyboard的 Completed 事件来将它们连贯起来:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.New)
{
Logger.LogAgent.GetInstance().WriteLog(this.GetType().FullName);
this.isBack = false;
await Windows.UI.ViewManagement.StatusBar.GetForCurrentView().HideAsync();
this.sb_LanternRotate.Begin();
}
else
{
this.isBack = true;
this.sb_Menu.Begin();
}
}
private void sb_LanternRotate_Completed(object sender, object e)
{
this.sb_Menu.Begin();
}
private void sb_Menu_Completed(object sender, object e)
{
if (!this.isBack)
{
this.sb_LanternMove.Begin();
}
}
private void sb_LanternMove_Completed(object sender, object e)
{
this.sb_About.Begin();
}
要注意的就是在OnNavigatedFrom中,需要把再次进入页面时还会移动的动画元素归位:
this.menu_Guess.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_Produce.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_Intro.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_My.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_Cloud.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
另外就是双击完全退出APP的功能:
我们在页面上添加一个默认隐藏的提示:
<Border x:Name="bd_QuitInfo" Background="#FFC40E12" Visibility="Collapsed" Opacity="0.75" CornerRadius="10" Canvas.ZIndex="99" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="5" BorderBrush="Gold" Margin="0,200,50,0">
<TextBlock Style="{StaticResource RiddleGoldMessage}" HorizontalAlignment="Center" Foreground="White" Margin="20,10">
<Run Text="再按一次"/>
<LineBreak/>
<Run Text="退出应用"/>
</TextBlock>
</Border>
然后handle一下后退按钮按下的事件:
在OnNavigatedTo中添加:
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
在OnNavigatedFrom中添加:
Windows.Phone.UI.Input.HardwareButtons.BackPressed -= HardwareButtons_BackPressed;
最后添加HardwareButtons_BackPressed事件处理方法:
DateTime _lastTimeClickBack = DateTime.MinValue;
bool isQuitInfoOpen = false;
void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e)
{
var deltaTime = DateTime.Now - _lastTimeClickBack;
// double click back button within 3 seconds
if (!isQuitInfoOpen)
{
this.menu_Guess.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_Produce.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_Intro.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_My.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
this.menu_Cloud.Projection.SetValue(PlaneProjection.RotationXProperty, 90);
_lastTimeClickBack = DateTime.Now;
this.bd_QuitInfo.Visibility = Windows.UI.Xaml.Visibility.Visible;
isQuitInfoOpen = true;
// close the tip within 3 seconds
ThreadPoolTimer.CreateTimer(async t =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.bd_QuitInfo.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
isQuitInfoOpen = false;
this.isBack = true;
this.sb_Menu.Begin();
});
}, new TimeSpan(0, 0, 3));
e.Handled = true;
}
else
{
//double click back button in 5 seconds to exit
if (deltaTime.TotalSeconds < 5)
{
Application.Current.Exit();
}
e.Handled = true;
}
我们精选了323个不同长度的,由微软亚洲研究院自然语言计算组研发的计算机自动猜字和出字谜系统生成的字谜。每猜出一个都可以得到最多10分,大家可以上传自己的分数,总分排名在前20的小伙伴就能出现在APP内的排行榜上哦。
猜字谜的部分比较重要,为了赶在元宵之前能够上线,我们的pm兼dev Xiaowu 同志牺牲春节假期实现了这一重要功能:
思路大致是这样的:
我们自定义了2种模板控件,CandidateControl 作为候选字控件,每个都显示一个字; CandidateArrayControl 则作为容器处理候选字的交互。
当我们进入猜谜页面,为了大家不至于觉得无从猜起,APP会生成18个候选字的列表,正确答案就藏在其中。
接着将列表绑定到容器的datacontext,再通过容器的DataContextChanged事件,把候选字绑定到对应的候选字控件。
当我们点击候选字控件,就将对应的候选字显示谜底位置,并在容器中隐藏对应的候选字控件。
如果答对,则会播放一个小动画,还有音效。
如果答错3次,那候选字列表就会大换血,所以乱点也是不大容易蒙对的。
另外还可以牺牲一点分数来减少干扰项。
出字谜则比较简单了。
我们可以写一个1~4个字的谜底,比如"我爱你",一段提示猜什么的谜目比如"打一句话",点击下一步。
APP就会把我们的谜底上传到云端的字谜系统,取得谜底每个字的候选谜面显示出来。
我们选好谜面(不满意字谜系统给出的话还可以自行修改),就可以给小伙伴(或者心上人)去猜啦。要是觉得不错,可以点击收藏,把谜语保存到我的字谜中,供以后使用或者分享。
在可以猜字谜的页面上,都可以分享或者求助。具体实现可以参照我们之前的博文 博客园客户端UAP开发随笔 -- 让自己的App连接世界:WinRT中的微博分享 和 博客园客户端UAP开发随笔 – 让自己的App连接世界(2):WinRT中的内置分享 。
另外我们还提供了分享到云端字谜的功能。
云端字谜都是小伙伴们分享上来的字谜,而且答对也能加分哦。想在排行榜上秀出来,就靠它了。
好吧,就写到这里,大家有意见欢迎拍过来啊。
我们的APP链接:微软字谜
MSRA的网页版字谜:微软字谜(在线)
最后祝大家元宵节快乐~