一时兴起想谈谈UWP按钮的设计。
按钮是UI中最重要的元素之一,可能也是用得最多的交互元素。好的按钮设计能够有效提升用户体验,构造让人眼前一亮的UI。并且按钮一般不会影响布局,小小的按钮不管怎么改也不会对性能有多大影响,因此很多注重细节的设计师最为热衷修改按钮。(例如 这位 )
目前UWP只提供了基础款的按钮样式,网上相关资源也较少,全部写了这篇文章用于介绍在UWP上设计按钮的入门知识。css
Button的CotrolTemplate(能够参考 这里 )中包含四个VisualState,分别是Normal、PointerOver、Pressed、Disabled。html
Normal: Button的默认状态,UWP的按钮是彻底扁平化的设计。没有边框。
git
PointerOver: 鼠标进入的状态,出现边框,背景颜色也会改变。
Windows中一般不会用改变鼠标指针来代表“这是一个Button”,而是让Button进入PointerOver状态。只有HyperlinkButton是特例,符合 W3C的建议 使用了CoreCursorType.Hand做为鼠标指针。
github
Pressed: 按下的状态,有趣的是除了改变颜色Button还应用了PointerDownThemeAnimation使得按钮向按下方向倾斜,营造一种有深度的设计。不过这个作法会致使Button的内容变模糊(Projection都有这个问题),若是介意的话可使用ScaleTransform缩小整个按钮,让整个按钮像被按了下去。
windows
Disabled: 当IsEnabled="False"
时的状态,通常控件都有这个状态,大部分都表现为背景变灰,字体颜色变浅,表示不可操做。有时偷懒我会直接将整个Button 设置成 Opacity = 0.4
代替分别为每一个元素设置颜色,或者只将Foreground设置成#66000000
。
api
习惯作触屏设计的话很容易就忽略PointerOver状态,目前UWP大部分的应用场景仍是在桌面上,因此应该在ControlTemplate中包含这四种VisualState。
除了这四种VisualState,Button还能够定义FocusStates,具体能够参考 这篇文章 。安全
在默认的ControlTemplate中VisualState之间的转换没有过渡,点击后弹起的动画也很迅速,我本身设计Button时也不作过渡动画,或者过渡动画的时间十分短,这么作有几点好处:网络
软件中按钮的风格取决于软件服务的群体,工业软件喜欢沉稳的风格,面向互联网的软件一般鲜艳、轻快。接下来给出一些经常使用切容易实现的Button设计,还会给出一些花哨的技巧。这些花哨的技巧并非鼓励任何人在系统上用标新立异的设计,设计良好的Button是生动有趣的UI中重要的一环,但前提是设计良好,若是团队里没有靠谱的设计师,仍是用最保守的设计最保险。app
使用无边框按钮能够去除多余的装饰,留给文字的空间更大,界面也更清爽,一般用在Toolbar里或拥挤的位置。无边框按钮其实最难用,由于它很用以看上去不像个按钮。有几种方法能够避免这个情况:ide
无边框按钮很容易实现,最简单的是以下所示使用默认Button样式移除边框和背景,或者直接使用AppBarButton
或HyperlinkButton
,我最推荐这种作法:
<Button Content="Edit Profile" Background="Transparent" BorderThickness="0" />
也可使用阴影作相似发光的效果,好看又简单:
<VisualState x:Name="PointerOver"> <Storyboard> <DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(DropShadowPanel.ShadowOpacity)" Storyboard.TargetName="dropShadowPanel"> <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.8" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentTextBlock"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPressed}" /> </ObjectAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(DropShadowPanel.ShadowOpacity)" Storyboard.TargetName="dropShadowPanel"> <EasingDoubleKeyFrame KeyTime="0" Value="0.5" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState>
若是是WPF的话记得将Button的背景设置为透明,这样可点选范围大些。
除此以外简单的颜色变化很受欢迎,PointerOver变亮,Pressed变暗:
<VisualState x:Name="PointerOver"> <VisualState.Setters> <Setter Target="ContentPresenter.(UIElement.Opacity)" Value="1" /> </VisualState.Setters> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPointerOver}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <VisualState.Setters> <Setter Target="ContentPresenter.(UIElement.Opacity)" Value="0.65" /> </VisualState.Setters> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPressed}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState>
或者PointerOver放大一点,Pressed在放大的同时像右下位移一个像素:
<VisualState x:Name="PointerOver"> <VisualState.Setters> <Setter Target="ContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Value="1.03" /> <Setter Target="ContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleY)" Value="1.03" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Pressed"> <VisualState.Setters> <Setter Target="ContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Value="1.03" /> <Setter Target="ContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleY)" Value="1.03" /> <Setter Target="ContentPresenter.(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Value="1" /> <Setter Target="ContentPresenter.(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Value="1" /> </VisualState.Setters> </VisualState>
上面两种VisualState之间变化有点太过含蓄,若是要明显的能够作成从黑色或白色过渡到Foreground颜色:
要为每一个按钮定义不一样的颜色过渡很麻烦,这里我定义了个SolidColorBrushBridge
,经过改变HSV中的Saturation和Value改变按钮的颜色。
<Grid.Resources> <local:SolidColorBrushBridge x:Name="solidColorBrushBridge" x:Key="SolidColorBrushBridge" Value="0" InputBrush="{TemplateBinding Foreground}" /> </Grid.Resources> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> ... <VisualState x:Name="PointerOver"> <Storyboard> <DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(SolidColorBrushBridge.Value)" Storyboard.TargetName="solidColorBrushBridge"> <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(SolidColorBrushBridge.Value)" Storyboard.TargetName="solidColorBrushBridge"> <EasingDoubleKeyFrame KeyTime="0" Value="0.8" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> ... </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter x:Name="ContentPresenter" ... Foreground="{Binding Source={StaticResource SolidColorBrushBridge},Path=OutputBrush}" />
protected virtual void OnSaturationChanged(double oldValue, double newValue) { UpdateOutputBrush(); } protected virtual void OnValueChanged(double oldValue, double newValue) { UpdateOutputBrush(); } private void UpdateOutputBrush() { var brush = InputBrush as SolidColorBrush; if (brush == null) { OutputBrush = null; } else { var color = brush.Color.ToHsv(); OutputBrush = new SolidColorBrush(ColorHelper.FromHsv(color.H, Saturation, Value)); } }
也能够不用这么麻烦,直接使用UWPCommunityToolkit中的 Saturation :
UWP给出了不少新的API,能够实现不少之前很难作到的效果,譬如 PointLight 就能够用来实现好玩的动画效果:
private void CreatePointLight() { var text = AssociatedObject.FindDescendant<TextBlock>(); _text = (UIElement)text ?? AssociatedObject; var visual = ElementCompositionPreview.GetElementVisual(_text); var compositor = visual.Compositor; _fontSize = (float)(AssociatedObject.FontSize/0.7); _pointLight = compositor.CreatePointLight(); _pointLight.Color = Color; _pointLight.CoordinateSpace = visual; _pointLight.Targets.Add(visual); _pointLight.Offset = new Vector3(-1000, (float)AssociatedObject.ActualHeight / 2, _fontSize); } private void OnPointerMoved(object sender, PointerRoutedEventArgs e) { var position = e.GetCurrentPoint(_text); //starts out to the left; vertically centered; light's z-offset is related to fontsize _pointLight.Offset = new Vector3((float) position.Position.X, (float) position.Position.Y, _fontSize); }
听说在秋季更新里能够在XAML中实现这些效果而不用写Code。
虽然很好玩,但我要再强调一下,不要由于能够作到就去作,尽可能用含蓄的设计是最保险的。不要忘记Word艺术字体的悲剧:
近年来,扁平化和极简主义推进了幽灵按钮的流行。
幽灵按钮,也就是Ghost Buttons,是一个透明的按钮,一般是矩形或者圆角矩形,仅保留基本的形制,使用细线来标识边界;按钮内的文字一般使用纤细的非衬线体字体的纯文本,来指明按钮功能。-- 2014年最值得掌握的趋势“幽灵按钮”
比起无边框按钮,它很容易代表本身是一个按钮,并且由于背景透明因此不会遮挡背景图片,最大限度避免了喧宾夺主,能够轻松搭配任何设计方案。一般幽灵按钮能够套用上面的无边框按钮设计,除此以外还能够是这几种:
仍是用SolidColorBrushBridge
实现:
幽灵按钮最大的好处是不会遮挡背景图片,我试着配合图片作了个例子,PointerOver状态背景使用Blur进行高斯模糊:
圆角能促进信息处理,并吸引用户聚焦到元素正中央。幽灵按钮很适合使用圆角的设计,但当UWP的其它按钮都是直角时使用圆角按钮实在不是一个好主意,要谨慎使用。
最近微软推出了新的设计语言Fluent Design System,前不久WIN10的计算器进行了一次更新,用上了Acrylic和Reveal元素。个人感想是:
简直好玩到停不下来。
除了Pressed效果我对这个设计仍是很满意的,目前Button须要主动引用样式才能够应用这个设计,不过也就一句话的事:
<Button Content="Button Content" Style="{StaticResource ButtonRevealStyle}"/>
用在前面幽灵按钮的例子上,再把背景的高斯模糊改成亚克力,哇哦:
至于这种按钮好很差看就见仁见智了。我还挺喜欢之前Metro那种纯数字化、科幻的感受,前段时间拿出Lumia 800用了用,感受仍是酷酷的,手感也比如今用的手机好不少。我看过不少UI设计的书,其中有些提到Metro,大部分对Metro自己的评价都很正面,但在大部分消费者眼里评价都不好,我想主要有如下几个缘由:
到如今普通消费者一提起metro就是“大色块”,其它Metro的元素一律不知。
FDS与其说是Metro的延伸,我以为更多的是模仿IOS和Material Design的痕迹。不会太前卫,但也更讨消费者欢心。对开发者来讲一套全新的API则使人蠢蠢欲动。目前FDS的设计还有缺陷,主页只是堆了一些漂亮口号和一闪而过的华丽动画,没有规范和指南,至于媒体如今还处处宣传毛玻璃回归,彻底没考虑过亚克力的感觉。但愿在正式发布时能有改善吧。
下面列举一些常见的Button设计错误。其实只要避免了这些错误而且规规矩矩、工工整整地使用按钮,就可使得按钮好看又好用。
最多见的是彻底没有实现任何VisualState,只是简单地将Cursor设置为Hand,这个错误Windows里处处都是。我以为这样偷懒其实也不算过度,大部分状况下也不会很影响使用。有些只有文字没有图标的无边框按钮恰恰设计成没有PointerOver和Pressed状态,这样彻底看不出是按钮。有些则是PointerOver或Pressed的状态设计错误:
另外一种常见的状况是没有正确实现Disabled状态,以下面这个按钮,明明不能点击恰恰要伪装是Normal状态,PointerOver才变成Disabled状态:
顺便一提,IsEnabled= 的按钮没办法显示Tooltip,一般这种状况是在它背后默默放一个透明的UIElement,让这个UIElement显示Tooltip:
<Grid> <Rectangle Fill="Transparent" ToolTipService.ToolTip="权限不足" ToolTipService.Placement="Right" /> <Button IsEnabled="False" Content="Edit Profile" /> </Grid>
另外,若是没有及时的反馈会致使用户觉得操做失败,而后重复点击,因此不建议Pressed的过渡动画时间太长。若是必要还能够加上声音提示,声音提示在银行或政府机构的程序上很常见,可能也是为了防止重复输入吧。
有些按钮喜欢Cosplay成ListBoxItem或者TtabItem,虽然他们可能真的是个ListBoxItem控件或TabItem控件,但彻底是Button的行为。例如Win10中邮件应用的“新邮件”按钮。一般来讲“新建邮件”应该使用动词短语,显眼的颜色或图标,这样容易识别,例如如下这些:
只有Win10邮件应用的“新建邮件”完美地Cosplay成导航菜单:
不止外观上就是导航菜单,“新邮件”这样的文字很容易使人误会成“新收到的邮件”,和“未读邮件”差很少。我老婆是国内某上市软件公司的用户体验设计师,对软件UI也算是见多识广。以前她一直用Outlook桌面或网页版收发邮件,前两个月她第一次用邮件应用的时候用了几分钟都找不到在哪里新建邮件,最后仍是我看不下去好心告诉她的(虽然也有我一直和她说话让她分心的缘由)。按钮应该用简单的动词,放在合理的位置,和其它功能区分开,显然邮件应用彻底作不到。
在Windows上全部应用程序的对话框的操做按钮顺序一直是“肯定、取消”,理由有很多:
最近几年随着IOS和Android愈来愈流行,Windows或网络上也出现很多用“取消、肯定”顺序的,它们也有合理的理由:
虽然各自的理由都挺合理,但在Windows上应该符合一直的习惯按“肯定、取消”排放按钮。在UWP中能够ContentDialog默认就是使用“肯定、取消”顺序。另外,使用ContentDialog时最好将主按钮换成要执行的操做的动词,代替“OK”:
若是公司里Android/IOS组十分强势,或者设计师十分喜欢Android/IOS,那么妥协将主要按钮放在右边也是没问题的。最重要是保持一致性,就算不是与系统保持一致,在程序内也应该保持一次,避免用户混淆,而混淆是致使操做出错的主要缘由。
如今来看看Win10上的邮件应用是怎么作的:
我是斯文人,我才不会用如今很流行的张学友那句经典台词来表达个人感想(虽然我内心千真万确是这样想的)。
除了肯定按钮放左边仍是右边这个问题,还能够延伸出放在上面仍是下面这个问题。
放在上面的理由有:
放在下面的理由有:
放在上面仍是下面都各有好处,若是在Windows系统上我不建议放在右上角,由于太容易点击到窗口的关闭按钮,一不当心辛辛苦苦输入的内容就随风而去了。并且不少时候“取消”按钮也能关闭窗口,将两个一样功能的按钮放在一块儿有些别扭。
为了不按错,一般会把默认按钮作突出显示。ContentDialog能够设置DefaultButton
令指定的按钮变得突出:
ContentDialog subscribeDialog = new ContentDialog { Title = "Subscribe to App Service?", Content = "Listen, watch, and play in high definition for only $9.99/month. Free to try, cancel anytime.", CloseButtonText = "Not Now", PrimaryButtonText = "Subscribe", SecondaryButtonText = "Try it", DefaultButton = ContentDialogButton.Primary };
若是用无边框按钮,真的要当心摆放的位置,一不当心就会被误会成标签或状态信息。放在约定俗成的位置的话就算看上去很像但也分得清哪些是标签,哪些是按钮。例如窗体顶部中间的文字就不会和其余白色文字同样被认为是按钮:
放在错误位置的另外一个坏处就是很容易点错,实在没办法能够将按钮作大些以避免点错成旁边的按钮。
像邮件应用的这个“发送”按钮,不但用了“取消、肯定”的模式,并且用窗口的“关闭”按钮和“删除”按钮夹着“发送”按钮,每次点击都要当心翼翼的:
另外要吐槽下这个“锁定”按钮:
要点中“锁定”,首先要避开“关机”按钮点中那个小小的“三角形”,而后再当心翼翼选中被“从新启动”和“注销”夹着的“锁定”按钮。注意在知道锁定的快捷键是“Win+L”的状况下,看到"注销(L)"要保证本身不被迷惑。我以为比起要快速点中“锁定”,用黑百合一枪爆头开大中的源氏还容易一些。若是“注销”和“锁定”互换位置,即便不当心点中了“切换用户”破坏性都没这么大。误操做了不少次后我终于放弃了这个按钮,学会了"Win+L"这个快捷键。Win10中将“关机、重启”和“注销、锁定”的功能分开了,并且增大了按钮面积,终于解决了这个问题。
最后一点,按钮最好摆放在出现反馈的提示附近(或者反过来讲反馈的提示要放在按钮附近)。人的视觉区域颇有限,点击“提交”时极可能彻底看不到表单顶部弹出的错误提示,或是点击“转帐”时看不到屏幕顶部的进度条已经出现而重复点击屡次。
这却是不常见,多是由于有使用图标的团队一般都有设计师。暂时只想起这个:
看起来很像TreeView或Expander,其实这三个都是HyperLink。虽然这个图标也不算错,若是图标放在右边也就不会被误会成TreeView了。
典型的错误是没有遵循“使用动词”这个原则,如上述邮件应用的“新邮件”。
另外一个常见错误是用不一样的术语表述同一个动做,典型的如“Create”和"Add","Search"和"Find"。
另外没有正确使用省略号也是常见的错误。
省略号帮助用户预先判断一个命令是会当即执行仍是会提示要求输入附加信息。若是用户可以预先知道点击一个不熟悉的按钮仅仅会弹出一个对话框,他们会以为这样更加安全。 --《GUI设计禁忌 2.0》
这一点Windows自带的传统软件都作得比较好。
并非全部打开窗口的按钮都须要省略号,并且如今不少软件为了界面简洁连省略号都会省略,有时则是使用了图标的按钮再用省略号设计上很差看。重要的还是保持软件内的一致性。
这是Win7的任务栏,我用了张浅色的背景壁纸,选中的状态下只能勉强看到右边是“新建文件夹”几个字。但在白天个人镜面屏显示器就不怎么靠谱了,几乎分辨不出那几个字。虽然没有这么极端,如今不少人都在阳光下使用笔记本、平板、手机,遇到高光的按钮仍是会很困扰。根据Web Content Accessibility Guidelines (WCAG) 2.0,文本的视觉呈现以及文本图像至少要有4.5:1的对比度。
有时同一个场景中相似功能的按钮使用了不一样的样式,或者大小,破坏了UI的一致性。同一个系统中适当的多样式能够提升用户体验,但也要保持必要的一致性。“肯定”、“取消”是不一样功能使用不一样的样式没有问题;若是只是宽度不一致也没什么问题。要避免出现高度或者Padding等属性不一致的按钮。
虽然我一直强调用UWP本来的按钮设计是最保险的,但有时彻底不修改就用也很难看,适当的多样性是必须的,不然我也不会在前面提出这么多按钮设计。
UWPCommunityToolkit中的 Expander 就是个让人失望的设计,Header部分的按钮保留了Pressed状态向下倾斜的设计,致使当点击Header时整个控件从中间断裂。
若是真的要本身实现一个Button,又不想从ButtonBase继承,能够参考下面的代码本身实现:
[TemplateVisualState(Name = StateNormal, GroupName = GroupCommon)] [TemplateVisualState(Name = StatePointerOver, GroupName = GroupCommon)] [TemplateVisualState(Name = StatePressed, GroupName = GroupCommon)] [TemplateVisualState(Name = StateDisabled, GroupName = GroupCommon)] public class SimpleButton : ContentControl { internal const string StateNormal = "Normal"; internal const string StatePointerOver = "PointerOver"; internal const string StatePressed = "Pressed"; internal const string StateDisabled = "Disabled"; internal const string GroupCommon = "CommonStates"; internal const string StateFocused = "Focused"; private bool _isPointerCaptured; public SimpleButton() { DefaultStyleKey = typeof(SimpleButton); IsEnabledChanged += OnIsEnabledChanged; } public bool IsPressed { get; private set; } public bool IsPointerOver { get; private set; } public event RoutedEventHandler Click; protected override void OnApplyTemplate() { base.OnApplyTemplate(); UpdateVisualState(false); } internal void UpdateVisualState(bool useTransitions = true) { if (IsEnabled == false) VisualStateManager.GoToState(this, StateDisabled, useTransitions); else if (IsPressed) VisualStateManager.GoToState(this, StatePressed, useTransitions); else if (IsPointerOver) VisualStateManager.GoToState(this, StatePointerOver, useTransitions); else VisualStateManager.GoToState(this, StateNormal, useTransitions); } protected override void OnPointerPressed(PointerRoutedEventArgs e) { base.OnPointerPressed(e); if (e.Handled) return; if (IsEnabled == false) return; e.Handled = true; _isPointerCaptured = CapturePointer(e.Pointer); if (_isPointerCaptured == false) return; IsPressed = true; Focus(FocusState.Pointer); UpdateVisualState(); } protected override void OnPointerReleased(PointerRoutedEventArgs e) { base.OnPointerReleased(e); if (e.Handled) return; if (IsEnabled == false) return; e.Handled = true; if (IsPressed) Click?.Invoke(this, new RoutedEventArgs()); IsPressed = false; ReleasePointerCapture(e.Pointer); _isPointerCaptured = false; UpdateVisualState(); } protected override void OnPointerMoved(PointerRoutedEventArgs e) { base.OnPointerMoved(e); if (_isPointerCaptured == false) return; var position = e.GetCurrentPoint(this).Position; if (position.X < 0 || position.Y < 0 || position.X > ActualWidth || position.Y > ActualHeight) IsPressed = false; else IsPressed = true; UpdateVisualState(); } protected override void OnPointerEntered(PointerRoutedEventArgs e) { base.OnPointerEntered(e); IsPointerOver = true; UpdateVisualState(); } protected override void OnPointerExited(PointerRoutedEventArgs e) { base.OnPointerExited(e); IsPointerOver = false; UpdateVisualState(); } private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { if (!IsEnabled) { IsPressed = false; IsPointerOver = false; _isPointerCaptured = false; } UpdateVisualState(); } }
不考虑Command、ClickMode等属性的话实现起来还算简单,只须要继承ContentControl并处理好Pointer事件就能够,ControlTemplate能够直接复制Button的ControlTemplate。要注意的是在OnPointerPressed
函数中调用public System.Boolean CapturePointer(Pointer value)
捕获指针,这样能够防止点击后没释放鼠标的状况下,又将鼠标移动到其它UIElement上时触发它们的Pointer事件。例以下面的场景,在左边按钮按下鼠标后鼠标再移动到其它按钮并不会改变那个按钮的状态:
而没处理好的按钮则是这样,若是在右边的按钮上放开鼠标,看上去就是点击了右边的按钮:
虽然这篇文章很长,但没什么深刻的技术,我也不是专业设计人员,各方面都只保留在“浅谈”的范畴。
和以往同样,大部份内容都适用于WPF。
并无鼓励任何人在UWP中使用自定义按钮样式的意思,只是看到太多很差的设计,想贡献本身的经验和建议以供参考。除非必要仍是用UWP提供的按钮样式最保险,只要使用合理的文字、摆在合理的位置就足够了。按钮设计的最基本要求是容易识别、容易操做、不易出错,华丽的外表只是其次。
当年在WP7上看到开始屏幕的磁贴向下倾斜的动画真的很惊艳,但那是2010年的事了,当时Android刚出2.0,如今Android都快出8了。多年来磁贴只进行了小幅更新,Windows自带按钮也没什么惊艳的设计。其实我很喜欢Win八、Win10的按钮设计(除了Focus状态下的粗边框),说难听点是平庸,说好听点是含蓄。但也想UWP提供一些惊艳的、多样的设计给开发者选择。Reveal正好知足了我这个需求。目前Reveal还不是那么完美,还在调整,但愿正式推出时能给我更多惊喜吧。
Modern Design at Microsoft
Fluent Design System
扁平化设计
处理指针输入 - UWP app developer
从“按钮”看设计风格的演变
GitHub - ButtonStyleGallery 本文中出现的按钮设计均可以在这里找到,只是做为Demo没有好好调整颜色和过渡动画,若是拿去用可能仍是须要本身修改。