React是Facebook開發的一款JS庫,那么Facebook為什么要建造React呢,主要為了解決什么問題,通過這個又是如何解決的?
從這幾個問題出發我就在網上搜查了一下,有這樣的解釋。
Facebook認為MVC無法滿足他們的擴展需求,由于他們非常巨大的代碼庫和龐大的組織,使得MVC很快變得非常復復雜,每當需要添加一項新的功能或特性時,系統的復雜度就成級數增長,致使代碼變得脆弱和不可預測,結果導致他們的MVC正在土崩瓦解。認為MVC不適合大規模應用,當系統中有很多的模型和相應的視圖時,其復雜度就會迅速擴大,非常難以理解和調試,特別是模型和視圖間可能存在的雙向數據流動。
解決這個問題需要“以某種方式組織代碼,使其更加可預測”,這通過他們(Facebook)提出的Flux和React已經完成。
Flux
是一個系統架構,用于推進應用中的數據單向流動。React
是一個JavaScript框架,用于構建“可預期的”和“聲明式的”Web用戶界面,它已經使Facebook更快地開發Web應用
對于Flux,目前還沒怎么研究,不怎么懂,這里就先把Flux的圖放上來,有興趣或者了解的可以再分享下,這里主要說下React。
那么React是解決什么問題的,在官網可以找到這樣一句話:
We built React to solve one problem: building large applications with data that changes over time.
構建那些數據會隨時間改變的大型應用,做這些,React有兩個主要的特點:
另外在React官網上,通過《Why did we build React?》為什么我們要建造React的文檔中還可以了解到以下四點:
Virtual DOM 虛擬DOM
傳統的web應用,操作DOM一般是直接更新操作的,但是我們知道DOM更新通常是比較昂貴的。而React為了盡可能減少對DOM的操作,提供了一種不同的而又強大的方式來更新DOM,代替直接的DOM操作。就是Virtual DOM
,一個輕量級的虛擬的DOM,就是React抽象出來的一個對象,描述dom應該什么樣子的,應該如何呈現。通過這個Virtual DOM去更新真實的DOM,由這個Virtual DOM管理真實DOM的更新。
為什么通過這多一層的Virtual DOM操作就能更快呢? 這是因為React有個diff算法,更新Virtual DOM并不保證馬上影響真實的DOM,React會等到事件循環結束,然后利用這個diff算法,通過當前新的dom表述與之前的作比較,計算出最小的步驟更新真實的DOM。
component 的使用在 React 里極為重要, 因為 components 的存在讓計算 DOM diff 更。
State 和 Render
React是如何呈現真實的DOM,如何渲染組件,什么時候渲染,怎么同步更新的,這就需要簡單了解下State和Render了。state屬性包含定義組件所需要的一些數據,當數據發生變化時,將會調用Render重現渲染,這里只能通過提供的setState方法更新數據。
好了,說了這么多,下面看寫代碼吧,先看一個官網上提供的Hello World
的示例:
<!DOCTYPE html> <html> <head> <script src="http://fb.me/react-0.12.1.js"></script> <script src="http://fb.me/JSXTransformer-0.12.1.js"></script> </head> <body> <div id="example"></div> <script type="text/jsx"> React.render( <h1>Hello, world!</h1>,
document.getElementById('example')
); </script> </body> </html>
這個很簡單,瀏覽器訪問,可以看到Hello, world!
字樣。JSXTransformer.js
是支持解析JSX語法的,JSX是可以在Javascript中寫html代碼的一種語法。如果不喜歡,React也提供原生Javascript的方法。
再來看下另外一個例子:
<html> <head> <title>Hello React</title> <script src="http://fb.me/react-0.12.1.js"></script> <script src="http://fb.me/JSXTransformer-0.12.1.js"></script> <script src="http://code.jquery.com/jquery-1.10.0.min.js"></script> <script src="http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script> <style> #content{ width: 800px; margin: 0 auto; padding: 5px 10px; background-color:#eee; } .commentBox h1{ background-color: #bbb; } .commentList{ border: 1px solid yellow; padding:10px; } .commentList .comment{ border: 1px solid #bbb; padding-left: 10px; margin-bottom:10px; } .commentList .commentAuthor{ font-size: 20px; } .commentForm{ margin-top: 20px; border: 1px solid red; padding:10px; } .commentForm textarea{ width:100%; height:50px; margin:10px 0 10px 2px; } </style> </head> <body> <div id="content"></div> <script type="text/jsx"> var staticData = [ {author: "張飛", text: "我在寫一條評論~!"}, {author: "關羽", text: "2貨,都知道你在寫的是一條評論。。"}, {author: "劉備", text: "哎,咋跟這倆逗逼結拜了!"} ]; var converter = new Showdown.converter();//markdown /** 組件結構: <CommentBox> <CommentList> <Comment /> </CommentList> <CommentForm /> </CommentBox> */ //評論內容組件 var Comment = React.createClass({ render: function (){ var rawMarkup = converter.makeHtml(this.props.children.toString()); return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author}: </h2> <span dangerouslySetInnerHTML={{__html: rawMarkup}} /> </div> ); } }); //評論列表組件 var CommentList = React.createClass({ render: function (){ var commentNodes = this.props.data.map(function (comment){ return ( <Comment author={comment.author}> {comment.text} </Comment> ); }); return ( <div className="commentList"> {commentNodes} </div> ); } }); //評論表單組件 var CommentForm = React.createClass({ handleSubmit: function (e){ e.preventDefault(); var author = this.refs.author.getDOMNode().value.trim(); var text = this.refs.text.getDOMNode().value.trim(); if(!author || !text){ return; } this.props.onCommentSubmit({author: author, text: text}); this.refs.author.getDOMNode().value = ''; this.refs.text.getDOMNode().value = ''; return; }, render: function (){ return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" ref="author" /><br/> <textarea type="text" placeholder="Say something..." ref="text" ></textarea><br/> <input type="submit" value="Post" /> </form> ); } }); //評論塊組件 var CommentBox = React.createClass({ loadCommentsFromServer: function (){ this.setState({data: staticData}); /* 方便起見,這里就不走服務端了,可以自己嘗試 $.ajax({ url: this.props.url + "?_t=" + new Date().valueOf(), dataType: 'json', success: function (data){ this.setState({data: data}); }.bind(this), error: function (xhr, status, err){ console.error(this.props.url, status, err.toString()); }.bind(this) }); */ }, handleCommentSubmit: function (comment){ //TODO: submit to the server and refresh the list var comments = this.state.data; var newComments = comments.concat([comment]); //這里也不向后端提交了 staticData = newComments; this.setState({data: newComments}); }, //初始化 相當于構造函數 getInitialState: function (){ return {data: []}; }, //組件添加的時候運行 componentDidMount: function (){ this.loadCommentsFromServer(); this.interval = setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, //組件刪除的時候運行 componentWillUnmount: function() { clearInterval(this.interval); }, //調用setState或者父級組件重新渲染不同的props時才會重新調用 render: function (){ return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data}/> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); } }); //當前目錄需要有comments.json文件 //這里定義屬性,如url、pollInterval,包含在props屬性中 React.render( <CommentBox url="comments.json" pollInterval="2000" />, document.getElementById("content") ); </script> </body> </html>
乍一看挺多,主要看腳本部分就可以了。方便起見,這里都沒有走后端。定義了一個全局的變量staticData
,可權當是走服務端,通過瀏覽器的控制臺改變staticData
的值,查看下效果,提交一條評論,查看下staticData的值的變化。
國外應用的較多,facebook、Yahoo、Reddit等。在github可以看到一個列表Sites-Using-React,國內的話,查了查,貌似比較少,目前知道的有一個杭州大搜車。大多技術要在國內應用起來一般是較慢的,不過React確實感覺比較特殊,特別是UI的組件化和Virtual DOM的思想,我個人比較看好,有興趣繼續研究研究。
和其他一些js框架相比,React怎樣,比如Backbone、Angular等。
與傳統PC桌面不同,手機屏幕的尺寸更加小巧操作,方式也已觸控為主,APP界面設計不但要保證APP功能的完整性和合理性,又要保證APP的功能性和實用性,在保證其擁有流暢的操作感受的同時,滿足人們的審美需求。
接下來為大家介紹幾款手機appui界面設計
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
--手機appUI設計--
(以上圖片均來源于網絡)
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
更多精彩文章:
閉包是一個讓初級JavaScript
使用者既熟悉又陌生的一個概念。因為閉包在我們書寫JavaScript
代碼時,隨處可見,但是我們又不知道哪里用了閉包。
關于閉包的定義,網上(書上)的解釋總是千奇百怪,我們也只能“取其精華去其糟粕”去總結一下。
ECMAScript中,閉包指的是:
從實踐角度:一下才算是閉包:
閉包跟詞法作用域,作用域鏈,執行上下文這幾個JavaScript
中重要的概念都有關系,因此要想真的理解閉包,至少要對那幾個概念不陌生。
閉包的優點:
閉包的缺點:
我們來一步一步引出閉包。
自執行函數也叫立即調用函數(IIFE),是一個在定義時就執行的函數。
var a=1;
(function() { console.log(a)
})()
上述代碼是一個最簡單的自執行函數。
在ES6之前,是沒有塊級作用域的,只有全局作用域和函數作用域,因此自執行函數還能在ES6之前實現塊級作用域。
// ES6 塊級作用域 var a = 1; if(true) { let a=111; console.log(a); // 111 } console.log(a); // 1
這里 if{} 中用let聲明了一個 a。這個 a 就具有塊級作用域,在這個 {} 中訪問 a ,永遠訪問的都是 let 聲明的a,跟全局作用域中的a沒有關系。如果我們把 let 換成 var ,就會污染全局變量 a 。
如果用自執行函數來實現:
var a = 1;
(function() { if(true) { var a=111; console.log(a); // 111 }
})() console.log(a); // 1
為什么要在這里要引入自執行函數的概念呢?因為通常我們會用自執行函數來創建閉包,實現一定的效果。
來看一個基本上面試提問題:
for(var i=0;i<5;i++) {
setTimeout(function() { console.log(i);
},1000)
}
在理想狀態下我們期望輸出的是 0 ,1 ,2 ,3 ,4。但是實際上輸出的是5 ,5 ,5 ,5 ,5。為什么是這樣呢?其實這里不僅僅涉及到作用域,作用域鏈還涉及到Event Loop、微任務、宏任務。但是在這里不講這些。
下面我們先解釋它為什么會輸出 5個5,然后再用自執行函數來修改它,以達到我們預期的結果。
提示:for 循環中,每一次的都聲明一個同名變量,下一個變量的值為上一次循環執行完同名變量的值。
首先用var聲明變量 for 是不會產生塊級作用域的,所以在 () 中聲明的 i 為全局變量。相當于:
// 偽代碼 var i; for(i=0;i<5;i++) {
setTimeout(function() { console.log(i);
},1000)
}
setTimeout中的第一個參數為一個全局的匿名函數。相當于:
// 偽代碼 var i; var f = function() { console.log(i);
} for(i=0;i<5;i++) {
setTimeout(f,1000)
}
由于setTimeout是在1秒之后執行的,這個時候for循環已經執行完畢,此時的全局變量 i 已經變成了 5 。1秒后5個setTimeout中的匿名函數會同時執行,也就是5個 f 函數執行。這個時候 f 函數使用的變量 i 根據作用域鏈的查找規則找到了全局作用域中的 i 。因此會輸出 5 個5。
那我們怎樣來修改它呢?
for(var i=0;i<5;i++) {
(function (){ setTimeout(function() { console.log(i);
},1000)
})();
}
上述例子會輸出我們期望的值嗎?答案是否。為什么呢?我們雖然把 setTimeout 包裹在一個匿名函數中了,但是當setTimeout中匿名函數執行時,首先去匿名函數中查找 i 的值,找不到還是會找到全局作用域中,最終 i 的值仍然是全局變量中的 i ,仍然為 5個5.
那我們把外層的匿名函數中聲明一個變量 j 讓setTimeout中的匿名函數訪問這個 j 不就找不到全局變量中的變量了嗎。
for(var i=0;i<5;i++) {
(function (){ var j = i;
setTimeout(function() { console.log(j);
},1000)
})();
}
這個時候才達到了我們預期的結果:0 1 2 3 4。
我們來優化一下:
for(var i=0;i<5;i++) {
(function (i){ setTimeout(function() { console.log(i);
},1000)
})(i);
}
*思路2:用 let 聲明變量,產生塊級作用域。
for(let i=0;i<5;i++) {
setTimeout(function() { console.log(i);
},1000)
}
這時for循環5次,產生 5 個塊級作用域,也會聲明 5 個具有塊級作用域的變量 i ,因此setTimeout中的匿名函數每次執行時,訪問的 i 都是當前塊級作用域中的變量 i 。
什么是理論中的閉包?就是看似像閉包,其實并不是閉包。它只是類似于閉包。
function foo() { var a=2; function bar() { console.log(a); // 2 }
bar();
}
foo();
上述代碼根據最上面我們對閉包的定義,它并不完全是閉包,雖然是一個函數可以訪問另一個函數中的變量,但是被嵌套的函數是在當前詞法作用域中被調用的。
我們怎樣把上述代碼foo 函數中的bar函數,在它所在的詞法作用域外執行呢?
下面的代碼就清晰的展示了閉包:
function foo() { var a=2; function bar() { console.log(a);
} return bar;
} var baz=foo();
baz(); // 2 —— 朋友,這就是閉包的效果。
上述代碼中 bar 被當做 foo函數返回值。foo函數執行后把返回值也就是 bar函數 賦值給了全局變量 baz。當 baz 執行時,實際上也就是 bar 函數的執行。我們知道 foo 函數在執行后,foo 的內部作用域會被銷毀,因為引擎有垃圾回收期來釋放不再使用的內存空間。所以在bar函數執行時,實際上foo函數內部的作用域已經不存在了,理應來說 bar函數 內部再訪問 a 變量時是找不到的。但是閉包的神奇之處就在這里。由于 bar 是在 foo 作用域中被聲明的,所以 bar函數 會一直保存著對 foo 作用域的引用。這時就形成了閉包。
我們先看個例子:
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope;
} return f;
} var foo = checkscope();
foo();
我們用偽代碼來解釋JavaScript
引擎在執行上述代碼時的步驟:
JavaScript
引擎遇到可執行代碼時,就會進入一個執行上下文(環境)
但是我們想一個問題,checkscope函數執行完畢,它的執行上下文從棧中彈出,也就是銷毀了不存在了,f 函數還能訪問包裹函數的作用域中的變量(scope)嗎?答案是可以。
理由是在第6步,我們說過當checkscope 執行函數執行完畢時,它的執行上下文會從棧中彈出,此時活動對象也會被回收,按理說當 f 在訪問checkscope的活動對象時是訪問不到的。
其實這里還有個概念,叫做作用域鏈:當 checkscope 函數被創建時,會創建對應的作用域鏈,里面值存放著包裹它的作用域對應執行上下文的變量對象,在這里只是全局執行上下文的變量對象,當checkscope執行時,此時的作用域鏈變化了 ,里面存放的是變量對象(活動對象)的集合,最頂端是當前函數的執行上下文的活動對象。端是全局執行上下文的變量對象。類似于:
checkscope.scopeChain = [
checkscope.AO
global.VO
]
當checkscope執行碰到了 f 函數的創建,因此 f 函數也會創建對應的作用域鏈,默認以包裹它的函數執行時對應的作用域鏈為基礎。因此此時 f 函數創建時的作用域鏈如下:
checkscope.scopeChain = [
checkscope.AO
global.VO
]
當 f 函數執行時,此時的作用域鏈變化如下:
checkscope.scopeChain = [
f.AO
checkscope.AO
global.VO
]
當checkscope函數執行完畢,內部作用域會被回收,但是 f函數 的作用域鏈還是存在的,里面存放著 checkscope函數的活動對象,因此在f函數執行時會從作用域鏈中查找內部使用的 scope 標識符,從而在作用域鏈的第二位找到了,也就是在 checkscope.AO 找到了變量scope的值。
正是因為JavaScript
做到了這一點,因此才會有閉包的概念。還有人說閉包并不是為了擁有它采取設計它的,而是設計作用域鏈時的副作用產物。
閉包是JavaScript
中最難的點,也是平常面試中常問的問題,我們必須要真正的去理解它,如果只靠死記硬背是經不起考驗的。
你知道嗎?視力,聽力和行動能力完全健康的人,可以輕松地讀寫,可以有效執行多任務,并且始終可以正常工作的人約占總人口的50%?其余的人都是戴著眼鏡或有色盲,手腕或耳朵受傷,生活在嘈雜的環境中或網絡信號質量差,忙碌或忙碌中,閱讀障礙或有注意力障礙等。
這意味著大約一半的用戶可能很難使用我們的產品或瀏覽我們的網站。因此,我們可能錯過了提高用戶滿意度并擴大受眾范圍的機會。
不過在設計階段實施一些簡單的原則就可以改善交互和整體用戶體驗,極限設計可以為所有人帶來價值,我們稱之為“包容性設計”。
什么是包容性設計?包容性設計考慮了盡可能多的人的需求和能力,而不僅僅是針對殘疾人。它認識到我們的需求會隨著時間和環境的變化而變化,因此它會預測錯誤,掙扎和不同的交互方式。它的目的是在問題發生之前解決問題,提高標準并改變良好產品設計的標準。
包容的用戶界面是善解人意,有意識且可訪問的。年齡,性別,教育程度,財富和能力等不同特征,在不同環境中生活或工作,獲得技術水平不同的不同人群可以舒適地使用它。
我們將使用POUR作為在用戶與界面之間創建簡單,輕松,快速交互的參考。
POUR代表
可以理解:數字內容可以輕松地以不同方式進行解釋或處理嗎?
可操作:數字產品能否輕松自如地進行功能和控制?
可以理解:用戶可以理解界面的功能和內部信息嗎?
健壯性:數字產品是否與不同的輔助技術和設備兼容?
設計師如何提供幫助
作為設計師,我們當然不能控制以上所有要求都能做到。但是我們應該承認,人們遇到的許多可訪問性問題是由設計階段未做過的決定引起的。因此,設計師有很多機會可以有所作為。僅通過做出更明智的設計決策,我們就可以影響(改進或協助)四種經驗。
視覺體驗:這包括形狀,顏色,對比,文本樣式-產品界面的所有圖形元素。
聽覺體驗:這是指與產品互動時產生的聲音,音量和清晰度。
認知經驗:這描述了用戶花費在解釋界面上的時間,以及使用界面需要多少注意力和精力。
運動體驗:這包括執行任務或與產品交互所需的所有動作和動作。
通常,可訪問性被認為是對創造力的挑戰;但是,如果我們認為這是一個創造性的挑戰,那么我們會開辟全新的可能性領域。真正好的可訪問性的訣竅不是在功能或功能上進行折衷,也不是在美學上取舍,而是使功能和創意體驗也可以訪問。
改善視覺體驗
1.顏色
對比度對比度是亮度或顏色的差異,使物體從周圍環境中脫穎而出,并可能對清晰度產生顯著影響。高對比度使視覺元素從背景中脫穎而出,更加引人注目。
專家提示:純粹的#000000黑白色會給眼睛帶來強烈的對比度,甚至會影響閱讀障礙者。這就是為什么我們傾向于避免使用它,而是選擇深灰色的原因。
亮度
亮度描述從光源發出的照明水平或從表面反射的光量。明亮的顏色反射更多的光線,并會干擾我們閱讀和處理信息的能力。
避免在背景或較大表面上使用鮮艷的顏色。請勿在文本上或文本附近使用鮮艷的顏色,以免干擾文本。如果品牌要求特定的高亮度顏色,請嘗試建議使用飽和或較深的顏色。如果你絕對必須使用明亮的顏色,則應將其用于突出顯示動作的方法最小化,并將其與較深的色相搭配以達到平衡和高對比度。
專家提示:任何含有超過50%黃色的顏色都會自然反射更多的光。這意味著黃色,橙色,綠色和藍綠色是高風險顏色,應謹慎使用。
色盲
色盲是無法區分特定顏色(通常是紅色和綠色,偶爾是藍色)的一種,它比你想象的要常見。
專家提示:不要僅僅依靠顏色;顏色不應該是傳達重要信息的唯一方法。您可以執行以下操作:
使用下劃線表示鏈接和斜體,或使用粗體突出顯示文本
將圖標與文本一起使用可傳達成功或失敗的信息
使用紋理或圖案作為圖表
為按鈕或明顯的通知使用清晰的視覺樣式,針對焦點或活動狀態使用不同的字體樣式
2.版式
字體選擇
通信是每個數字產品的首要目標,可以借助印刷術及其正確應用來實現。內容應清晰易讀,這意味著易于識別和解釋,輕松閱讀和處理。
簡潔明了對于快速閱讀和解釋至關重要,請避免使用復雜的字體,因為它們只會增加視覺干擾。選擇正確的字體家族,針對那些具有清晰定義和獨特形狀的字符,因為視力障礙或閱讀障礙的人可能會因某些字符或其組合而感到困惑。
字體樣式
字體樣式還會影響弱視或閱讀障礙者的閱讀性能。我們應該注意并謹慎使用字體樣式(如斜體,下劃線和大寫)的頻率和位置。
根據“英國閱讀障礙協會”的規定,應避免使用斜體,特別是對于較大的副本塊或較小的字體。這是因為它們使字母傾斜,顯得更加尖銳,因此更難以閱讀。
正文也應避免使用帶下劃線的字體樣式。給長的段落加下劃線會增加視覺噪音,從而降低閱讀性能,而給短的句子或單詞加下劃線會與活動鏈接相關聯,并可能引起混亂。粗體是添加對比度和強調的更好選擇。
盡管沒有確鑿的研究,但有一些證據支持也應避免主要針對正文使用大寫字母。似乎所有大寫字母的統一外觀會降低單詞形狀的對比度,從而使掃描變得不那么容易。此外,大寫看起來有點緊張,可能感覺好像有人在向您大喊大叫。
專家提示:平衡是關鍵。謹慎使用每個樣式并賦予其含義甚至可以提高可讀性。
字體大小
您知道絕大多數人戴眼鏡或隱形眼鏡嗎?實際上,十分之六以上!此外,約有62%的人通過手機訪問互聯網,這還不包括應用程序的使用情況。當視力不佳的人在旅途中在小屏幕上使用技術時,可能會出什么問題?
使用較大的字體。通常,16px被認為是最具有包容性的,但是請注意,字體可以以不同的比例站立,并且字體的大小可以相差很大。切勿低于14px,事實上,大多數現代網站的正文都使用18px字體,而標簽,標題或工具提示僅使用14px或16px。
專家提示:此外,避免使用薄而輕的字體,因為對于較小的字體或在明亮的光線下可能難以閱讀。
段落格式
幫助人們輕松瀏覽內容應該是我們的首要目標,因為只有20%的人可以閱讀內容,其中55%的人可以快速瀏覽內容。我們的工作是通過使用舒適的段落格式來盡可能地支持人們。
研究表明,用于支持可讀性的平均在線行長(包括空格)約為70個字符。標題,字幕和項目符號點將有助于掃描,而左段對齊將使文本更易于閱讀。
較長的文字墻使人們參與的機會大大減少。成功的段落長度不超過5到6個句子。
空格將幫助患有認知和注意力障礙的人,保持閱讀重點。對于其余的內容,它只會使閱讀更加愉快和流暢。根據WCAG,最佳做法是將行高(行之間的間距)設置為相對于該類型大小的1.5相對值。段落之間的間距也至少應比行間距大1.5倍,因此必須明確定義。
提示:行距不應超過2.0,因為它可能產生相反的效果并分散讀者注意力。
復制版面
作為設計師,我們經常陷入過度設計布局的陷阱,以使它們看起來引人注目或獨特,從而將可用性放在一邊。這就是為什么我們看到諸如文本的一部分之類的趨勢在彩色或帶紋理的背景上重疊圖像或文本的趨勢。只要我們知道如何以及何時使用它們,我們仍然可以享受其中的一些趨勢。
當在彩色或帶紋理的背景上使用文本時,我們需要確保它們之間的色彩對比度足夠高,同時在整個重疊區域都保持一致-意味著在副本下沒有較淺和較暗的區域,也沒有過多的細節干擾。較大的字體大小和較重的字體粗細也會提高對比度。
專家提示:一如既往地“了解您的用戶”。時髦的布局并不適合所有人。
改善聽覺體驗
您可能在想,視覺設計如何影響聽覺體驗?因此,想象一下您正在與一個俱樂部的朋友交談。我敢打賭,您只能聽見她說的話的一半,但是您可以通過看著她的嘴唇移動,肢體語言和面部表情來保持對話的進行。由于視覺效果的支持增強了模棱兩可的聲音,因此您最終可以理解它們。
在用戶界面中,聲音對于不同的人可能意味著各種各樣的事情。它們也很容易在嘈雜的背景中丟失,因此最好以視覺提示來支持它們。
我們的目標應該是提供聽覺和視覺提示的反饋,支持錯誤,通知以及與相關和鄰近圖形元素的重大交互。我們還必須確保視覺線索保持足夠長的活動時間,以使人們能夠看到和閱讀,同時又不隱藏任何重要的內容。
一個好的做法-不限于支持聲音輔助技術,是在UI元素中添加描述性標簽,并在圖像中添加標題,以便于在屏幕閱讀器中輕松導航。為視頻使用字幕是改善聽力體驗的另一種方法,對非母語人士也有幫助。
最后,我們不應該忽略聲音是問題的情況,這就是為什么我們需要視覺替代的原因。有些人可能對特定的聲音敏感,或者處于聲音可能引起干擾的情況下。然后,這是一個好習慣,讓人們可以選擇關閉聲音而不必調低揚聲器音量,從而使此功能清晰可見。
專家提示:避免使用不必要的自動播放聲音和音樂,因為它們會打擾甚至驚嚇別人。
改善認知體驗
1.知覺
視覺清晰度
清晰度是用戶界面中的第一個也是最重要的屬性。成功的用戶界面使用戶能夠識別和解釋他們所看到的內容,了解產品的價值和所要采取的行動,預測使用產品時會發生什么以及與產品成功交互。
形式跟隨功能是一項原則,指出對象應反映其預期的功能或目的。為了在用戶界面中實現此目的,我們使用了附加功能,附加到UI的視覺提示/屬性,以顯示用戶與其交互的可能方式。
支付能力取決于用戶的身體能力,目標,過去的經驗,當然還取決于他們認為可能的情況。按鈕應該看起來像按鈕,就像鏈接,菜單標簽,表單等一樣。使用清晰的符號/功能可以幫助用戶識別或解釋界面,并輕松進行交互。
在用戶界面中使用熟悉的和已建立的設計解決方案將幫助用戶預測結果并自信地采取行動。因此,使用設計模式來解決常見問題是一個好習慣,該設計模式是經過測試,優化和可重用的解決方案。
設計模式建立在過去的經驗和可能性的基礎上,并附加到特定的目標上。為避免眼前的問題,選擇正確的設計模式應該是我們避免混淆或壓力大的交互的第一要務。
建立一致的視覺語言是獲得更全面界面的關鍵。具有相同功能和/或重要性的重復交互式UI組件應始終以相同的方式外觀和操作。因此,導航,按鈕,鏈接,標簽,錯誤等元素應在整個產品中具有一致的樣式,顏色和動畫。
值得注意的是,一致的視覺語言不僅可以通過附加含義和減少視覺噪音來幫助互動,而且還可以增強產品的個性,提升品牌知名度,建立情感聯系和信任。
層次結構
視覺層次結構是指圖形元素的視覺重量及其排列方式,使用戶可以輕松地探索和發現內容。通過為頁面元素分配不同的視覺權重,我們可以對內容進行分組并影響人們感知信息和瀏覽產品的順序。
顏色是第一大關注焦點。彩色元素將脫穎而出,因此在層次結構中位于較高位置。明亮的顏色會更加突出,因此,考慮到這一點,我們應該仔細安排和分配顏色,以將眼睛引導至正確的位置。
視覺元素的大?。ɡ缬∷?,按鈕,圖標和圖像)在確定重要性方面幾乎與顏色一樣強大。較大的圖形吸引了用戶的注意,并且顯得很重要。對于排版,明顯不同的尺寸縮放比例可以幫助建立內容層次結構,并使內容掃描變得輕松而輕松。
輔助視覺層次結構的另一種方法是通過設計一致性和例外。一致對齊,外觀相似,重復或相鄰的元素給人的印象是它們是相關且同等重要的,而偏離元素以及不尋常的形狀和有趣的紋理或樣式將更加顯著。太多的設計例外會引起人們的關注,并會增加復雜性,因此,謹慎使用它們是一個好習慣。
專家提示:研究格式塔原理及其在UI設計中的應用將有助于我們理解視覺感知和分組以改善視覺層次。
色彩應用
顏色不應該是傳達信息或增加意義的唯一方法,但它仍然有用且很有影響力,因此不應將其視為裝飾性元素。顏色具有含義,盡管沒有硬性規定,但是太多的顏色會導致信息疲勞,并且不一致地使用顏色會導致混亂。
避免使用太多顏色。通常,三種顏色足以描述頁面的所有重要視覺元素。60–30–10規則可以幫助我們建立完美的和諧。其中60%的彩色項目由原色組成,以創建統一的產品主題,具有30%的輔助顏色增強含義和/或創建引人注目的效果,以及10%的強調色,以補充和輔助主顏色和輔助顏色。
此外,我們需要確保為消息使用正確的色調。除了美學,顏色還可以創造情感和無意識的聯系。特定陰影的含義會因我們所處的文化和環境而異,并且顏色通常具有不同的含義-在西方世界,錯誤是紅色,成功是綠色,信息是藍色等。
專家提示:可以將我們自己的含義分配給顏色,只要它們不與既定規范重疊,并且我們在整個產品中使它們保持一致。
符號學
符號學是對符號/圖標及其含義的研究。它著重于人們如何形成和解釋這些含義,這取決于人們所看到的上下文。在用戶界面中,圖標是可視語言的一部分,用于表示功能,功能或內容。符號學可以幫助我們設計立即被識別和理解的圖像。
盡管這些年來,我們已經開發出具有大多數人接受和理解的含義的圖標。用戶還習慣于使用特定于平臺的圖標,并且可以輕松地進行解釋。在可能的情況下,最好遵循這些既定的解決方案,以獲得熟悉和流暢的體驗。
當然,在某些情況下,我們需要設計具有特定功能的自定義產品特定圖標。這些圖標必須盡可能簡單明了,以確保清晰度。它們還應該具有一致的視覺樣式,以傳達其功能或與其他非功能性元素區分開。
最后,我們不應該僅僅依靠視覺隱喻來傳達含義,因為某些關聯可能并不那么明顯。如果圖標需要標題來描述其含義,則可能不合適。如果不確定,請與實際用戶一起測試我們的設計會有所幫助。
專家提示:圖標不僅易于解釋,而且還可以具有多種含義。因此,將標記與功能圖標結合使用是一種很好的做法。
2.互動
記憶
許多心理學實驗表明,健康個體的處理能力非常有限。在我們的短期記憶中,我們大多數人平均可以保留7項,具體取決于個人。我們的大腦并未針對數字產品所需的抽象思維和數據記憶進行優化,因此良好的設計會有所作為。
減少頁面上可用選項和信息的數量,以及使用清晰的標題,面包屑和“后退”選項來訪問以前的內容,將幫助用戶記住或提醒自己他們在哪里,打算做什么或要做什么。是必需的。
交互元素上或附近的清晰可見副本將幫助用戶在整個交互過程中保持知情和自信。例如,表單標簽應始終可見,動作不應隱藏在懸停后面,按鈕應提供目標位置的上下文,并且各節的標題應明確。
專家提示:通過稱為“塊”的過程可以增加我們的短期記憶和處理能力。這是我們在視覺上將項目分組以形成更容易記住的較大項目的地方。
改善運動體驗
菲茨法
菲茨法則為人類的運動和互動提供了一個模型。它指出,將指針(光標或手指)快速移動到目標區域所需的時間是其距目標的距離除以目標大小的函數。意味著較小的目標會增加互動時間。
根據Fitts法則,我們旨在減小用戶與目標之間的距離,同時增加其尺寸。該法律主要適用于導航和按鈕。菜單和子菜單元素應在附近,而按鈕,鏈接和分頁應在較大區域上單擊,以實現更快更準確的交互。
專家提示:根據可用性最佳實踐,按鈕/鏈接的最小尺寸為42x42像素(重擊尺寸)。
獎勵:提高績效
到目前為止,我們已經建立了包容性設計,旨在讓盡可能多的人訪問并實現他們的目標或解決他們的問題,盡管他們有自己的情況。我們可能很幸運,可以使用進的設備或超高速互聯網,但是當我們的信號不太好時,我們會感到掙扎。對于大多數人來說,老式設備和糟糕的互聯網已成為常態,因此,為獲得最佳性能而設計是一件大事。
極簡主義是關鍵。如果我們打算創造一種可以被盡可能多的人使用的產品,那么我們就應該擺脫不必要的一切。圖形,圖像或動畫是有價值的,還是增加了視覺噪音和加載時間?如果是的話,那就必須走了。
圖像優化是幫助提高數字產品性能的另一個標準。通過將圖像調整為合適的大小,然后通過諸如ImageOptim和TinyPNG之類的工具運行它們,可以節省寶貴的千字節和實際的加載時間。
開發人員通常使用的一種提高性能的技術是“延遲加載”模式,其中圖像的加載是異步的,并延遲到需要時才加載。例如,如果您快速滾動到頁面底部,則在網站完全加載之前,您可能會看到類似網站線框的內容。“漸進圖像加載”的一種替代方法是“漸進圖像加載”,其中我們顯示一個空的占位符框<div>,然后用小的低質量模糊圖像填充它,最后用所需的高質量圖像替換它。
在每個數字產品中都遵循上述最佳實踐,這是高可訪問性標準的良好起點。但是總會有改進的余地,并且更好地了解我們的用戶可以揭示提高無障礙標準的新方法。因此,有必要花費一些時間和金錢來更多地了解我們的不同類型的用戶,因為他們可以教會我們很多有關使包容性體驗成為現實的知識。
了解我們的用戶將幫助我們練習同理心。“賦權”不是偶然的設計思維過程的第一步。在移情階段,我們的目標是加深對我們正在設計的人員及其獨特視角的了解,因此我們可以在進行任何設計決策時與他們認同并代表他們。
zhuanz
this
this是我們在書寫代碼時最常用的關鍵詞之一,即使如此,它也是JavaScript最容易被最頭疼的關鍵詞。那么this到底是什么呢?
如果你了解執行上下文,那么你就會知道,其實this是執行上下文對象的一個屬性:
executionContext = {
scopeChain:[ ... ],
VO:{
...
},
this: ?
}
執行上下文中有三個重要的屬性,作用域鏈(scopeChain)、變量對象(VO)和this。
this是在進入執行上下文時確定的,也就是在函數執行時才確定,并且在運行期間不允許修改并且是永久不變的
在全局代碼中的this
在全局代碼中this 是不變的,this始終是全局對象本身。
var a = 10;
this.b = 20;
window.c = 30;
console.log(this.a);
console.log(b);
console.log(this.c);
console.log(this === window) // true
// 由于this就是全局對象window,所以上述 a ,b ,c 都相當于在全局對象上添加相應的屬性
如果我們在代碼運行期嘗試修改this的值,就會拋出錯誤:
this = { a : 1 } ; // Uncaught SyntaxError: Invalid left-hand side in assignment
console.log(this === window) // true
函數代碼中的this
在函數代碼中使用this,才是令我們最容易困惑的,這里我們主要是對函數代碼中的this進行分析。
我們在上面說過this的值是,進入當前執行上下文時確定的,也就是在函數執行時并且是執行前確定的。但是同一個函數,作用域中的this指向可能完全不同,但是不管怎樣,函數在運行時的this的指向是不變的,而且不能被賦值。
function foo() {
console.log(this);
}
foo(); // window
var obj={
a: 1,
bar: foo,
}
obj.bar(); // obj
函數中this的指向豐富的多,它可以是全局對象、當前對象、或者是任意對象,當然這取決于函數的調用方式。在JavaScript中函數的調用方式有一下幾種方式:作為函數調用、作為對象屬性調用、作為構造函數調用、使用apply或call調用。下面我們將按照這幾種調用方式一一討論this的含義。
作為函數調用
什么是作為函數調用:就是獨立的函數調用,不加任何修飾符。
function foo(){
console.log(this === window); // true
this.a = 1;
console.log(b); // 2
}
var b = 2;
foo();
console.log(a); // 1
上述代碼中this綁定到了全局對象window。this.a相當于在全局對象上添加一個屬性 a 。
在嚴格模式下,獨立函數調用,this的綁定不再是window,而是undefined。
function foo() {
"use strict";
console.log(this===window); // false
console.log(this===undefined); // true
}
foo();
這里要注意,如果函數調用在嚴格模式下,而內部代碼執行在非嚴格模式下,this 還是會默認綁定為 window。
function foo() {
console.log(this===window); // true
}
(function() {
"use strict";
foo();
})()
對于在函數內部的函數獨立調用 this 又指向了誰呢?
function foo() {
function bar() {
this.a=1;
console.log(this===window); // true
}
bar()
}
foo();
console.log(a); // 1
上述代碼中,在函數內部的函數獨立調用,此時this還是被綁定到了window。
總結:當函數作為獨立函數被調用時,內部this被默認綁定為(指向)全局對象window,但是在嚴格模式下會有區別,在嚴格模式下this被綁定為undefined。
作為對象屬性調用
var a=1;
var obj={
a: 2,
foo: function() {
console.log(this===obj); // true
console.log(this.a); // 2
}
}
obj.foo();
上述代碼中 foo屬性的值為一個函數。這里稱 foo 為 對象obj 的方法。foo的調用方式為 對象 . 方法 調用。此時 this 被綁定到當前調用方法的對象。在這里為 obj 對象。
再看一個例子:
var a=1;
var obj={
a: 2,
bar: {
a: 3,
foo: function() {
console.log(this===bar); // true
console.log(this.a); // 3
}
}
}
obj.bar.foo();
遵循上面說的規則 對象 . 屬性 。這里的對象為 obj.bar 。此時 foo 內部this被綁定到了 obj.bar 。 因此 this.a 即為 obj.bar.a 。
再來看一個例子:
var a=1;
var obj={
a: 2,
foo: function() {
console.log(this===obj); // false
console.log(this===window); // true
console.log(this.a); // 1
}
}
var baz=obj.foo;
baz();
這里 foo 函數雖然作為對象obj 的方法。但是它被賦值給變量 baz 。當baz調用時,相當于 foo 函數獨立調用,因此內部 this被綁定到 window。
使用apply或call調用
apply和call為函數原型上的方法。它可以更改函數內部this的指向。
var a=1;
function foo() {
console.log(this.a);
}
var obj1={
a: 2
}
var obj2={
a: 3
}
var obj3={
a: 4
}
var bar=foo.bind(obj1);
bar();// 2 this => obj1
foo(); // 1 this => window
foo.call(obj2); // 3 this => obj2
foo.call(obj3); // 4 this => obj3
當函數foo 作為獨立函數調用時,this被綁定到了全局對象window,當使用bind、call或者apply方法調用時,this 被分別綁定到了不同的對象。
作為構造函數調用
var a=1;
function Person() {
this.a=2; // this => p;
}
var p=new Person();
console.log(p.a); // 2
上述代碼中,構造函數 Person 內部的 this 被綁定為 Person的一個實例。
總結:
當我們要判斷當前函數內部的this綁定,可以依照下面的原則:
函數是否在是通過 new 操作符調用?如果是,this 綁定為新創建的對象
var bar = new foo(); // this => bar;
函數是否通過call或者apply調用?如果是,this 綁定為指定的對象
foo.call(obj1); // this => obj1;
foo.apply(obj2); // this => obj2;
函數是否通過 對象 . 方法調用?如果是,this 綁定為當前對象
obj.foo(); // this => obj;
函數是否獨立調用?如果是,this 綁定為全局對象。
foo(); // this => window
DOM事件處理函數中的this
1). 事件綁定
<button id="btn">點擊我</button>
// 事件綁定
function handleClick(e) {
console.log(this); // <button id="btn">點擊我</button>
}
document.getElementById('btn').addEventListener('click',handleClick,false); // <button id="btn">點擊我</button>
document.getElementById('btn').onclick= handleClick; // <button id="btn">點擊我</button>
根據上述代碼我們可以得出:當通過事件綁定來給DOM元素添加事件,事件將被綁定為當前DOM對象。
2).內聯事件
<button onclick="handleClick()" id="btn1">點擊我</button>
<button onclick="console.log(this)" id="btn2">點擊我</button>
function handleClick(e) {
console.log(this); // window
}
//第二個 button 打印的是 <button id="btn">點擊我</button>
我認為內聯事件可以這樣理解:
//偽代碼
<button onclick=function(){ handleClick() } id="btn1">點擊我</button>
<button onclick=function() { console.log(this) } id="btn2">點擊我</button>
這樣我們就能理解上述代碼中為什么內聯事件一個指向window,一個指向當前DOM元素。(當然瀏覽器處理內聯事件時并不是這樣的)
定時器中的this
定時器中的 this 指向哪里呢?
function foo() {
setTimeout(function() {
console.log(this); // window
},1000)
}
foo();
再來看一個例子
var name="chen";
var obj={
name: "erdong",
foo: function() {
console.log(this.name); // erdong
setTimeout(function() {
console.log(this.name); // chen
},1000)
}
}
obj.foo();
到這里我們可以看到,函數 foo 內部this指向為調用它的對象,即:obj 。定時器中的this指向為 window。那么有什么辦法讓定時器中的this跟包裹它的函數綁定為同一個對象呢?
1). 利用閉包:
var name="chen";
var obj={
name: "erdong",
foo: function() {
console.log(this.name) // erdong
var that=this;
setTimeout(function() {
// that => obj
console.log(that.name); // erdong
},1000)
}
}
obj.foo();
利用閉包的特性,函數內部的函數可以訪問含義訪問當前詞法作用域中的變量,此時定時器中的 that 即為包裹它的函數中的 this 綁定的對象。在下面我們會介紹利用 ES6的箭頭函數實現這一功能。
當然這里也可以適用bind來實現:
var name="chen";
var obj={
name: "erdong",
foo: function() {
console.log(this.name); // erdong
setTimeout(function() {
// this => obj
console.log(this.name); // erdong
}.bind(this),1000)
}
}
obj.foo();
被忽略的this
如果你把 null 或者 undefined 作為 this 的綁定對象傳入 call 、apply或者bind,這些值在調用時會被忽略,實例 this 被綁定為對應上述規則。
var a=1;
function foo() {
console.log(this.a); // 1 this => window
}
var obj={
a: 2
}
foo.call(null);
var a=1;
function foo() {
console.log(this.a); // 1 this => window
}
var obj={
a: 2
}
foo.apply(null);
var a=1;
function foo() {
console.log(this.a); // 1 this => window
}
var obj={
a: 2
}
var bar = foo.bind(null);
bar();
bind 也可以實現函數柯里化:
function foo(a,b) {
console.log(a,b); // 2 3
}
var bar=foo.bind(null,2);
bar(3);
更復雜的例子:
var foo={
bar: function() {
console.log(this);
}
};
foo.bar(); // foo
(foo.bar)(); // foo
(foo.bar=foo.bar)(); // window
(false||foo.bar)(); // window
(foo.bar,foo.bar)(); // window
上述代碼中:
foo.bar()為對象的方法調用,因此 this 綁定為 foo 對象。
(foo.bar)() 前一個() 中的內容不計算,因此還是 foo.bar()
(foo.bar=foo.bar)() 前一個 () 中的內容計算后為 function() { console.log(this); } 所以這里為匿名函數自執行,因此 this 綁定為 全局對象 window
后面兩個實例同上。
這樣理解會比較好:
(foo.bar=foo.bar) 括號中的表達式執行為 先計算,再賦值,再返回值。
(false||foo.bar)() 括號中的表達式執行為 判斷前者是否為 true ,若為true,不計算后者,若為false,計算后者并返回后者的值。
(foo.bar,foo.bar) 括號中的表達式之行為分別計算 “,” 操作符兩邊,然后返回 “,” 操作符后面的值。
箭頭函數中的this
箭頭函數時ES6新增的語法。
有兩個作用:
更簡潔的函數
本身不綁定this
代碼格式為:
// 普通函數
function foo(a){
// ......
}
//箭頭函數
var foo = a => {
// ......
}
//如果沒有參數或者參數為多個
var foo = (a,b,c,d) => {
// ......
}
我們在使用普通函數之前對于函數的this綁定,需要根據這個函數如何被調用來確定其內部this的綁定對象。而且常常因為調用鏈的數量或者是找不到其真正的調用者對 this 的指向模糊不清。在箭頭函數出現后其內部的 this 指向不需要再依靠調用的方式來確定。
箭頭函數有幾個特點(與普通函數的區別)
箭頭函數不綁定 this 。它只會從作用域鏈的上一層繼承 this。
箭頭函數不綁定arguments,使用reset參數來獲取實參的數量。
箭頭函數是匿名函數,不能作為構造函數。
箭頭函數沒有prototype屬性。
不能使用 yield 關鍵字,因此箭頭函數不能作為函數生成器。
這里我們只討論箭頭函數中的this綁定。
用一個例子來對比普通函數與箭頭函數中的this綁定:
var obj={
foo: function() {
console.log(this); // obj
},
bar: () => {
console.log(this); // window
}
}
obj.foo();
obj.bar();
上述代碼中,同樣是通過對象 . 方法調用一個函數,但是函數內部this綁定確是不同,只因一個數普通函數一個是箭頭函數。
用一句話來總結箭頭函數中的this綁定:
個人上面說的它會從作用域鏈的上一層繼承 this ,說法并不是很正確。作用域中存放的是這個函數當前執行上下文與所有父級執行上下文的變量對象的集合。因此在作用域鏈中并不存在 this 。應該說是作用域鏈上一層對應的執行上下文中繼承 this 。
箭頭函數中的this繼承于作用域鏈上一層對應的執行上下文中的this
var obj={
foo: function() {
console.log(this); // obj
},
bar: () => {
console.log(this); // window
}
}
obj.bar();
上述代碼中obj.bar執行時的作用域鏈為:
scopeChain = [
obj.bar.AO,
global.VO
]
根據上面的規則,此時bar函數中的this指向為全局執行上下文中的this,即:window。
再來看一個例子:
var obj={
foo: function() {
console.log(this); // obj
var bar=() => {
console.log(this); // obj
}
bar();
}
}
obj.foo();
在普通函數中,bar 執行時內部this被綁定為全局對象,因為它是作為獨立函數調用。但是在箭頭函數中呢,它卻綁定為 obj 。跟父級函數中的 this 綁定為同一對象。
此時它的作用域鏈為:
scopeChain = [
bar.AO,
obj.foo.AO,
global.VO
]
這個時候我們就差不多知道了箭頭函數中的this綁定。
繼續看例子:
var obj={
foo: () => {
console.log(this); // window
var bar=() => {
console.log(this); // window
}
bar();
}
}
obj.foo();
這個時候怎么又指向了window了呢?
我們還看當 bar 執行時的作用域鏈:
scopeChain = [
bar.AO,
obj.foo.AO,
global.VO
]
當我們找bar函數中的this綁定時,就會去找foo函數中的this綁定。因為它是繼承于它的。這時 foo 函數也是箭頭函數,此時foo中的this綁定為window而不是調用它的obj對象。因此 bar函數中的this綁定也為全局對象window。
我們在回頭看上面關于定時器中的this的例子:
var name="chen";
var obj={
name: "erdong",
foo: function() {
console.log(this.name); // erdong
setTimeout(function() {
console.log(this); // chen
},1000)
}
}
obj.foo();
這時我們就可以很簡單的讓定時器中的this與foo中的this綁定為同一對象:
var name="chen";
var obj={
name: "erdong",
foo: function() {
// this => obj
console.log(this.name); // erdong
setTimeout(() => {
// this => foo中的this => obj
console.log(this.name); // erdong
},1000)
}
}
obj.foo();
5G以其更快的速度、連接和云訪問,將大數據引進車內
在MWC上,華為、小米、三星等通訊企業紛紛發布了5G手機,而吉利也在2月26日的MWC上,宣布了與高通和高新興合作發布吉利全球首批支持5G和C-V2X的量產車型計劃??梢?G時代對于各大車企來說有著巨大的影響,尤其是車聯網產業。那么,在5G環境下,汽車行業究竟有什么變化呢?
5G是一代的移動網絡,憑借高帶寬和低延遲,提供了更快的速度、連接和云訪問。5G的最大速度可達到每秒20GB,比4G要快100倍。它可以應用于手機、無人駕駛、VR、電影、充電樁、醫療、農業等多個領域。而基于5G通訊技術推出的C-V2X,是實現無人駕駛和車內技術的重要前提。
萬物互聯
萬物互聯是自動駕駛汽車發展的關鍵,基于5G通訊技術推出的C-V2X能讓聯網車輛與交通基礎設施進行通信。通過5G可以實現自動駕駛汽車彼此之間所有數據的溝通與互聯。并與交通燈、道路、傳感器、停車場等基礎設施之間的信息互聯,最終實現車路協同、萬物互聯。
車車互聯提升駕駛輔助
在5G環境下的汽車,可以通過云計算來計算車與車之間的距離、車輛的下一步動作、隱藏車輛可視化、零誤差高清導航等信息。同時,也可以與其他車輛實現共享數據,提升ADAS和AEB等駕駛輔助功能,來避免車輛之間發生碰撞。
智慧交通引領場景化設計
5G環境下,大量的數據將被引入車內,來提供更準確的數據信息。如,高速收費、紅綠燈、RTTI、實時車位情況、消費支付、行人檢測等情況。眾多的交互有助于車輛更好的了解環境信息,并作出反饋,從而提供更好的場景化設計。如,自動超車、協作式避讓、自動播報前方道路擁擠程度并重新規劃路線功能等場景化設計。
再比如,哈曼正在研發的交通信號燈速度優化建議技術,幫助司機根據紅綠燈信息調整時速;西亞特測試了在交通信號燈中安裝熱像儀,以檢測行人的動作并將數據反饋給汽車。
車、商業、家居互聯改善駕駛體驗
當車輛與酒店、商場、影院、餐廳、健身房、加油站、家居、充電樁等場所相連接,就需要以5g結合C-V2X技術的部署為基準。從而根據車主的需求,更快地預定房間、訂餐、定電影票、充電樁、商場優惠等活動,實現終端之間更的通信。
比如,我們要去看電影,那么車輛會根據實時路況(是否擁堵、有幾個紅綠燈、是否有車禍)為您選擇最佳的路線方案,并通知停車場到達時間,以方便確定是否有符合您時間的停車位,從而得到及時的反饋。
車載娛樂
可以說,如今我們所說的無人駕駛、車載觸屏、全息投影、AR(增強現實)、VR游戲、AR-HUD、實時電影、車輛之間影片共享、移動辦公、多模態交互等車載信息娛樂,都是需要在5G環境下來完成的。在未來,你可以在車內利用幾秒的時間下載一部電影,也可以在車內與其他車輛之間建立網絡游戲通信,實現虛擬內容與車輛運動的實時對接。
奔馳與哈曼合作開發的MBUX信息娛樂平臺也引入了增強現實(AR)的車載導航系統。
MBUX信息AR車載導航系統
BMW Vision Next 100 AR-HUD
奧迪發布的“沉浸式車內娛樂系統”,讓乘客在車內佩戴VR眼鏡,然后車輛會根據行駛路線路況,實時匹配逼真的電競類影片。
奧迪發布的“沉浸式車內娛樂系統”
手勢交互
車載機器人
全息投影
移動辦公
安全駕駛
未來的汽車將變得更加安全和,因為5G憑借其更高的帶寬、最小的延遲和零誤差高清導航,能夠預防事故和觀察到車身周圍的各個角落。再結合C-V2X技術將極大地促進車輛之間、車輛與行人、道路基礎設施之間的信息流動和監控車輛異常情況。從而做到提前預知危險,并迅速作出響應來提高道路安全性。另外,5G對于阻止黑客攻擊和數據攔截能夠得到更快響應,從而保障通訊安全。
想象一下,如果你的車可以在1毫秒內做出反應并將反饋傳達給數百人,那么,危險系數就會降低很多。5G時代,端到端的時長為1毫秒,同時1平方公里內可同時連接100萬個網絡,足以滿足智能交通和自動駕駛的要求。
無人駕駛
5G是實現無人駕駛最為關鍵的因素,如今,5G的成功研發有助于車輛之間大量的數據傳輸和存儲,實現車聯網的同時,也保障了車輛行駛的安全性;另一方面,5G可實現數據更快速的云訪問,從而更有效地減少傳感器技術的成本,最終實現無人駕駛。
總結
5G以其更快的速度、連接和云訪問,將大數據引進車內。從而,在提升駕駛體驗的同時,實現了汽車與萬物的互聯,保障了汽車駕駛中的安全性,從而為無人駕駛汽車提供了技術支撐和更便捷和的信息娛樂系統。
5G將成為未來十年全球主導的移動通信標準;
基于5G的C-V2X技術,會加快無人駕駛技術的落地;
5G環境下的車載信息娛樂系統將改善用戶駕駛體驗,同時催生大量的市場新機會;
共享平臺下,5G將根據實時路況作出更準確的判斷,從而減少時間成本和停車成本;
5G遠不止應用于汽車、手機,它將應用于醫療、機器人、農作業、航空等更多方面;
5G技術為無人駕駛的實現提供了技術支撐和安全保障,同時也降低了安裝傳感器技術的成本;
5G實現了車與車、道路、家居、商業之間真正意義上的“車聯網”,為交通提供更準確的交通信息和通信信息;
5G將會提供更多的場景,從而加快市場社會變革和新的商機,但需要大量的資金投入,能否將技術化為盈利是一大問題;
5G以毫秒的速度,提供更精細的地圖數據和更高級的駕駛輔助,并作出快速的反饋,提高了駕駛的安全性,對于減少交通擁堵,提高交通效率和道路安全有著積極的作用。
轉自-站酷
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
課程介紹
近些年,瀏覽器的功能越來越強大,漸漸得成為了復雜應用和圖形的平臺。同時,現有大多數瀏覽器實現了對 WebGL 的支持,但要直接使用 WebGL 相關接口進行開發,則需要學習復雜的著色器語言,且開發周期長,不利于項目的快速開發。
面對這種情況,Three.js 應運而生,它不但對 WebGL 進行了封裝,將復雜的接口簡單化,而且基于面向對象思維,將數據結構對象化,非常方便我們開發。Three.js 的發展十分迅速,然而配套的學習材料卻比較匱乏,于是便有了當前的這個課程。
本課程作為入門課程,不會深入做源碼解析,主要協助初學者了解 Three.js 的數據結構,基礎 API 以及相關輔助插件的使用。幫助初學者達到快速入門的目的。
本課程共包含四大部分。
第一部分(第01-02課),入門前概述,帶你初步認識 Three.js、框架選擇標準、開發工具,源碼獲取,實現一個“Hello World”輔助工具。
第二部分(第03-08課),基礎功能篇,主要包括 Object3D、Scene、Mesh、Group、Geometry、Materials、Lights、Cameras、粒子等相關功能的介紹。
第三部分(第09-15課),進階篇,主要包括 Controls、Loaders、Animation、Tween、核心對象,與場景之間的交互以及性能優化介紹。
第四部分(第16課),實戰篇,帶大家利用所學知識實現一個 3D 小案例。
鄭世強,現就職于上海某網絡公司擔任前端工程師,CSDN 博客作者,長期活躍于各大論壇,擅長前端開發、WEBGL 開發。
WebGL(Web 圖形庫)是一種 JavaScript API,用于在任何兼容的 Web 瀏覽器中呈現交互式 3D 和 2D 圖形,而無需使用插件。WebGL 通過引入一個與 OpenGL ES 2.0 緊密相符合的 API,可以在 HTML5 <canvas> 元素中使用(簡介引自 MDN)。
以我的理解,WebGL 給我們提供了一系列的圖形接口,能夠讓我們通過 JavaScript 去使用 GPU 來進行瀏覽器圖形渲染的工具。
Three.js 是一款 webGL 框架,由于其易用性被廣泛應用。Three.js 在 WebGL 的 API 接口基礎上,又進行的一層封裝。它是由居住在西班牙巴塞羅那的程序員 Ricardo Cabbello Miguel 所開發,他更為人知的網名是 Mr.doob。
Three.js 以簡單、直觀的方式封裝了 3D 圖形編程中常用的對象。Three.js 在開發中使用了很多圖形引擎的高級技巧,極大地提高了性能。另外,由于內置了很多常用對象和極易上手的工具,Three.js 的功能也非常強大。最后,Three.js 還是完全開源的,你可以在 GitHub 上找到它的源代碼,并且有很多人貢獻代碼,幫助 Mr.doob 一起維護這個框架。
WebGL 原生 API 是一種非常低級的接口,而且還需要一些數學和圖形學的相關技術。對于沒有相關基礎的人來說,入門真的很難,Three.js 將入門的門檻降低了一大截,對 WebGL 進行封裝,簡化我們創建三維動畫場景的過程。只要你有一定的 JavaScript 基礎,有一定的前端經驗,我堅信,用不了多長時間,三維制作會變得很簡單。
用最簡單的一句話概括:WebGL 和 Three.js 的關系,相當于 JavaScript 和 jQuery 的關系。
Three.js 作為 WebGL 框架中的佼佼者,由于它的易用性和擴展性,使得它能夠滿足大部分的開發需求,Three.js 的具體功能如下:
Three.js 掩蓋了 3D 渲染的細節:Three.js 將 WebGL 原生 API 的細節抽象化,將 3D 場景拆解為網格、材質和光源(即它內置了圖形編程常用的一些對象種類)。
面向對象:開發者可以使用上層的 JavaScript 對象,而不是僅僅調用 JavaScript 函數。
功能非常豐富:Three.js 除封裝了 WebGL 原始 API 之外,Three.js 還包含了許多實用的內置對象,可以方便地應用于游戲開發、動畫制作、幻燈片制作、髙分辨率模型和一些特殊的視覺效果制作。
速度很快:Three.js 采用了 3D 圖形最佳實踐來保證在不失可用性的前提下,保持極高的性能。
支持交互:WebGL 本身并不提供拾取(Picking)功能(即是否知道鼠標正處于某個物體上)。而 Three.js 則固化了拾取支持,這就使得你可以輕松為你的應用添加交互功能。
包含數學庫:Three.js 擁有一個強大易用的數學庫,你可以在其中進行矩陣、投影和矢量運算。
內置文件格式支持:你可以使用流行的 3D 建模軟件導出文本格式的文件,然后使用 Three.js 加載,也可以使用 Three.js 自己的 JSON 格式或二進制格式。
擴展性很強:為 Three.js 添加新的特性或進行自定義優化是很容易的事情。如果你需要某個特殊的數據結構,那么只需要封裝到 Three.js 即可。
支持HTML5 Canvas:Three.js 不但支持 WebGL,而且還支持使用 Canvas2D、Css3D 和 SVG 進行渲染。在未兼容 WebGL 的環境中可以回退到其它的解決方案。
雖然 Three.js 的優勢很大,但是它也有它的不足之處:
官網文檔非常粗糙,對于新手極度不友好。
國內的相關資源匱乏。
Three.js 所有的資料都是以英文格式存在,對國內的朋友來說又提高了門檻。
Three.js 不是游戲引擎,一些游戲相關的功能沒有封裝在里面,如果需要相關的功能需要進行二次開發。
隨著 WebGL 的迅速發展,相關的 WebGL 庫也豐富起來,接下來介紹幾個比較火的 WebGL 庫。
Babylon.JS 是最好的 JavaScript 3D 游戲引擎,它能創建專業級三維游戲。主要以游戲開發和易用性為主。與 Three.js 之間的對比:
Three.js 比較全面,而 Babylon.js 專注于游戲方面。
Babylon.js 提供了對碰撞檢測、場景重力、面向游戲的照相機,Three.js 本身不自帶,需要依靠引入插件實現。
對于 WebGL 的封裝,雙方做得各有千秋,Three.js 淺一些,好處是易于擴展,易于向更底層學習;Babylon.js 深一些,好處是易用擴展難度大一些。
Three.js 的發展依靠社區推動,出來的比較早,發展比較成熟,Babylon.js 由微軟公司在2013推出,文檔和社區都比較健全,國內還不怎么火。
PlayCanvas 是一個基于 WebGL 游戲引擎的企業級開源 JavaScript 框架,它有許多的開發工具能幫你快速創建 3D 游戲。與 Three.js 之間的對比:
PlayCanvas 的優勢在于它有云端的在線可視化編輯工具。
PlayCanvas 的擴展性不如 Three.js。
最主要是 PlayCanvas 不完全開源,還商業付費。
Cesium 是國外一個基于 JavaScript 編寫的使用 WebGL 的地圖引擎,支持 3D、2D、2.5D 形式的地圖展示,可以自行繪制圖形,高亮區域。與 Three.js 對比:
Cesium 是一個地圖引擎,專注于 Gis,相關項目推薦使用它,其它項目還是算了。
至于庫的擴展,其它的配套插件,以及周邊的資源都不及Three.js。
通過以上信息我們發現,Three.js 在其庫的擴展性,易用性以及功能方面有很好的優勢。學習 Three.js 入門 3D 開發不但門檻低,而且學習曲線不會太陡,即使以后轉向 WebGL 原生開發,也能通過 Three.js 學習到很多有用的知識。
現在最火的微信小游戲跳一跳也是在 Three.js 的基礎上開發出來的。所以,Three.js 是我們必須要學的 WebGL 框架。
Three.js 可以使用 WebGL 在所有現代瀏覽器上渲染場景。對于舊版瀏覽器,尤其是 Internet Explorer 10 及更低版本,您可能需要回退到其他渲染器(CSS2DRenderer、CSS3DRenderer、SVGRenderer、CanvasRenderer)。
注意:如果您不需要支持這些舊版瀏覽器,則不推薦使用其他渲染器,因為它們速度較慢并且支持的功能比 WebGLRenderer 更少。
即可下載當前版本的代碼及相關案例,文件下載解壓后是這樣的:
其中相關文件夾的內容是:
build:里面含有 Three.js 構建出來的 JavaScript 文件,可以直接引入使用,并有壓縮版;
docs:Three.js 的官方文檔;
editor:Three.js 的一個網頁版的模型編輯器;
examples:Three.js 的官方案例,如果全都學會,必將成為大神;
src:這里面放置的全是編譯 Three.js 的源文件;
test:一些官方測試代碼,我們一般用不到;
utils:一些相關插件;
其他:開發環境搭建、開發所需要的文件,如果不對 Three.js 進行二次開發,用不到。
還有第三種,就是直接去 GitHub 上下載源碼,和在官網上下載的代碼一樣。
<!DOCTYPE html><html><head> <meta charset=utf-8> <title>我的第一個Three.js案例</title> <style> body { margin: 0; } canvas { width: 100%; height: 100%; display: block; } </style></head><body onload="init()"><script src="https://cdn.bootcss.com/three.js/92/three.js"></script><script> //聲明一些全局變量 var renderer, camera, scene, geometry, material, mesh; //初始化渲染器 function initRenderer() { renderer = new THREE.WebGLRenderer(); //實例化渲染器 renderer.setSize(window.innerWidth, window.innerHeight); //設置寬和高 document.body.appendChild(renderer.domElement); //添加到dom } //初始化場景 function initScene() { scene = new THREE.Scene(); //實例化場景 } //初始化相機 function initCamera() { camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); //實例化相機 camera.position.set(0, 0, 15); } //創建模型 function initMesh() { geometry = new THREE.BoxGeometry( 2, 2, 2 ); //創建幾何體 material = new THREE.MeshNormalMaterial(); //創建材質 mesh = new THREE.Mesh( geometry, material ); //創建網格 scene.add( mesh ); //將網格添加到場景 } //運行動畫 function animate() { requestAnimationFrame(animate); //循環調用函數 mesh.rotation.x += 0.01; //每幀網格模型的沿x軸旋轉0.01弧度 mesh.rotation.y += 0.02; //每幀網格模型的沿y軸旋轉0.02弧度 renderer.render( scene, camera ); //渲染界面 } //初始化函數,頁面加載完成是調用 function init() { initRenderer(); initScene(); initCamera(); initMesh(); animate(); }</script></body></html>
請將上面的代碼復制到 HTML 文件中,然后使用瀏覽器打開,我們就會發現下面的效果:
————————————————
版權聲明:本文為CSDN博主「GitChat的博客」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/valada/java/article/details/80871701
用例
讓我們從介紹幾種不同的填充用例開始。
標簽和值
假設你在同一行上有標簽和值,例如 name:zhangsan 和 Phone Number:(555)-555-1234。如果把他們放在一起看起來會有點奇怪,會是這樣:
Name: zhangsan
Phone Number: (555)-555-1234
你可能想要這個。
Name: zhangsan
Phone Number: (555)555-1234
或這個...
Name: zhangsan
Phone Number: (555)555-1234
金額
在中國,顯示價格時通常顯示兩位數的角、分。所以代替這個...
¥10.1
你會想要這個。
¥10.01
日期
對于日期,日期和月份都需要2位數字。所以代替這個...
2020-5-4
你會想要這個。
2020-05-04
時間
與上面的日期類似,對于計時器,你需要2位數字表示秒,3位數字表示毫秒。所以代替這個...
1:1
你會想要這個。
01:001
padstart()
讓我們從 padStart() 以及標簽和值示例開始。假設我們希望標簽彼此正確對齊,以使值在同一位置開始。
Name: zhangsan
Phone Number: (555)555-1234
由于 Phone Number 是兩個標簽中較長的一個,因此我們要在 Name 標簽的開頭加上空格。為了將來的需要,我們不要把它專門填充到電話號碼的長度,我們把它填充到長一點,比如說20個字符。這樣一來,如果你在未來使用較長的標簽,這一招仍然有效。
在填充之前,這是用于顯示此信息的入門代碼。
const label1 = "Name";
const label2 = "Phone Number";
const name = "zhangsan"
const phoneNumber = "(555)-555-1234";
console.log(label1 + ": " + name);
console.log(label2 + ": " + phoneNumber);
//Name: zhangsan
//Phone Number: (555)-555-1234
現在,讓我們填充第一個標簽。要調用 padStart(),你需要傳遞兩個參數:一個用于填充字符串的目標長度,另一個用于你希望填充的字符。在這種情況下,我們希望長度為20,而填充字符為空格。
const label1 = "Name";
const label2 = "Phone Number";
const name = "zhangsan"
const phoneNumber = "(555)-555-1234";
console.log(label1.padStart(20, " ") + ": " + name);
console.log(label2 + ": " + phoneNumber);
// Name: zhangsan
////Phone Number: (555)-555-1234
現在填充第二行。
const label1 = "Name";
const label2 = "Phone Number";
const name = "zhangsan"
const phoneNumber = "(555)-555-1234";
console.log(label1.padStart(20, " ") + ": " + name);
console.log(label2.padStart(20, " ") + ": " + phoneNumber);
// Name: zhangsan
//// Phone Number: (555)-555-1234
padEnd()
對于相同的標簽和值示例,讓我們更改填充標簽的方式。讓我們將標簽向左對齊,以便在末尾添加填充。
初始代碼
const label1 = "Name";
const label2 = "Phone Number";
const name = "zhangsan"
const phoneNumber = "(555)-555-1234";
console.log(label1 + ": " + name);
console.log(label2 + ": " + phoneNumber);
//Name: zhangsan
//Phone Number: (555)-555-1234
現在,讓我們填充第一個標簽,與我們之前所做的類似,但有兩個小區別?,F在,我們使用 padEnd() 而不是padStart(),并且需要在填充之前將冒號與標簽連接起來,這樣我們就能確保冒號在正確的位置。
const label1 = "Name";
const label2 = "Phone Number";
const name = "zhangsan"
const phoneNumber = "(555)-555-1234";
console.log((label1 + ': ').padEnd(20, ' ') + name);
console.log(label2 + ": " + phoneNumber);
//Name: zhangsan
//Phone Number: (555)-555-1234
現在兩行都已填充。
const label1 = "Name";
const label2 = "Phone Number";
const name = "zhangsan"
const phoneNumber = "(555)-555-1234";
console.log((label1 + ': ').padEnd(20, ' ') + name);
console.log((label2 + ': ').padEnd(20, ' ') + phoneNumber);
//Name: zhangsan
//Phone Number: (555)-555-1234
數字(價格、日期、計時器等)呢?
padding函數是專門針對字符串而不是數字的,所以,我們需要先將數字轉換為字符串。
價格
讓我們看一下顯示價格的初始代碼。
const rmb = 10;
const cents = 1;
console.log("¥" + rmb + "." + cents); //¥10.1
要填充分,我們需要先將其轉換為字符串,然后調用 padStart() 函數,指定長度為1且填充字符為'0';
const rmb = 10;
const cents = 1;
console.log("¥" + rmb + "." + cents.toString().padStart(2,0)); //¥10.01
日期
這是顯示日期的初始代碼。
const month = 2;
const year = 2020;
console.log(year + "-" + month); //2020-2
現在,讓我們填充月份以確保它是兩位數。
const month = 2;
const year = 2020;
console.log(year + "-" + month.toString().padStart(2,"0")); // 2020-02
計時器
最后是我們的計時器,我們要格式化兩個不同的數字,即秒和毫秒。盡管有相同的原則。這是初始代碼。
const seconds = 1;
const ms = 1;
console.log(seconds + ":" + ms); //1:1
現在要填充,我將在單獨的行上進行填充,以便于閱讀。
const seconds = 1;
const formattedSeconds = seconds.toString().padStart(2,0);
const ms = 1;
const formattedMs = ms.toString().padStart(3,0);
console.log(formattedSeconds + ":" + formattedMs); // 01:001
最后
雖然編寫自己的padding函數并不難,但既然已經內置在JavaScript中,為什么還要自己去做呢?有很多有趣的函數已經內置了。在你自己構建一些東西之前,可能值得先快速搜索一下。
這次我們不聊視覺,也不暢想未來,只說說當下 HMI 產品設計與交互體驗。
本文內容會涉及一些專業的汽車知識名詞,因為篇幅有限,如有些知識名詞不太明白可以百度一下。
說到 HMI 大多數設計師應該是既熟悉又陌生,HMI 是 Human Machine Interface 的縮寫,「人機接口」,也叫人機界面,人機界面(又稱用戶界面或使用者界面)是系統和用戶之間進行交互和信息交換的媒介, 它實現信息的內部形式與人類可以接受形式之間的轉換,凡參與人機信息交流的領域都存在著人機界面。
聽起來是不是覺得這不就是 UI 嗎?有什么區別嗎?似乎差不多,幾乎是沒有區別的,只不過是在某些場合和設備上管他叫 UI,比如移動端設備,而在另外某些場所和設備上管他就叫 HMI,比如汽車車機和數控機床。所以這個概念也不用去特別較真,HMI 就權當作是汽車上的 UI 界面吧。畢竟汽車是高科技與工業結合的完美產物,「HMI」念出這個詞時候就感覺是蠻專業的!很般配!
剛才說 HMI 最早更應用于工業上,比如常見的各種機床、制造裝備。
或者說讓時間再向前推進一點!
而這里通常意義的 HMI 則更加聚焦點,基本特指汽車車機或者車載多媒體設備。
說到這里還是要從車載儀表盤說起,從德國人卡爾·本茨發明世界第一輛汽車,距今已經 100 多年的時間了,在那些還沒有 HMI 這個名詞的年代,那么他是以什么形態出現的?那就不得不提「儀表盤」了。
當然寫這篇文章并不是去評測誰家 HMI 更優秀,而是希望通過一些假設、實驗和推斷,和大家一起來探討一下如何更有效地設計 HMI。
屏幕越大越好?車內到底需要幾塊屏幕?
我們先從屏幕開始。
說到屏幕,設計師都是比較敏感的,因為我們最終的設計交互創意都是需要都是在屏幕上顯示展示出來的,HMI 當然也不例外?,F在在車載屏幕上你能看到最大尺寸多大?
拿特斯拉為例,Model S 和 Model X 車型都是 17英寸,Model 3 為 15 英尺。
當然他肯定不是最大的,熟悉汽車朋友你應該知道我想說誰了,沒錯就是他!擁有 48 寸可多段升降屏幕的 BYTON 新能源概念車 M-Byte!48 寸的確很夸張,難道屏幕越來越大就是未來 HMI 的方向嗎?
當然這個問題肯定是否定的,為什么?那就要從車載屏幕的作用來說起。
首先我是作為一個曾經就職于汽車公司的設計師,并且是一名地道的汽車發燒友,憑借對汽車還算熟悉和熱愛做出一些產品交互分析,以下如有不妥之處還望海涵。
按照功能場景總體可分為三類:主行駛狀態信息、附設備狀態信息、多媒體 & 外設
不可缺少還需要與使用者、場景結合,我們先來做一個大概的用戶畫像。
對應這些需求,汽車需要有儀表臺(屏)控制和顯示的區域有五個。
五個區域分別是:
其中前三個是主流配置,后兩個比較少見。
關于汽車設備這塊我們不做深入展開了,畢竟這篇文章主要討論的還是設計,直接看結果!
題外音:屏幕安全性的考量
汽車是比較特殊的設備,基于安全性考慮,汽車內屏幕尺寸不宜太大與太多。
屏幕總體為玻璃材質,但與車窗擋風玻璃的材質不同,當汽車遭遇碰撞的時候,車內屏幕極易破損并形成尖銳物,極大可能會乘坐人員造成二次傷害,所以車內屏幕不易太多,更不易太大。雖然車載屏幕變大變多已不可逆轉,而且隨著屏幕技術的提升,柔性 OLED 的應用也將會在一定范圍解決安全問題。但也需要汽車相關設計者多在安全方面進行考慮,任何產品體驗應該建立在安全基礎之上的,特別是交通工具。
為什么大屏幕操控成為了當前的 HMI 主流了呢?那不得不去提一下另外一個我們熟悉的設備——手機!
同樣一個有限的區域,如果用物理按鍵那么這個區域只能是固定的功能,而屏幕就可以無限擴展。特別是在汽車中控屏上集成內容會很多,體現就更加突出。
但是在汽車上的全部使用屏幕真的是最佳選擇嗎?顯然這是有待商榷的。
不可否認屏幕的確有很強的擴展性,但是缺點也是明顯的:1.觸控反饋缺乏 2.交互效率不高
對于這樣的判斷,我們可以通過兩個實驗來進行驗證。
將類似于 Surface Dial 這種智能按鈕交互裝置引入汽車的屏幕控制中,每個按鈕可以根據情景進行自定義,并且吸附到汽車屏幕的任何位置進行交互操作,相信這一定是一種全新的使用體驗。當然這一定是需要解決比如吸附力、安全性等一系列問題。
雖然目前的屏幕還無法做到完美觸控反饋,但已經出現了一些新的硬件技術來試圖解決這些問題,比如 Tanvas Touch,其定義為 「手指與觸摸界面之間的電子壓力控制」。簡單來說他們的產品就 「皮膚的磁鐵」 一樣,能夠更加精準地感應手指的動作,最后結果就是比 Apple 的 3D Touch 更加具有壓感的觸摸操作表現。
原理是利用手指尖觸摸顯示屏時產生的靜電引力來模擬觸感,通過電磁脈沖把更的反饋發送到用戶的指尖。
Tanvas 也正在與汽車制造商們合作把這項技術嵌入到汽車或屏幕上,讓人們更容易感觸受到不同物體的表面。
也許在未來我們真的會遇到他。
文章來源:優設 作者:殘酷de樂章
坐標系是能夠使每個數組在維度空間內找到映射關系的定位系統,更偏向數學/物理概念。在數據可視化中,最常用的坐標系分為笛卡爾坐標系和極坐標系,本文介紹的坐標軸設計主要也是圍繞直角坐標系展開。
在說坐標軸之前先來介紹下什么是坐標系。坐標系是能夠使每個數組在維度空間內找到映射關系的定位系統,更偏向數學/物理概念。
維基百科對坐標系的定義是:對于一個 n 維系統,能夠使每一個點和一組 n 個標量構成一一對應的系統,它可以用一個有序多元組表示一個點的位置。
數據可視化中,最常用的坐標系有兩種:笛卡爾坐標系和極坐標系,均為二維坐標系。
下文介紹的坐標軸設計主要也是圍繞直角坐標系展開,用到極坐標系的圖表有餅圖、圓環圖、雷達圖等。
坐標軸是坐標系的構成部分,是定義域軸和值域軸的統稱。系的范圍更大,而軸包含在系的概念里。由于可視化圖表繪制的數據大部分都有一定的現實意義,因此我們可以根據坐標軸對應的變量是連續數據還是離散數據,將坐標軸分成連續軸、時間軸、分類軸三大類。軸的類型不同在設計處理上也有差異。
介紹坐標軸設計前,我們先將坐標軸拆分成「原子」要素,具體分為軸線、軸刻度、軸標簽、軸標題/單位、網格線。
根據坐標軸的構成,分類討論下每個構成要素容易被忽視的設計細節。
軸線一般只考慮是否顯示,例如柱狀圖、折線圖等,在有背景網格線的情況下,會隱藏 y 軸線,條形圖則是隱藏 x 軸線,以達到信息降噪,突出視覺重點的目的。
軸刻度通常不顯示,只有在肉眼無法定位到某個標簽對應的數據點時,會顯示刻度線,輔助用戶定位,比如折線圖,或抽樣顯示的柱狀圖。
網格線用于定位數據點的值域范圍,跟隨值域軸的位置單向顯示,柱狀圖采用水平網格,條形圖采用垂直網格。樣式為虛實線的最多,斑馬線由于感知過強,一般不用。
軸標題/單位主要用于說明定義域軸、值域軸的數據含義。當可視化圖表標題、圖例、軸標簽已經能充分表達數據含義,無需單獨顯示標題/單位,「如無必要,勿增實體」。
軸標簽的設計就比較復雜,涉及到的細節點很多,而且對于定義域軸和值域軸上的標簽和單位設計要考慮的細節點還有差異。下文將定義域軸和值域軸看成 x 軸和 y 軸,便于說明。
x 軸標簽的設計重點在顯示規則上,不同的坐標軸類型有不同的處理方式。
連續軸/時間軸的標簽顯示
連續軸/時間軸,是由一組前后包含同等差值的等差數列組成,缺少幾個數值也能明顯看出中間的對應關系。當多個標簽在容器內全顯示發生重疊,我們可以利用抽樣顯示的手段來避免這種情況。這里不推薦使用旋轉,一方面從美觀度上,旋轉可能會破壞界面整體協調,另一方面,連續/時間軸非必須顯示所有軸標簽,抽樣標簽已經能滿足用戶對當前數組定義域的理解。
介紹一種常見的抽樣方式:等分抽樣
當多個標簽在 x 軸無法完全顯示,優先保留首尾標簽,其余標簽按同等步長間隔顯示。間隔等分的前提是間隔數是合數,能被 1 和其本身以外的數整除。如果間隔數為質數,就需要「-1」轉成合數。
舉個例子:11 個標簽,間隔數是 10,能被 2 和 5 整除,即分成 2 等分和 5 等分。12 個標簽,間隔數是 11,無法等分,需要在間隔數基礎上再「-1」,轉成合數 10 后再等分,此時最后一個標簽顯示在倒數第二個數據點上。
有人會問了,能被那么多數等分,到底該選哪個呢?這就要根據標簽長度來定,選擇能放下最多標簽的等分值。由于連續軸/時間軸,一般是數值、日期、時間等,字符長度有限,即使抽樣后也能保證顯示出一定數量的標簽。
等分抽樣不太適用于表達某個時間周期內的走勢折線圖,因為最后一個標簽不一定對應最后一個數據點。對于這類折線圖,能清楚表明起始時間和末尾時間,相比顯示更多時間標簽重要性來的更高。設計上可以只顯示首尾標簽,或首尾 + 中間值。
分類軸的標簽顯示
分類軸是由幾組離散數據組成,相互之間獨立存在,無緊密邏輯關聯。若采用抽樣規則,隱藏一些標簽,用戶對圖表認知就會有困難,違背了數據可視化清晰、有效的設計原則。分類軸最佳處理方式是標簽旋轉 45 度,若 45 度仍顯示不下,繼續旋轉 90 度。如果 90 度還是放不下就要考慮結合圖表交互或反轉圖表。
標簽旋轉方向也有講究,因為人的視覺習慣是從左到右,從上到下,標簽順時針旋轉 45 度更符合用戶的瀏覽動線。
分類軸標簽字段有長有短,長文本標簽直接旋轉不僅影響美觀,而且也不利于用戶閱讀。如果數據量比較少只有 2~4 個,長文本標簽更適合水平展示,顯示不下省略即可;如果數據量比較多,就限定字符數后旋轉。
y 軸標簽的設計重點在標簽數量、取值范圍和數據格式上。標簽顯示區域一般根據最長標簽寬度自適應縮放。如果數組是固定的,就寫成固定寬度,節省圖表計算量,提高渲染速度。
y軸標簽數量
標簽數量不建議過多,太多的標簽必定導致橫向網格線變多,造成元素冗余,干擾圖形信息表達。根據 7±2 設計原則,y 軸標簽數量最多不超過這個范圍。
y軸標簽取值范圍
y 軸標簽的取值范圍決定了圖形在整個繪圖區域的顯示高度。
折線圖 y 軸標簽取值一般保證圖形約占繪圖區域的 2/3,以更有效的傳達數據波動幅度,避免掩蓋和夸大變化趨勢。2/3 即斐波那契數列第二位起,相鄰兩數之比,也是黃金分割最簡單的計算方法。
柱狀圖的 y 軸標簽取值應從 0 基線開始,以恰當反映數值。如果展示被截斷的柱狀圖,可能會誤導觀眾做出錯誤的判斷。
y軸標簽數據格式
y 軸標簽的數據格式在 ant.vision 寫的比較詳細,重復內容不在此說明,重點講下一些特殊的設計細節。標簽保留的小數位數保持統一,不要因為某些軸標簽是整數值,就略去小數點。
正負向的 y 軸標簽,由于負值帶「-」符號,整個 y 軸看起來會有視覺偏差,特別是雙軸圖的右 y 軸更明顯。這里建議正負向 y 軸給正值標簽帶上「+」,以達到視覺平衡的效果。
寫了那么多關于坐標軸的設計,你是不是恍然大悟,原來小小的坐標軸還有如此之多的細節。往常我們做圖表設計,可能只是用網上自動生成的圖表簡單調整下,或者按照通用樣式來設計。然而,通用樣式雖然能表達數據意義,但也缺少了對圖表細節的把控,失了精致優雅的感覺。
作為數據可視化設計的一小部分,就是這些設計細節,決定了圖表最終的傳達效果。
文章來源:優設 作者:米粒的DesignNote
藍藍設計的小編 http://www.syprn.cn