首页

UI按钮设计发展史

蓝蓝设计的小编

当我们在网上购物,提交我们个人信息都需要用到按钮。网页UI设计的增长很快,风格似乎也是一个月一变。最近几年,我们经历过从文 本链接到拟物化设计再到扁平化瀑布流设计。导航是网页设计中最重要的元素,并且按钮是最重要的行为工具。

vue在ie9中的兼容问题

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

问题总结  https://github.com/vuejs-templates/webpack/issues/260

  1. 首先npm install --save babel-polyfill

  2. 然后在main.js中的最前面引入babel-polyfill

    import 'babel-polyfill'

  3. 在index.html 加入以下代码(非必须)

    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

  4. 在config中的webpack.base.conf.js中,修改编译配置


    
  1. entry:{
  2. app:['babel-polyfill','./src/main.js']
  3. }

当然,如果你只用到了 axios 对 promise进行兼容,可以只用 es6-promise

npm install es6-promise --save

在 main.js 中的最前面 引入

import 'es6-promise/auto' 
  • 以上配置,ie9兼容就完成了

那么,就有一个问题了,build之后的dist文件只有放在服务器上才能查看,但本地如何查看呢,参考一下配置

  1. 修改config文件夹中的index.js文件,将build对象中的打包路径,'/‘改为'./',由绝对路径改为相对路径,建议将sourceMap改为false,编译的时候会快一点

    build: { assetsPublicPath: './', productionSourceMap: false, },

  2. 修改完之后,重新 npm run build ,得到新的dist文件夹

  3. 然后进入dist文件夹

    cd dist

  4. 全局安装简易node服务器

    npm install http-server -g

  5. 启动简易node服务器

    http-server

  6. 出现如下图所示,就代表你的服务器启动成功了,那你也能在5000端口查看编译打包后的项目了,可以在ie浏览器中直接测试了

    这里写图片描述

  7. IE在处理日期的时候,不支持-支持/的日期方式 如 2017-01-01应该 


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


各家UI时代一览

蓝蓝设计的小编

前方山高水长,我们都在路上。

何为第一

蓝蓝设计的小编

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

蓝蓝写

在班级考试的时候,在同行竞标时,在工作业绩评定时,大家都想赢,争第一才有机会。很多时候,大家在为争这个第一而烦恼。总是保持第一很难,得到后又怕失去,心情易受此情绪化。享受赢的感觉很好,但如果第一才是赢,那输的人岂不是很多?

得第一一旦养成习惯,做起事来会追求完美,不怕苦不怕累,但是一旦有场合一定要输,使不想参与,觉得不公平。人生不可能每次都是考试第一,每场投标都中,每次比稿都只自己的最好,成败是常见的事情,直面输赢,是对心智的磨练。

爸爸对我说,不要每次考试都让我女儿考第一,这样是不对的,要把身体锻炼好,身体好了,成绩自然就好。

期中考试考前我激励女儿:要好好学习,这次还能不能再有进步?期末考试是很重要的。女儿说:不就是期末考试吗?有什么重要的?也不可能永远都进步。

我觉得他们说的话都有道理,以前每次投标我基本上是晚12点睡早五点起,睡中朦胧也会思考怎么做的更好,投完标基本上都会病一场,追求完美给自己压力太大,影响健康。

所以这次考试中只问女儿:今天身体怎么样?累不累?热不热?放松去考,考砸了,下次再努力,女儿很高兴,放松,觉得得到理解和关心。

何为第一?今日读到一段定义:所有的人都需要你的时候,你能够给他们带来所需要的,你就是第一。任何事情都有它的顺序,讲因缘,时节,果报,要一步一步来,时间未到,你想也没有,时间一到,你不想它也有。

德和得分别代表了内在和外在,它也代表了同一种东西的两个状态:能量和质量。

当内在大于外在时,能量就会转化为物质,于是财富会自动存在;当外在大于内在,也就是能量不够的时候,物质会自动消失,对应到生活当中就是出现健康、财富等外在的损失甚至灾难。

由此就能更好地理解我们常说的“厚德载物”是什么意思,物质就是钱、地位、房子、子孙、家庭、名利。

厚德=后得。

厚德厚物,薄德薄物,缺德缺物,无德无物。因为能量和质量永远要追求一种平衡。WechatIMG90.jpeg

遵守职业道德,做人的品德是基础,否则水里来火里去存不住物质财富,赢得第一意味着名利,意味着才华和能力能够给带来机会和财富,但是品德修养是更为重要的根本,决定财富持有的时间和量级。

去掉攀比的好胜心,把视角转向对内的反省和探索,目标不再定为得第一,而定为不断地完善自我,完善工作品质。甘当无私的绿叶,在别人需要时帮助别人,减少狭隘的利益判断,更宽胸怀的帮助别的公司建立设计驱动的价值。

有德的人,不为自己求安乐,但为众生得离苦,众生是一个很大的团队,如果由己及人,把团队扩大到同事,社区,同行,胸怀越来越大,人也就会变得越来越包容,平和。追求亦也不再是自己的第一,而是带领更多的人走向卓越,完善自我的修行之路。

WechatIMG91.jpg


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



ES6新特性Promise异步调用

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

Promise 的含义

Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件--更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了语法,原生提供了Promise

所谓Promise ,简单说就是一个容器,里面保存着某个未来才回结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。 
Promise 对象的状态不受外界影响

三种状态:

  • pending:进行中
  • fulfilled :已经成功
  • rejected 已经失败

状态改变: 
Promise对象的状态改变,只有两种可能:

  • 从pending变为fulfilled
  • 从pending变为rejected。

这两种情况只要发生,状态就凝固了,不会再变了,这时就称为resolved(已定型

基本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例


    
  1. const promist = new Promise(function(resolve,reject){
  2. if(/*异步操作成功*/){
  3. resolve(value);
  4. }else{
  5. reject(error);
  6. }
  7. })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; 
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise的源码分析:

1.回调地狱

曾几何时,我们的代码是这样的,为了拿到回调的结果,不得不callback hell,这种环环相扣的代码可以说是相当恶心了


            
  1. let fs = require('fs')
  2. fs.readFile('./a.txt','utf8',function(err,data){
  3. fs.readFile(data,'utf8',function(err,data){
  4. fs.readFile(data,'utf8',function(err,data){
  5. console.log(data)
  6. })
  7. })
  8. })

终于,我们的盖世英雄出现了,他身披金甲圣衣、驾着七彩祥云。好吧打岔儿了,没错他就是我们的Promise,那让我们来看看用了Promise之后,上面的代码会变成什么样吧


            
  1. let fs = require('fs')
  2. function read(url){
  3. return new Promise((resolve,reject)=>{
  4. fs.readFile(url,'utf8',function(error,data){
  5. error && reject(error)
  6. resolve(data)
  7. })
  8. })
  9. }
  10. read('./a.txt').then(data=>{
  11. return read(data)
  12. }).then(data=>{
  13. return read(data)
  14. }).then(data=>{
  15. console.log(data)
  16. })

2.重点开始,小眼睛都看过来

2.1 Promise/A+

首先我们要知道自己手写一个Promise,应该怎么去写,谁来告诉我们怎么写,需要遵循什么样的规则。当然这些你都不用担心,其实业界都是通过一个规则指标来生产Promise的。让我们来看看是什么东西。传送门☞Promise/A+

2.2 constructor

我们先声明一个类,叫做Promise,里面是构造函数。如果es6还有问题的可以去阮大大的博客上学习一下(传送门☞es6


            
  1. class Promise{
  2. constructor(executor){
  3. //控制状态,使用了一次之后,接下来的都不被使用
  4. this.status = 'pendding'
  5. this.value = undefined
  6. this.reason = undefined
  7. //定义resolve函数
  8. let resolve = (data)=>{
  9. //这里pendding,主要是为了防止executor中调用了两次resovle或reject方法,而我们只调用一次
  10. if(this.status==='pendding'){
  11. this.status = 'resolve'
  12. this.value = data
  13. }
  14. }
  15. //定义reject函数
  16. let reject = (data)=>{
  17. if(this.status==='pendding'){
  18. this.status = 'reject'
  19. this.reason = data
  20. }
  21. }
  22. //executor方法可能会抛出异常,需要捕获
  23. try{
  24. //将resolve和reject函数给使用者
  25. executor(resolve,reject)
  26. }catch(e){
  27. //如果在函数中抛出异常则将它注入reject中
  28. reject(e)
  29. }
  30. }
  31. }

那么接下来我会分析上面代码的作用,原理

  • executor:这是实例Promise对象时在构造器中传入的参数,一般是一个function(resolve,reject){}
  • status:``Promise的状态,一开始是默认的pendding状态,每当调用道resolve和reject方法时,就会改变其值,在后面的then方法中会用到
  • value:resolve回调成功后,调用resolve方法里面的参数值
  • reason:reject回调成功后,调用reject方法里面的参数值
  • resolve:声明resolve方法在构造器内,通过传入的executor方法传入其中,用以给使用者回调
  • reject:声明reject方法在构造器内,通过传入的executor方法传入其中,用以给使用者回调

2.3 then

then方法是Promise中最为重要的方法,他的用法大家都应该已经知道,就是将Promise中的resolve或者reject的结果拿到,那么我们就能知道这里的then方法需要两个参数,成功回调和失败回调,上代码!


            
  1. then(onFufilled,onRejected){
  2. if(this.status === 'resolve'){
  3. onFufilled(this.value)
  4. }
  5. if(this.status === 'reject'){
  6. onRejected(this.reason)
  7. }
  8. }

这里主要做了将构造器中resolve和reject的结果传入onFufilledonRejected中,注意这两个是使用者传入的参数,是个方法。所以你以为这么简单就完了?要想更Swag的应对各种场景,我们必须得再完善。继续往下走!

3.异步的Promise

之前我们只是处理了同步情况下的Promise,简而言之所有操作都没有异步的成分在内。那么如果是异步该怎么办?

3.1 callback!!!!

最早处理异步的方法就是callback,就相当于我让你帮我扫地,我会在给你发起任务时给你一个手机,之后我做自己的事情去,不用等你,等你扫完地就会打手机给我,诶,我就知道了地扫完了。这个手机就是callback,回调函数。

首先我们需要改一下构造器里的代码,分别添加两个回调函数的数组,分别对应成功回调和失败回调。他们的作用是当成功执行resolve或reject时,执行callback。


            
  1. //存放成功回调的函数
  2. this.onResolvedCallbacks = []
  3. //存放失败回调的函数
  4. this.onRejectedCallbacks = []
  5. let resolve = (data)=>{
  6. if(this.status==='pendding'){
  7. this.status = 'resolve'
  8. this.value = data
  9. //监听回调函数
  10. this.onResolvedCallbacks.forEach(fn=>fn())
  11. }
  12. }
  13. let reject = (data)=>{
  14. if(this.status==='pendding'){
  15. this.status = 'reject'
  16. this.reason = data
  17. this.onRejectedCallbacks.forEach(fn=>fn())
  18. }
  19. }

然后是then需要多加一个状态判断,当Promise中是异步操作时,需要在我们之前定义的回调函数数组中添加一个回调函数。


            
  1. if(this.status === 'pendding'){
  2. this.onResolvedCallbacks.push(()=>{
  3. // to do....
  4. let x = onFufilled(this.value)
  5. resolvePromise(promise2,x,resolve,reject)
  6. })
  7. this.onRejectedCallbacks.push(()=>{
  8. let x = onRejected(this.reason)
  9. resolvePromise(promise2,x,resolve,reject)
  10. })
  11. }

ok!大功告成,异步已经解决了

3.2 resolvePromise

这也是Promise中的重头戏,我来介绍一下,我们在用Promise的时候可能会发现,当then函数中return了一个值,我们可以继续then下去,不过是什么值,都能在下一个then中获取,还有,当我们不在then中放入参数,例:promise.then().then(),那么其后面的then依旧可以得到之前then返回的值,可能你现在想很迷惑。让我来解开你心中的忧愁,follow me


            
  1. then(onFufilled,onRejected){
  2. //解决onFufilled,onRejected没有传值的问题
  3. onFufilled = typeof onFufilled === 'function'?onFufilled:y=>y
  4. //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后then的resolve中捕获
  5. onRejected = typeof onRejected === 'function'?onRejected:err=>{ throw err ;}
  6. //声明一个promise对象
  7. let promise2
  8. if(this.status === 'resolve'){
  9. //因为在.then之后又是一个promise对象,所以这里肯定要返回一个promise对象
  10. promise2 = new Promise((resolve,reject)=>{
  11. setTimeout(()=>{
  12. //因为穿透值的缘故,在默认的跑出一个error后,不能再用下一个的reject来接受,只能通过try,catch
  13. try{
  14. //因为有的时候需要判断then中的方法是否返回一个promise对象,所以需要判断
  15. //如果返回值为promise对象,则需要取出结果当作promise2的resolve结果
  16. //如果不是,直接作为promise2的resolve结果
  17. let x = onFufilled(this.value)
  18. //抽离出一个公共方法来判断他们是否为promise对象
  19. resolvePromise(promise2,x,resolve,reject)
  20. }catch(e){
  21. reject(e)
  22. }
  23. },0)
  24. })
  25. }
  26. if(this.status === 'reject'){
  27. promise2 = new Promise((resolve,reject)=>{
  28. setTimeout(()=>{
  29. try{
  30. let x = onRejected(this.reason)
  31. resolvePromise(promise2,x,resolve,reject)
  32. }catch(e){
  33. reject(e)
  34. }
  35. },0)
  36. })
  37. }
  38. if(this.status === 'pendding'){
  39. promise2 = new Promise((resolve,reject)=>{
  40. this.onResolvedCallbacks.push(()=>{
  41. // to do....
  42. setTimeout(()=>{
  43. try{
  44. let x = onFufilled(this.value)
  45. resolvePromise(promise2,x,resolve,reject)
  46. }catch(e){
  47. reject(e)
  48. }
  49. },0)
  50. })
  51. this.onRejectedCallbacks.push(()=>{
  52. setTimeout(()=>{
  53. try{
  54. let x = onRejected(this.reason)
  55. resolvePromise(promise2,x,resolve,reject)
  56. }catch(e){
  57. reject(e)
  58. }
  59. })
  60. })
  61. })
  62. }
  63. return promise2
  64. }

一下子多了很多方法,不用怕,我会一一解释

  1. 返回Promise?:首先我们要注意的一点是,then有返回值,then了之后还能在then,那就说明之前的then返回的必然是个Promise
  2. 为什么外面要包一层setTimeout?:因为Promise本身是一个异步方法,属于微任务一列,必须得在执行栈执行完了在去取他的值,所以所有的返回值都得包一层异步setTimeout。
  3. 为什么开头有两个判断?:这就是之前想要解决的如果then函数中的参数不是函数,那么我们需要做处理。如果onFufilled不是函数,就需要自定义个函数用来返回之前resolve的值,如果onRejected不是函数,自定义个函数抛出异常。这里会有个小坑,如果这里不抛出异常,会在下一个then的onFufilled中拿到值。又因为这里抛出了异常所以所有的onFufilled或者onRejected都需要try/catch,这也是Promise/A+的规范。当然本人觉得成功的回调不需要抛出异常也可以,大家可以仔细想想。
  4. resolvePromise是什么?:这其实是官方Promise/A+的需求。因为你的then可以返回任何职,当然包括Promise对象,而如果是Promise对象,我们就需要将他拆解,直到它不是一个Promise对象,取其中的值。

那就让我们来看看这个resolvePromise到底长啥样。


            
  1. function resolvePromise(promise2,x,resolve,reject){
  2. //判断x和promise2之间的关系
  3. //因为promise2是上一个promise.then后的返回结果,所以如果相同,会导致下面的.then会是同一个promise2,一直都是,没有尽头
  4. if(x === promise2){//相当于promise.then之后return了自己,因为then会等待return后的promise,导致自己等待自己,一直处于等待
  5. return reject(new TypeError('循环引用'))
  6. }
  7. //如果x不是null,是对象或者方法
  8. if(x !== null && (typeof x === 'object' || typeof x === 'function')){
  9. //为了判断resolve过的就不用再reject了,(比如有reject和resolve的时候)
  10. let called
  11. try{//防止then出现异常,Object.defineProperty
  12. let then = x.then//取x的then方法可能会取到{then:{}},并没有执行
  13. if(typeof then === 'function'){
  14. //我们就认为他是promise,call他,因为then方法中的this来自自己的promise对象
  15. then.call(x,y=>{//第一个参数是将x这个promise方法作为this指向,后两个参数分别为成功失败回调
  16. if(called) return;
  17. called = true
  18. //因为可能promise中还有promise,所以需要递归
  19. resolvePromise(promise2,y,resolve,reject)
  20. },err=>{
  21. if(called) return;
  22. called = true
  23. //一次错误就直接返回
  24. reject(err)
  25. })
  26. }else{
  27. //如果是个普通对象就直接返回resolve作为结果
  28. resolve(x)
  29. }
  30. }catch(e){
  31. if(called) return;
  32. called = true
  33. reject(e)
  34. }
  35. }else{
  36. //这里返回的是非函数,非对象的值,就直接放在promise2的resolve中作为结果
  37. resolve(x)
  38. }
  39. }

它的作用是用来将onFufilled的返回值进行判断取值处理,把最后获得的值放入最外面那层的Promise的resolve函数中。

  1. 参数promise2(then函数返回的Promise对象),x(onFufilled函数的返回值),resolve、reject(最外层的Promise上的resolve和reject)。
  2. 为什么要在一开始判断promise2x?:首先在Promise/A+中写了需要判断这两者如果相等,需要抛出异常,我就来解释一下为什么,如果这两者相等,我们可以看下下面的例子,第一次p2是p1.then出来的结果是个Promise对象,这个Promise对象在被创建的时候调用了resolvePromise(promise2,x,resolve,reject)函数,又因为x等于其本身,是个Promise,就需要then方法递归它,直到他不是Promise对象,但是x(p2)的结果还在等待,他却想执行自己的then方法,就会导致等待。

            
  1. let p1 = new Promise((resolve,reject)=>{
  2. resolve()
  3. })
  4. let p2 = p1.then(d=>{
  5. return p2
  6. })
  1. called是用来干嘛的?:called变量主要是用来判断如果resolvePromise函数已经resolve或者reject了,那就不需要在执行下面的resolce或者reject。
  2. 为什么取then这个属性?:因为我们需要去判断x是否为Promise,then属性如果为普通值,就直接resolve掉,如果是个function,就是Promise对象,之后我们就需要将这个x的then方法进行执行,用call的原因是因为then方法里面this指向的问题。
  3. 为什么要递归去调用resolvePromise函数?:相信细心的人已经发现了,我这里使用了递归调用法,首先这是Promise/A+中要求的,其次是业务场景的需求,当我们碰到那种Promise的resolve里的Promise的resolve里又包了一个Promise的话,就需要递归取值,直到x不是Promise对象。

4.完善Promise

我们现在已经基本完成了Promise的then方法,那么现在我们需要看看他的其他方法。

4.1 catch

相信大家都知道catch这个方法是用来捕获Promise中的reject的值,也就是相当于then方法中的onRejected回调函数,那么问题就解决了。我们来看代码。


            
  1. //catch方法
  2. catch(onRejected){
  3. return this.then(null,onRejected)
  4. }

该方法是挂在Promise原型上的方法。当我们调用catch传callback的时候,就相当于是调用了then方法。

4.2 resolve/reject

大家一定都看到过Promise.resolve()、Promise.reject()这两种用法,它们的作用其实就是返回一个Promise对象,我们来实现一下。


            
  1. //resolve方法
  2. Promise.resolve = function(val){
  3. return new Promise((resolve,reject)=>{
  4. resolve(val)
  5. })
  6. }
  7. //reject方法
  8. Promise.reject = function(val){
  9. return new Promise((resolve,reject)=>{
  10. reject(val)
  11. })
  12. }

这两个方法是直接可以通过class调用的,原理就是返回一个内部是resolve或reject的Promise对象。

4.3 all

all方法可以说是Promise中很常用的方法了,它的作用就是将一个数组的Promise对象放在其中,当全部resolve的时候就会执行then方法,当有一个reject的时候就会执行catch,并且他们的结果也是按着数组中的顺序来排放的,那么我们来实现一下。


            
  1. //all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
  2. Promise.all = function(promises){
  3. let arr = []
  4. let i = 0
  5. function processData(index,data){
  6. arr[index] = data
  7. i++
  8. if(i == promises.length){
  9. resolve(arr)
  10. }
  11. }
  12. return new Promise((resolve,reject)=>{
  13. for(let i=0;i<promises.length;i++){
  14. promises[i].then(data=>{
  15. processData(i,data)
  16. },reject)
  17. }
  18. })
  19. }

其原理就是将参数中的数组取出遍历,每当执行成功都会执行processData方法,processData方法就是用来记录每个Promise的值和它对应的下标,当执行的次数等于数组长度时就会执行resolve,把arr的值给then。这里会有一个坑,如果你是通过arr数组的长度来判断他是否应该resolve的话就会出错,为什么呢?因为js数组的特性,导致如果先出来的是1位置上的值进arr,那么0位置上也会多一个空的值,所以不合理。

4.4 race

race方法虽然不常用,但是在Promise方法中也是一个能用得上的方法,它的作用是将一个Promise数组放入race中,哪个先执行完,race就直接执行完,并从then中取值。我们来实现一下吧。


            
  1. //race方法
  2. Promise.race = function(promises){
  3. return new Promise((resolve,reject)=>{
  4. for(let i=0;i<promises.length;i++){
  5. promises[i].then(resolve,reject)
  6. }
  7. })
  8. }

原理大家应该看懂了,很简单,就是遍历数组执行Promise,如果有一个Promise执行成功就resolve。

Promise语法糖 deferred

语法糖这三个字大家一定很熟悉,作为一个很Swag的前端工程师,对async/await这对兄弟肯定很熟悉,没错他们就是generator的语法糖。而我们这里要讲的语法糖是Promise的。


            
  1. //promise语法糖 也用来测试
  2. Promise.deferred = Promise.defer = function(){
  3. let dfd = {}
  4. dfd.promise = new Promise((resolve,reject)=>{
  5. dfd.resolve = resolve
  6. dfd.reject = reject
  7. })
  8. return dfd
  9. }

什么作用呢?看下面代码你就知道了


            
  1. let fs = require('fs')
  2. let Promise = require('./promises')
  3. //Promise上的语法糖,为了防止嵌套,方便调用
  4. //坏处 错误处理不方便
  5. function read(){
  6. let defer = Promise.defer()
  7. fs.readFile('./1.txt','utf8',(err,data)=>{
  8. if(err)defer.reject(err)
  9. defer.resolve(data)
  10. })
  11. return defer.Promise
  12. }

没错,我们可以方便的去调用他语法糖defer中的Promise对象。那么它还有没有另外的方法呢?答案是有的。我们需要在全局上安装promises-aplus-tests插件npm i promises-aplus-tests -g,再输入promises-aplus-tests [js文件名] 即可验证你的Promise的规范。


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


我用这个极限推敲法,有效推进视觉设计

涛涛

每当提到设计方法论时,总难免会给人一种比较虚空偏理论,或者说比较难应用到设计实战中的感觉;但是这一次,经过了自己的实践之后,我觉得确实有一个比较靠谱的视觉推进方法可以分享给大家,所以才有了这篇总结。

先直接概括一下这个方法,极限推敲法:遇到难题时,先把问题拆解成一个或者几个维度,选取某个维度,往两个极端进行尝试,随后逐渐中和极端方案,逐步找到自己想要的预期点,明确该维度的方向。

一、使用方法

  • 第一步,提炼一个或者几个关键维度,作为尝试的方向;
  • 第二步,优先尝试两端极限的方案;
  • 第三步,进一步在其间琢磨并尝试方案,直到找到符合预期的方案;
  • 第四步,若一直没有满意的方案,则可以继续以某个方案为基准,精细微调方案进行探索;
  • 第五步,得出最终方向,并最终调整形成终稿。

二、案例

例一:雄狮的logo设计

期望比较简洁的同时,又不能丢了狮子的辨识度,比较纠结的是,这个度该如何把握?

按照极限推敲法来,我们先确立「极简」和「写实」作为关键维度,然后优先尝试两端比较极限的方案;了解清楚两种极限的可能性之后,继续尝试,加减细节,寻找一个符合预期的图形状态;最后的定稿,既保持了鲜明的特征和辨识度,同时也不失简洁。

(推进过程中出方案的顺序在图中用数字标出,「1」即为第一次尝试,以此类推;具体推敲过程不在此赘述。)

例二:网易邮箱大师Tab icon改版设计

在网易邮箱大师5.0改版中,常驻底部的 Tab icon 是单独进行推敲优化的。在之前的版本中,图标的样式相对比较的刻板,偏向纯表意,表现力不强;在5.0视觉改版的策略中,我们是以「优雅」作为一个关键方向进行优化的,所以轴的两端就定为「刻板」和「优雅」,虽然并不是完全对立的两个方向,同时也包含了一些「表意」等其他层面的因素,但是其中有部分感受是存在对立面的,我们就以此作为关键点进行推敲。

可以从图中看出,在不影响表意的前提下,各方案间的图标改动非常微小,在有限的空间内去做一些变化;几经尝试之后,还是选择了更常规更偏表意优先,但是也适当加入曲线因素体现「优雅」的方案作为最终方向。

例三:内容信息流列表设计

期望内容列表的展示能给人以精品的感觉,但又不失去过多的阅读效率。

首先确立以信息量的多少作为关键维度,在形成了初步的样式之后,继续调整在一屏内条目的疏密以及排版来感知信息量的多少;在尝试之后,最终讨论决定,在初期内容源还不够「精」的情况下,还是最右侧方案的信息呈现更为合适,并以此做了最终调整。

三、方法原理

很多时候,极端情况是很难想象或者预判出来的,只有尝试之后,才能更清楚的了解到实际会出来什么效果,达到怎样的预期;在多次推敲之后,就能更好的了解期望的状态到底应该在哪个「位置」,让模糊的概念逐渐变得清晰;该方法最大的好处在于,让尝试变的更有目标,而不是胡乱尝试,让每一次尝试都更有意义。

这个方法也算是视觉标准可量化的一次探索,我称之为「伪量化」,因为最后的决策仍然是依靠感官去衡量判断的;把错综复杂的感官拆解成一些更具体单一的关键维度也是非常关键的一个步骤,是开始「伪量化」的前提。

四、适用场景

在以上所举的例子中,有 logo设计,icon设计和界面设计(所用到的例子都是实战,均为过往项目);其实对象可以是各种各样的设计,只要存在某种程度把握上的纠结,无法预判或者不明确方向的情况,都可以通过这个方法有效的推进并找到当下的最优解。在实战中,假如没有那么多的时间和精力去细化,可以仅选取关键维度,然后在关键维度内尝试方向探索性的方案(粗略的尝试),只要能感受到差异,即是有效的方案;当然如果有充足的时间,那完全可以精细化出方案,这样方案所传达的感受也会更加精准细致。

五、误区

要注意的是,这个方法并不是一种妥协的方式,也不是一种择中方案;我们所要寻找的是在一个维度中感受符合预期的位置,如果适合,也可以是非常激进的方案。

六、注意事项

这个方法只是提供了一种思路,如何更有效的尝试方案来推进视觉产出的思路;其中最主要的环节还是靠自己去摸索出方案的实践部分,去实打实的尝试;在这其中投入精力的多少,也受其他客观因素的影响,例如项目截止时间等等;不过我认为个人的主观能动性才是主导,一定不能懒,要作图,要动起来,仅靠凭空想象方案在脑中「出图」,一来说服不了别人,二来自己也底气不足。只要图形图像语言一直存在,那么我觉得对视觉设计师来说有一句话就是永远的真理:没图说个XX。

智能汽车UI与手机UI的设计区别

蓝蓝设计的小编

随着特斯拉智能电动汽车凭借其全新的电动能源和智能化的大屏操作系统在全球的走红。在中国也引起了强烈的蝴蝶效应,小鹏汽车,蔚来汽车和威马汽车等主打互联网智能的汽车应运而生。在新的一年汽车UI可能会成为下一个设计热点。

么去控制浏览器对资源文件的处理行为

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

通常当用户打开一个资源的url,如果浏览器支持这个格式的文件的情况下,浏览器会尝试去再页面里展示它而不是直接下载。例如一张图片(jpg, png, gif等),几乎所有浏览器都会去将图片在浏览器里面展示。


对于压缩格式的文件(zip, tar, gzip等)浏览器总是会直接去下载它们,另外一些格式的文件,根据浏览器的不同也会有差异的处理方法,例如Microsoft Word文件(doc, docx)在ie浏览器下通常会在浏览器中展示,但是其他浏览器几乎都会直接下载它,同样的对于PDF文件chrome有自己的pdf 转换器它会尝试去在浏览器上展示该文件。

强制下载文件

对于浏览器这种行为,一般情况下是可以接受的,因为用户可以在浏览器显示文件后将文件保存到电脑中,但是一些情况下用户可能希望文件直接被下载而不是在浏览器中被打开,比如有时候用户想去下载一个歌曲,但是浏览器可能回去播放该音频。那么怎么让浏览器强制去下载文件要怎么做呢

a标签的download属性

html5中 a 标签新增了 download 属性,该属性指示浏览器下载url的内容而不是导航到url,因此如果配置了此属性用户会直接下载url的内容。作为开发如果想直接触发该事件我们可以直接用代码模拟a标签和点击事件


    
  1. const link = document.createElement('a');
  2. link.addEventListener('click', function() {
  3. link.download = xxx;
  4. link.href = xxx;
  5. });
  6. const e = document.createEvent('MouseEvents');
  7. e.initEvent('click', false, false);
  8. link.dispatchEvent(e);
  • download属性只在同域时候有效,当跨域请求时候该属性将会被忽略。
  • 当前并非所以浏览器都支持该属性,需要浏览器考虑兼容性。

改变资源格式

浏览器会根据资源类型去判断是否支持,如果支持时会尝试去在页面上展示该资源。浏览器判断资源类型是根据返回头Content-Type的值,因此一方面我们在服务端可以设置返回头字段为文件流'Content-Type': 'application/octet-stream',或者根据一些具体资源直接压缩后传输,浏览器不能分析zip之类的压缩文件所以会直接去下载它们。

配置Content-Disposition

在HTTP场景中,Content-Disposition 消息头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。

  • inline 默认参数表示消息体会以页面的一部分或者整个页面的形式展示。
  • attachment 消息体应该被下载到本地,将参数filename的值预填为下载后的文件名

因此当我们希望该资源被直接下载时候,我们可以设置返回头Content-Disposition的值为attachment


    
  1. //example
  2. Content-Disposition: attachment; filename="fname.ext"

这里设置名称时候,要注意下filename的编码格式。

用户自己在浏览器设置

浏览器既然定义了该行为,根据浏览器的不同用户可能在设置页面去配置某些格式的文件是否希望浏览器去展示该文件。一些有争议的情况下,你也可以提示用户自己根据需求去设置这些参数。


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


设计师如何洞察需求本质,做出一稿过的设计?

涛涛

本文主要从需求类型、表现形式、如何做正确设计来分析,设计师应如何洞察需求本质?如何做正确的设计?

前言:设计师的痛

有这样两种情况在设计师工作中经常会遇到:业务方改来改去,经过几十遍的修改最终选了第一稿;一群非专业的人或领导在设计师身边指来指去,出发点都是「我觉得」主观感受,没一个能提出有点含金量的意见。

  • 客户或业务反复改
  • 众人指点江山

实际工作里我们经常遇到业务方一直在针对几个问题要求设计师在不停修改 :颜色、大小、氛围等。但是却很少看到设计师跟业务方沟通设计结果出现的场景?是为了达到什么目标?导致设计师一直处于不停「改改改」的苦逼状态。

本篇文章内容为:设计师如何洞察需求本质?如何做正确的设计?

一、设计对接的需求来源类型

设计师在工作中接触到的需求一般分为三类:业务需求、用户需求、产品功能需求。

1. 业务需求

业务需求是指产品和公司为了实现商业目标产生的需求。不以结婚为目的的谈恋爱就是耍流氓,不以赚钱为目的的产品也是耍流氓,说来说去最终目的都是为了实现商业价值。

业务需求多数来源老板、公司战略、运营策略等,一般围绕如何提升公司销售额度、如何提升用户量、如何提升利润、如何提升用户转化、如何推广运营等等。

2. 用户需求

用户需求是指通过调研或客服投诉搜集到的用户反馈,一般来源于用户在使用产品过程中的需求。在 kano模型中对用户需求一般分为:基本需求、期望需求、魅力需求、无差异需求、逆向需求。针对这些需求公司都会结合发展战略与优先级去分析,可分为必做、应做、可做、不做。

3. 产品功能需求

基于业务和用户价值进行需求分析的结果,为满足业务和用户提出的一组产品功能列表,功能需求构成了一个完整的产品抽象模型,是团队之间进行产品设计研发的基础。

二、三类需求常见的表现形式

1. 业务需求常见形式

这有个万能的公式:销售额=流量x客单价x转化率

拿某某购物中心举个例子:销售额就是我们听到的某某购物中心在五一期间卖了xxx亿的额度,流量就是五一期间有多少顾客进来了这个购物中心,客单价就是额度除以购买人数,转化率是订单除以访客。在同等访客量的情况下订单越低,转化率越低。

这个公式换到互联网产品中也一样,多少用户进入 app,有多少人产生购买行为,以及每个人购买的单价,这些因素都是决定这个产品最终的销售额。

业务需求是流量增长

在产品发展初期,面临的第一个问题就是访问量,也就是产品的曝光率,用户到达率。我们在生活中会经常看到一些公司请一些自带流量的网红、明星来做宣传为公司带来一些流量。在互联网产品中常见的一些流量增长的策划为拉新奖励、高流量为低流量导流、免费效应等。

拉新奖励:推荐有奖、注册有奖。

导流:高流量为低流量导流。

免费:0元购、秒杀等。

业务需求是活跃度

产品活跃度是指用户每天开启 app 的频次,每天停留的时间。不同属性的产品活跃度的规律也不一定,比如:新闻类、理财类、工具类的产品,针对不同用户、不同属性的产品提升活跃度的方式不一样。

比较常见的提升活跃度的策略是:奖励刺激、有价值的内容吸引、通过成长等级来引起用户参与。

奖励:补贴、答题奖励、分享得积分、返现金、签到、打卡奖励。

内容:推送激活、话题讨论、焦点&热点内容。

荣誉感:排行榜、等级。

业务需求是用户留存

经过运营渠道推广流量提升后,倒入一些新用户,用户经过一段时间体验后是否留存关键在于:用户需求被满足、以及体验流程流畅决定的。当用户发现产品对自己没有价值、体验没有好感就会选择离开。

比较常见的留存策略是:翻倍奖励、产品体验迭代优化、评分、用户成长体系等。

业务需求是品牌感知

用户对同类产品的选择很大程度取决于产品品牌的影响力,业务方在推广中也会有针对品牌提升感知力。

比较常见的有:h5的情感推广、公益活动等。

网易的h5活动在2017年经常刷爆全屏,他们主打的是情感系列,深入人心,不仅提升主品牌影响,也提升了用户对网易哒哒的产品感知。

小结:业务需求设计要点

  • 用户感知:在设计中对设计表现要处理好文案层级关系/视觉氛围吸引度/提升用户注意力。
  • 效果目标:要考虑业务需求的目标是什么,通过什么形式(创意/用户记忆/内容价值)达到拉新、留存、流量的目的。
  • 数据推导:通过数据可以让我们理性分析出用户关注点是什么?
2. 用户需求常见形式

对于用户提出的一些功能的需求,设计师不能盲目听从,需要真正去识别有用的用户、有价值的需求。我们可以通过一些调研、观察,分析用户的特征、习惯、场景和行为,以及期望得到什么效果,挖掘用户的内在动机和原因,只有搞明白了用户需求的本质,才有可能做出用户满意的产品。

KANO模型定义了五类需求,我主要针对下面3类经常遇见的说下,也可以分为3个步骤去做:基本需求 — 期望需求 — 魅力需求。

用户基本需求

指用户认为产品必须要有的属性或者功能,比如:购买流程的完整体验、完整注册流程,这些基本需求一般来源产品初期迭代中的优化完善。

用户期望需求

用户在主流程体验过程中增加一些利于自己快捷、便利的需求,这些功能并不影响主流程体验,比如:购买过程中对收藏、历史价格对比、搜索等,这些期望需求可以转化成一些潜在的竞争优势。

例如:下图用户需求搜集中用户集中在对历史价位走势、提醒等功能的一些期望需求上。

△ 根据用户期望需求调整的设计构思

用户兴奋魅力需求

魅力需求是每一个产品追寻的方向,给用户带来一些超越他们预期的体验。可以理解为在满足用户的基本需求、期望需求的基础上给用户创造附加价值体验,属于创新设计思维范围,可以提升好感及忠诚度。比如:京东推出vr口红试妆体验,天猫推出试装直播等。

小结:对用户需求设计要点

  • 用户集体共性体验:在主流程上识别有价值用户群体,角色化的用户的共同特征,符合用户基本功能流畅、无障碍体验。
  • 体验体验场景维度设计:对用户使用的场景考虑,设计符合用户的习惯的操作。比如:课堂播放,用户使用场景是碎片时间:通勤、吃饭、健身等场景下,用户对产品期望是储存、收听便捷的期望体验。
  • 用户情感维度设计:超越用户预期,带来惊喜、共情。
3. 产品功能需求

注册流程简化

大量注册资料让用户失去耐心,流程简化提升新用户流量。

漏斗数据下的流程转化

在公司xx流程里,我们看到每个页面的流失漏斗,发现用户在购买决策「结果分析」那一步流失特别多,决定砍掉了介绍步骤,把之前5步改成三步决策。

这是一个非常妙的设计:对用户来说,直接触达决策前的结果分析,用户对结果的强感知转化下一步操作,从数据来看这一步转化率提高不少。

△ 公司实际创新项目不做露出具体页面

留存

新用户进入 app 后,一般分为3个时期:振荡期间、选择期、稳定期。震荡期用户流失较多,经过一些市场对比后选择留下的用户会进入一个稳定期。一个用户在产品内留存越久,带来的商业价值越高。

在产品体验流程中根据不同需求,策略也有不同。举一个 ofo 退押金例子:ofo 在不久前遭遇了资金问题,我在退押金这一流程体验中误操作了多次,最主要问题就是信息引导。

△ 主流程中对用户再三挽留

△ 主流程操作中弱化强需求信息

小结:对功能需求设计要点

  • 统一、体验:减少用户页面跳转,能1步完成不要放到3、4步完成。
  • 信息明确:在主流程中给用户传达正确信息关系,减少误操作。
  • 数据验证可用性价值:通过数据观察用户行为,验证需求产生的价值。

四、如何从需求达到设计目标

1. 需求到设计目标的过程

在需求发起到设计目标会经过这样几个过程:罗列与信息搜集➝深入挖掘➝聚焦核心➝创新发散➝实现与落地。

这五步的节奏是:先发散 – 聚拢 – 再发散 – 聚拢。

罗列与信息搜集

  • 需求罗列:根据佐藤可士和整理术,把需求按照类别、优先级进行。
  • 用户调研:定性于定量调研结果得到用户真正的诉求点。
  • 数据结果:通过数据结果能观察用户的行为特征。

深入挖掘

5w:what、when、wher、who、why这五步定位法,是让设计师清楚需求的本质与动机。

聚焦核心:滤 — 炼 — 导

结合用研结果、数据结果、定位结果去除不重要、不必要的信息、提炼有价值的信息、导出核心关键。

创新发散

我们发现前三步是基于一些基础方法论,让设计师具备一些观察用户、分析用户行为、洞悉本质、同理心的训练,目的是让我们把思维聚焦在核心的关键点上,而真正超越预期体验的来源于第4步创新设计思维。

  • 创新设计思维:通过发散寻找针对聚焦点不一样的方案的突破口,很多创新思维来源于多人思维火花的碰撞。
  • 移情设计:在发散后结合用户情感的体验,打造深入人心、愉悦的体验感受。
  • 技术可行性。

实现与落地

  • 草图沟通;
  • 开发—测试;
  • 上线—迭代—优化。

总结:设计的道与术

  • 道:设计创新、价值观、愿景。
  • 术:思维方式、工作流程、洞察力、观察、分析。

对于设计师,解决问题能力远高于单纯作图能力,而了解需求本质是解决问题的前提条件,在没有明确需求的动机和目的,设计方向是迷茫的。洞察、分析是设计师的基本能力,勤于思考分析,做正确有价值的设计。

互联网行业几点意见

蓝蓝设计的小编

在这个国家领导人都在强调打造一个网络强国的时代,互联网行业无疑是大学毕业生入职最好的选择。互联网很大,很多传统行业都在向互联网转型,如果抓住这个机遇,学习一技之能,那么你就将拥有撬动地球的杠杆。

日历

链接

个人资料

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

存档