使你的代碼可讀和可維護的快速提示。
有多少次,你打開一個舊的項目,發現混亂的代碼,當你添加一些新的東西時,很容易崩潰?我們都有過這樣的經歷。
為了減少難以讀懂的javascript的數量,我提供了以下示例。這些都是我過去所犯過的錯誤。
對具有多個返回值的函數使用數組解構
假設我們有一個返回多個值的函數。一種可能的實現是使用數組解構,如下所示:
const func = () => {
const a = 1;
const b = 2;
const c = 3;
const d = 4;
return [a,b,c,d];
}
const [a,b,c,d] = func();
console.log(a,b,c,d); // 1,2,3,4
盡管上面的方法很好用,但確實引入了一些復雜性。
當我們調用函數并將值分配給 a,b,c,d 時,我們需要注意返回數據的順序。這里的一個小錯誤可能會成為調試的噩夢。
此外,無法確切指定我們要從函數中獲取哪些值,如果我們只需要 c 和 d 怎么辦?
相反,我們可以使用對象解構。
const func = () => {
const a = 1;
const b = 2;
const c = 3;
const d = 4;
return {a,b,c,d};
}
const {c,d} = func();
現在,我們可以輕松地從函數中選擇所需的數據,這也為我們的代碼提供了未來的保障,允許我們在不破壞東西的情況下增加額外的返回變量。
不對函數參數使用對象分解
假設我們有一個函數,該函數將一個對象作為參數并對該對象的屬性執行一些操作。一種幼稚的方法可能看起來像這樣:
// 不推薦
function getDaysRemaining(subscription) {
const startDate = subscription.startDate;
const endDate = subscription.endDate;
return endDate - startDate;
}
上面的方法按預期工作,但是,我們創建了兩個不必要的臨時引用 startDate 和 endDate。
一種更好的實現是對 subscription 對象使用對象解構來在一行中獲取 startDate 和 endDate。
// 推薦
function getDaysRemaining(subscription) {
const { startDate, endDate } = subscription;
return startDate - endDate;
}
我們可以更進一步,直接對參數執行對象析構。
// 更好
function getDaysRemaining({ startDate, endDate }) {
return startDate - endDate;
}
更優雅,不是嗎?
在不使用擴展運算符的情況下復制數組
使用 for循環遍歷數組并將其元素復制到新數組是冗長且相當丑陋的。
可以以簡潔明了的方式使用擴展運算符來達到相同的效果。
const stuff = [1,2,3];
// 不推薦
const stuffCopyBad = []
for(let i = 0; i < stuff.length; i++){
stuffCopyBad[i] = stuff[i];
}
// 推薦
const stuffCopyGood = [...stuff];
使用var
使用 const 保證不能重新分配變量。這樣可以減少我們代碼中的錯誤,并使其更易于理解。
// 不推薦
var x = "badX";
var y = "baxY";
// 推薦
const x = "goodX";
const y = "goodX";
果你確實需要重新分配變量,請始終選擇 let 而不是 var。
這是因為 let 是塊作用域的,而 var 是函數作用域的。
塊作用域告訴我們,只能在定義它的代碼塊內部訪問變量,嘗試訪問塊外部的變量會給我們提供ReferenceError。
for(let i = 0; i < 10; i++){
//something
}
print(i) // ReferenceError: i is not defined
函數作用域告訴我們,只能在定義其的函數內部訪問變量。
for(var i = 0; i < 10; i++){
//something
}
console.log(i) // 10
let 和 const 都是塊范圍的。
不使用模板字面值
手動將字符串連接在一起相當麻煩,而且輸入時可能會造成混淆。這是一個例子:
// 不推薦
function printStartAndEndDate({ startDate, endDate }) {
console.log('StartDate:' + startDate + ',EndDate:' + endDate)
}
模板文字為我們提供了一種可讀且簡潔的語法,該語法支持字符串插值。
// 推薦
function printStartAndEndDate({ startDate, endDate }) {
console.log(`StartDate: ${startDate}, EndDate: ${endDate}`)
}
模板文字也提供了嵌入新行的簡便方法,你所需要做的就是照常按鍵盤上的Enter鍵。
// 兩行打印
function printStartAndEndDate({ startDate, endDate }) {
console.log(`StartDate: ${startDate}
EndDate: ${endDate}`)
}
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
在多方合作的項目中,我們需要規劃項目的合理落地方案,并在執行過程中和各方有效溝通。結合近期羚瓏商家合作測試項目的實踐經歷,聊聊我在項目中的心得體會。
我們想了解京東首焦banner中,設計因素對點擊效果的影響情況。做這件事最大的限制在于投放數據會受多種因素影響,不僅是一個設計因素的選擇,還有比如人群、出價、品牌、類目等因素的不同,都能影響到最終的投放結果。因此,從總體樣本中取樣進行分析的意義不大。為了解決上述問題,我們招募了一些能控制其他因素的品牌廣告主,與有能力支持投放的測試投放系統合作,共同完成A/B測試。
不難發現這個需求會涉及到多個項目角色,意味著將有復雜的推進流程,需要提前拆解與規劃,才能保證項目進度可控。
△ 測試項目流程與分工
我們可以分三步來進行:
1. 找到項目關鍵步驟
關鍵步驟是將一個項目的推進拆解成多個階段,找到關鍵步驟,也就知道了每個階段的主要任務,可以進一步分工來推進項目。
如何找到關鍵步驟?我們可以從項目目標中提取。測試項目的目標是找到設計因素與投放點擊效果的影響關系。這個目標里提取重要的關鍵詞為:設計因素、投放點擊效果、影響關系。面對這些關鍵詞我們會有疑問產生如「測試設計因素是什么?有哪些?」、「如何投放」、「如何判斷影響關系」,再進行串聯梳理,得到完整步驟流程。
2. 明確角色任務與產出
每個階段的主要任務,可以拆解分工給項目角色,項目角色也就有了各自的任務,角色產出物則是任務執行的結果,將直接推動項目進入下一環節。所以為了順利推進項目,我們需要根據項目實際情況評估角色任務分工是否合理,產出是否滿足項目要求。
比如在「設計測試圖片」這個步驟中,有兩個分工方案,讓廣告主或我們自己設計圖片。我們從項目時長、溝通成本、潛在風險等維度進行評估,判斷廣告主把控變量不嚴謹會導致測試數據無效,且外部合作溝通成本較高時間不夠,所以最后將設計圖片的任務分給了內部。
3. 預判項目風險
對流程規劃得越詳細,在后續合作過程中越容易把控項目的節奏,項目風險越低。
再如「設計測試圖片」這個關鍵步驟,詳細展開執行流程會發現涉及到交互設計師、兩方視覺設計師、廣告主四個角色。對于項目執行角色,產出物的質量及按時交付尤為重要,所以重點把控輸出準確性與完成時間。對于項目決策角色,也就是決策最終投放哪些圖片的廣告主,溝通配合尤為重要。所以基于規避風險也對項目流程進行了優化,前置在項目之初,要求廣告主提前準備交付內容,規定交付時間及格式,以及充分溝通說明圖片設計產出之后不能進行較大改動。
1. 保證數據有效
測試結論來源于測試數據,我們首先應該保證回收數據的有效性。測試項目的關鍵指標是點擊率,即點擊量與曝光量的比值。當圖片本身曝光量低時,我們認為隨著曝光量增加,點擊率比值波動范圍仍然較大,數據還未穩定在某個區間段,會影響結論準確性,判定為無效數據。發現類似這種問題,我們會和廣告主商量繼續投放,延長測試時間來增加曝光。
2. 充分測試
要想得到可靠普適的結論,需要對比多組樣本的測試結果。對于某個設計因素,我們先進行了單一廣告主的投放數據對比,可以找到投放效果最優和最差的設計水平。然后又將多個廣告主的結果進行對比,會發現存在不一致的情況,驗證了單組樣本結論不能作為類目結論輸出。如果多個廣告主結果一致,或呈現某一趨勢,則結論在該類目可以認為較為普適。
3. 差異分析
對于多組樣本結果不一致的情況,可以從組間因素差異著手分析。
△ 背景設計因素測試結果
上圖是兩個廣告主分別測試背景設計因素得到的結果。第一組的投放結果為實景設計效果更好,第二組則為普通平面設計效果更好。產生這樣差異的原因是什么呢?我們先找出組間可能導致這一結果的因素,分別是顏色和產品。從顏色上看,是否平面+黑色效果更好呢?我們看了其他廣告主結果,否定了這一猜想。再從產品圖來看,兩個產品的識別度是不同的,我們將其他廣告主該因素測試圖按產品是否容易識別分組,最終得出兩組不同的結論:當產品圖易識別時,背景設計對效果影響不大;當產品圖不易識別時,實景圖效果更好。這也就解釋了上圖結果不一致的原因。
回顧整個項目,我個人認為項目中最重要的工作是溝通。下面我來分別談談內外部溝通的經驗。
與外部團隊、廣告主合作推進項目,需要及時有效的溝通,什么是有效的溝通呢?
1. 圍繞對方利益談配合
廣告主只看轉化結果,我們如果只談設計不談轉化,廣告主是不會想要出錢參與項目的,那么項目也將停滯不前。所以在合作前,我們做了項目及團隊包裝,用真實的案例讓廣告主快速理解參與項目所能帶來的價值,并用團隊以往的作品、能力展示讓廣告主了解與我們合作的優勢。廣告主越認同項目價值及我們的專業度,配合度就會越高。
△ 招募PPT中的真實案例介紹
2. 圍繞對方目標來「推銷」方案
在推進項目這一點上,找到目標一致的點更容易促成各方意見達成一致。
我們合作的廣告主很多會選擇外包banner設計,所以廣告主們習慣了做傳說中的「甲方爸爸」,難免會對視覺設計方案有各種主觀意見。比如,某電視廣告主不喜歡紅色圖片設計,想要藍色。對于這樣的分歧,我個人喜歡用引導式的溝通方法。首先,不要急于表達自己或否定對方的意見,可以以疑問句式來猜測對方的目的?!赶矚g藍色是希望用沉穩的顏色來表現產品的高級感嗎?」,如果猜測正確,對方會因自己的需求被理解而更容易溝通,這時再繼續闡述用紅色如何表達高級感;如果猜測不正確,對方會順勢說出原因,我們再圍繞對方的目標進一步溝通。如果對方毫無理由「不喜歡這個設計」,而設計師并不覺得有設計問題,那么我們需要停止專業角度的溝通,從設計是否滿足當前階段項目需求的角度來溝通,方案則更容易被接受。
內部合作分交互、視覺、用研三種不同的崗位角色,在項目中都發揮著自己重要的作用。
交互擅長統籌與拆解問題,團隊的內部溝通能產生多種解題思路,對項目的推進至關重要;在遇到如需要統計學分析、樣本設置等專業問題時,用研可以提供更專業的分析方法與幫助,為我們實現科學分析提供有力支持;視覺設計師對banner設計有豐富的經驗,可以在各個環節中發揮優勢。從項目之初就參與設計因素拆解、測試方案設置,可以補充視覺相關的設計因素,在優化測試方案、風險預期方面都可以提供重要的建議。在結論推導中,對圖中的因素差異敏感度高,可以根據數據提出關鍵猜想。
文章來源:優設 作者:京東設計中心JDC
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
色彩在UI設計中的作用:加深品牌印象與品牌感、引導用戶視覺凹增加易讀性、區分信息交互的狀態、營造氛圍傳遞熱度……
不管是做 UI 設計還是畫插畫,有很多同學覺得自己是因為天賦不夠所以對色彩的敏感度不夠,其實不然。一個可能是大家總結的太少,從來都是憑感覺和運氣去配色,但配色都是有講究的。
一個設計作品呈現到用戶面前,第一眼進入眼簾的就是產品的視覺表現,而產品的色彩在其中起到了舉足輕重的作用,毫無疑問色彩搭配對于設計師來說是非常重要的。那么具體到實際項目中該使用什么怎樣的色彩,需要怎么做呢?
用戶界面是一個設計師用理性思維解決用戶感性需求的窗口。如果對色彩的運用不加以克制,界面可能會顯得花哨而沒有主次;但過于拘謹又容易使界面保守,難以激發用戶情緒,下面以Bee express項目的實例來理性推導制定一套色彩系統。
做過設計的同學應該都知道顏色模式:RGB、CMYK、Lab 等等,這里不做過多的解釋了。另外每個顏色具有一定的性格特征和表達方式,而且都會有正反兩面。雖然每種色彩都有正向性格特征,但是我們在定位主體色之前一定要知道所選擇色彩的負面特征對企業是否會帶來負面的影響,
開始之前我們需要了解在配色過程中需要避免出現的問題,如果你經常出現下列的問題,保證你在試用期內一次性就能拿到全部薪資,emmm……
雖然設計是相通的,但是在不同的設計領域進行配色時,依然會存在巨大的區別。更換品牌的主體色,都不會是因為設計師自己的決定,而是公司在商業策略上優先做出了調整,然后通過品牌視覺上的變更將這個信息傳遞給消費者。
Bee Express快遞、速遞柜業務為主,前期的主色及視覺形象以橙黃色為主,為了避免視覺跳躍性太大,以及后期IP形象(蜜蜂吉祥物)打造,本次品牌色彩升級在原有基礎上優化了色調,以保證后期產品的易用性和延展性,并利用最科學、最適用的方式推導出輔助色,以提升應用視覺的豐富性和感官體驗。
express原主色:
為了不影響原有色調前期的視覺傳播,即在原有主體色的基礎上調整SHB的數值,讓色彩更具視覺沖擊力,在色彩襯托(字體、圖標)更清晰。
通過調整后的主體色也能看出,明亮清晰的主體色(品牌色)也更適合在界面中的運用,為信息傳遞、引導操作、品牌價值帶來更大的提升。
同色系為統一的色相,使用中可以加深品牌色的感知,可以讓界面更有層次,同時可以讓界面保持色彩上的一致性,整體感較強,產生低對比度的和諧美感,給人協調統一的感覺。
具體是指與品牌色 H(色相)一致,通過改變 S(飽和度)與 B(明度)變化產生的色組。分別往淺色/深色方向按均勻數據增減,各產生5個坐標值。
綜上能看出,使用同一色系即可完成一個項目,但是對于中大型項目來說實在是過于單調,沒有太多的層次感,因此我們需要多色搭配為輔。多色的輔助顏色可設定不同的任務屬性和情感表達,再搭配中性色黑白灰,能賦予更多的變化和層次。
根據主體色 H(色相)為基礎,不斷地遞增、遞減 15,在 0-360 之間可以得出 24 個顏色,也就是將 360° 色環分割為 24 份,(24份在360°色環上,每一個色相的角度為15°),最終得到下圖24色。
輔助色需要滿足的兩個條件:
和品牌色有明顯區分:避免所選輔助色感官上給用戶視覺區別與品牌色差距不大,傳遞的調性太過一致;
不能過于突兀:根據色彩原理,互補色是最能與品牌色本色產生視覺感官對比的顏色,但可能會有些突兀。為了讓顏色的輔助起到豐富畫面的作用,而不是反而讓整個版面顯得不和諧,所以選擇互補色的鄰近色作為輔助色,避免直接使用互補色。
基于品牌色可衍生出 3 個輔助色:一個與品牌色傳遞調性有明顯區分的類似色;兩個互補色的鄰近色。
類似色搭配:使用色相相近的顏色,頁面元素不會相互沖突,更加協調有質感。
互補色搭配:選擇使用互補色,最佳搭配是一種作為主色,另一種用于強調。它們有著非常強烈的對比度,用在需要特別強調某個元素時會非常有效。
每一種顏色都有自己的「感官明度」,也就是發光度。根據現有的使用場景,類似色和互補色大都用在同層級的信息展示上,而當我們將最終得到的輔助色擺放在一起之后發現,雖然我們提取出的輔助色明度色值都一致,因為顏色本身自帶的感官明度屬性有所區別,導致視覺上會有明顯的明暗差別。需要通過發光度來進行最終的顏色校正。
校準方式:依次在輔助色上疊加一層純黑圖層,將該純黑圖層顏色模式調整為 Hue(色相),就可以通過無彩色系下的明度色值,進行對比,使色彩視覺感官保持一致(青色和藍色屬冷色調,固需加深)。
根據上面同色系的明度、純度對比規則,對所有定義的輔助色進行明度和純度的輔助色彩輸出,最終得到輔助色色板。H(色相)一致,通過改變 S(飽和度)與 B(明度)變化產生色組。分別往淺色/深色方向按均勻數據增減,各產生5個坐標值
刪除最左側的3種同色系,因明度過低時,顏色已經非常接近于黑色,色相在肉眼上幾乎已經趨于一致。最后得到基于品牌色推導出的全色系色階色板。
配色常常從確定主色開始,根據行業類型和視覺訴求的需要,選擇一種居于支配的色彩作為主調色彩,構成畫面的整體色彩傾向。然后選擇輔色,添加點綴色,最后按照色彩組合的原則完成設計中的需求。
雖然有了以上的配色方式,但一套標準的色彩系統還會包含中性色規范、顏色的使用規范等等。相信解決了大部分的需求,剩下的工作也難不倒大家了。畢竟以上的方式只是給大家提供了一個理性科學的方法,如果想更加優秀,還需要進一步深入地去學習色彩理論知識,多看優秀的配色作品提升審美,總之要多看、多實踐和多思考。
文章來源:優設 作者:能量眼球
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
沒有 switch 就沒有復雜的代碼塊
switch很方便:給定一個表達式,我們可以檢查它是否與一堆case子句中的其他表達式匹配。 考慮以下示例:
const name = "Juliana";
switch (name) {
case "Juliana":
console.log("She's Juliana");
break;
case "Tom":
console.log("She's not Juliana");
break;
}
當 name 為“Juliana”時,我們將打印一條消息,并立即中斷退出該塊。 在switch函數內部時,直接在 case 塊使用 return,就可以省略break。
當沒有匹配項時,可以使用 default 選項:
const name = "Kris";
switch (name) {
case "Juliana":
console.log("She's Juliana");
break;
case "Tom":
console.log("She's not Juliana");
break;
default:
console.log("Sorry, no match");
}
switch在 Redux reducers 中也大量使用(盡管Redux Toolkit簡化了樣板),以避免產生大量的if。 考慮以下示例:
const LOGIN_SUCCESS = "LOGIN_SUCCESS";
const LOGIN_FAILED = "LOGIN_FAILED";
const authState = {
token: "",
error: "",
};
function authReducer(state = authState, action) {
switch (action.type) {
case LOGIN_SUCCESS:
return { ...state, token: action.payload };
case LOGIN_FAILED:
return { ...state, error: action.payload };
default:
return state;
}
}
這有什么問題嗎?幾乎沒有。但是有沒有更好的選擇呢?
從 Python 獲得的啟示
來自 Telmo 的這條 Tweet引起了我的注意。 他展示了兩種“switch”風格,其中一種非常接近Python中的模式。
Python 沒有開關,它給我們一個更好的替代方法。 首先讓我們將代碼從 JavaScript 移植到Python:
LOGIN_SUCCESS = "LOGIN_SUCCESS"
LOGIN_FAILED = "LOGIN_FAILED"
auth_state = {"token": "", "error": ""}
def auth_reducer(state=auth_state, action={}):
mapping = {
LOGIN_SUCCESS: {**state, "token": action["payload"]},
LOGIN_FAILED: {**state, "error": action["payload"]},
}
return mapping.get(action["type"], state)
在 Python 中,我們可以使用字典來模擬switch 。 dict.get() 可以用來表示 switch 的 default 語句。
當訪問不存在的key時,Python 會觸發一個 KeyError 錯誤:
>>> my_dict = {
"name": "John",
"city": "Rome",
"age": 44
}
>>> my_dict["not_here"]
# Output: KeyError: 'not_here'
.get()方法是一種更安全方法,因為它不會引發錯誤,并且可以為不存在的key指定默認值:
>>> my_dict = {
"name": "John",
"city": "Rome",
"age": 44
}
>>> my_dict.get("not_here", "not found")
# Output: 'not found'
因此,Pytho n中的這一行:
return mapping.get(action["type"], state)
等價于 JavaScript中的:
function authReducer(state = authState, action) {
...
default:
return state;
...
}
使用字典的方式替換 switch
再次思考前面的示例:
const LOGIN_SUCCESS = "LOGIN_SUCCESS";
const LOGIN_FAILED = "LOGIN_FAILED";
const authState = {
token: "",
error: "",
};
function authReducer(state = authState, action) {
switch (action.type) {
case LOGIN_SUCCESS:
return { ...state, token: action.payload };
case LOGIN_FAILED:
return { ...state, error: action.payload };
default:
return state;
}
}
如果不使用 switch 我們可以這樣做:
function authReducer(state = authState, action) {
const mapping = {
[LOGIN_SUCCESS]: { ...state, token: action.payload },
[LOGIN_FAILED]: { ...state, error: action.payload }
};
return mapping[action.type] || state;
}
這里我們使用 ES6 中的計算屬性,此處,mapping的屬性是根據兩個常量即時計算的:LOGIN_SUCCESS 和 LOGIN_FAILED。
屬性對應的值,我們這里使用的是對象解構,這里 ES9((ECMAScript 2018)) 出來的。
const mapping = {
[LOGIN_SUCCESS]: { ...state, token: action.payload },
[LOGIN_FAILED]: { ...state, error: action.payload }
}
你如何看待這種方法?它對 switch 來說可能還能一些限制,但對于 reducer 來說可能是一種更好的方案。
但是,此代碼的性能如何?
性能怎么樣?
switch 的性能優于字典的寫法。我們可以使用下面的事例測試一下:
console.time("sample");
for (let i = 0; i < 2000000; i++) {
const nextState = authReducer(authState, {
type: LOGIN_SUCCESS,
payload: "some_token"
});
}
console.timeEnd("sample");
測量它們十次左右,
for t in {1..10}; do node switch.js >> switch.txt;done
for t in {1..10}; do node map.js >> map.txt;done
clipboard.png
人才們的 【三連】 就是小智不斷分享的最大動力,如果本篇博客有任何錯誤和建議,歡迎人才們留言,最后,謝謝大家的觀看。
原文:https://codeburst.io/alternat...
代碼部署后可能存在的BUG沒法實時知道,事后為了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。采用了全局單例模式,將組件的共享狀態抽離出來管理,使得組件樹中每一個位置都可以獲取共享的狀態或者觸發行為。
那么什么是狀態呢?我把狀態理解為在沒有使用vuex時,在當前組件中data內需要共用的數據為狀態。
vuex使得狀態或行為成為了共享的狀態,所共享的狀態或行為可以在各個組件中都可以訪問到,省去了子父或子子之間傳遞變量,提高了開發效率。
當我們不使用vuex時,對于組件之間傳遞信息會較為麻煩。
App.vue文件中:
<template>
<div id="app">
<Fruits :fruitList="fruitList"/>
</div>
</template>
<script> import Goods from './components/Goods'; export default { name: 'App',
components:{
Fruits,
Goods
}, data(){
return{ goodList:[
{
name:'doll',
price:12 },
{ name:'glass',
price:10 }
],
}
}
}
</script>
<style>
</style>
Good.vue文件中:
<template>
<div class="hello">
<ul>
<li v-for="(good,index) in goodList" :key="index"> name:{{good.name}} number: {{good.number}} {{index}}
</li>
</ul>
</div>
</template>
<script> export default { props:['goodList'],
}
</script>
<style>
</style>
首先先創建一個js文件作為兩兄弟之間傳輸的紐扣,這里起名為msg.js
//創建并暴露vue import Vue from 'vue';
export default new Vue
兄弟組件Goods:
<template>
<div>
<button @click="deliver">點擊</button>
</div>
</template>
<script> import MSG from '../msg';
export default {
data(){ return{
msg:'hahah' }
},
methods:{
deliver() {
MSG.$emit('showMsg',this.msg)
}
}
}
</script>
<style>
</style>
兄弟組件Fruits:
<template>
<div>
<div class="fruit">
{{text}}
</div>
</div>
</template>
<script> import MSG from '../msg';
export default {
data(){ return{
text:'' }
},
created(){ this.getMsg()
},
methods:{
getMsg(){
MSG.$on('showMsg',(message)=>{ this.text = message
})
}
}
}
</script>
<style>
</style>
在App組件中的代碼:
點擊按鈕:
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
一套 UI 界面當中,核心的 APP 圖標是用戶每天都要接觸、經常使用的視覺組件和交互對象。設計圖標的時候,要用到大家最熟悉的元素才能貼合用戶認知,要醒目、統一,也要足夠「隱形」,避免喧賓奪主。今天這篇文章,來自著名的 Tubik Studio 團隊,他們為華為旗下的 EMUI 10 系統設計了核心的圖標系統,來看看他們的設計過程吧。
我們總不會低估一個操作系統基礎圖標,對于產品的可用性和合意性的影響?;A圖標雖然小巧,但是對于整個操作系統而言,總是極具影響力的,因為他們是用戶界面的核心元素,幫助用戶快速直觀地在系統中定位、瀏覽、導航。但是,對于設計師而言,圖標的設計始終是挑戰,它看起來最為簡單,但實則為最為艱難的工作。
圖標需要,讓人一目了然,但是也要具備良好的可識別性,在傳統和創新之間達到平衡。這一次,Tubik Studio 設計團隊將要給華為的 EMUI 10 來設計圖標,而這篇文章將會為你揭示背后的設計過程。
這次的項目主要是由 Sergii Valiukh 、Arthur Avakyan 和 Polina Taran 來負責。這次的設計項目從最初的探索構思入手,逐漸開始繪制草圖,探索樣式,一直到最后打磨,完成設計。
為華為 EMUI 10 系統的用戶界面設計基礎的圖標
我們在 2019 年夏季,收到了來自華為的邀約,這次的項目要重新設計 EMUI 這套基于 Android 系統的用戶界面基礎圖標,這套圖標會用來適配華為旗下的旗艦手機,這些圖標將會隨著新版的系統部署到這些手機產品當中。我們的任務,是創建總計 54 款符合當下潮流趨勢的圖標,這些圖標要能夠貼合品牌和已有用戶的偏好,并且能夠吸引新的用戶。
這個圖標設計項目如今已經在當下的華為旗下手機產品中應用且部署好了,最早使用這套圖標的智能手機型號為 Mate 30 ,緊接其后的是 P40。
在整個操作系統中,這些圖標是始終位于用戶視野以內、最基礎的最核心的交互元素,通常用戶每天都會同這些核心的基礎 APP 進行交互,有時候一天多達幾十次,因此它們應當具備良好的功能性,還應該足夠美觀,給用戶帶來滿足感。同時,這套圖標的設計,也應當足夠統一,以協調的體驗切入到整個 EMUI 的設計系統當中,貼合整體的樣式特征。
所以,我們使用了一整套圖標網格系統,應對不同需求,在設計的過程中,這套網格有助于確保所有圖標外部尺寸的一致性,也保證了內部元素的統一性。
為了發掘全新的視角,我們決定從傳統的手繪圖標開始,尋找重新塑造圖標設計的方法。這些圖標所涉及到的元素,早已為全球數百萬用戶所熟知,要重新設計圖標外觀,并且還要成套且統一,這本身就是一個巨大的挑戰。
比如「電話」圖標所對應的聽筒元素、「信息」圖標所對應的氣泡對話框這樣的元素,是不可能完全拋棄重新創造的,所以我們的真正的切入點是在形態和色彩上尋求解決方案。
越是簡單的東西,重塑起來就越難。
在實際的設計過程中,我們嘗試了數以百計的造型變體,從完全放飛、非常規的的外部造型,到極其常規,大家熟知的造型解決方案,我們都逐一試過。而在色彩上的嘗試相對會顯得更加具有實驗性:我們嘗試使用明亮的紫羅蘭色、栗色和淺綠色來進行混搭。
當然,我們很清楚,這樣的實驗性的工作是探索過程中的一小步,但是它是必須的,是創造新設計的種子,是尋求正確答案的必經之路。
在設計過程在,有一件有趣的事情發生在設計「相機」圖標的過程中。我們嘗試過很多不同的圖標造型,不同的元素,不同的樣式,但是其中始終有一個細節是不變的,那就是右上角的小紅點。這是為了暗示用戶,華為的攝像頭模組來自徠卡,這個紅色的小點就是向其致敬的標識。
而下面的概念設計,則是強化了圖標之間的幾何輪廓的差異,從而提升圖標在智能手機屏幕上的對比度和識別度。
下一步,我們基于幾何圖形外觀差異性,設計了多種造型不同但同樣優雅的圖標。在基于這種風格概念進行延伸的過程中,我們會優先考慮圓形的元素。而「天氣」圖標明顯既不適合圓形也不適合方形來呈現,所以我們使用的是云和太陽兩種元素的組合?!稿X包」圖標使用的是矩形,然后搭配卡片的組合。盡管造型整體上是相對自由的,但是所有的圖標都是遵循圖標網格,并且在視覺權重上盡可能統一。
在色彩和樣式上,我們則更加傾向于漸變。沒有色彩漸變,純扁平的圖標顯得過于幼稚和「古早」,沒有足夠的品質感,所以,我們在新的圖標設計上,開始加入漸變色彩,提升質感。
不過,在最終版本當中,我們還是保留了圖標外部的圓角矩形的外輪廓,然后將圖標元素的內徑進行了適當地縮減,漸變和核心的簡約幾何特征依然是整套設計的最高優先級。
這套設計方案展現了在整套 UI 界面中,圖標這個小元素的設計上所需要投入的精力和潛在的難度。想要圖標足夠協調、美觀、易用還要貼合品牌特征、還要區別于以往,是一件相當不容易的事情。
細節里藏著魔鬼,任何細小的元素、線條輪廓、色彩的變化都可能會在屏幕上放大、被感知到。
文章來源:優設 作者:Tubik Studio藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
It is ultra experience
ISUX Design Trend Report
——————————
身為用戶體驗設計師,無時無刻不被世界上的新事物沖刷著認知——互聯網紅利下降帶來變化莫測的商業動向、循著摩爾定律野蠻生長日新月異的新技術、各類亞文化群體催生出多元復雜的圈層文化、腦洞口味越來越獨特的年輕人,甚至眼下席卷全球的黑天鵝事件......
任何一個新事物的悄悄冒頭,都有可能在未知的將來影響著用戶體驗設計師。我們能做的是,在起初感受到微微震幅時,便沿著震感逐步尋找源頭,并思考未來的發展走向。趕在變化降臨前先擁抱變化。
本文通過研究近一兩年科技、社會文化以及自身用戶體驗領域的變化,從用戶體驗領域關鍵的用戶、媒介(設備與應用)、交互行為、信息與場景的五個角度出發,探索用戶體驗設計未來的趨勢,希望能帶來啟發。
隨著人工時代到來,過去機械的單向交互方式逐漸被打破,機器漸漸演化成了會主動“觀察”真實場景,“感受”用戶情感,預判用戶意圖并自動完成任務的貼心小棉襖。機器如何為人們提供更智能便捷的服務,未來還有非常大的想象空間。
隨著人工時代到來,過去機械的單向交互方式逐漸被打破,機器漸漸演化成了會主動“觀察”真實場景,“感受”用戶情感,預判用戶意圖并自動完成任務的貼心小棉襖。機器如何為人們提供更智能便捷的服務,未來還有非常大的想象空間。
隨著AI技術的發展,智能設備可以越來越無縫地將數字世界和物理世界嫁接起來,主動感知用戶所處情境并智能提供相應服務。
在2019的 Google I/O 大會上,Google Lens 展示的AR點菜功能可以智能識別用戶掃描的菜單并將美食網站上的相關推薦直接呈現在屏幕上。
當用 Google Lens 識別到小票信息時,可快速提取小票上的金額,且可自動彈起計算器快速幫助用戶計算人均消費,節省人工計算的時間成本。
隨著信息入口從數字空間延伸到周圍的物理空間中,未來萬物皆可為用戶體驗的媒介,設計師未來在設計的時候需要注意:
尋找適合的打通真實世界的切入點:在陌生語言、信息復雜或者難以處理等苛刻的環境下,充分發揮智能設備對信息智能讀取、批量識別與翻譯等強大能力,幫助用戶完成任務;
將用戶旅程的上下游串聯:根據生活常識和經驗預判用戶行為目的,前置推薦服務;
更加系統細心地考量干擾因素:真實場景是動態變化的,需要更全方位考慮光線的強弱、多源的噪音、實體的可視性、人員和事件的打斷等因素。
為了完成一項任務,用戶往往需要借助多個應用來回切換配合,使用起來瑣碎麻煩。如今應用越做越強大也越復雜,過去僅僅解決單一場景的解決方案不再適應于用戶對于完成任務的訴求。
Google Assistant 的新能力 Duplex on the web 可以通過自動跨應用任務處理來簡化用戶旅程。只需要用戶發出語音指令“預定一輛去某地的車”,助手便可自動跨郵件、日歷、付款等應用調取信息、自動根據使用習慣做選擇,并自動填寫信息,而用戶全程需要的只是在關鍵節點輕敲“確認”即可。
2019年隨著 iOS 13 的更新,“快捷指令”推出了“自動化”能力,用戶通過“if...then...”語法便可為自己的App設計一套程序,實現如:“當我回到公司時提醒我打卡”、“每天早上10點給我的女朋友發送一條表白短信”等能力,將不相關的場景動作串聯字一起自動化執行,大大節省人工操作成本。
提升使用效率是用戶體驗設計孜孜不倦努力的方向之一。在利用新技術進一步簡化用戶旅程時,設計師可以充分利用以下因素:
借助語音輸入:比起界面觸控操作,語音交互的直達性可以“穿透”復雜界面,讓設備第一時間明確用戶目標;
基于用戶行為形成習慣記憶:對用戶長期重復的行為做分析處理,構建用戶習慣模型并主動提供服務;
適當考慮專家級用戶:隨著部分用戶的智能設備使用水平越來越高,可以考慮為專家用戶提供自定義操作腳本,滿足其自身的獨特需求。
隨著人臉識別、表情識別、肢體跟蹤等技術的提升,機器逐漸學會感性語言,主動感知用戶內在情感和心理需求。
2019年1月的CES展上起亞亮相的互動式“情感駕駛空間”技術,可通過傳感器讀取用戶的面部表情、心率等反應,調整駕駛空間內的燈光、影片類型、音樂風格等,舒緩艙內乘客心情,由此提供更人性化的出行體驗。
用戶總是會期待更貼心的服務,設計師未來對同理心的情感嗅覺更加敏銳:
利用感性線索定位用戶情緒:需要通過面部表情、特殊時間節點或者識別到的關鍵詞,對用戶情緒進行理解和定位,判斷用戶情感理解用戶內心訴求是自由探索、趣味娛樂、或者靜謐修行并提供符合用戶當下心境的服務。
綜合使用感性元素進行設計:通過使用線條、色彩、聲音和動作等傳達并喚起相對應的情感,提供更加人性化的體驗。
更智能的服務提供方式會讓人們生活擁有更多可能性,但一旦火候把握不得當,可能就會造成對人們生活的野蠻入侵。關于如何讓科技更好造福于人們,早在上個世紀,施樂帕克研究中心提出了寧靜技術(Calm Technology)的愿景,認為影響最深遠的技術應該是隱匿不見的,它們如纖維般融入日常生活,絲絲入扣,直至不可分辨。
隨著科技的發展,設計師對新技術不應是不加克制地應用,而應該潤物細無聲般地提供服務,幫助人們從繁雜喧囂的數字世界中解脫出來,將寶貴的注意力資源投放在讓生活更美好的事物上。
回顧人類和機器的交流語言,從命令行界面、圖形用戶界面到自然用戶界面,人機交互方式越來越貼合人與人之間更自然的交流方式,其背后是心智模型與實現模型的高度擬合的趨勢。
在自然用戶界面中,為滿足新形態智能硬件對新接口的需求,以及人們對更豐富強大的交互方式的自然訴求,越來越多的自然用戶界面被開發出來。語音交互和隔空手勢交互便是近幾年迅速發展并落地的兩種交互方式。
為了讓機器更好地讀懂用戶的身體語言,能夠感知深度信息的攝像頭走進了日常手機。2019年國內外手機廠商的發布大會上,LG 手機 G8 ThinQ 以及華為發布 Mate 30 系列推出的隔空手勢,可實現一些簡單的諸如滑動、切歌、截屏等效果。
除此以外,隔空手勢支持更加細微的手勢,如旋轉、揉搓等,可以更直觀、更靈活的方式操控界面,讓用戶獲得一種像魔術師用意念控制事物運作的快感。
對于隔空手勢操作網上的言論褒貶不一,其中爭議性最大的就是隔空手勢宛如“殺雞用牛刀”,明明可以用更加精準的手勢觸控,為什么還要用看似很酷炫其實精準度更低的隔空手勢操作?
隔空手勢并不是要替代觸控手勢成為主流的人機交互方式,更多是對情境式障礙場景的補充。在某些場景下,用戶使用設備的條件可能是充滿干擾的。想想看當你邊看手機食譜邊炒菜的時候、邊煲劇邊剝小龍蝦的時候、疫情期間出門佩戴橡膠手套無法正常觸控手機屏幕時.....隔空手勢是不是特別好用?
每個人在特殊的場景下都有可能面臨感官障礙,未來的設計也應該更多地考慮情境式障礙的場景,讓用戶無論身處何時何地依舊能一如既往無障礙地使用設備。
語音交互作為更趨近于人與人之間最自然的交流方式,近些年有許多發展的突破點。
在發展主線上,語音交互趨向更自然、更人性化、更個性化。過去反人類的一些溝通方式慢慢被“調教”。此外,多人會話場景下的技術方案日漸增多。
2019的 Google I/O 大會展示了一個視頻片段,視頻中的兩位嘉賓相繼吐槽,經常出現針鋒相對難以聽清的時候,這時用戶可以調節音源音量選擇性增強自己關注的人物聲音,讓另一個人“靜音”。
滑動選擇音源
此外,語音交互除了在智能音箱領域廣泛應用以外,也逐漸應用在廣告等更多的傳播媒介中,刷新人們日常使用體驗。2020年2月索尼提交了一項廣告播放新專利。當用戶在觀看電視節目時,如果出現廣告,只要站起來大喊廣告中對應品牌的名字,便可直接跳過這個廣告。
設計師在語音交互場景下,需要留意以下幾個比較容易被忽視的因素:
用戶語音交互習慣培養:如今還處于培養用戶語音交互使用習慣階段,設計師需要更多地考慮應用的語音交互規則如何才能更趨近于人們日常的溝通習慣,并進一步為人們的社會習俗所接納。
真實場景下的多人音源:在現實情境中, 在多人對話場景下將面臨音源不清、穿插停頓、噪音過多等影響體驗的情況,由于計算機聽覺分析能力開始從單人音源拓寬到了多人音源,多人對話解決方案上還有很大想象空間。
改變傳統的視聽體驗:在使用場景上,語音交互接口也將逐漸運用到更多的媒介上,更全面地刷新用戶體驗。
人類擁有雙手、眼睛、耳朵和發聲的嘴巴,但是并不總是在每個使用場景下都能自如地使用:在安靜的自習室下聲音收到限制,在駕駛場景下注意力受到限制,在雙手拎著東西場景下雙手受到限制......但目前許多產品設計都建立在用戶能完整使用感官功能這一理想化的基礎上。
未來的發展趨勢傾向于將視、聽、觸、嗅等多通道信息完美整合起來,綜合使用多種輸入通道和輸出通道,根據用戶使用場景用最恰當的方式傳遞服務,滿足用戶多方位的需求。
盡管喬布斯曾斷言3.5英寸是手機的黃金尺寸,但作為人們日常內容消費與娛樂的窗口,手機屏幕毫無疑問地變得越來越大,甚至超出傳統物理限制。人們對大屏享受的追求與設備攜帶便捷性之間的矛盾由來已久,硬件形態的變化對舊有的用戶體驗設計思路帶來的新的挑戰。
屏幕橫縱比越來越大,而人類的手部具有先天限制,曾經慣用的界面布局方式在高橫縱比的屏幕上可能無法被大拇指無障礙全覆蓋,使得越來越多的設計更加重視利用移動屏幕下半部分。
操作與信息進一步下移:
高德地圖、蘋果地圖的搜索框下移,方便單手操作用戶快速激活輸入框;
影視資訊平臺IMDB強化底部標簽欄功能,雙擊“搜索”tab即可激活輸入框,無須艱難地觸摸頂部。
即時戰斗類手游皇室戰爭的說明卡片主要展示在下半部分,方便用戶進行卡片上的相關操作。
底部導航被賦予更多能力:
Pocket的底部標簽欄現在兼任漢堡菜單功能,在激活狀態下再次點擊主頁icon可選擇主頁上須展示的內容。
利用下滑手勢代替點擊:
Snapchat的許多表示前后進退關系的頁面都不是”返回“按鈕,而是向下箭頭,用戶可下滑退出當前頁面。
為了解決設備形態和人類手部先天限制之間的矛盾,折疊屏誕生瀏覽并顛覆舊有的界面設計方式。
更靈活的信息布局
過去在單屏設計下,考慮到用戶注意力由上到下縱向衰減,因此信息布局更多是按照優先級從上往下排序。而折疊屏中,屏幕展開后便可以開辟出更大的可利用空間,將次級頁面或者較為重要的內容曝光在第二屏,對信息的布局將帶來全新的變化。設計師為保證大小屏下順暢的閱讀體驗,需要對信息模塊在不同空間布局下的流動性有更強的把控能力。
更便捷的多任務操作
在過去的單屏體驗中,用戶只能將注意力完全集中在當前的界面中,一次只做一件事。但在實際生活中,用戶面臨的情景往往是主線任務和支線任務的頻繁交錯,并且根據會任務不同的性質自由調動自己的注意力重心,如邊看視頻邊聊天、邊看直播邊逛街等等。在折疊屏中,設計師可以探索更多主線和支線交錯進行的場景,利用折疊屏帶來的更大的屏幕空間,可以讓用戶在不離開主線場景的基礎上進行支線任務的處理,大大節約了在不同App上來回切換的操作成本。
更直觀的拖拽交互
此外,隨著多任務處理越來越廣泛使用,拖拽交互將成為重要的交互模式之一。文本、表情包、圖片、視頻等交互對象,不再需要經過復雜的分享轉發流程才能在不同App中流轉,通過拖拽的方式可以更直觀地進行交互。
雙面屏互動玩法
外折疊屏在折疊狀態下可轉為雙面屏,等于是給用戶增加多一個觀看視角。例如華為 Mate X 的鏡像拍攝可以讓被拍攝者即時獲知自己的鏡頭影像是否滿意,這一拍女友神器有望成為直男拍攝終結者。在未來更多的多人觀看和互動玩法將被開拓出來。
華為Mate X 的鏡像拍攝
未來隨著5G通訊技術的成長,越來越多的設備可以同時加入物聯網,人們的生活將被各種智能設備圍繞,設計師需要參與更多屏幕外的設計,讓不同設備串聯在一起協同合作,讓用戶能更加自在地享受科技的便利。
席卷全球的新冠疫情讓數十億用戶乖乖待在家里。過去需要花費大量精力去教育的用戶使用習慣因為疫情紛紛轉變。云購物、云蹦迪、云賞櫻、云監工......人們足不出戶便可還原許多線下場景。隨著用戶線上和線下生活的界限進一步模糊,用戶對于應用的效率和情感訴求也發生了變化。
疫情讓遠程辦公學習需求劇增,多人協作場景越來越頻繁,許多企業隨之升級了電話、視頻會議、文檔制作等多人協作效率軟件。過去僅僅考慮少人場景協作的方式不適用,設計師需要比以往更多地考慮多人協作場景下,如何對海量密集的信息進行分析處理和展示。
在學習方式上,由于線下學習轉移至線上,學生群體對于娛樂向軟件也有了效率訴求。為了順應用戶訴求變化,2020年5月QQ推出學習模式,屏蔽娛樂性的內容推送,讓學生更專注在學習上。
除了效率訴求急劇提升以外,隨著長時間的線上學習與辦公所產生社交疏離感和缺失感,人們對于線上學習工作的情感化訴求也進一步增強。
2020年推出的plagi遠程辦公軟件支持設置每個人的avartar形象,讓大家在遠程辦公時依舊能時刻感受到彼此的存在。在完成任務時還可以放鞭炮慶祝,讓員工能感受到親密無間的線上辦公體驗。
設計師需要更加關注如何讓線上生活進一步與現實生活圈和時間線接軌,通過拓展真實社交下的更多伴生行為讓線上也能還原線下的真實場景細節和互動體驗,以彌補用戶對真實社交的缺失感。
疫情的發生加速了人與信息之間的連接。人們越來越習慣將自身的身體資料、心情狀態等信息沉淀在智能設備上。
為了做好廣大市民群眾的健康監測服務,輔助疫情防控工作,微信和支付寶在2020年年初都上線了健康碼服務,不同顏色的健康碼代表人們不同的健康情況,市民出入特定場所都需初始健康碼。
隨著人的數據化越來越深入,個人身份信息的線上化在各平臺上將成為更加通用的能力。設計師需要考慮如何更自然更低成本地將線下動態變化的資料信息線上化,更有效地對用戶信息進行加工處理,以及記憶用戶的使用習慣和行為,以便幫助用戶更地完成任務。
疫情的出現加速了線下生活線上化,短短時間內我們看到日常習以為常的應用為響應疫情下的特殊需求紛紛出現改造,釘釘、QQ群被改造成上網課、批改作業的地方,醫療衛生公眾號開辟了實時疫情播報與辟謠通道,無接觸設計和服務需求異常突出......這也啟發了設計師需要保持對突發事件的敏感力以及應急能力,在日常生活中留心思考,為日后突發事件提供充足的場景支撐。
在洶涌的資本語境下,互聯網設計師裹挾在商業驅動的結果導向中狂奔,對設計的倫理和責任鮮有發聲,但伴隨著互聯網紅利退潮,發展放緩,狂奔之下的人本問題也逐漸浮出水面。在大趨勢下,UX設計師需要培養自身設計對倫理和責任的敏感度,在滿足商業目的外,重拾節操,為多群體,為大社會設計,更加注重“以人為本”。
包容性設計師指在做設計產品的時候,考慮到各類用戶的訴求,輸出具有包容性的設計方案。包容性設計依舊是2020年設計主題之一,伴隨著互聯網產品全球化,在通用性和包容性上也提出了新的要求。
為身障人士設計
三星在2019年針對東南亞市場推出了一款讓聾盲人士和健全人實時交流的app:Good Vibes,盲聾人輕擊屏幕輸入摩斯電碼,預先連線好的另一臺手機就會顯示從盲聾人發來的短信。健全人用普通的文字輸入回復,在盲聾人這一端就會翻譯成摩斯電碼、以手機振動的方式讀出短信內容。
GOOD VIBES宣傳視頻
餓了么:在餓了么送貨騎手中,約8%受色盲色弱的困擾(全國男性群體中紅綠色盲色弱占比達8%-9%,餓了么騎手男性占比90%),為此餓了么設計團隊在2019年對app的進行了重新設計,包括使用WCAG無障礙色彩對比度,以及無障礙色盤,以及調整字階,使用輔助圖形等設計手段來解決部分騎手在送貨途中使用APP的痛點問題。
餓了么UED:《為騎士創造平等 — 配送 App 的包容性設計》
跨年齡段設計
谷歌助手禮貌功能 ( Google Pretty Please ) :開啟谷歌助手禮貌功能后,如果使用者在下達指令的語句中包括“Please”,谷歌助手會對禮貌的請求表示感謝,以此培養孩子的禮貌言行。
Google Pretty Please功能宣傳
Swift Playground:當10后小學生VITA君的編程課被“可敬的發量”刷滿彈幕時,Swift playgrounds功不可沒,這款為兒童新手學習編程的軟件,用趣味的游戲方式為4歲以上低齡用戶提供了一個學習編程的低門檻平臺。
為性別平等而設計
蘋果emoji:回看歷年蘋果emoji的更新,從膚色平等,到性別、性向平等,再到為殘疾人設計,2020年再為跨性別者增加新表情,性別平等依舊是包容性設計中重要一環。
Airbnb插畫:愛彼迎在插畫系統中,也為不同膚色,不同職業,不同性別,以及身障人士進行了人物的繪制。
2019是互聯網科技隱私問題沉浮的一年,國外有Facebook因泄露隱私收到史上最大罰單,國內則打響了“人臉識別第一案”。籠罩在隱私信任危機下,個人信息和數據立法突飛猛進,美國推動《加州消費者隱私法案》,國內也將在2020年出臺《個人信息保護法》和《數據安全法》。
MIUI12推出隱匿面具功能
Android開放生態導致的權限隱私問題一直被用戶所詬病,某些APP存在用戶不授權就無法使用情況,針對這一情況,MIUI12推出了隱匿面具功能。當用戶在開啟某些APP要求授權權限時,可以選擇空白通行證進行授權,從而保護用戶真實信息。
在MIUI12的更新中,還推出了照明彈、攔截網兩項隱私保護功能
iOS 14剪貼板提醒
在iOS 14的更新中,保護用戶隱私方面進一步升級。
其中剪貼板提醒設計很貼心,當用戶打開應用,如果該應用讀取了你剪貼板的內容,會在系統頂部彈出提示,用戶能在第一時間意識到剪貼板內容被讀取,幫助用戶更好的保護自己的隱私內容。
科技的發展是一把雙刃劍,互聯網產品的發展給用戶帶來便捷和沉浸體驗的同時,也使得用戶沉溺于科技所帶來的惰性和投食之下,逐漸喪失了對真實生活的把控權,被科技綁架。
數字福祉(digital wellbeing)近年被頻頻提起,指科技產品需要權衡好數碼產品和真實生活之間的平衡,防止數碼產品過渡分散用戶的注意力而影響生活質量。
Android Q 專注模式 Google Android Q Focus Mode
Android Q的更新加入了專注模式,用戶在專注模式下,可以在系統層面快捷地關閉使你分心的應用,讓你聚焦于更重要的事情。
防沉迷系統升級
推薦技術的進步,產品體驗的升級,給用戶帶來了更合胃口的菜式和沉浸體驗,但同時也被冠上了“電子海洛因”的稱號。游戲或者內容產品的防沉迷系統依舊會是數字福祉下不可避免的趨勢。
王者榮耀在2020年升級防沉迷系統,對青少年的娛樂時間和點券充值的限制進行了進一步升級。承接話。B站在2019年推出青少年模式,在該模式下,使用時長和內容推薦等做了定制化處理。
2020年的UI設計趨勢,一方面是對往年風格的衍變和細化,另一方面,在扁平克制的界面風格盛行后,設計師們向往更自由、更突破的視覺表達。
2019年iOS 13深色模式姍姍來遲,緊接著大廠APP相繼推出此功能。在2020年,深色模式會繼續普及外,也會在可視性和實現成本方面有更多細節打磨和研究。
設計趨勢的發展是螺旋式上升的,在扁平化設計流行之后,對物體的擬真再一次回歸設計圈,新擬態以一種對舊擬物風格的再創新,重新流行起來。
新擬物風格(Neumorphism)緣起于設計師Alexander Plyuto發布在dribbble的一組作品,以投影重新對扁平界面進行了塑造,模仿出類似浮雕的視覺效果,感受耳目一新,引起大量設計師相盡模仿。
新擬態的實用性和可落地性有待商榷,但是作為一種新的風格受到設計師擁躉,也不失為下一波風潮到來前的靈感繆斯。
WWDC2020對mac OS的更新也重新定義了新擬態設計語言,在mac OS新系統Big Sur中,圖標的設計增添了輕微的漸變、投影、高光,以此來營造圖標內元素之間的縱深關系。
在扁平簡潔UI風格盛行之后,豐富的色彩依舊是設計趨勢之一,大面積色塊,碰撞配色,帶來更具沖擊感的視覺體驗。
UI界面逐漸扁平,色塊圖標弱化,為突出頁面重心和內容,iOS 11在界面標題上使用更大的字號,更粗的字重。近年在大標題的風格衍變下,文字在傳達信息外,也開始有了裝飾性作用,采用超大字體,成為頁面排版美化的一部分。
大圓角的風格會繼續延續,相較以往,卡片的處理圓角會更大,隨之帶來的是多的留白處理,結合大字號,帶來更透氣通透的視覺感受。
Mac OS Big Sur的界面相對舊版本采用了更大的圓角;系統圖標的設計統一成圓角矩形。
UI插圖的豐富體現在樣式和內容上,樣式上開始3D化,內容上更注重插圖敘事的表達。
3D插圖
3D圖形往年更多運用在動態影像或運營類設計中,隨著3D的普及運用,UI插圖也會迎來3D化,給用戶帶來更立體,更新鮮的視覺感受。
講求敘事表意
相較于往年追求形式的UI插圖,新趨勢下的插圖更講求功能性,每一副插圖都承載一定的作用——傳達功能信息或透傳品牌情感;同時插圖更講求畫面表意和情節,給用戶敘事性的視覺體驗,增進用戶和產品之間的情感聯系。
插圖組件化
插畫的流行,隨之而來的是成本的水漲船高——一套系列插圖為保持風格統一,往往由唯一設計師繪制,同時為兼容各類場景,設計師往往要繪制多張。
為解決插圖的成本和效率,插圖開始以組件化的方式進行繪制——插圖設計師將插畫進行拆分繪制——不同人物,不同場景,不同物件等,再通過組件化的拼接合成,使用組件的設計師可以根據需求場景自由組合,也避免了風格不統一問題。
設計師Pablo Stanley將日常繪制的插畫制成一套矢量插圖組件庫,將人物分為:半身、全身和坐姿3大類。通過不同表情、發型和服裝可自由搭配出近60萬種組合。
Pablo Stanley人物插畫系統
新趨勢下,動畫一方面回溯復古線描手繪風格,另一方面追求更三維的體驗,同時幀率進一步提升,追求更流暢的視覺感受。
手繪動畫
手繪插圖是往年的熱門,其隨性自然的筆觸,能給用戶帶來親切的感受,在新的趨勢下,動畫的加入賦予手繪插圖一份靈性和趣味。
3D運動
Google Material Design通過卡片投影層級和二維動畫規律,賦予扁平界面Z軸的縱深感。隨著3D的普及流行,新趨勢下的界面,界面的運動從二維走向三維,表現出3D場景下透視感。
高幀率動畫
高幀率影視從線下電影院移步到線上流媒體,手機高幀率屏幕從90Hz到120Hz逐步升級,用戶對畫面流暢的定義一再刷新,UI動畫的幀率升級也會是新的一輪趨勢。
Telegram的表情采用了高幀率動畫,給用戶更流暢的視覺感受。
體驗的持續升級,產品的高速迭代,對UX設計師的設計師效率提出了更高的要求。的設計方式是一個永恒的趨勢。
傳統的文件交接方式效率低下,導致設計師之間信息不對稱,最終影響產品的一致性體驗。近些年在線設計協同工具發展迅速,從UI設計、 設計交付以及組件協同等環節上給設計師提供更加實時的協作體驗,獲得大量UX設計師的簇擁。在2019 uxtool的設計工具調研中,在線設計協同工具佼佼者figma以其協作和性能優勢,大有追趕sketch之勢。
隨著團隊對設計效率要求的提高,設計文檔從本地走向云端協作是不可逆趨勢。不過設計工具的迭代是需要成本的,尤其在大型設計團隊,設計工具需要渡過陣痛期來完成迭代,進而提升設計效率和體驗一致性。
UX的發展,從早期的靜態規范到當下的動態設計系統,是為解決產品迭代增速后帶來的設計效率和產品體驗問題。商業驅動下的產品迭代速度有增無減,設計系統依舊會是未來幾年的設計趨勢之一。
這里說的設計系統不是廣義上的設計系統,而是在互聯網設計的發展中,對組件化設計逐步迭代升華的一套設計協作方法:
“設計系統(Design systems)是一組為了共同目標而服務的內在相互聯系的設計模式和多人協同執行的方法?!保ㄒ浴禗esign systems》,Alla Kholmatova,C7210翻譯)。
設計系統歷程衍變
組件化的發展歷經規范文檔到UI組件,再到設計系統,形態從最初對設計一致性的指導規范,到對產品研發流程的規范,以及產品設計價值觀的輸出,當下的設計系統以集大成者形式影響整個產品的設計形態。
設計系統的結構見下圖:
設計系統的求同存異
設計系統并非一成不變的,他是一個動態進化的系統,會根據團隊性質、產品特性在內容上有所區分——比如大團隊更應該大而全,小團隊更傾向小而精;成熟產品的設計系統更傾向于打造完整閉環的合作流程機制,新產品的設計系統應該以小為始,快速迭代……
隨著產品的垂直化,細分化,設計系統的趨勢會是在趨勢大同之下找到適合產品和團隊自身的形態和節奏。
Material Design是一個包含了指導規范、組件,以及設計開發工具的自適應性設計系統。
它作為平臺型性設計系統,更為大而全的規范了整個生態系統的設計風格,以及提供工具讓研發者能快速產出符合規范的產品。
Google生態龐大繁雜,Material Design更為全面
Ant Design作為一個為to B產品提供解決方案的平臺,更多從設計可用性和完整性考慮設計系統的搭建。
Ant Design通過模塊化解決方案,降低冗余的生產成本,讓設計者專注于更好的用戶體驗
QQ作為一款面向95后的2C社交產品,其設計系統Q語言從風格調性上對設計進行規范,同時給予設計師一定的自由度;也考慮到QQ內兼顧多個產品,以及界面主題樣式,對基礎組件的使用場景和代碼進行了規范,方便設計和開發敏捷開發。
Q語言,給予產品的自由調性之外,也針對主題和基礎組件進行了規范
每個產品和團隊都有自身的特征,設計系統的建設也應該有的放矢,沒有可照搬的標準答案,在大方向下找到適合自身的解決方案才是的可行之道,將效率最大化。
科學有效的優化迭代
組件是設計系統中的重要組成部分,但是以往靜態的、孤立的協作方式使得組件的更新迭代滯后和阻塞。隨著設計系統的發展,設計師組件化思維的普及,組件的更新需要更科學的方式進行管理。
Figma在2019年推出的Design System Analytics功能,組件設計師可以借此查看組件的使用情況,包括引用次數,解組次數等,并可以生成組件使用情況的曲線趨勢圖,以數據的形式,科學地推動組件的優化迭代。
1.選擇分析的時間段;2.組件使用的次數曲線圖;3.團隊使用情況;4.所有組件使用情況
未來的用戶體驗會出現什么新趨勢?人工智能等算法的發展、5G技術普及、新的智能設備形態、新的信息處理技術、新一代用戶的喜好和口味......這些往后或將影響用戶體驗發展的走向。未來用戶對體驗的要求只會越來越高。
用戶體驗設計師需要了解更多的技術動向,但安身立命之本還是讓用戶真正受益:立足于用戶真實使用場景,在理性價值層面上,打造可用、易用、的設計;在感性需求層上賦予情感上的愉悅性,在反思層面賦予意義價值。
文章來源:站酷 作者:百度MEUX
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
用戶隱私安全在產品設計中是很重要的一個環節,本文從用戶體驗角度切入,從匿名模式、減少永久性和減少公開性三個方面展開分析。
我們先看?組來??優的2019年6?的調研數據:70%的美國?認為,與5年前相?個?信息變得更不安全。尤其是?學歷?收?群體。由此可??戶對個?信息數據的隱私擔憂?以往更甚。
?戶隱私安全很重要,涉及的范圍和?度也很多。本次的分析從?戶體驗?度切?,涉及如下三個??:
Incognito Mode匿名模式;
Reducing Permanence減少永久性;
Reducing Publicity減少公開性。
下圖是Google系App(Google AppChromeYouTubeandGoogle Map)匿名模式切換,從交互體驗上來說有如下?個特點:
統?在右上?;
可以便捷切換?匿名模式,反過來也很容易切回登陸狀態;
匿名模式的狀態提示,例如YouTube 在匿名模式下在界?底部有?字提示“您 當前處于匿名模式”。
匿名模式不是最近才流?的功能,最早提供隱匿模式的是蘋果safari瀏覽器,早在 2005年就?持了匿名模式。Chrome瀏覽器在2008年就開始?持此模式。雖然由來已久,為什么到了2020年,匿名模式依然是國外互聯???趨勢呢?
我們看?組數據:
這是來?DuckDuckGo 2019年9?的調研(DuckDuckGo是美國的?款不記錄?戶?為保護?戶隱私的搜索引擎)。樣本來?美國、英國、德國和澳?利亞的成年??戶,共計3,411?的調研得出。各國?戶對使?搜索引擎的個?隱私安全?常在意(是否搜集個?的數據和記錄搜索?為)。
2020年5?DuckDuckGo?均搜索次數為6200萬。對?看2019年11?底?均搜索次數4900萬,2018年10?是2900萬。
最近?年的持續活躍度?幅增?證明了不記錄個?隱私的搜素引擎越來越受到?戶的?睞。
國內,頭條、UC瀏覽器在搜索框輸?狀態也提供了“?痕瀏覽”??。
不僅是搜索引擎領域,保護?戶隱私也成為Facebook最重要的戰略?向之?。Facebook CEO Mark Zuckerberg在2019年 F8開發者?會上喊出“THE FUTURE IS PRIVATE”。2019年3?Mark Zuckerberg發?,主題就是《聚焦于保護隱私的社交?絡》。
我們先看國外社交媒體Stories(?故事)產品形態的流?。
?們總是對于所分享的內容永遠記錄在?上感到擔憂。Stories24?時消失緩解了?們的隱私顧慮,這讓?戶更安?地?然分享。
Stories由Snapchat?創,由 Facebook發揚光?。早在2019年4?,Facebook+Messenger Stories, Instagram Stories?活?戶數就突破5億。 2020年2-3?LinkedIn,Twitter也先后宣布將上線類似功能。
來??優的調研報告:41%的美國?經歷過?絡騷擾,最常?的就是在社交媒體上。23%的?戶最近經歷的?絡騷擾來?評論區的評論內容。27%的?戶經歷?絡騷擾后決定不再發布任何內容。
我們以限定評論互動的公開性為例:
2020年5?Twitter上線了新的評論功能,可以限定誰可以回復帖?的功能,提供了三種選項:誰都可以評論,只有被關注者可以評論,只有被提及者可以評論三種公開度的限定。
Instagram也在測試“評論限制”新功能,批量屏蔽/限制評論。?前已經上線的?個例?:?戶(評論發布者)如果發布的評論含有攻擊性敏感詞,發布前伴有提示,提醒評論含有攻擊性敏感詞是否真的要發布。
注重隱私提供僅好友可?/僅??可?/僅作者可?/等多重維度的隱私設定有助于?戶更安?地參與互動。
另外?個例?是付費頻道會員:付費頻道會員-限定頻道的公開性讓內容創作者減輕隱私顧慮不僅能獲得?告收?,也能得到來?會員、會費的收?,形成“忠實粉絲”社區,有助于內容?態的社區化建設。
我們主要看YouTube的頻道會員案例:
YouTube有兩種會員模式。?種是YouTube整個平臺的付費會員,去?告,看原創美劇影視,消費?樂,可下載內容的模式。第?種模式是Youtuber個?頻道付費會員,吸引忠實粉絲加?。我想說的就是第?種。
為什么?V?紅有意愿開通頻道會員?
除了獲得忠實粉絲收?變現的商業價值以及付費頻道會員可以為忠實粉絲提供各種專屬功能,背后也和?紅?V對個?隱私顧慮有關。
?紅?V在完全公開的社交?絡上需要始終保持?夠克制謹慎,避免引起爭議。但在忠實粉絲付費頻道專屬會員群中,?紅?V會減輕隱私顧慮,更加回歸?我。
?如在頻道會員中發布更多與個??活相關的內容,表達更多不便在完全公開的社交?絡中的想法和感受等,因為忠實粉絲通常更具包容度,更不容易引起爭議。
YouTube頻道會員費?可以從三種會費(按?)區間選擇,?持多選:
低階 Low Levels $0.99~3.99;
中階 Medium Levels $4.99~14.99;
?階 High Levels $19.99~49.99;
頻道會員功能在2018年開始測試,?向粉絲數過10萬的YouTuber開放。
以上綜述,我們可以說:
1.匿名模式:
雖然匿名模式由來已久,但仍然是當前的??基本?戶體驗設計趨勢,尤其是匿名模式的切換便捷性?常重要。
2.減少永久性:
Stories?故事24?時消失緩解了?們的隱私顧慮,這讓?戶更安?地?然分享,已經成為國外社交媒體平臺的必備功能,Facebook, Instagram平臺的最主要、最具影響?的功能之?。
3.減少公開性:
?戶總是對在社交媒體平臺發表評論有所顧忌,限定評論的公開性能夠有助于促進?戶發帖表達,其他?戶也可以更安?地參與互動。
付費頻道會員可以限定頻道的公開性,讓內容創作者減輕隱私顧慮不僅能獲得?告收?,也能得到來?會員會費的收?,形成“忠實粉絲”社區,有助于內容?態的社區化建設。
從UE?度,我們可以為頻道會員提供專屬身份設計例如專屬徽章,專屬表情等。
THE FUTURE IS PRIVATE, 注重?戶隱私的體驗設計越來越重要!
文章來源:站酷 作者:百度MEUX
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
凡是要知其然知其所以然
文件上傳相信很多朋友都有遇到過,那或許你也遇到過當上傳大文件時,上傳時間較長,且經常失敗的困擾,并且失敗后,又得重新上傳很是煩人。那我們先了解下失敗的原因吧!
據我了解大概有以下原因:
服務器配置:例如在PHP中默認的文件上傳大小為8M【post_max_size = 8m】,若你在一個請求體中放入8M以上的內容時,便會出現異常
請求超時:當你設置了接口的超時時間為10s,那么上傳大文件時,一個接口響應時間超過10s,那么便會被Faild掉。
網絡波動:這個就屬于不可控因素,也是較常見的問題。
基于以上原因,聰明的人們就想到了,將文件拆分多個小文件,依次上傳,不就解決以上1,2問題嘛,這便是分片上傳。 網絡波動這個實在不可控,也許一陣大風刮來,就斷網了呢。那這樣好了,既然斷網無法控制,那我可以控制只上傳已經上傳的文件內容,不就好了,這樣大大加快了重新上傳的速度。所以便有了“斷點續傳”一說。此時,人群中有人插了一嘴,有些文件我已經上傳一遍了,為啥還要在上傳,能不能不浪費我流量和時間。喔...這個嘛,簡單,每次上傳時判斷下是否存在這個文件,若存在就不重新上傳便可,于是又有了“秒傳”一說。從此這"三兄弟" 便自行CP,統治了整個文件界?!?
注意文中的代碼并非實際代碼,請移步至github查看代碼
https://github.com/pseudo-god...
分片上傳
HTML
原生INPUT樣式較丑,這里通過樣式疊加的方式,放一個Button.
<div class="btns">
<el-button-group>
<el-button :disabled="changeDisabled">
<i class="el-icon-upload2 el-icon--left" size="mini"></i>選擇文件
<input
v-if="!changeDisabled"
type="file"
:multiple="multiple"
class="select-file-input"
:accept="accept"
@change="handleFileChange"
/>
</el-button>
<el-button :disabled="uploadDisabled" @click="handleUpload()"><i class="el-icon-upload el-icon--left" size="mini"></i>上傳</el-button>
<el-button :disabled="pauseDisabled" @click="handlePause"><i class="el-icon-video-pause el-icon--left" size="mini"></i>暫停</el-button>
<el-button :disabled="resumeDisabled" @click="handleResume"><i class="el-icon-video-play el-icon--left" size="mini"></i>恢復</el-button>
<el-button :disabled="clearDisabled" @click="clearFiles"><i class="el-icon-video-play el-icon--left" size="mini"></i>清空</el-button>
</el-button-group>
<slot
//data 數據
var chunkSize = 10 * 1024 * 1024; // 切片大小
var fileIndex = 0; // 當前正在被遍歷的文件下標
data: () => ({
container: {
files: null
},
tempFilesArr: [], // 存儲files信息
cancels: [], // 存儲要取消的請求
tempThreads: 3,
// 默認狀態
status: Status.wait
}),
一個稍微好看的UI就出來了。
選擇文件
選擇文件過程中,需要對外暴露出幾個鉤子,熟悉elementUi的同學應該很眼熟,這幾個鉤子基本與其一致。onExceed:文件超出個數限制時的鉤子、beforeUpload:文件上傳之前
fileIndex 這個很重要,因為是多文件上傳,所以定位當前正在被上傳的文件就很重要,基本都靠它
handleFileChange(e) {
const files = e.target.files;
if (!files) return;
Object.assign(this.$data, this.$options.data()); // 重置data所有數據
fileIndex = 0; // 重置文件下標
this.container.files = files;
// 判斷文件選擇的個數
if (this.limit && this.container.files.length > this.limit) {
this.onExceed && this.onExceed(files);
return;
}
// 因filelist不可編輯,故拷貝filelist 對象
var index = 0; // 所選文件的下標,主要用于剔除文件后,原文件list與臨時文件list不對應的情況
for (const key in this.container.files) {
if (this.container.files.hasOwnProperty(key)) {
const file = this.container.files[key];
if (this.beforeUpload) {
const before = this.beforeUpload(file);
if (before) {
this.pushTempFile(file, index);
}
}
if (!this.beforeUpload) {
this.pushTempFile(file, index);
}
index++;
}
}
},
// 存入 tempFilesArr,為了上面的鉤子,所以將代碼做了拆分
pushTempFile(file, index) {
// 額外的初始值
const obj = {
status: fileStatus.wait,
chunkList: [],
uploadProgress: 0,
hashProgress: 0,
index
};
for (const k in file) {
obj[k] = file[k];
}
console.log('pushTempFile -> obj', obj);
this.tempFilesArr.push(obj);
}
分片上傳
創建切片,循環分解文件即可
createFileChunk(file, size = chunkSize) {
const fileChunkList = [];
var count = 0;
while (count < file.size) {
fileChunkList.push({
file: file.slice(count, count + size)
});
count += size;
}
return fileChunkList;
}
循環創建切片,既然咱們做的是多文件,所以這里就有循環去處理,依次創建文件切片,及切片的上傳。
async handleUpload(resume) {
if (!this.container.files) return;
this.status = Status.uploading;
const filesArr = this.container.files;
var tempFilesArr = this.tempFilesArr;
for (let i = 0; i < tempFilesArr.length; i++) {
fileIndex = i;
//創建切片
const fileChunkList = this.createFileChunk(
filesArr[tempFilesArr[i].index]
);
tempFilesArr[i].fileHash ='xxxx'; // 先不用看這個,后面會講,占個位置
tempFilesArr[i].chunkList = fileChunkList.map(({ file }, index) => ({
fileHash: tempFilesArr[i].hash,
fileName: tempFilesArr[i].name,
index,
hash: tempFilesArr[i].hash + '-' + index,
chunk: file,
size: file.size,
uploaded: false,
progress: 0, // 每個塊的上傳進度
status: 'wait' // 上傳狀態,用作進度狀態顯示
}));
//上傳切片
await this.uploadChunks(this.tempFilesArr[i]);
}
}
上傳切片,這個里需要考慮的問題較多,也算是核心吧,uploadChunks方法只負責構造傳遞給后端的數據,核心上傳功能放到sendRequest方法中
async uploadChunks(data) {
var chunkData = data.chunkList;
const requestDataList = chunkData
.map(({ fileHash, chunk, fileName, index }) => {
const formData = new FormData();
formData.append('md5', fileHash);
formData.append('file', chunk);
formData.append('fileName', index); // 文件名使用切片的下標
return { formData, index, fileName };
});
try {
await this.sendRequest(requestDataList, chunkData);
} catch (error) {
// 上傳有被reject的
this.$message.error('親 上傳失敗了,考慮重試下呦' + error);
return;
}
// 合并切片
const isUpload = chunkData.some(item => item.uploaded === false);
console.log('created -> isUpload', isUpload);
if (isUpload) {
alert('存在失敗的切片');
} else {
// 執行合并
await this.mergeRequest(data);
}
}
sendReques。上傳這是最重要的地方,也是容易失敗的地方,假設有10個分片,那我們若是直接發10個請求的話,很容易達到瀏覽器的瓶頸,所以需要對請求進行并發處理。
并發處理:這里我使用for循環控制并發的初始并發數,然后在 handler 函數里調用自己,這樣就控制了并發。在handler中,通過數組API.shift模擬隊列的效果,來上傳切片。
重試: retryArr 數組存儲每個切片文件請求的重試次數,做累加。比如[1,0,2],就是第0個文件切片報錯1次,第2個報錯2次。為保證能與文件做對應,const index = formInfo.index; 我們直接從數據中拿之前定義好的index。 若失敗后,將失敗的請求重新加入隊列即可。
關于并發及重試我寫了一個小Demo,若不理解可以自己在研究下,文件地址:https://github.com/pseudo-god... , 重試代碼好像被我弄丟了,大家要是有需求,我再補吧!
// 并發處理
sendRequest(forms, chunkData) {
var finished = 0;
const total = forms.length;
const that = this;
const retryArr = []; // 數組存儲每個文件hash請求的重試次數,做累加 比如[1,0,2],就是第0個文件切片報錯1次,第2個報錯2次
return new Promise((resolve, reject) => {
const handler = () => {
if (forms.length) {
// 出棧
const formInfo = forms.shift();
const formData = formInfo.formData;
const index = formInfo.index;
instance.post('fileChunk', formData, {
onUploadProgress: that.createProgresshandler(chunkData[index]),
cancelToken: new CancelToken(c => this.cancels.push(c)),
timeout: 0
}).then(res => {
console.log('handler -> res', res);
// 更改狀態
chunkData[index].uploaded = true;
chunkData[index].status = 'success';
finished++;
handler();
})
.catch(e => {
// 若暫停,則禁止重試
if (this.status === Status.pause) return;
if (typeof retryArr[index] !== 'number') {
retryArr[index] = 0;
}
// 更新狀態
chunkData[index].status = 'warning';
// 累加錯誤次數
retryArr[index]++;
// 重試3次
if (retryArr[index] >= this.chunkRetry) {
return reject('重試失敗', retryArr);
}
this.tempThreads++; // 釋放當前占用的通道
// 將失敗的重新加入隊列
forms.push(formInfo);
handler();
});
}
if (finished >= total) {
resolve('done');
}
};
// 控制并發
for (let i = 0; i < this.tempThreads; i++) {
handler();
}
});
}
切片的上傳進度,通過axios的onUploadProgress事件,結合createProgresshandler方法進行維護
// 切片上傳進度
createProgresshandler(item) {
return p => {
item.progress = parseInt(String((p.loaded / p.total) * 100));
this.fileProgress();
};
}
Hash計算
其實就是算一個文件的MD5值,MD5在整個項目中用到的地方也就幾點。
秒傳,需要通過MD5值判斷文件是否已存在。
續傳:需要用到MD5作為key值,當唯一值使用。
本項目主要使用worker處理,性能及速度都會有很大提升.
由于是多文件,所以HASH的計算進度也要體現在每個文件上,所以這里使用全局變量fileIndex來定位當前正在被上傳的文件
執行計算hash
正在上傳文件
// 生成文件 hash(web-worker)
calculateHash(fileChunkList) {
return new Promise(resolve => {
this.container.worker = new Worker('./hash.js');
this.container.worker.postMessage({ fileChunkList });
this.container.worker.onmessage = e => {
const { percentage, hash } = e.data;
if (this.tempFilesArr[fileIndex]) {
this.tempFilesArr[fileIndex].hashProgress = Number(
percentage.toFixed(0)
);
}
if (hash) {
resolve(hash);
}
};
});
}
因使用worker,所以我們不能直接使用NPM包方式使用MD5。需要單獨去下載spark-md5.js文件,并引入
//hash.js
self.importScripts("/spark-md5.min.js"); // 導入腳本
// 生成文件 hash
self.onmessage = e => {
const { fileChunkList } = e.data;
const spark = new self.SparkMD5.ArrayBuffer();
let percentage = 0;
let count = 0;
const loadNext = index => {
const reader = new FileReader();
reader.readAsArrayBuffer(fileChunkList[index].file);
reader.onload = e => {
count++;
spark.append(e.target.result);
if (count === fileChunkList.length) {
self.postMessage({
percentage: 100,
hash: spark.end()
});
self.close();
} else {
percentage += 100 / fileChunkList.length;
self.postMessage({
percentage
});
loadNext(count);
}
};
};
loadNext(0);
};
文件合并
當我們的切片全部上傳完畢后,就需要進行文件的合并,這里我們只需要請求接口即可
mergeRequest(data) {
const obj = {
md5: data.fileHash,
fileName: data.name,
fileChunkNum: data.chunkList.length
};
instance.post('fileChunk/merge', obj,
{
timeout: 0
})
.then((res) => {
this.$message.success('上傳成功');
});
}
Done: 至此一個分片上傳的功能便已完成
斷點續傳
顧名思義,就是從那斷的就從那開始,明確思路就很簡單了。一般有2種方式,一種為服務器端返回,告知我從那開始,還有一種是瀏覽器端自行處理。2種方案各有優缺點。本項目使用第二種。
思路:已文件HASH為key值,每個切片上傳成功后,記錄下來便可。若需要續傳時,直接跳過記錄中已存在的便可。本項目將使用Localstorage進行存儲,這里我已提前封裝好addChunkStorage、getChunkStorage方法。
存儲在Stroage的數據
緩存處理
在切片上傳的axios成功回調中,存儲已上傳成功的切片
instance.post('fileChunk', formData, )
.then(res => {
// 存儲已上傳的切片下標
+ this.addChunkStorage(chunkData[index].fileHash, index);
handler();
})
在切片上傳前,先看下localstorage中是否存在已上傳的切片,并修改uploaded
async handleUpload(resume) {
+ const getChunkStorage = this.getChunkStorage(tempFilesArr[i].hash);
tempFilesArr[i].chunkList = fileChunkList.map(({ file }, index) => ({
+ uploaded: getChunkStorage && getChunkStorage.includes(index), // 標識:是否已完成上傳
+ progress: getChunkStorage && getChunkStorage.includes(index) ? 100 : 0,
+ status: getChunkStorage && getChunkStorage.includes(index)? 'success'
+ : 'wait' // 上傳狀態,用作進度狀態顯示
}));
}
構造切片數據時,過濾掉uploaded為true的
async uploadChunks(data) {
var chunkData = data.chunkList;
const requestDataList = chunkData
+ .filter(({ uploaded }) => !uploaded)
.map(({ fileHash, chunk, fileName, index }) => {
const formData = new FormData();
formData.append('md5', fileHash);
formData.append('file', chunk);
formData.append('fileName', index); // 文件名使用切片的下標
return { formData, index, fileName };
})
}
垃圾文件清理
隨著上傳文件的增多,相應的垃圾文件也會增多,比如有些時候上傳一半就不再繼續,或上傳失敗,碎片文件就會增多。解決方案我目前想了2種
前端在localstorage設置緩存時間,超過時間就發送請求通知后端清理碎片文件,同時前端也要清理緩存。
前后端都約定好,每個緩存從生成開始,只能存儲12小時,12小時后自動清理
以上2中方案似乎都有點問題,極有可能造成前后端因時間差,引發切片上傳異常的問題,后面想到合適的解決方案再來更新吧。
Done: 續傳到這里也就完成了。
秒傳
這算是最簡單的,只是聽起來很厲害的樣子。原理:計算整個文件的HASH,在執行上傳操作前,向服務端發送請求,傳遞MD5值,后端進行文件檢索。若服務器中已存在該文件,便不進行后續的任何操作,上傳也便直接結束。大家一看就明白
async handleUpload(resume) {
if (!this.container.files) return;
const filesArr = this.container.files;
var tempFilesArr = this.tempFilesArr;
for (let i = 0; i < tempFilesArr.length; i++) {
const fileChunkList = this.createFileChunk(
filesArr[tempFilesArr[i].index]
);
// hash校驗,是否為秒傳
+ tempFilesArr[i].hash = await this.calculateHash(fileChunkList);
+ const verifyRes = await this.verifyUpload(
+ tempFilesArr[i].name,
+ tempFilesArr[i].hash
+ );
+ if (verifyRes.data.presence) {
+ tempFilesArr[i].status = fileStatus.secondPass;
+ tempFilesArr[i].uploadProgress = 100;
+ } else {
console.log('開始上傳切片文件----》', tempFilesArr[i].name);
await this.uploadChunks(this.tempFilesArr[i]);
}
}
}
// 文件上傳之前的校驗: 校驗文件是否已存在
verifyUpload(fileName, fileHash) {
return new Promise(resolve => {
const obj = {
md5: fileHash,
fileName,
...this.uploadArguments //傳遞其他參數
};
instance
.post('fileChunk/presence', obj)
.then(res => {
resolve(res.data);
})
.catch(err => {
console.log('verifyUpload -> err', err);
});
});
}
Done: 秒傳到這里也就完成了。
后端處理
文章好像有點長了,具體代碼邏輯就先不貼了,除非有人留言要求,嘻嘻,有時間再更新
Node版
請前往 https://github.com/pseudo-god... 查看
JAVA版
下周應該會更新處理
PHP版
1年多沒寫PHP了,抽空我會慢慢補上來
待完善
切片的大小:這個后面會做出動態計算的。需要根據當前所上傳文件的大小,自動計算合適的切片大小。避免出現切片過多的情況。
文件追加:目前上傳文件過程中,不能繼續選擇文件加入隊列。(這個沒想好應該怎么處理。)
更新記錄
組件已經運行一段時間了,期間也測試出幾個問題,本來以為沒BUG的,看起來BUG都挺嚴重
BUG-1:當同時上傳多個內容相同但是文件名稱不同的文件時,出現上傳失敗的問題。
預期結果:第一個上傳成功后,后面相同的問文件應該直接秒傳
實際結果:第一個上傳成功后,其余相同的文件都失敗,錯誤信息,塊數不對。
原因:當第一個文件塊上傳完畢后,便立即進行了下一個文件的循環,導致無法及時獲取文件是否已秒傳的狀態,從而導致失敗。
解決方案:在當前文件分片上傳完畢并且請求合并接口完畢后,再進行下一次循環。
將子方法都改為同步方式,mergeRequest 和 uploadChunks 方法
BUG-2: 當每次選擇相同的文件并觸發beforeUpload方法時,若第二次也選擇了相同的文件,beforeUpload方法失效,從而導致整個流程失效。
原因:之前每次選擇文件時,沒有清空上次所選input文件的數據,相同數據的情況下,是不會觸發input的change事件。
解決方案:每次點擊input時,清空數據即可。我順帶優化了下其他的代碼,具體看提交記錄吧。
<input
v-if="!changeDisabled"
type="file"
:multiple="multiple"
class="select-file-input"
:accept="accept"
+ οnclick="f.outerHTML=f.outerHTML"
@change="handleFileChange"/>
重寫了暫停和恢復的功能,實際上,主要是增加了暫停和恢復的狀態
之前的處理邏輯太簡單粗暴,存在諸多問題?,F在將狀態定位在每一個文件之上,這樣恢復上傳時,直接跳過即可
封裝組件
寫了一大堆,其實以上代碼你直接復制也無法使用,這里我將此封裝了一個組件。大家可以去github下載文件,里面有使用案例 ,若有用記得隨手給個star,謝謝!
偷個懶,具體封裝組件的代碼就不列出來了,大家直接去下載文件查看,若有不明白的,可留言。
組件文檔
Attribute
參數 類型 說明 默認 備注
headers Object 設置請求頭
before-upload Function 上傳文件前的鉤子,返回false則停止上傳
accept String 接受上傳的文件類型
upload-arguments Object 上傳文件時攜帶的參數
with-credentials Boolean 是否傳遞Cookie false
limit Number 最大允許上傳個數 0 0為不限制
on-exceed Function 文件超出個數限制時的鉤子
multiple Boolean 是否為多選模式 true
base-url String 由于本組件為內置的AXIOS,若你需要走代理,可以直接在這里配置你的基礎路徑
chunk-size Number 每個切片的大小 10M
threads Number 請求的并發數 3 并發數越高,對服務器的性能要求越高,盡可能用默認值即可
chunk-retry Number 錯誤重試次數 3 分片請求的錯誤重試次數
Slot
方法名 說明 參數 備注
header 按鈕區域 無
tip 提示說明文字 無
后端接口文檔:按文檔實現即可
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
藍藍設計的小編 http://www.syprn.cn