首页

10分钟彻底搞懂单页面应用路由

seo达人

单页面应用特征

假设: 在一个 web 页面中,有1个按钮,点击可跳转到站内其他页面。


多页面应用: 点击按钮,会从新加载一个html资源,刷新整个页面;


单页面应用: 点击按钮,没有新的html请求,只发生局部刷新,能营造出一种接近原生的体验,如丝般顺滑。


SPA 单页面应用为什么可以几乎无刷新呢?因为它的SP——single-page。在第一次进入应用时,即返回了唯一的html页面和它的公共静态资源,后续的所谓“跳转”,都不再从服务端拿html文件,只是DOM的替换操作,是模(jia)拟(zhuang)的。


那么js又是怎么捕捉到组件切换的时机,并且无刷新变更浏览器url呢?靠hash和HTML5History。


hash 路由

特征

类似www.xiaoming.html#bar 就是哈希路由,当 # 后面的哈希值发生变化时,不会向服务器请求数据,可以通过 hashchange 事件来监听到 URL 的变化,从而进行DOM操作来模拟页面跳转

不需要服务端配合

对 SEO 不友好

原理

hash


HTML5History 路由

特征

History 模式是 HTML5 新推出的功能,比之 hash 路由的方式直观,长成类似这个样子www.xiaoming.html/bar ,模拟页面跳转是通过 history.pushState(state, title, url) 来更新浏览器路由,路由变化时监听 popstate 事件来操作DOM

需要后端配合,进行重定向

对 SEO 相对友好

原理

Html5 History


vue-router 源码解读

以 Vue 的路由vue-router为例,我们一起来撸一把它的源码。


Tips:因为,本篇的重点在于讲解单页面路由的两种模式,所以,下面只列举了一些关键代码,主要讲解:


注册插件

VueRouter的构造函数,区分路由模式

全局注册组件

hash / HTML5History模式的 push 和监听方法

transitionTo 方法

注册插件

首先,作为一个插件,要有暴露一个install方法的自觉,给Vue爸爸去 use。


源码的install.js文件中,定义了注册安装插件的方法install,给每个组件的钩子函数混入方法,并在beforeCreate钩子执行时初始化路由:


Vue.mixin({

 beforeCreate () {

   if (isDef(this.$options.router)) {

     this._routerRoot = this

     this._router = this.$options.router

     this._router.init(this)

     Vue.util.defineReactive(this, '_route', this._router.history.current)

   } else {

     this._routerRoot = (this.$parent && this.$parent._routerRoot) || this

   }

   registerInstance(this, this)

 },

 // 全文中以...来表示省略的方法

 ...

});

区分mode

然后,我们从index.js找到整个插件的基类 VueRouter,不难看出,它是在constructor中,根据不同mode 采用不同路由实例的。


...

import {install} from './install';

import {HashHistory} from './history/hash';

import {HTML5History} from './history/html5';

...

export default class VueRouter {

 static install: () => void;

 constructor (options: RouterOptions = {}) {

   if (this.fallback) {

     mode = 'hash'

   }

   if (!inBrowser) {

     mode = 'abstract'

   }

   this.mode = mode

         

   switch (mode) {

     case 'history':

       this.history = new HTML5History(this, options.base)

       break

     case 'hash':

       this.history = new HashHistory(this, options.base, this.fallback)

       break

    case 'abstract':

       this.history = new AbstractHistory(this, options.base)

       break

    default:

     if (process.env.NODE_ENV !== 'production') {

       assert(false, `invalid mode: ${mode}`)

     }

   }

 }

}

全局注册router-link组件

这个时候,我们也许会问:使用 vue-router 时, 常见的<router-link/>、 <router-view/>又是在哪里引入的呢?


回到install.js文件,它引入并全局注册了 router-view、router-link组件:


import View from './components/view';

import Link from './components/link';

...

Vue.component('RouterView', View);

Vue.component('RouterLink', Link);

在 ./components/link.js 中,<router-link/>组件上默认绑定了click事件,点击触发handler方法进行相应的路由操作。


const handler = e => {

 if (guardEvent(e)) {

   if (this.replace) {

     router.replace(location, noop)

   } else {

     router.push(location, noop)

   }

}

};

就像最开始提到的,VueRouter构造函数中对不同mode初始化了不同模式的 History 实例,因而router.replace、router.push的方式也不尽相同。接下来,我们分别扒拉下这两个模式的源码。


hash模式

history/hash.js 文件中,定义了HashHistory 类,这货继承自 history/base.js 的 History 基类。


它的prototype上定义了push方法:在支持 HTML5History 模式的浏览器环境中(supportsPushState为 true),调用history.pushState来改变浏览器地址;其他浏览器环境中,则会直接用location.hash = path 来替换成新的 hash 地址。


其实最开始读到这里是有些疑问的,既然已经是 hash 模式为何还要判断supportsPushState?是为了支持scrollBehavior,history.pushState可以传参key过去,这样每个url历史都有一个key,用 key 保存了每个路由的位置信息。


同时,原型上绑定的setupListeners 方法,负责监听 hash 变更的时机:在支持 HTML5History 模式的浏览器环境中,监听popstate事件;而其他浏览器中,则监听hashchange。监听到变化后,触发handleRoutingEvent 方法,调用父类的transitionTo跳转逻辑,进行 DOM 的替换操作。


import { pushState, replaceState, supportsPushState } from '../util/push-state'

...

export class HashHistory extends History {

 setupListeners () {

   ...

   const handleRoutingEvent = () => {

       const current = this.current

       if (!ensureSlash()) {

         return

       }

       // transitionTo调用的父类History下的跳转方法,跳转后路径会进行hash化

       this.transitionTo(getHash(), route => {

         if (supportsScroll) {

           handleScroll(this.router, route, current, true)

         }

         if (!supportsPushState) {

           replaceHash(route.fullPath)

         }

       })

     }

     const eventType = supportsPushState ? 'popstate' : 'hashchange'

     window.addEventListener(

       eventType,

       handleRoutingEvent

     )

     this.listeners.push(() => {

       window.removeEventListener(eventType, handleRoutingEvent)

     })

 }

 

 push (location: RawLocation, onComplete?: Function, onAbort?: Function) {

   const { current: fromRoute } = this

   this.transitionTo(

     location,

     route => {

       pushHash(route.fullPath)

       handleScroll(this.router, route, fromRoute, false)

       onComplete && onComplete(route)

     },

     onAbort

   )

 }

}

...


// 处理传入path成hash形式的URL

function getUrl (path) {

 const href = window.location.href

 const i = href.indexOf('#')

 const base = i >= 0 ? href.slice(0, i) : href

 return `${base}#${path}`

}

...


// 替换hash

function pushHash (path) {

 if (supportsPushState) {

   pushState(getUrl(path))

 } else {

   window.location.hash = path

 }

}


// util/push-state.js文件中的方法

export const supportsPushState =

 inBrowser &&

 (function () {

   const ua = window.navigator.userAgent


   if (

     (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&

     ua.indexOf('Mobile Safari') !== -1 &&

     ua.indexOf('Chrome') === -1 &&

     ua.indexOf('Windows Phone') === -1

   ) {

     return false

   }

   return window.history && typeof window.history.pushState === 'function'

 })()

HTML5History模式

类似的,HTML5History 类定义在 history/html5.js 中。


定义push原型方法,调用history.pusheState修改浏览器的路径。


与此同时,原型setupListeners 方法对popstate进行了事件监听,适时做 DOM 替换。


import {pushState, replaceState, supportsPushState} from '../util/push-state';

...

export class HTML5History extends History {


 setupListeners () {


   const handleRoutingEvent = () => {

   const current = this.current;

   const location = getLocation(this.base);

   if (this.current === START && location === this._startLocation) {

     return

   }


   this.transitionTo(location, route => {

     if (supportsScroll) {

       handleScroll(router, route, current, true)

     }

   })

   }

   window.addEventListener('popstate', handleRoutingEvent)

   this.listeners.push(() => {

     window.removeEventListener('popstate', handleRoutingEvent)

   })

 }

 push (location: RawLocation, onComplete?: Function, onAbort?: Function) {

   const { current: fromRoute } = this

   this.transitionTo(location, route => {

     pushState(cleanPath(this.base + route.fullPath))

     handleScroll(this.router, route, fromRoute, false)

     onComplete && onComplete(route)

   }, onAbort)

 }

}


...


// util/push-state.js文件中的方法

export function pushState (url?: string, replace?: boolean) {

 saveScrollPosition()

 const history = window.history

 try {

   if (replace) {

     const stateCopy = extend({}, history.state)

     stateCopy.key = getStateKey()

     history.replaceState(stateCopy, '', url)

   } else {

     history.pushState({ key: setStateKey(genStateKey()) }, '', url)

   }

 } catch (e) {

   window.location[replace ? 'replace' : 'assign'](url)

 }

}

transitionTo 处理路由变更逻辑

上面提到的两种路由模式,都在监听时触发了this.transitionTo,这到底是个啥呢?它其实是定义在 history/base.js 基类上的原型方法,用来处理路由的变更逻辑。

先通过const route = this.router.match(location, this.current)对传入的值与当前值进行对比,返回相应的路由对象;接着判断新路由是否与当前路由相同,相同的话直接返回;不相同,则在this.confirmTransition中执行回调更新路由对象,并对视图相关DOM进行替换操作。


export class History {

...

transitionTo (

   location: RawLocation,

   onComplete?: Function,

   onAbort?: Function

 ) {

   const route = this.router.match(location, this.current)

   this.confirmTransition(

     route,

     () => {

       const prev = this.current

       this.updateRoute(route)

       onComplete && onComplete(route)

       this.ensureURL()

       this.router.afterHooks.forEach(hook => {

         hook && hook(route, prev)

       })


       if (!this.ready) {

         this.ready = true

         this.readyCbs.forEach(cb => {

           cb(route)

         })

       }

     },

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

如何设计一个超长长长长长的复杂表单?

涛涛

导语

你平时填写过的最复杂的表单是什么?调查问卷还是文档信息录入?如果一个表单字段内容巨多,结构多变,填写耗时耗力,那你将如何设计你的表单,使之体验更佳?

面临的问题

1. 业务复杂,功能较多;

不知道大家是否看过法律合同之类的文件,多则好几箩筐,少也有厚厚一叠,类似的文档如果进行线上结构化,势必要同样要花费巨大的人力去填写表单,完成基础信息的录入工作,同时,由于录入的时间不确定,流程不明确等问题,也制约着表单的填写。

2. 流程较长,操作繁琐;

多个不同表单之间的互有关联又相互区别,填写的时候需要来回查看以确认信息,查阅和填写相互并行,操作繁琐。

3. 字段较多,关联项较多;

几乎每一个字段都有对应的关联项,每个单选字段的不同项决定不同的内容,同时,由于字段数量,层级划分不明确,会使填写的人失去定位,产生迷惑。

解决的方案

1. 内容分组,分步填写;

根据业务内容分级,合理运用颜色、间距、字体大小、卡片层级等进行信息分级

如何设计一个超长长长长长的复杂表单?

2. 实时保存,避免数据丢失,提供草稿功能,避免任务中断;

如何设计一个超长长长长长的复杂表单?

3. 字段分组,示意结构,联动项隐喻;

如何设计一个超长长长长长的复杂表单?

4. 信息自动带入,节省时间;

一般表单是与某项功能挂钩的,信息会在多个入口录入,因此在填写长表单的时候,如果能从系统中自动获取到数据,就可以自动为其填充,可根据业务场景,判断是否让其修改和更新。

如何设计一个超长长长长长的复杂表单?

5. 提供二次编辑功能,防止信息输入有误;

一般的长表单在涉及非审批流的时候,可以让其无限二次编辑,如果是处于审批流,则需要根据业务场景限制其编辑次数或者限定其编辑规则(草稿可编辑,一旦提交则不可编辑)。

6. 提供多人协作编辑功能;

如果一个长表单,需要多个不同的业务域的人来填写,那么需要协同编辑,并实时显示编辑的人员信息,同时,为了避免信息丢失和编辑错乱,在同一个表单下,同一时间应该限制只允许一个人进行编辑,等其提交完后,可允许其他人进行编辑。

如何设计一个超长长长长长的复杂表单?

7. 实时检验;

前端实时校验字段输入规则,后端统一校验信息交换规则。

比如对于数字输入框的校验、电话号码的校验、身份证号的校验,应该在前端实时完成,在鼠标离开焦点区域或定位到下一个字段的时候,提示其填写有误,这样做的目的是减少后续修改的次数,在长表单下,统一提示其填写错误会是一场灾难。

如何设计一个超长长长长长的复杂表单?

在点击保存并填写下一步或点击提交信息的时候,就需要跟后端交换数据,验证录入的信息,如果不匹配,则提示错误,并从上至下定位至相应的错误字段。

8. 做好填写引导功能;

要通过多种方式,引导表单的填写,

在开始填写之前,简要说明该表单的业务目标,大概需要花费的时间等;

如何设计一个超长长长长长的复杂表单?

开始填写后,关于每个字段的特殊说明,都需要标注出来,重要的要显示在页面上,不重要的就收起在注释符号中;

如何设计一个超长长长长长的复杂表单?

填写的过程中,切记不要到最后才告诉用户哪里出错了,重要的信息一定要提示到位,否则一旦出错,前功尽弃;

如何设计一个超长长长长长的复杂表单?

填写完成后,引导其下一步的操作,或者返回至列表。

如何设计一个超长长长长长的复杂表单?

9. 详情页也需要注意信息分级

表单填写完毕后的产出物就是详情页,详情页是需要浏览的,因此在设计详情页的时候,应该本着让用户浏览方便的原则去设计,需要注意以下几个点:

结构清晰。是指不要将内容一股脑的全堆在页面上,要做好信息的分类,同时,注意规划页面的层级。

设置快捷导航。如果一个表单是长且复杂的,那么其对应的详情页也会变得复杂和冗长,因此在页面的右侧或者顶部设置合理的快捷导航是很有必要的。

如何设计一个超长长长长长的复杂表单?

最后:小细节,大体验

1. 提供快速返回顶部的按钮;

快速返回顶部按钮的使用要注意场景,如果你的页面比较长,且没有分组浏览的导航,那就需要设置快速返回顶部的按钮,但是在存在分组浏览导航和顶部悬浮标签的情况下,不建议使用快速返回顶部的按钮,因为在填写表单的时候,使用快速置顶的场景比较少。

2. 提供分组模块收起展开功能;

当一个模块混杂着各种信息的时候,单纯的模块分组已经无法处理它的复杂度了,因此需要收起高频且信息量大的模块,可以合理的减少页面的复杂度。

如何设计一个超长长长长长的复杂表单?

3. 步骤提供信息填写完成度提示;

步骤条可以单纯的作为步骤指示器使用,也可以作为一个表单完成度的提示区域。

如何设计一个超长长长长长的复杂表单?

4. 重要说明性文字尽量显示而非收起;

在填写大量字段的表单时,阅读表单内容和填写表单同样耗时耗力,如果我们将所有的提示信息隐藏在提示符中,一般情况下,用户不会去查看,但是如果去挨个查看提示信息,则会多花费一个步骤去点击或者悬停来查看提示信息,浪费了大量的时间,因此如果涉及到重要的提示信息,请直接展示在字段的后面,不要隐藏起来。

如何设计一个超长长长长长的复杂表单?

5. 产品内组件应该规范统一;

在后台产品上,关于组件的规范统一,想必是人尽皆知的设计原则,无论是各类平台型设计组件,还是各个公司自造的设计组件,保持统一和规范对产品设计有着重要的作用,在这里不赘述组件应该怎样规范统一,因为无论是Ant Design还是其他设计语言,都有详尽的关于组件的定义方法,我在这里讲述一个产品设计更高层面或者更深层面的原因:

组件的规范统一并不仅仅是为了省时省力,而是为了使用户在使用的过程中达到认知上的统一和行为上的统一,在进行高频次的操作后,界面的流程或者组件样式已大致在用户脑海中形成固定印象,因此在操作相同类的流程时,用户会有更多的掌控感,试想一下,如果你在操作人事相关的流程后,去填写绩效部分的内容时,发现一个迥异的界面或者弹窗,你肯定觉得这是不是哪里出错了,甚至会怀疑这是否是同一个系统,目前大多数公司的管理系统经过多次缝缝补补,内部的跳转逻辑已经异常感人,界面风格也大放异彩,但是使用起来却无从下手,深感迷茫。

因此大到界面样式,小到间距大小,产品设计的规范和统一应该是最基础又不可缺少的原则。

6. 庞大的信息录入,表单内部要分步填写,外部可拆分成不同的表单分别填写;

对付复杂的表单,你需要解决的主要问题并不是填写方式或者页面设计,而是信息分级和结构拆分,解决了这个问题,基本上就解决了业务问题,其余部分就跟我们常用的表单一致。

将复杂度降低并不意味着减少页面的信息,而是通过设计师合理的信息划分,降低视觉上的复杂度和流程上的复杂度,这样才会达到当前场景下的「最佳解决方案」。

如何设计一个超长长长长长的复杂表单?

如何设计一个超长长长长长的复杂表单?

结语

随着互联网信息化的深入发展,复杂是避免不了的,我知道大家都推崇简洁的设计,但那只是对视觉和样式的定义,而非对信息的定义,我们所处的世界是复杂的,行业更是复杂的,信息的复杂度与日俱增,想要处理复杂的信息,就需要从复杂中寻求规律,这规律与业务息息相关,


文章来源:优设    作者:

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

如何让你的「按钮设计」上档次?

涛涛

按钮在界面设计中,属于最基础的元素部分组成,按钮设计的精致,整个页面的品质也会上升不少的档次。今天给大家分享这篇文章,主要讲解在设计按钮时我们应该考虑哪些因素,包括视觉上,有哪些万能的方法及公式,能够正确的制定按钮的设计标准,来提升整个设计的系统性。

如何让你的「按钮设计」上档次?送你这份万能公式!

按钮有哪些作用?

在设计按钮之前,需要先理解按钮起到的代表含义。什么地方该加按钮,什么地方不加按钮,在系统化设计思路中需要非常有讲究。通常按钮在页面,主要起到以下三点作用:

1. 某一类型的功能操作

这种比较常见,如一些控件形态的按钮,比如加减、折叠、展开,下拉等。这类按钮会起到一些功能形态的作用,常用于交互场景。所以在这类按钮设计中,应当弱化按钮形式,重点强调功能,突出主体信息。

如何让你的「按钮设计」上档次?送你这份万能公式!

2. 下一步的明确指引

当页面内容信息过多后,用户容易失去信息焦点,从而忘记下一步操作。信息种类越多,用户权衡的时间就会越久,用户选择罢手及跳出的几率也会越大。所以这个时候,在合适的地方增添按钮,能够很好的引导用户进行下一步操作,提升整体操作的成功率。其次从体验层面,也一定程度能起到页面动线的引导作用,比如下方的一组卡片,在增添了按钮后行动点很明确,非常有点击欲望~

如何让你的「按钮设计」上档次?送你这份万能公式!

3. 固定习惯,明确心理预期

当用户知悉某个按钮能指向某个操作,或者获取某类信息后,长期以往用户就会形成使用这个按钮的习惯,这样对提升复访及固定心智是非常有效果。

所以如果你认为你负责的产品或者是内容,能持续为用户带来价值,那么在页面的关键节点,不如将按钮设计的更醒目。这样用户下次再看到这个按钮时,固定习惯会引导他持续的点击。

如何让你的「按钮设计」上档次?送你这份万能公式!

按钮有哪些类型?

这里我不以按钮的长相来区分按钮的类型,如汉堡按钮或者别的什么的,意义不大。我主要还是想通过以按钮的功能区分,来划分类型,这样大家理解起来更为清晰。

1. 功能性质按钮

这类按钮见到的最多,我们常用的APP里,大量都充斥了这类按钮,这类按钮会起到重点的功能交互,帮助用户得到TA想要的信息。其次样式上面,其实圆形的点击欲,会更强一些,看起来也更利于点击。而方型的按钮,则显得更为正式、严谨。

如何让你的「按钮设计」上档次?送你这份万能公式!

公式:如果是圆形按钮,圆角的半径=高度的50%比较合适,而如果是方按钮,边角的小圆角半径控制在15%以下比较合适,我个人喜好用10%。

如何让你的「按钮设计」上档次?送你这份万能公式!

2. 聚焦大按钮

这类按钮通常见于一些核心页面的强指引,比如登录、注册、提交表单、或者是保存,等对页面全局进行操作的一些按钮。需要注意的是,这类按钮只适合对页面全局进行操作,而且页面中大按钮的数量不宜超过2个,信息尽量需要保持聚焦。

如何让你的「按钮设计」上档次?送你这份万能公式!

公式:基于@2x,这类大按钮的高度≥72px是比较合适的,通常的尺寸有 80px、88px、96px,大家可以根据产品面向的人群来定高度,如果页面面向的人群较为广泛,我建议采用 88px 或者是 96px 的这种大号版本,毕竟操作起来更为方便。

如何让你的「按钮设计」上档次?送你这份万能公式!

3. 吸底按钮

这类按钮的优先级,在整个页面属于最高,页面的所有信息,都将聚焦在这个按钮中。由于按钮是吸底的,所以会一直浮在页面上,不受页面篇幅影响控制。

如何让你的「按钮设计」上档次?送你这份万能公式!

需要注意的是,吸底按钮一定是页面最重要的功能,或者是整个页面的下一步指引,比如淘宝的立即购买,或者是饿了么、美团的立即下单,又或者是常见的充值界面。

如何让你的「按钮设计」上档次?送你这份万能公式!

公式:基于@2x,吸底的高度≥80px是比较合适,常见的尺寸有88px、100px、112px ,按钮的大小可以根据内容来定,建议高度保持在72px以上比较合适。这里需要注意的是,吸底的按钮,需要产出两套设计稿,一套为常规稿,一套为iPhoneX的适配稿。iPhoneX底部控件的区域高度为68px,所以iPhoneX设计稿的吸底高度=常规设计稿吸底高度+68px

如何让你的「按钮设计」上档次?送你这份万能公式!

按钮有哪些状态?

另外在设计按钮的时候,也别忘了补充按钮的多个状态的设计稿。常见的状态,有以下四种:

1. Normal-正常态

这个为按钮的正常显示态,就是正常页面中的显示效果。

如何让你的「按钮设计」上档次?送你这份万能公式!

2. Hover-悬浮态

这个为按钮的悬浮态,一般只会出现在使用鼠标的时候。当鼠标指针停留在按钮时,按钮发出的特殊反馈,则为悬浮态。这类形式在移动端交互中无作用,所以移动界面设计中不需要考虑这个状态。

如何让你的「按钮设计」上档次?送你这份万能公式!

公式:正常情况 Hover 态增加 10% 黑色就可以,原理如下

如何让你的「按钮设计」上档次?送你这份万能公式!

3. Pressed-点击态

这个为按钮的按压态,就是按钮在被点击或者是按压后的效果。

如何让你的「按钮设计」上档次?送你这份万能公式!

公式:在APP设计中,点击后的效果我们设一个标准值让开发实现就好了。常用的值有按钮减少20%的透明度,或者增添20%的暗度,这两个都可以。通常我建议在亮色上的按钮,使用暗度叠加(增添20%的黑色),在暗色上的按钮,则使用透明度减少(透明度改为80%),实现效果原理参考 Hover 态那张配图

4. Disable-禁用态

当信息未填充完整,或者是某类条件未到,按钮会出现不可点击的状态,处于禁用形式,这个时候,按钮就会呈现禁用态。这个禁用态无论是web还是app,很多场景都会用到,所以建议设定一套标准的设计规范,避免重复定义这个效果。

如何让你的「按钮设计」上档次?送你这份万能公式!

公式:禁用态尺寸及大小不变,仅使用色值做区分。建议使用灰色或者是不透明色,常用的禁用色有#CCC或者#999,需要尽可能把样式做弱,避免用户做无效的点击。

按钮的风格及尺寸

在目前移动互联网设计中,虽然按钮的种类很多,但风格变的逐渐统一,更多都是色值及细节上的差异。从大的风格来看,按钮还是分为这这几种类型:扁平化、轻拟物、重拟物及游戏按钮。

1. 扁平化按钮

这类按钮我们设计用的最多,信息简洁,操作方便,形式追随功能。这里也给大家分享一下我在设计扁平化按钮的一些经验,比如高度宽度,以及阴影的色值。

公式:按钮高度,这个通常是文字字号的2.4倍然后取4的倍数整数,比如字号是24,那么按钮的高度=57.6,离4倍数最近的是56,所以高度=56,圆角=10%的高度,取整后是6px。

如何让你的「按钮设计」上档次?送你这份万能公式!

另外如果觉得不合适,也可以单位往8递增或者是递减即可,例如 56、64、72、80、88 px

按钮宽度:如果不是那种全局按钮,通常按钮的宽度=最多容纳字数的宽度+按钮高度,就好啦。还是以上面那个例子为例,按钮高度=56,文字宽度=96,那么按钮的宽度=56+96=152

如何让你的「按钮设计」上档次?送你这份万能公式!

2. 轻拟物按钮

这类按钮近几年变的非常流行,甚至QQ、淘宝,都开始大面积使用,因为这类按钮在保持信息简洁的同时,仍然有较强的点击欲,视觉上面也能够增添页面的品质感。

如何让你的「按钮设计」上档次?送你这份万能公式!

公式:渐变方向,建议采用水平渐变,重色在右侧,轻色在左侧更为合适。阴影色值我之前就写过,不知道大家还记得么,阴影颜色=按钮颜色的 Alpha50%,x=0,y=按钮高度的20%,模糊值=按钮高度的50%,扩展=按钮高度的 -15%,高级又简单,完美!

如果觉得这个弥散阴影太大的同学,也可以自己手动简单调整下,不碍事。(这个公式仅适用于Sketch,用PS的同学,也可以按照这个逻辑自行研究一下)

如何让你的「按钮设计」上档次?送你这份万能公式!

另外说一句,实际上这个阴影公式并没有什么很多的依据,大多数都是我个人原创总结出来的,简单好用。比如下面的这些按钮的样式,用了公式后的效果大家可以自行感受~

如何让你的「按钮设计」上档次?送你这份万能公式!

 

3. 重拟物及游戏按钮

在一些营销页面中,按钮的样式通常需要做的比较游戏化。游戏化的按钮,大部分会采取游戏场景中的元素,再采用拟物的手法,来进行打造。

通常游戏化的按钮,需要重点几个部分组成,学过素描的同学应该会知道,立体的物体,通常会有几大特征,分别为高光,亮部,暗部,投影及反光。那么如果我们需要绘制一个在营销或者游戏场景中使用的按钮,只需要保证这个按钮有高光,亮部,暗部,投影及反光的这些特征,然后饱满一点就,立马就可以出效果啦。

如何让你的「按钮设计」上档次?送你这份万能公式!

当然,我举的这几个例子都是最基础版本,如果你想做的更丰富一些,那也是没问题的,这个可以case by case 来定。

这个没有太多的公式可以总结,更多的是看设计师的基础美术水平啦~~

如何让你的「按钮设计」上档次?送你这份万能公式!

新拟态按钮

在写这篇文章的时候,突然刷到了一套新拟态的控件设计风格,有种眼前一亮的感觉。虽然这套设计视觉上很有层次很好看,不过感觉短时间之内,比较难大面积推广,因为开发实现起来还是会比较耗费成本。

如何让你的「按钮设计」上档次?送你这份万能公式!

我把源文件保存下来了,对这个感兴趣或者好奇这种效果如何实现的同学,可以下载源文件研究~~ sketch、psd、Figma 格式都有。

文章来源:优设    作者:UX小学

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

Vue.js教程-目录

前端达人

简介

  • 本目录作为Vue教程的首页,所以会持续更新。
  • 如果某篇章节中有错误的地方,希望大家能够指出来,我会更正,私信和评论里说都可以,不懂的地方也可以说,如果我也不会那就请教一下大佬们吧,毕竟我对前端的东西也不是特别了解,多多包涵!嘿嘿。

章节列表

章节名称 地址
Vue.js教程-安装和HelloWorld https://coderhqf.blog.csdn.net/article/details/107574556
Vue.js教程-Vue项目的目录结构和.vue文件的构成 https://coderhqf.blog.csdn.net/article/details/107621070
Vue.js教程-Vue基本指令 https://coderhqf.blog.csdn.net/article/details/107677588
Vue.js教程-组件化开发 https://coderhqf.blog.csdn.net/article/details/107783664

Vue简介

  • Vue官网
  • Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。
  • Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
  • Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

Vue特点

  • 易用:在有HTML CSS JavaScript的基础上,快速上手。
  • 灵活:简单小巧的核心,渐进式技术栈,足以应付任何规模的应用。
  • 性能:20kb min+gzip 运行大小、超快虚拟 DOM 、最省心的优化。

Vue中数据观测的实现

  • Vue.js利用了ES5的Object.defineProperty方法,直接将原生数据对象的属性改造为getter和setter,在这两个函数内部实现依赖的收集和触发,而且完美支持嵌套的对象结构。对于数组,则通过包裹数组的可变方法(比如push)来监听数组的变化。这使得操作Vue.js的数据和操作原生对象几乎没有差别[注:在添加/删除属性,或是修改数组特定位置元素时,需要调用特定的函数,如obj.$add(key, value)才能触发更新。这是受ES5的语言特性所限。],数据操作的逻辑更为清晰流畅,和第三方数据同步方案的整合也更为方便。

Vue项目打包

  • 在构建大型应用时,推荐使用Webpack+vue-loader这个组合以使针对组件的开发更。
  • 在后面的章节中会细说怎么打包并部署到服务器上,也会讲怎么白嫖阿里云(学生版),好像有大佬写过这个文章,大家搜一下也行,最重要的还是开发。

Vue的组件化开发

  • Vue最主要的是组件化开发,因为是单页面,也就是在一个页面中渲染出多个页面的效果,这个特点能够让非常多的组件在不同的项目中重复使用。
  • Vue中的组件基于Web components进行了上层功能的实现,例如数据绑定、动画系统等。

Vue与后端的数据交互:axios

  • 传统的一般都用Ajax,但如果请求有先后关系的话就容易产生回调地狱,套娃套的吧。
  • Vue2之后就推荐使用axios了,等写到前后端交互的时候再讲这个就行。

相关说明

  • Vue参考了AngularJS、KnockoutJS、Ractive.js、Rivets.js,可以是把他们的缺点都优化成了自己的优点,参考过程中不但去其糟粕,还加入了自己的特性,但目前也只有国内用Vue的比较多,毕竟社区小,资源少,但以后应该会是潮流,因为开发快好上手。
  • 其实Vue相对来说是非常好上手的,因为它只专注于视图层。如果只是要用的话,其实对原理也不用太纠结,但既然要全栈,何不贯彻到底,我也是在学习中,如果想正规学习的话,我比较推荐coderwhy-王红君老师的课,他讲的挺好的,有些原理讲的也是很清楚的,渠道嘛,B站大学、腾讯课堂啥的都有,还有他的微博,大家可以去网上找。
  • 再强调一遍,如果发现不对的地方请联系我,因为不想误人子弟,毕竟这是自己的总结,也不想以后自己还用着错误的东西,嘿嘿嘿。。。
  • Vue作为现在国内最潮流的前端框架,也逐渐成为后端开发人员需要学的新知识了,我看现在很多后端岗位的面试里都会提到这个前端框架,所以大家学一下是不亏的。
  • 在CSDN杂志中有一篇文章:Vue.js:轻量的前端组件化方案,如果大家有兴趣就看看吧。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_45062103/article/details/107763788

手机appUI界面设计赏析(五)

前端达人

与传统PC桌面不同,手机屏幕的尺寸更加小巧操作,方式也已触控为主,APP界面设计不但要保证APP功能的完整性和合理性,又要保证APP的功能性和实用性,在保证其拥有流畅的操作感受的同时,满足人们的审美需求。

接下来为大家介绍几款手机appui界面设计

点击查看原图


   --手机appUI设计--

点击查看原图

 --手机appUI设计--

点击查看原图

 --手机appUI设计--

微信图片_20200805221704.png

 --手机appUI设计--

微信图片_20200805221707.jpg

 --手机appUI设计--

微信图片_20200805221712.jpg

 --手机appUI设计--

微信图片_20200805221759.png

 --手机appUI设计--

微信图片_20200805221803.jpg

 --手机appUI设计--

微信图片_20200805221808.png

 --手机appUI设计--

微信图片_20200805221813.jpg

 --手机appUI设计--

微信图片_20200805221823.jpg

 --手机appUI设计--

(以上图片均来源于网络)



  蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服



  更多精彩文章:

       手机appUI界面设计赏析(一)

       手机appUI界面设计赏析(二)

       手机appUI界面设计赏析(三)

       手机appUI界面设计赏析(四)




这样设计光影轻拟物,想不脱颖而出都难!

涛涛

还记得2019年4月上映的复仇者联盟4么,漫威电影宇宙的第三阶段结束了,电影很精彩,但最令人震撼的是先导版的电影海报!就是那个「五彩斑斓」的黑~

这样设计光影轻拟物,想不脱颖而出都难!

从设计上看,海报的设计师是把光运用到极限了,通过逆光和环境塑造出了酷+神秘的视觉感受。光是一切视觉表现的基础,是构图和传递调性的关键,也是视觉表现重要的组成部分。所以今天就和大家聊聊啥样的光是一个牛X的光以及如何塑造一个合格的光影?

光影的基本原理

常见形式1-聚光

这样设计光影轻拟物,想不脱颖而出都难!

这样设计光影轻拟物,想不脱颖而出都难!

△ 图片来源:小米米家台灯1S

聚光是最常见光,也是在设计中用到最多的光,通常在塑造一个物体的时候就会用的到。

这样设计光影轻拟物,想不脱颖而出都难!

因为聚光的原因,场景更像个舞台,凸显「主角」的存在。具体的原理可以根据下图去理解。

这样设计光影轻拟物,想不脱颖而出都难!

常见形式2-自然光

这样设计光影轻拟物,想不脱颖而出都难!

自然光其实就是阳光,理论上讲其实光源是太阳也是聚光,但由于光源太过于庞大,无法真正的聚焦,所以就把这种光当成一种泛光源就好。在产品设计中也会经常看到类似的光源出现,比如行为召唤按钮:

这样设计光影轻拟物,想不脱颖而出都难!

因为不需要强有力的表现和氛围的营造,所以通常产品设计中更需要自然光来作为核心光源,通过泛光源去统一控制产品的光影体系就好(发布的 Mac OS – big Sur 的整体光源同样是自然光,下文会讲到)。

常见形式3-逆光

这样设计光影轻拟物,想不脱颖而出都难!

坦诚的讲逆光也是聚光的一种,只不过由于角度特殊,呈现的视觉效果也非常不一样,所以就单独把逆光拿出来说一说。当画面需要逆光来营造氛围的时候,只需要在固有色上加上黑色蒙板和边缘的高光就可以大概塑造出一个处于逆光的物体了。

这样设计光影轻拟物,想不脱颖而出都难!

小米是典型的逆光氛围营造高手,但万变不离其宗,依旧可以从海报里看到相同的方法。

这样设计光影轻拟物,想不脱颖而出都难!

△ 图片来源:小米传播物料

光影的塑造(光影层级)

通常现实中的光源并非那么理想,光线的复杂超出肉眼所见。所以我们在绘制的过程需要注意到,可以适当的抽象。举个例子,自然光是普照的,所以我们抽象为四个小光源平均分布,依次打到物体上:

这样设计光影轻拟物,想不脱颖而出都难!

在他们叠加的部分可以清晰的看到,1是最重的,2其次,3再次。按照这个办法就可以获得光影的层级关系,在绘制轻拟态的图标或者运营活动中更加细腻。

这样设计光影轻拟物,想不脱颖而出都难!

体积塑造+色彩对光影的影响(反光/对比光)

这样设计光影轻拟物,想不脱颖而出都难!

△ 图片来源:09UI设计工作室-陌陌直播礼物设计

所有的光影其实都是帮助主体塑造体积感,一个合格的立体图形必须具备:高光/过度色/明暗交界线和反光这四个基本属性。

这样设计光影轻拟物,想不脱颖而出都难!

然后需要一点超现实主义的手法,把太阳光过滤下,从「赤橙黄绿青蓝紫」的色调里提取跟主体和谐的颜色(通常是补色),营造出介于真实与玄幻之间的美妙效果hiahia~

这样设计光影轻拟物,想不脱颖而出都难!

然后再在投影上加一点点色彩倾向,一个完美的黑八就出现啦~按照这种方法,你可以试着去尝试更多的物体/场景。(下图是笔者作品「插着红旗的地球」hiahia)

这样设计光影轻拟物,想不脱颖而出都难!

光影/材质与产品设计中的层级关系

这样设计光影轻拟物,想不脱颖而出都难!

影响主体的除了光影之外就是材质了,近年来的三大巨头apple/Microsoft/Google的设计都在材质上下足了功夫,苹果的毛玻璃,微软的亚克力和谷歌的「纸」。

从趋势上看,材质的引入对产品中拉开层级关系上有巨大意义,以往的设计仅仅是通过光影和焦距来拉开关系,这两个维度在少量的叠加界面中还能有效果,但到了复杂的多窗口互压互叠里就不是那么奏效了,所以铁子们要善于运用材质区分产品显示的优先级。

这样设计光影轻拟物,想不脱颖而出都难!

图标与插图的光影使用技巧

这样设计光影轻拟物,想不脱颖而出都难!

△ 图片来源:Eric Hoffman – Big Sur Mac Icons

这样设计光影轻拟物,想不脱颖而出都难!

△ 图片来源:JIAJIE – WeSing Live gift

图标好坏除了造型之外最重要的就是质感了,通常图标也就是4种形式(如下图),类似苹果的系统图标和抖音直播间礼物的图标都是最后一种形式。

这样设计光影轻拟物,想不脱颖而出都难!

但如果仅仅是这样就太水了,既然都说了是干货预警,那就要拿出哥们看家的本领~此图是大家关注公号后就会收到的推图,主体就是一个POI的图标加上微信的对话框和代表干货的小星星营造出的氛围。

这样设计光影轻拟物,想不脱颖而出都难!

刨析下这个图,三个发光体和底下的投影,通过上文的讲解依次绘制完成~

这样设计光影轻拟物,想不脱颖而出都难!

之后就到了amazing的时刻了,打开photoshop找到「滤镜-模糊画廊-场景模糊」设置几个key-point,调试模糊值和透明度/亮度,最终完成对光影的塑造。

这样设计光影轻拟物,想不脱颖而出都难!

借助环境塑造光影

空气中的灰尘相信大家都不陌生,这种情况多数是一束光影从窗户里射入后,在光的折射下把平时看不到的灰尘统统照了个遍。

这样设计光影轻拟物,想不脱颖而出都难!

如果你是mac用户一定熟知keynote里的动画效果「轰然坠落」,这个效果是在模拟物体振动后弹开周围灰尘,非常震撼。在PPT的设计中你也可以同样借助光和雾来营造你想要的效果,下图是我在做工作总结的时候为了凸显Q4工作采用的办法。

这样设计光影轻拟物,想不脱颖而出都难!

小结一下

光影轻拟物在产品设计中的应用面还是很广的,比如:图标、数据可视化、插图等等。而在大量扁平设计时代适量使用会显得很出彩,当然再好的教程也比不上大家多动手试试练练,所以铁汁们行动起来咯~

文章来源:优设    作者:Nana的设计锦囊

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

JavaScript中的for循环

seo达人

JavaScript 语言中的 for 循环用于多次执行代码块,它是 JavaScript 中最常用的一个循环工具,还可用于数组的遍历循环等。


我们为什么要使用 for 循环呢?打个比方,例如我们想要控制台输出1到1000之间的所有数字,如果单写输出语句,要写1000句代码,但是如果使用 for 循环,几句代码就能实现。总之,使用 for 循环能够让我们写代码更方便快捷(当然啦,否则要它干嘛)。


for 循环语法

语法如下所示:


for(变量初始化; 条件表达式; 变量更新) {

   // 条件表达式为true时执行的语句块

}

变量初始化,表示代码块开始前执行。

条件表达式,定义运行循环代码块的条件。

变量更新,在循环代码块每次被执行之后再执行。

示例:

例如我们在一个HTML文件中,编写如下代码,实现计算1到100的总和:


<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>JS_侠课岛(9xkd.com)</title>

</head>

<body>

<script>

 var result = 0;

 for(var i = 1; i <= 100; i++) {

   result = result + i;

 }

 alert(result);

</script>

</body>  

</html>

在浏览器中打开这个文件,会弹出一个弹出层,弹出层中显示的是1到100的总和:



上述代码中,我们声明了一个变量 result 并给它赋值为 0,表示初始的总和为 0 。


然后在 for 循环中三个语句:


变量初始化 i = 1,表示从 1 开始计算。

条件表达式 i <= 100,表示只要 i 小于等于 100 循环就会一直执行,当 i 大于 100 循环会停止。

变量更新 i++,之前我们学运算符的时候学过,这是递增运算符 ++,表示为其操作数增加 1。

此时我们可以一点点来看这个 for 循环:


第一次循环: result = 0 + 1   // 此时result值为0,  i的值为1

第二次循环: result = 1 + 2   // 此时result值为0+1,i的值为2

第三次循环: result = 3 + 3   // 此时result值为1+2,i的值为3

第四次循环: result = 6 + 4   // 此时result值为3+3,i的值为4

第五次循环: result = 10 + 5  // 此时result值为6+4,i的值为5

...

我们只需要搞清楚 for 循环中的执行原理,不需要手动来计算求和,只要写好代码,执行代码后计算机会很快会告诉我们1到 100 的总和。


再补充一下,上述代码中result = result + i,我们也可以写成 result += i,这是我们之前学过的加赋值运算符,还记得吗?


示例:

再来看一个例子,例如我们可以使用 for 循环来实现数组遍历,首先定义一个数组 lst:


var lst = ["a", "b", "c", "d", "e"];

在写 for 循环时,首先就是要搞清楚小括号里面的三个语句,因为我们可以通过数组中元素的下标索引来获取元素的值,而数组的索引又是从 0 开始,所以变量初始化可以设置为i = 0。第二个条件表达式,因为数组中最后一个索引为 lst.length - 1,所以只要小于等于 lst.length - 1,循环就会一直执行。而i <= lst.length - 1 就相当于 i<lst.length。第三个变量更新,当循环每循环一次,索引值就加一,所以为 i++。


所以循环可以像下面这样写:


for(i = 0; i<lst.length; i++){

   console.log(lst[i]);  // 输出数组中的元素值,从索引为0的值开始输出,每次加1,一直到lst.length-1

}

输出:


a

b

c

d

e

其实遍历数组还有一种更好的方法,就是使用 for...in 循环语句来遍历数组。


for...in 循环

for...in 循环主要用于遍历数组或对象属性,对数组或对象的属性进行循环操作。for...in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。


语法如下:


for (变量 in 对象) {

   // 代码块

}

for 循环括号内的变量是用来指定变量,指定的可以是数组对象或者是对象属性。


示例:

使用 for...in 循环遍历我们定义好的 lst 数组:


var lst = ["a", "b", "c", "d", "e"];

for(var l in lst){

   console.log(lst[l]);

}

输出:


a

b

c

d

e

除了数组,for...in 循环还可以遍历对象,例如我们遍历 侠侠 的个人基本信息:


var object = {

   姓名:'侠侠',

   年龄:'22',

   性别:'男',

   出生日期:'1997-08-05',

   职业:'程序员',

   特长:'跳舞'

}


for(var i in object) {

   console.log(i + ":" + object[i]);

}

输出:


姓名: 侠侠

年龄: 22

性别: 男

出生日期: 1997-08-05

职业:程序员

特长:跳舞

动手小练习

请自定义一个长度为7的数组,然后通过 for 循环将数组中的元素遍历出来。

求和:1~100的奇数和。

求和:1~100的偶数和。

使用对象定义一个人的个人信息(包括姓名、性别、年龄、出生日期、兴趣爱好、职业、特长等),然后使用 for...in 循环将这些信息遍历输出。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

封装element-ui表格

seo达人

表格需求

一般管理系统对表格会有以下需求


可以分页(需要有分页条)

可以多选(表格带复选框)

顶部需要加一些操作按钮(新增,删除等等)

表格每行行尾有操作按钮

表格行可以编辑

如下图为一个示例表格




如果我们直接使用element-ui提供的组件的话,那么开发一个这样的表格就需要使用到以下内容


需要使用表格的插槽功能,开发每一行的按钮

需要通过样式调整顶部按钮,表格,分页条的布局样式

需要监听分页的事件然后去刷新表格数据

顶部按钮或操作按钮如果需要获取表格数据,需要调用表格提供的api

对于有行编辑的需求,还需要通过插槽去渲染行编辑的内容,同时要控制行编辑的开关

不仅仅开发表格比较麻烦,而且还要考虑团队协作,如果每个人实现表格的方式存在差别,那么可能后期的维护成本也会变得很高。那怎么办呢?


项目安装

安装插件

在使用element-ui的项目中,可以通过以下命令进行安装


npm install vue-elementui-table -S

在项目中使用

在main.js中添加以下代码


import ZjTable from 'vue-element-table'


Vue.use(ZjTable)

然后即可像下文中的使用方式进行使用


表格配置

为了满足团队快速开发的需要,小编对上面提出来的需求进行了封装,然后使用的时候,开发人员只需要配置一些JSON便可以完成以上功能的开发。


基础配置

一个基础的表格包含了数据和列信息,那么如何用封装的表格去配置呢?


<template>

 <zj-table

   :columns="columns"

   :data="data"

   :pagination="false"

 />

</template>

<script>

export default {

 data() {

   return {

     // 表格的列信息, 数组每一项代表一个字段,可以使用element 列属性的所有属性,以下仅为示例

     columns: Object.freeze([

       {

         // 表头显示的文字

         label: '姓名',

         // 对应数据里面的字段

         prop: 'name'

       },

       {

         label: '性别',

         prop: 'sex',

         // 格式化表格,与element-ui 的表格属性相同

         formatter(row, column, cellValue) {

           return cellValue === 1 ? '男' : '女'

         }

       },

       {

         label: '年龄',

         prop: 'age'

       }

     ]),

     data: [

       {

         name: '子君',

         sex: 1,

         age: 18

       }

     ]

   }

 }

}

</script>

通过上面的配置,就可以完成一个基础表格的开发,完整代码见 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/base.vue,效果如下图所示




表格默认会显示复选框,也可以通过配置selectable属性来关闭掉


添加分页

简单的表格用封装之后的或未封装的开发工作量区别并不大,我们继续为表格添加上分页


<template>

   <!--

   current-page.sync 表示页码, 添加上 .sync 在页码发生变化时自动同步页码

   page-size.sync 每页条数

   total  总条数

   height="auto" 配置height:auto, 表格高度会根据内容自动调整,如果不指定,表格将保持充满父容器,同时表头会固定,不跟随滚动条滚动

   @page-change 无论pageSize currentPage 哪一个变化,都会触发这个事件

 -->

 <zj-table

   v-loading="loading"

   :columns="columns"

   :data="data"

   :current-page.sync="currentPage"

   :page-size.sync="pageSize"

   :total="total"

   height="auto"

   @page-change="$_handlePageChange"

 />

</template>

<script>

export default {

 data() {

   return {

     columns: Object.freeze([

       // 列字段与上例一样,此处省略

     ]),

     data: [],

     // 当前页码

     currentPage: 1,

     // 每页条数

     pageSize: 10,

     // 总条数

     total: 0,

     // 是否显示loading

     loading: false

   }

 },

 created() {

   this.loadData()

 },

 methods: {

   // 加载表格数据

   loadData() {

     this.loading = true

     setTimeout(() => {

       // 假设总条数是40条

       this.total = 40

       const { currentPage, pageSize } = this

       // 模拟数据请求获取数据

       this.data = new Array(pageSize).fill({}).map((item, index) => {

         return {

           name: `子君${currentPage + (index + 1) * 10}`,

           sex: Math.random() > 0.5 ? 1 : 0,

           age: Math.floor(Math.random() * 100)

         }

       })

       this.loading = false

     }, 1000)

   },

   $_handlePageChange() {

     // 因为上面设置属性指定了.sync,所以这两个属性会自动变化

     console.log(this.pageSize, this.currentPage)

     // 分页发生变化,重新请求数据

     this.loadData()

   }

 }

}

</script>

完整代码请参考 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/pagination.vue


通过封装,表格将自带分页功能,通过上面代码,实现效果如下所示,是不是变得简单了一些。接下来我们继续给表格添加按钮




添加顶部按钮

表格上面可能会有新增,删除等等按钮,怎么办呢,接下来我们继续通过配置去添加按钮


<template>

 <zj-table

   :buttons="buttons"

 />

</template>

<script>

export default {

 data() {

   return {

     buttons: Object.freeze([

       {

         // id 必须有而且是在当前按钮数组里面是唯一的

         id: 'add',

         text: '新增',

         type: 'primary',

         icon: 'el-icon-circle-plus',

         click: this.$_handleAdd

       },

       {

         id: 'delete',

         text: '删除',

         // rows 是表格选中的行,如果没有选中行,则禁用删除按钮, disabled可以是一个boolean值或者函数

         disabled: rows => !rows.length,

         click: this.$_handleRemove

       },

       {

         id: 'auth',

         text: '这个按钮根据权限显示',

         // 可以通过返回 true/false来控制按钮是否显示

         before: (/** rows */) => {

           return true

         }

       },

       // 可以配置下拉按钮哦

       {

         id: 'dropdown',

         text: '下拉按钮',

         children: [

           {

             id: 'moveUp',

             text: '上移',

             icon: 'el-icon-arrow-up',

             click: () => {

               console.log('上移')

             }

           },

           {

             id: 'moveDown',

             text: '下移',

             icon: 'el-icon-arrow-down',

             disabled: rows => !rows.length,

             click: () => {

               console.log('下移')

             }

           }

         ]

       }

     ])

   }

 },

 created() {},

 methods: {

   // 新增

   $_handleAdd() {

     this.$alert('点击了新增按钮')

   },

   // 顶部按钮会自动将表格所选的行传出来

   $_handleRemove(rows) {

     const ids = rows.map(({ id }) => id)

     this.$alert(`要删除的行id为${ids.join(',')}`)

   },

   // 关注作者公众号

   $_handleFollowAuthor() {}

 }

}

</script>

表格顶部可以添加普通的按钮,也可以添加下拉按钮,同时还可以通过before来配置按钮是否显示,disabled来配置按钮是否禁用,上面完整代码见 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/button.vue


通过上面的代码就可以配置出下面的表格,是不是很简单呢?




表格顶部可以有按钮,行尾也是可以添加按钮的,一起来看看


行操作按钮

一般我们会将一些单行操作的按钮放在行尾,比如编辑,下载等按钮,那如何给行尾配置按钮呢?


<template>

 <zj-table

   :columns="columns"

 />

</template>

<script>

export default {

 data() {

   return {

     columns: Object.freeze([

       {

         // 可以指定列的宽度,与element-ui原生用法一致

         width: 220,

         label: '姓名',

         prop: 'name'

       },

       // 行编辑按钮,在表格末尾出现,自动锁定右侧

       {

         width: 180,

         label: '操作',

         // 通过 actions 指定行尾按钮

         actions: [

           {

             id: 'follow',

             text: '关注作者',

             click: this.$_handleFollowAuthor

           },

           {

             id: 'edit',

             text: '编辑',

             // 可以通过before控制按钮是否显示,比如下面年龄四十岁的才会显示编辑按钮

             before(row) {

               return row.age < 40

             },

             click: this.$_handleEdit

           },

           {

             id: 'delete',

             text: '删除',

             icon: 'el-icon-delete',

             disabled(row) {

               return row.sex === 0

             },

             // 为了拿到this,这里需要用箭头函数

             click: () => {

               this.$alert('女生被禁止删除了')

             }

           }

         ]

       }

     ])

   }

 },

 methods: {

   // 关注作者公众号

   $_handleFollowAuthor() {

           console.log('微信搜索【前端有的玩】,这是对小编最大的支持')

   },

   /**

    * row 这一行的数据

    */

   $_handleEdit(row, column) {

     this.$alert(`点击了姓名为【${row.name}】的行上的按钮`)

   }

 }

}

</script>

行操作按钮会被冻结到表格最右侧,不会跟随滚动条滚动而滚动,上面完整代码见, https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/button.vue


通过上面的代码就可以完成以下效果




最后再来一起看看行编辑


行编辑

比如上例,我希望点击行尾的编辑按钮的时候,可以直接在行上面编辑用户的姓名与性别,如何配置呢?


<template>

 <zj-table

   ref="table"

   :columns="columns"

   :data="data"

 />

</template>

<script>

export default {

 data() {

   return {

     columns: Object.freeze([

       {

         label: '姓名',

         prop: 'name',

         editable: true,

         field: {

           componentType: 'input',

           rules: [

             {

               required: true,

               message: '请输入姓名'

             }

           ]

         }

       },

       {

         label: '性别',

         prop: 'sex',

         // 格式化表格,与element-ui 的表格属性相同

         formatter(row, column, cellValue) {

           return cellValue === '1' ? '男' : '女'

         },

         editable: true,

         field: {

           componentType: 'select',

           options: [

             {

               label: '男',

               value: '1'

             },

             {

               label: '女',

               value: '0'

             }

           ]

         }

       },

       {

         label: '年龄',

         prop: 'age',

         editable: true,

         field: {

           componentType: 'number'

         }

       },

       {

         label: '操作',

         actions: [

           {

             id: 'edit',

             text: '编辑',

             // 如果当前行启用了编辑,则不显示编辑按钮

             before: row => {

               return !this.editIds.includes(row.id)

             },

             click: this.$_handleEdit

           },

           {

             id: 'save',

             text: '保存',

             // 如果当前行启用了编辑,则显示保存按钮

             before: row => {

               return this.editIds.includes(row.id)

             },

             click: this.$_handleSave

           }

         ]

       }

     ]),

     data: [

       {

         // 行编辑必须指定rowKey字段,默认是id,如果修改为其他字段,需要给表格指定row-key="字段名"

         id: '0',

         name: '子君',

         sex: '1',

         age: 18

       },

       {

         // 行编辑必须指定rowKey字段,默认是id,如果修改为其他字段,需要给表格指定row-key="字段名"

         id: '1',

         name: '子君1',

         sex: '0',

         age: 18

       }

     ],

     editIds: []

   }

 },

 methods: {

   $_handleEdit(row) {

     // 通过调用 startEditRow 可以开启行编辑

     this.$refs.table.startEditRow(row.id)

     // 记录开启了行编辑的id

     this.editIds.push(row.id)

   },

   $_handleSave(row) {

     // 点击保存的时候,通过endEditRow 结束行编辑

     this.$refs.table.endEditRow(row.id, (valid, result, oldRow) => {

       // 如果有表单验证,则valid会返回是否验证成功

       if (valid) {

         console.log('修改之后的数据', result)

         console.log('原始数据', oldRow)

         const index = this.editIds.findIndex(item => item === row.id)

         this.editIds.splice(index, 1)

       } else {

         // 如果校验失败,则返回校验的第一个输入框的异常信息

         console.log(result)

         this.$message.error(result.message)

       }

     })

   }

 }

}

</script>

不需要使用插槽就可以完成行编辑,是不是很开心。上述完整代码见 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/row-edit.vue


效果如下图所示:




其他功能

除了上面的功能之外,表格还可以配置其他许多功能,比如


可以指定字段为链接列,需要给列配置link属性

可以通过插槽自定义顶部按钮,行操作按钮,行字段等

可以在按钮区域右侧通过插槽配置其他内容

其他等等

表格开发说明

通过上面的代码示例,我们已经知道了封装之后的表格可以完成哪些事情,接下来一起来看看表格是如何实现的。完整代码见 https://github.com/snowzijun/vue-element-table/tree/master/src/components/zj-table


表格布局

整个表格是通过JSX来封装的,因为JSX使用起来更加灵活。对于我们封装的表格,我们从竖向可以分为三部分,分别是顶部按钮区,中间表格区,底部分页区,如何去实现三个区域的布局呢,核心代码如下


render(h) {

   // 按钮区域

   const toolbar = this.$_renderToolbar(h)

   // 表格区域

   const table = this.$_renderTable(h)

   // 分页区域

   const page = this.$_renderPage(h)


   return (

     <div class="zj-table" style={{ height: this.tableContainerHeight }}>

       {toolbar}

       {table}

       {page}

     </div>

   )

 }

通过三个render函数分别渲染对应区域,然后将三个区域组合在一起。


渲染表格列

通过前文的讲解,我们可以将表格的列分为以下几种


常规列

行编辑列

操作按钮列

插槽列

链接列(文档后续完善)

嵌套列(文档后续完善)

   $_renderColumns(h, columns) {

     // 整体是否排序

     let sortable = this.sortable ? 'custom' : false

     return columns

       .filter(column => {

         const { hidden } = column

         if (hidden !== undefined) {

           if (typeof hidden === 'function') {

             return hidden({

               columns,

               column

             })

           }

           return hidden

         }

         return true

       })

       .map(column => {

         const {

           useSlot = false,

           // 如果存在操作按钮,则actions为非空数组

           actions = [],

           // 是否可编辑列, 对于可编辑列需要动态启用编辑

           editable = false,

           // 是否有嵌套列

           nests,

           // 是否可点击

           link = false

         } = column

         let newSortable = sortable

         if (column.sortable !== undefined) {

           newSortable = column.sortable ? 'custom' : false

         }

         column = {

           ...column,

           sortable: newSortable

         }

         if (nests && nests.length) {

           // 使用嵌套列

           return this.$_renderNestColumn(h, column)

         } else if (editable) {

           // 使用编辑列

           return this.$_renderEditColumn(h, column)

         } else if (useSlot) {

           // 使用插槽列

           return this.$_renderSlotColumn(h, column)

         } else if (actions && actions.length > 0) {

           // 使用操作列

           column.sortable = false

           return this.$_renderActionColumn(h, column)

         } else if (link) {

           // 使用链接列

           return this.$_renderLinkColumn(h, column)

         } else {

           // 使用默认列

           return this.$_renderDefaultColumn(h, column)

         }

       })

   },

行编辑列

当前表格行编辑支持input,select,datepicker,TimeSelect,InputNumber等组件,具体渲染代码如下所示


// 编辑单元格

   $_renderEditCell(h, field) {

     const components = {

       input: Input,

       select: ZjSelect,

       date: DatePicker,

       time: TimeSelect,

       number: InputNumber

     }

     const componentType = field.componentType

     const component = components[componentType]

     if (component) {

       return this.$_renderField(h, field, component)

     } else if (componentType === 'custom') {

       // 如果自定义,可以通过component指定组件

       return this.$_renderField(h, field, field.component)

     }

     return this.$_renderField(h, field, Input)

   },

   $_renderField(h, field, Component) {

     // 编辑行的id字段

     const { rowId, events = {}, nativeEvents = {} } = field


     const getEvents = events => {

       const newEvents = {}

       Object.keys(events).forEach(key => {

         const event = events[key]

         newEvents[key] = (...rest) => {

           const args = [

             ...rest,

             {

               rowId,

               row: this.editRowsData[rowId],

               value: this.editRowsData[rowId][field.prop]

             }

           ]

           return event(...args)

         }

       })

       return newEvents

     }

     // 事件改写

     const newEvents = getEvents(events)

     const newNativeEvents = getEvents(nativeEvents)

     return (

       <Component

         size="small"

         on={newEvents}

         nativeOn={newNativeEvents}

         v-model={this.editRowsData[rowId][field.prop]}

         {...{

           attrs: field,

           props: field

         }}

       />

     )

   }

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务


UI 配色方法

涛涛

配色,设计师的世纪难题。从平面到屏幕,CMYK 到 RGB,墨点到像素,色彩越来越丰富,形式越来越复杂。UI 的发展从拟物的繁琐细节中挣脱出来,却在色彩的展现中放飞了自我。

零售业有个有趣的研究成果 —— 「七秒钟定律」:人们在挑选商品和服务时 ,只需要 7 秒钟就可以确定是否感兴趣,而在这短暂的 7 秒钟内,色彩的作用占到了 67%。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

要在小小的手机屏幕中加入这么多颜色,并保持其中的联系和逻辑,着实不易。如果你还对配色一无所知,完全不知道配色应该怎么入手,那么你需要了解一下,我几年经验总结的配色思路。

拾色器中的黄金三分法

无论我们用 PS、AI,还是 Sketch、XD、Figma,和色彩打交道最多的地方就是拾色器窗口,我们来看看这些案例:

10年经验的资深设计师,总结了这份 UI 配色终极奥义

虽然是不同的应用,但是这个拾色器的用法大同小异,但是,很多新人并没有搞懂拾色器的正确应用逻辑。

很多人知道,UI 的色彩使用 RGB 模式,但是拾色器主要的选色原理遵循的是 HSB 模式的逻辑,也就是色相(H)、饱和度(S)、明度(B)。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

HSB 是色彩科学中对所有颜色属性的理论划分,是种概念上的定义,可以用来解释任何色彩,也就是说可以和 RGB 和 CMYK 相互转化,且 HSB 的选色逻辑更清晰、简洁、干练。

因为一个正确的选色过程,是先确定出色相,然后再在这个色相维度下选出明度和饱和度,所以我们首先要关注色相选择条。

细心的同学应该已经发现了,它们的首尾都是红色,那是因为色相的序列是一个首尾相接的环形模式,所以它实际上就是色环的柱状展示图,应用起来和色环没有实际区别。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

接下来就要说到重点,饱和度和明度选择区,我自己使用的习惯,是将这个选择区通过黄金三分法的方式切割成等比的 9 个区域,然后明确它们在 UI 中的对应情绪和应用场景。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

在过去的大量分析中,互联网产品的主色和重要辅助色都会往右上角聚集,一些次要、不可点的色彩聚集在中上方,而文字背景色则聚集在左侧,无人区则是我们重点避开的对象。

下面我们分析几个案例,看看它们在这个选择区中的色彩分布情况:

10年经验的资深设计师,总结了这份 UI 配色终极奥义

10年经验的资深设计师,总结了这份 UI 配色终极奥义

10年经验的资深设计师,总结了这份 UI 配色终极奥义

大家也可以自己拿一些主流的应用做截图,然后把它们的 UI 元素色彩排列到拾色面板中,就会得到基本一致的结果。牢记这9个区域的场景划分,可以帮助我们非常、安全地完成 UI 配色。

UI配色中的色彩选择

在众多的 UI 设计规范中,色彩部分的介绍,都必然包含三种类型,分别是:

  • 主色:应用的核心色彩,品牌色
  • 辅色:丰富页面视觉和传达效果的次要颜色
  • 中性:没有色相的文字、背景用色

1. 主色的选择

主色是一个应用的最核心的色彩,品牌的象征色,比如想到饿了么的蓝色、微信的绿色、京东的红色、淘宝的橙色。

确定主色,并没有大家想象的那么复杂,它的要点在于——你想让用户感受到哪种情绪,然后通过情绪关联一个大致的色彩范围,再进行微调。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

在今天的互联网产品中,主色的应用选择范围在拾色器区域的右上角,前面已经有解释了。这和平面设计中的用色有非常大的不同。

移动端产品要在一个方寸大的空间中争夺用户的注意力,引起用户的兴趣,那么低饱和清淡的色调是无法实现这个目标的,所以今天主色饱和度越来越,比如我们之前整理的一篇总结:

为什么支付宝要换 Logo 颜色?分析下目前 Logo 的主色趋势

再加上屏幕的 RGB 显示特性,高对比度,高动态范围的特质能给用户提供更好的观感。所以选择主色最安全的做法,就是在确定色相类型后,在右上方区域选出合适的色值。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

2. 辅助色的选择

辅助色是丰富应用中的次要色彩,它会包含一到若干个和主色不同的色彩,除了品牌传达外,具有更强的实用性。

前面我们提到过色环,这里就要派上用场了。我们知道色环是个色彩序列首尾相连的环形模型,它蕴含一个最朴素的原则,即两个颜色在这个环形中角度越大,那么视觉差异性越大,对比越强,比如下图的展示:

10年经验的资深设计师,总结了这份 UI 配色终极奥义

这些配色的模式是不能闭着眼随便挑的,它们仅仅作为一个色彩对比度的判断标准。而真正辅助色的选择,是根据实际场景的功能决定的。

比如通知、提醒、取消用大红色,确认、同意用绿色或者蓝色,收藏、打分、评价用橙黄色。都是已经在用户心智中建立了标准的用色类型,跟着常规方法来做,是没有其它思路的情况下最简单、最安全的辅助色选择方式。

没有标准元素用色的情况下,再考虑应用色环的 「角度原则」,越需要被突出的颜色,可以在色环中离主色越远,越不需要被突出的则越近。

比如下方携程的案例,主色在蓝色的情况下,支付、保险金标签这些需要被重点突出的色彩,使用了主色的互补色, 让我们一眼就能看见并产生强烈的操作欲望。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

3. 中性色的选择

中性色,是页面中文字、背景用到的颜色,它们承担起最基本的层次表现、便于阅读的重任。多数新手觉得中性色无关紧要,实际情况恰恰相反。

主色辅助色决定了界面视觉是否出彩,而中性色的应用直接决定了页面能不能正常使用。如果看过比较多的原型案例,就应该明白,即使只有黑白灰的状态下,我们理解这些页面和进行使用也不会有丝毫的障碍。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

中性色的配置,就是制定一个由深到浅的灰度阶梯,应用在对应权重的元素上,色彩轻重的主要判断依据是 HSB 中的 B(明度) 值。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

中性色虽然指的是无个性,但并不是只能用纯灰色,常见的一种做法,就是为中性色添加适量的蓝色饱和度,提升观看体验(满足RGB的某种特性)。

这种做法,颜色越浅的时候饱和度应用色值就越低,将这个规律在拾色器中进行表现,那么我们就可以得到一个 L 型曲线,我称它为 「中性曲线」。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

掌握对于主色、辅助色、中性色的选择方法,那么对于UI配色的奥义来说,你已经掌握了一半,接下来就要了解更具体的实践思路了。

配色方式的四象限

配色并不是只有色彩的色值本身,大家一直在研究各种色彩心理学和理论,却很少关心它们如何应用,如何配置。

所以,我根据主色和辅助色应用总结了一个配色的四象限表格,再分别看看它们对应的案例:

10年经验的资深设计师,总结了这份 UI 配色终极奥义

1. 主色占比大,色彩丰富度高

主色会作为顶部标题栏或其它重要模块中的背景色,进行大面积应用,加深用户对品牌的认知和辨识度。而产品中又包含了大量功能和服务,需要用丰富的色彩来进行暗示,吸引用户关注。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

10年经验的资深设计师,总结了这份 UI 配色终极奥义

2. 主色占比大,色彩丰富度低

这类配色中,主色的应用占比也大,出现频率高,鲜有其它颜色出现。比较适用于图片内容丰富的题材中,或者是相对正式、品牌感强的应用。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

10年经验的资深设计师,总结了这份 UI 配色终极奥义

3. 主色占比小,色彩丰富度高

这是多数主流应用的趋势,降低主色占比,留出更多的空间给内容模块的展示上,突出自身带有的服务和功能。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

10年经验的资深设计师,总结了这份 UI 配色终极奥义

4. 主色占比小,色彩丰富度低

通常,会应用这种方式是因为产品的服务是相对单一,但也需要用户投入注意力的应用,设计师就会尽力避免给予用户过多的干扰。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

10年经验的资深设计师,总结了这份 UI 配色终极奥义

每次在进行配色的时候,我们都需要对自己要应用哪种配色应用方式做出规划,然后再动手执行。有了这个目标,后面在整个项目的设计中的配色步骤就是水到渠成的事情了。

配色流程演示

在实践前,我们要简单讲讲一个应用或者界面的标准配色的流程了,流程顺序如下:

10年经验的资深设计师,总结了这份 UI 配色终极奥义

具体应该怎么使用这套流程,我们用一个图虫APP改版的案例来做演示,首先从四象限中的第一个主色占比高、色彩丰富度高的类型做起。

1. 配色流程演示

原型是 UI 设计的基本艺能了,在开始具体设计、配色前,搭建页面的框架原型是一个必备的条件,下面,是我们已经准备好的原型案例。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

然后,我们确定以橙色作为应用主色,并在拾色器中进行确认。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

有了主色,就可以对页面进行色彩填充和图片填充了。既然我们主色是占比大的,那么首先可以用到的就是顶部标题栏的背景色了,以及底部 Tabbar 中的选中色,大按钮色等。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

接着,我们开始整理中性色的使用,选择新的颜色来填充文字和背景,清晰的表现模块层级,文字信息的权重。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

最后,就是添加辅助色和其它色彩的元素了,这个步骤建议都是放在最后一步操作。因为色彩越丰富,越难控制,容易让整个画面显得杂乱无序,所以先完成基础搭建,可以更好的帮助我们判断彩色的使用是否合理。

下面,我们使用彩色的金刚区图标,然后将用户关注、认证用户、标签等元素使用其它色彩,来丰富页面的色彩内容。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

2. 其他配色类型应用

根据第一个方案,我们可以再用这个原型来实现其余的三个方案的配色。

比如下面的主色占比大,但是色彩丰富度低的。因为已经不太适用其它辅助色,所以主色填充上我们不再填充顶部导航栏的背景,而是将更多元素应用主色,减少辅助色数量。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

然后是主色占比小,色彩丰富度高的方案,进一步降低主色应用的比例,然后在金刚区、标签等处使用较为丰富的配色。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

最后,就是主色占比小,色彩丰富性也低的方案,那么使用中性色的元素就开始增多,只保留最核心的一些元素使用主色,和极少的辅助色。

10年经验的资深设计师,总结了这份 UI 配色终极奥义

根据四种不同的配色方案,我们就可以得到四种不同的配色结果和风格,在每次设计开始实施前,我们都可以根据这种做法来做尝试,并选出自己满意的方案。

要再次强调,UI 配色是极其强调形式的应用科学,最后做的往往会和一开始想的效果有极大出入,所以需要我们有几个备选方案,可以随时进行调整,并选出合理的那个。

总结

以上是我们关于配色有关知识点的分享,希望可以帮助大家提升对 UI 配色的认识。

文章来源:优设    作者:超人的电话亭

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

让你的 commit 更有价值

seo达人

提交规范

AngularJS 在开发者文档中关于 git commit 的指导说明,提到严格的 git commit 格式规范可以在浏览项目历史的过程中看到更易读的信息,并且能用 git commit 的信息直接生成 AngularJS 的 change log 。


commit messages 格式规范

commit messages 由 header 、body 、footer 组成。


header 又包含 type 、scope 、subject 。header 是必需的,不过其中的 scope 是可选的。


body 和 footer 可以省略。


<type>(<scope>): <subject>

// 空行

<BLANK LINE>

<body>

// 空行

<BLANK LINE>

<footer>

注:为了能在 github 以及各种 git 工具中看得更清晰,commit messages 的每一行都不要超过 100 个字符。

Header

Type

类型必须是以下几种之一:


feat: 新功能

fix: bug 修复

docs: 仅修改文档

style: 修改格式(空格,格式化,省略分号等),对代码运行没有影响

refactor: 重构(既不是修 bug ,也不是加功能)

build: 构建流程、外部依赖变更,比如升级 npm 包、修改 webpack 配置等

perf: 性能优化

test: 测试相关

chore: 对构建过程或辅助工具和库(如文档生成)的更改

ci: ci 相关的更改

除此之外,还有一个特殊的类型 revert ,如果当前提交是为了撤销之前的某次提交,应该用 revert 开头,后面加上被撤销的提交的 header,在 body 中应该注明: This reverts commit <hash>. ,hash 指的就是将要被撤销的 commit SHA 。


// 例如


revert: feat(user): add user type


This reverts commit ca16a365467e17915f0273392f4a13331b17617d.

Scope

scope 可以指定提交更改的影响范围,这个视项目而定,当修改影响超过单个的 scope 时,可以指定为 * 。


Sbuject

subject 是指更改的简洁描述,长度约定在 50 个字符以内,通常遵循以下几个规范:


用动词开头,第一人称现在时表述,例如:change 代替 changed 或 changes

第一个字母小写

结尾不加句号(.)

Body

body 部分是对本地 commit 的详细描述,可以分成多行。


跟 subject 类似,用动词开头,第一人称现在时表述,例如:change 代替 changed 或 changes。


body 应该说明修改的原因和更改前后的行为对比。


Footer

footer 基本用在这两种情况:


不兼容的改动( Breaking Changes ),通常用 BREAKING CHANGE: 开头,后面跟一个空格或两个换行符。剩余的部分就是用来说明这个变动的信息和迁移方法等。

关闭 Issue, github 关闭 Issue 的例子

// BREAKING CHANGE: 的例子

BREAKING CHANGE: isolate scope bindings definition has changed and

   the inject option for the directive controller injection was removed.


   To migrate the code follow the example below:


   Before:


   scope: {

     myAttr: 'attribute',

     myBind: 'bind',

     myExpression: 'expression',

     myEval: 'evaluate',

     myAccessor: 'accessor'

   }


   After:


   scope: {

     myAttr: '@',

     myBind: '@',

     myExpression: '&',

     // myEval - usually not useful, but in cases where the expression is assignable, you can use '='

     myAccessor: '=' // in directive's template change myAccessor() to myAccessor

   }


   The removed `inject` wasn't generaly useful for directives so there should be no code using it.




// Closes Issue 例子

Closes #2314, #3421

完整的例子

例一: feat

feat($browser): onUrlChange event (popstate/hashchange/polling)


Added new event to $browser:

- forward popstate event if available

- forward hashchange event if popstate not available

- do polling when neither popstate nor hashchange available


Breaks $browser.onHashChange, which was removed (use onUrlChange instead)

例二: fix

fix($compile): couple of unit tests for IE9


Older IEs serialize html uppercased, but IE9 does not...

Would be better to expect case insensitive, unfortunately jasmine does

not allow to user regexps for throw expectations.


Closes #392

Breaks foo.bar api, foo.baz should be used instead

例三: style

style($location): add couple of missing semi colons

查看更多例子

规范 commit message 的好处

首行就是简洁实用的关键信息,方便在 git history 中快速浏览

具有详实的 body 和 footer ,可以清晰的看出某次提交的目的和影响

可以通过 type 过滤出想要查找的信息,也可以通过关键字快速查找相关提交

可以直接从 commit 生成 change log

// 列举几个常用的 log 参数


// 输出 log 的首行

git log --pretty=oneline


// 只输出首行的 commit 信息。不包含 hash 和 合并信息等

git log --pretty=format:%s


// 查找有关“更新菜单配置项”的提交

git log --grep="更新菜单配置项"


// 打印出 chenfangxu 的提交

git log --author=chenfangxu


// 红色的短 hash,黄色的 ref , 绿色的相对时间

git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset'

用工具实现规范提交

上面介绍了规范提交的格式,如果让各位同学在 git commit 的时候严格按照上面的规范来写,首先心智是有负担的,得记住不同的类型到底是用来定义什么的,subject 怎么写,body 怎么写,footer 要不要写。其次,对人的规范大部分都是反人性的,所以很可能在过不了多久,就会有同学渐渐的不按照规范来写。靠意志力来控制自己严格按照规范来写是需要额外耗费一些精力的,把精力耗费在这种事情上面实在有些浪费。


用工具实现规范提交的方案,一种是在提交的时候就提示必填字段,另一种是在提交后校验字段是否符合规范。这两种在实际项目中都是很有必要的。


Commitizen

Zen-like commit messages for internet citizens. 嗯~~一种禅意

Commitizen 是一个帮助撰写规范 commit message 的工具。他有一个命令行工具 cz-cli,接下来会把使用 Commitizen 分成几个阶段来介绍。


体验 git cz

// 全局安装 Commitizen

npm install -g commitizen

你的仓库可能还不是对 Commitizen 友好的,此时运行 git cz 的效果跟 git commit 一样,也就是没有效果。 不过,可以执行 npx git-cz 来体验。


如果想直接运行 git cz 实现语义化的提交,可以根据 streamich/git-cz 文档中说的全局安装 git cz。


// 全局安装 git cz

npm install -g git-cz

除此之外还有一种更推荐的方式,就是让你的仓库对 Commitizen 友好。


Commitizen 友好

全局安装 Commitizen 后,用 cz-conventional-changelog 适配器来初始化你的项目


// 初始化 cz-conventional-changelog 适配器

commitizen init cz-conventional-changelog --save-dev --save-exact

上面的初始化做了三件事:


安装 cz-conventional-changelog 依赖

把依赖保存到 package.json 的 dependencies 或 devDependencies 中

在根目录的 package.json 中 添加如下所示的 config.commitizen

"config": {

   "commitizen": {

     "path": "./node_modules/cz-conventional-changelog"

   }

 }

或者,在项目根目录下新建一个 .czrc 文件,内容设置为


{

 "path": "cz-conventional-changelog"

}

现在运行 git cz 效果如下:




cz-customizable 自定义中文配置

通过上面的截图可以看到,提交的配置选项都是英文的,如果想改成中文的,可以使用 cz-customizable 适配器。


运行下面的命令,注意之前已经初始化过一次了,这次再初始化,需要加 --force 覆盖


npm install cz-customizable --save-dev


commitizen init cz-customizable --save-dev --save-exact --force

现在 package.json 中 config.commitizen 字段为:


"config": {

   "commitizen": {

     "path": "./node_modules/cz-customizable"

   }

 }

cz-customizable 文档中说明了查找配置文件的方式有三种,我们按照第一种,在项目根目录创建一个 .cz-config.js 的文件。按照给出的示例 cz-config-EXAMPLE.js 编写我们的 config。 commit-type 可以参考 conventional-commit-types 。


可以点击查看我配置好的文件 qiqihaobenben/commitizen-git/.cz-config.js ,里面中详细的注释。


commitlint 校验提交

Commitizen 文档中开始就介绍到,Commitizen 可以在触发 git commit 钩子之前就能给出提示,但是也明确表示提交时对 commit messages 的校验也是很有用的。毕竟即使用了 Commitzen,也是能绕过去,所以提交最后的校验很重要。


commitlint 可以检查 commit messages 是否符合常规提交格式,需要一份校验配置,推荐 @commitlint/config-conventional 。


npm i --save-dev @commitlint/config-conventional @commitlint/cli

在项目根目录创建 commitlint.config.js 文件并设置校验规则:


module.exports = {

 extends: ["@commitlint/config-conventional"],

 // rules 里面可以设置一些自定义的校验规则

 rules: {},

};

在项目中安装 husky ,并在项目根目录新建 husky.config.js 文件,加入以下设置:


// 安装 husky

npm install --save-dev husky



// husky.config.js 中加入以下代码

module.exports = {

 "hooks": {

   "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"

 }

}

注意:因为 @commitlint/config-conventional 校验规则遵循 Angular 的规范, 所以我们在用 cz-customizable 自定义中文配置时, 是按照给出的符合 Angular 规范的示例 cz-config-EXAMPLE.js 编写.cz-config.js 的。但是如果你自定义的 Commitizen 配置不符合 Angular 规范,可以使用 commitlint-config-cz 设置校验规则。(推荐还是按照 Angular 规范进行 cz-customizable 自定义配置)

// 安装 commitlint-config-cz

npm install commitlint-config-cz --save-dev



// commitlint.config.js 改为

module.exports = {

 extends: [

   'cz'

 ]

};

git commit 触发 git cz

在提交的时候,我们都习惯了 git commit ,虽然换成 git cz 不难,但是如果让开发者在 git commit 时无感知的触发 git cz 肯定是更好的,

而且也能避免不熟悉项目的人直接 git commit 提交一些不符合规范的信息。


我们可以在 husky.config.js 中设置:


"hooks": {

 "prepare-commit-msg": "exec < /dev/tty && git cz --hook || true",

}

注意: 在 window 系统,可能需要在 git base 中才能生效。

生成 CHANGELOG

standard-version

是一个使用 semver 和 conventional-commits 支持生成 CHANGELOG 进行版本控制的实用程序。

standard-version 不只是能生成 CHANGELOG , 还能根据 commit 的 type 来进行版本控制。


// 安装 standard-verison

npm i --save-dev standard-version


// 在 package.json 中的 scripts 加入 standard-version

{

 "scripts": {

   "release": "standard-version"

 }

}

示例项目

可以查看 commitizen-git ,里面归纳了快速配置 Commitizen 友好仓库的步骤。

差不多三五分钟就能搞定。


可以看一下配置完后,执行 git commit 的效果。




扩展

更复杂的自定义提示

cz-customizable 中自定义配置项通常情况是够用的,

commitlint 中校验的规则基本上也是够用的,但是会有比较硬核的开发者会觉得还是不够,还要更多。比如一些 prompt 更加自定义,

提交时询问的 question 添加更多的逻辑,比如可以把一些重要的字段校验提前到 Commitizen 中,或者添加更多自定义的校验。


如果真想这么干,那就去 fork 一份 cz-conventional-changelog 或者 cz-customizable 来改,

或者直接自己写一个 adapter。


Commitizen 友好徽章

如果把仓库配置成了对 Commitizen 友好的话,可以在 README.md 中加上这个小徽章

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档