隨著大數(shù)據(jù)產(chǎn)業(yè)的蓬勃發(fā)展,很多企業(yè)都開始應(yīng)用數(shù)據(jù)可視化。所以數(shù)據(jù)可視化設(shè)計(jì),絕對是熱門的設(shè)計(jì)之一。很多 UI 設(shè)計(jì)師突然會接到公司數(shù)據(jù)可視化設(shè)計(jì)的需求,如果不了解數(shù)據(jù)可視化設(shè)計(jì),肯定是一頭霧水,不知從何入手。本文結(jié)合最近設(shè)計(jì)案例,分享大屏可視化設(shè)計(jì)過程中遇到的一些問題以及設(shè)計(jì)思路,供大家一起交流與學(xué)習(xí)。
△ 最終動態(tài)效果圖
首先放的是項(xiàng)目改版前的頁面:
1. 需求介紹
某某應(yīng)用云,分為五大云平臺模塊:云端綜合調(diào)度、數(shù)據(jù)查詢通道、數(shù)據(jù)應(yīng)用處理、數(shù)據(jù)存儲管理、管理運(yùn)行維護(hù)。每個(gè)大模塊下?有若干個(gè)子系統(tǒng)。
可視化?屏首頁需要展示的內(nèi)容包括:
2. 需求分析
分析大屏可視化的一些共性:
結(jié)合大屏的一些共性特點(diǎn)針對看到的線上舊版本設(shè)計(jì),分析存在的問題。
3. 布局
整合數(shù)據(jù),分析出主要數(shù)據(jù)、次要數(shù)據(jù)、總量數(shù)據(jù)、細(xì)分?jǐn)?shù)據(jù)、各數(shù)據(jù)的維度等等。首先優(yōu)化頁面布局,可以先在紙上畫一畫,然后腦子里有大概思路以后再用電腦繪制,如下圖:
采用柵格化對稱布局,讓整體視覺左右平衡。
4. 風(fēng)格
一提到數(shù)據(jù)可視化大家往往能想到科技、數(shù)據(jù)、藍(lán)色等一些普遍關(guān)鍵詞。
了解到客戶是想做一個(gè)科技感強(qiáng)、炫酷的視覺效果??梢栽诰W(wǎng)上找一些效果圖或是自己曾經(jīng)做過的案例供客戶選擇,確定一個(gè)大致的風(fēng)格,然后結(jié)合具體的業(yè)務(wù)場景進(jìn)行設(shè)計(jì)。
5. 顏色
顏色上結(jié)合產(chǎn)品使用場景,以及整個(gè)產(chǎn)品調(diào)性還是以藍(lán)色為主,背景選用深色調(diào),讓視覺更好聚焦,內(nèi)容部分采用比較透亮的藍(lán)色系,保證內(nèi)容與背景有一定的對比關(guān)系,便于業(yè)務(wù)信息傳達(dá)。
6. 主體地圖
地圖為大屏的主要展示內(nèi)容,首先分析展示的目的是為了看清各個(gè)城市間的不同分布情況,和城市數(shù)據(jù)的匯集效果。
如圖:
改版前:地圖過于單薄,沒有立體感,太平缺乏層次,顏色黃色不符合產(chǎn)品調(diào)性。
改版后:主色調(diào)改為科技藍(lán),在原有地圖上增加外發(fā)光和多層陰影疊加,增加地圖的立體感,地圖上增加科技線條上升的效果代表每個(gè)城市數(shù)據(jù)變化的提升,地圖背景采用比較弱化的轉(zhuǎn)動線條圓形,襯托地圖主體,使得畫面更加豐富。
7. 地圖效果的實(shí)現(xiàn)方法
首先用 ps 拉框助手新建一個(gè)山東的地圖(拉框助手的使用獲取方法可以參照上篇文章)。
完成后會得到一個(gè)叫 map 文件夾的地圖分層文件,如圖所示。這里需要對每個(gè)城市的顏色進(jìn)行調(diào)整,為了區(qū)分每個(gè)城市之間的數(shù)據(jù)不同關(guān)系。
調(diào)整完塊之間的顏色后,就需要給地圖整體增加立體效果。
首先,是整體給地圖加了一個(gè)描邊和外發(fā)光。描邊是為了強(qiáng)化地圖邊緣,外發(fā)光是為了地圖與背景有一個(gè)區(qū)分。
其次為了增加地圖立體感,需要給地圖增加多層陰影疊加的效果。復(fù)制現(xiàn)有形狀層,拼合成一個(gè)山東省的地圖,如下圖:
最后,把拼合好的圖層移動到 map 文件夾下面,陰影可以添加多層,這里針對每一層進(jìn)行不同顏色大小的調(diào)整,就是下面的這種效果了,地圖的體積厚度感也就出來了。這里只是提供一個(gè)大概的思路,大家可以多去嘗試。
整體地圖效果調(diào)整完成后,就是給地圖增加些紋理,和上升線條這些細(xì)節(jié)上的效果了。紋理可以用圖案疊加,或者找一張紋理圖剪切蒙版實(shí)現(xiàn),最后再添加上升線條的效果,地圖的效果就完成了。
最后加上線條上升的動態(tài)效果:
8. 數(shù)據(jù)圖表拆分
在選定數(shù)據(jù)圖表之前,首先要確定圖表之間的關(guān)系,可以從以下四個(gè)維度進(jìn)行思考分析:
可以參照下面這個(gè)圖:
△ 圖片來自于網(wǎng)絡(luò),侵刪
當(dāng)確定好分析維度后,事實(shí)上我們所能選用的圖表類型也就基本確定了。接下來我們只需要從少數(shù)幾個(gè)圖表里篩選出最能體現(xiàn)我們設(shè)計(jì)意圖的那個(gè)就好了。
傳統(tǒng)的圖表比如 echarts 圖表在視覺上展示可能不是很美觀好看,可根據(jù)選擇的圖表在其基礎(chǔ)之上進(jìn)行美化設(shè)計(jì),總之選定圖表最重要的兩個(gè)點(diǎn)就是:易理解、可實(shí)現(xiàn)。
易理解:就是要考慮最終用戶,可視化結(jié)果應(yīng)該是一看就懂,不需要思考和過度理解,因而選定圖表時(shí)要理性,避免為了視覺上的效果而選擇一些對用戶不太友好的圖形及元素。
可實(shí)現(xiàn):主要是跟開發(fā)前期溝通好實(shí)現(xiàn)方式,一般都采用開源組件庫的居多,比如 echarts 組件庫,我們設(shè)計(jì)的圖形圖表,要開發(fā)能夠?qū)崿F(xiàn)。實(shí)際工作中,一些可視化效果開發(fā)用代碼寫很容易實(shí)現(xiàn),效果也不錯(cuò),但這些效果設(shè)計(jì)師用 Ps/Ai/Ae 這些工具模擬時(shí)會發(fā)現(xiàn)比較困難。同樣的,某些效果設(shè)計(jì)師用設(shè)計(jì)工具可以輕易實(shí)現(xiàn),但開發(fā)要用代碼落地卻非常困難,所以大屏設(shè)計(jì)中跟開發(fā)常溝通非常重要,我們需要明確哪些地方設(shè)計(jì)師可以盡情發(fā)揮,哪些地方需要謹(jǐn)慎設(shè)計(jì)。一個(gè)項(xiàng)目總有周期與預(yù)算限制,不會無限期的修改迭代,所以設(shè)計(jì)師在這里需要抓住重點(diǎn),有取舍,不鉆牛角尖、死磕不放。
案例中在圖表選擇上,強(qiáng)化科技感元素,條形圖打破傳統(tǒng)條形圖的展示形式,采用電池晶格的展示形式,在保持圖表功能的同時(shí)更加凸顯科技感。
從頁面的整體看,已經(jīng)有兩處用到了條形圖、柱狀圖,如果這里還是條形圖,那么頁面看起來會很單調(diào),圖表也沒有表現(xiàn)出多樣性,所以現(xiàn)在設(shè)計(jì)要體現(xiàn)圖表的多樣性也能夠有排名的直觀呈現(xiàn)。以下圖表采用科技圓盤的形式,運(yùn)用科技線條的上升狀態(tài)代表排名的先后順序,所有圖表都采用數(shù)據(jù)降序來展示排名更加直觀。
改版前的圖標(biāo)樣式比較單一,改版后針對每組數(shù)據(jù)不同的對比形式,采用比較貼合的圖表進(jìn)行展示,篇幅原因就不一一做展示了。
附上最終視覺效果圖:
大屏設(shè)計(jì)需要注意的點(diǎn):
以上是我對數(shù)據(jù)可視化大屏的案例總結(jié),希望能幫助到你。除此之外還有很多地方?jīng)]有涉及到,包括具體設(shè)計(jì)的操作方式、選取圖形元素的具體方法,以及在各種大屏中所需要的相對應(yīng)的組件等,在龐大的可視化大屏設(shè)計(jì)系統(tǒng)中,還有很多值得學(xué)習(xí)參考和優(yōu)化的知識,歡迎溝通交流,大家一起努力。
文章來源:優(yōu)設(shè)
有句話叫:「設(shè)計(jì)無小事」,很多看似不起眼的東西卻起著至關(guān)重要的作用,比如這期要說的線條,很多人對于線條的理解有局限性,比如:線條的形態(tài)可以是曲線、直線、折線、粗線、細(xì)線、實(shí)線、虛線等等。其實(shí)已經(jīng)牽扯到了點(diǎn)、線、面的知識,這也是很多科班生在學(xué)校必學(xué)的知識點(diǎn),但是這期所說的線與點(diǎn)線面中的線還是有所不同的,點(diǎn)線面中的線可以是線條、可以是文字或者是看不到的視線,而是今天著重說的是設(shè)計(jì)中很直觀的線條。下面我們還是通過實(shí)際的案例逐一分析:
設(shè)計(jì)類的知識很多都和日常生活息息相關(guān),嘗試著把設(shè)計(jì)類的知識點(diǎn)與日常生活想結(jié)合,對于記憶和理解來說會更加得心應(yīng)手,例如:
圖中的閃電可以視作為設(shè)計(jì)中的線條,給人的視覺感受是通過閃電把天與地連接為一個(gè)整體,而閃電在圖中的作用就是串聯(lián)整體,那么回到這里的正題:線條有引導(dǎo)視覺的作用該怎么理解呢?再舉一個(gè)現(xiàn)實(shí)生活中的案例:
我們選擇從北京到拉薩開車去,出發(fā)之前可能需要在地圖上看下路線,知道途徑哪些省市,規(guī)劃好行程路線,這里綠色的虛線就起到了引導(dǎo)視覺的作用?;貧w設(shè)計(jì)中道理是一樣的,線條可以引導(dǎo)用戶把原本雜亂無章的視覺點(diǎn)規(guī)整為有次序的視覺元素,例如:
當(dāng)看到左側(cè)這張海報(bào)時(shí)我們視覺次序會出現(xiàn)很多變化,比如:1>A>3>B>4>C>2 或者 A>2>C>4>B>3>1 等等 N 多種順序,這時(shí)給人的感覺就是雜亂無章的,毫無視覺次序而言;而看右側(cè)的海報(bào)給人的感覺卻是條例清晰的,相比而言只是多了兩條線,但是卻在整個(gè)海報(bào)中起到了引導(dǎo)視覺的作用,它可以給與用戶閱讀海報(bào)時(shí)視覺輔助的作用,讓用戶以右>左>右的視覺次序欣賞、閱讀,看似很不起眼,其作用卻至關(guān)重要。
前面也說了,線的形態(tài)可以有很多種,例如:
這里是以真實(shí)的可口可樂吸管作為設(shè)計(jì)中的線條,同樣起到了視覺引導(dǎo)的作用,但是我們不難發(fā)現(xiàn),這里的線條不僅僅只有一個(gè)作用,也牽扯到另一個(gè)作用:線條有串聯(lián)整體的作用。
在排版時(shí)我們有分組原則,即把互想關(guān)聯(lián)的元素彼此靠近,無關(guān)聯(lián)的相互疏遠(yuǎn)。在頁面中我們會把同一色塊上的元素視作為一個(gè)整體;下面我們說下線條所帶來的串聯(lián)整體的作用是如何體現(xiàn)的,比如:
△ 圖一
△ 圖二
圖一因?yàn)榇竺娣e的留白能使得用戶很容易分辨出自行車與文案是一個(gè)整體,但是相較于圖二而言,其整體性略顯不足,而且給人的感覺太過單薄、重心不穩(wěn);圖二的整體性更強(qiáng),這里的矩形線條就起到了串聯(lián)主題的作用,類似的還有:
不難看出,這些案例中的線條都有串聯(lián)主題的作用,線條使得主題元素整體感更強(qiáng)、畫面板式更加嚴(yán)謹(jǐn);對于整體的視覺傳達(dá)也起到了串聯(lián)、引導(dǎo)的作用;在文字排版中,也有類似的線條,比如:
同樣是通過線條把文案更加整體化,也起到了串聯(lián)的作用。
突出主題的方式有很多種,像我們之前所說到的留白、對比。接下來繼續(xù)說下另一個(gè)可以突出主題的方式—線條,下面看個(gè)案例:
通過對比觀察我們發(fā)現(xiàn),右側(cè)海報(bào)整體感更強(qiáng),主題文案信息更加清晰,主體更明確。其中的原理可以理解為:因?yàn)榫€條的存在,使得主題信息有了一定的范圍,在視覺上等于是在海報(bào)中劃定了視覺焦點(diǎn),從而起到了突出主題的作用。當(dāng)然還有其他的表現(xiàn)形式,比如:
很好的詮釋了線條的作用——突出標(biāo)題序號。在進(jìn)行創(chuàng)作時(shí),作品的每個(gè)元素都要做到有理有據(jù),否則只是一味的抄襲,到再創(chuàng)作時(shí)腦袋里還是一片空白,只有明白了其中的設(shè)計(jì)原理,才能做到活學(xué)活用。再看幾個(gè)案例:
突出主題也許一個(gè)線條就可以表現(xiàn)的淋漓盡致,因設(shè)計(jì)目的的不同,線條所發(fā)揮的作用也不盡相同。下面繼續(xù)分析:
前面說了線條有串聯(lián)整體的作用,而這里又說可以分割整體,是否存在矛盾呢?下面舉個(gè)簡單的例子:
在小文案的區(qū)域中間我加了兩個(gè)線條,看似很小的改變,其目的是把文案很準(zhǔn)確、嚴(yán)謹(jǐn)?shù)胤指顬槿齻€(gè)小整體,希望能給用戶帶來識別性更強(qiáng)的閱讀性,再舉個(gè)例子:
這里的線條把月份和日期分割、英文和中文分割開,使得用戶對于信息的捕捉能力以及可辨識性都提升了很多,而線條的作用就起到了分割的作用。
線條也能起到修飾、襯托的作用,很多小伙伴會忽視這一點(diǎn),其實(shí)線條也可以成為海報(bào)中襯托畫面、修飾主題的元素,例如:
海報(bào)中的線條起到了襯托、修飾主題的作用,假如把這些線條都刪除,畫面整體會顯得相對單薄。
更多設(shè)計(jì)中線條的應(yīng)用:
線條的作用我們分為四個(gè)逐一分析,其實(shí)它們之間也存在著相輔相成的作用,不能以一概全,線條所起到的作用可以是一種,也可以是多種,比如:我們前面「可口可樂」的案例,即有串聯(lián)整體的作用,又有引導(dǎo)視覺的作用。只要我們在使用的時(shí)候能明確目的,而不是機(jī)械式的抄襲,那么最終一定會得心應(yīng)手。
文章來源:優(yōu)設(shè) 作者:美工美邦
未來熒黑是一個(gè)基于思源黑體、Fira Sans和Raleway的開源字體項(xiàng)目,支持簡體中文、繁體中文與日文。
思源黑體的7種字重被擴(kuò)展為9種字重,并增加了5種字體寬度,全家族共包含44款字體。
相比于思源黑體,未來熒黑的造型更加簡明現(xiàn)代,版面效果清新輕快。未來熒黑的中宮與字面更加收斂,重心在字重之間經(jīng)過了重新配置;筆畫細(xì)節(jié)上處理得更加干練,簡潔而平直化。
開發(fā)者:Celestial Phineas
字體文件以SIL Open Font License 1.1發(fā)布,構(gòu)建字體開發(fā)的代碼以MIT License發(fā)布。
發(fā)布地址:github.com/welai/glow-sans
網(wǎng)盤地址:https://pan.baidu.com/s/1f2UuFO8ZxWa8v5XXYUEmig 提取碼 2e8w
備份下載鏈接:https://pan.baidu.com/s/1E1woRsZX91bCrq5FT1SAzg 提取碼: 92t2
文章來源:優(yōu)設(shè) 作者:GrayDesign
微信在3.23號上線了傳聞已久的 「暗黑版」,用來適配 iOS 的深色模式,相信不少同學(xué)已經(jīng)安裝并體驗(yàn)上了,如果還沒安裝的可以看看下面圖例。
微信每有一次大動作都會引發(fā)全網(wǎng)性的討論,而針對設(shè)計(jì)上的調(diào)整,往往只會迎來一片罵聲。比如我們看看知乎中討論的內(nèi)容,感覺民憤都快壓抑不住了。
但我們先別急著參與網(wǎng)上的聲討,現(xiàn)在站在設(shè)計(jì)師的角度,來評價(jià)一下遲到的微信深色模式。
很多人會把深色模式和夜間模式搞混,這里必須強(qiáng)調(diào)這是兩種不同的模式,所以我們要對還沒搞清楚狀況的同學(xué)先做一個(gè)掃盲(最近掃盲好像搞的比較多…)。
先說夜間模式,是一個(gè)專門針對夜晚環(huán)境適配的設(shè)計(jì)版本。騰訊的 ISUX 團(tuán)隊(duì)之前做過調(diào)研,有 71.1% 的用戶習(xí)慣在夜間不開燈看手機(jī)。
如果在深夜漆黑的房間中看手機(jī)屏幕,對我們的健康有非常大的損害,不僅表現(xiàn)在對視力的傷害上面,視網(wǎng)膜和神經(jīng)元容易受損,同時(shí)主流的研究項(xiàng)目還表明會這會抑制我們褪黑素的分泌造成失眠。
所以,夜間模式,是一個(gè)用來降低屏幕對用戶傷害,提升夜間使用體驗(yàn)的特殊模式。
通常,夜間模式會采取 降低尼特值、減少對比度、降低色彩明度、增加深色遮罩的方法,比如之前 QQ 官方的夜間模式示意圖,大家感受一下,是不是有內(nèi)味兒了?
這里提一下尼特這個(gè)概念,尼特是用來說明亮度的術(shù)語,1nit=1坎德拉/平方米。是現(xiàn)在各大手機(jī)發(fā)布會中介紹屏幕的時(shí)候都要強(qiáng)調(diào)的參數(shù)之一,因?yàn)槟崽刂翟礁?,證明在戶外大白天的環(huán)境中屏幕內(nèi)容可以越清晰,而夜間模式做的就是用來降低顯示亮度尼特值的。
然后再解釋一下蘋果的深色模式,蘋果的 DarkMode,并不是一個(gè)專門面向深夜環(huán)境的模式。官方對此版本的解釋,詳見我們翻譯的 iOS 官方文檔中 112 頁。
這是一個(gè)面向全天候的視覺風(fēng)格,同時(shí)通過深色背景的對比,來加凸顯圖片、文字內(nèi)容。包括上面那種官方的配圖,大家應(yīng)該就能感覺到主體元素比白色模式下更凸出,更激烈。
所以了解了這兩個(gè)模式的區(qū)別,我們才能好好展開對微信深色模式的討論。
接下來,我們先來總結(jié)一遍微信的深色模式。首先是分析一遍它使用的背景色。
背景色純灰色,有3個(gè)等級的灰度。熟悉我的都了解,看色彩的奧秘,靠16進(jìn)制代碼和 RGB 那是分析不出個(gè)什么所以然的。如果我們把它們轉(zhuǎn)化成 HSB 一看,規(guī)律性就來了。
背景色從深到淺色的灰度值 B 值分為 10、14、18,是不是朗朗上口。應(yīng)用的層級雖然和官方規(guī)范一樣使用了三級,但是設(shè)置和官方的不同。
然后再看看其中使用的其它配色,其中主色保持了不變,其它應(yīng)用到圖標(biāo)色彩,都進(jìn)行了明度的調(diào)整,比如下圖案例。
再看看文字的用色,也是純灰色,標(biāo)題使用 B:85,正文使用 B:65,注釋使用 B:35(主要用色)。
而官方使用的文字色彩中,卻并不是純灰色,除了第一級的白色以外,其它灰階的文字是由帶有藍(lán)色色相的色彩通過降低透明度來呈現(xiàn)的。
對中性色增加藍(lán)色色值是一個(gè)常規(guī)操作,可以讓這種灰色看起來有一點(diǎn)活力,不會像純灰色那么單調(diào),不過這次微信明顯在文字的應(yīng)用上更保守,一點(diǎn)色相也沒有給。
這次微信被大面積吐槽的,就是顏色的應(yīng)用上和官方的規(guī)范不一致,作為從業(yè)人員直接開噴是相當(dāng)不專業(yè)的(最起碼要先走個(gè)形式),這就是我要分析的重點(diǎn)了。
要說微信的 UED 團(tuán)隊(duì),專業(yè)性肯定是國內(nèi)最頂尖的,你們網(wǎng)上所有看過有關(guān)交互的方法論、可用性測試的分享, 他們肯定都有做過,而且執(zhí)行得更專業(yè)。
直接用官方規(guī)范的黑底白字模式,微信團(tuán)隊(duì)不可能沒有出過這樣的方案。但很明顯,這個(gè)方案最后被斃了,上了我們看見的這個(gè)版本,雖然不知道以后會不會變。
再看看下面官方發(fā)的一條微博。
其中已經(jīng)提及了,是和蘋果 「共同探索」 出來的方案,這是非常耐人尋味的。也就是說,這個(gè)不用官方的模式是蘋果團(tuán)隊(duì)也通過的。
那么為什么要做的不一樣呢?沒有內(nèi)幕消息,就根據(jù)自己的經(jīng)驗(yàn)來判斷一下。
我自己認(rèn)為的一個(gè)非常重要的原因,就是對于 「夜間模式「 的兼容。前面不是講暗黑模式和夜間模式不一樣嘛?為什么微信的暗黑模式又去兼容夜間模式了。
這里面有幾個(gè)小彩蛋,首先就是官方對深色模式的態(tài)度,在我看來越來越曖昧了。比如在顯示與亮度設(shè)置頁面里,有一個(gè)自動設(shè)置外觀 —— 日出前保持深色外觀的功能。這樣,就等于默認(rèn)將深色模式和夜間模式掛鉤。
還有,如果過去我們沒有整理 iOS13 的翻譯,就不會發(fā)現(xiàn),上面我們展示的那句專注于內(nèi)容的解釋,現(xiàn)在在官網(wǎng)已經(jīng)被刪掉了(你品,你細(xì)品)。
再說關(guān)于用戶認(rèn)知的問題上面,在 UI 群體中,能了解暗黑模式和夜間模式是不一樣的可能就只占 10 分之一,那么對于普通用戶來說,這個(gè)情況就更不樂觀,能有 1% 的用戶了解這個(gè)概念就不錯(cuò)了。所以,絕大多數(shù)用戶會直接認(rèn)為暗黑模式就是夜間模式。
如果暗黑模式直接當(dāng)成夜間模式用,在深夜的環(huán)境里,觀感會特別差,因?yàn)?—— 明暗對比度過高。
如果在黑底中直接用白字,那么可以說屏幕的文字和背景的對比度就是 100(HSB中 B 的差值),在一個(gè)漆黑的環(huán)境中會非常應(yīng)驗(yàn) 「讓內(nèi)容脫穎而出」 的效果,刺激性會非常強(qiáng)烈,文字將變得非常尖銳,比如 QQ 暗黑模式,大家可以在被窩里打開下面這張圖感受一下。
新的深色模式版本中,文字和背景的對比度基本控制在 50 左右,降低了一半。
并且,中英文字形在正負(fù)形上的差異(簡單理解就是中文筆劃更復(fù)雜),這個(gè)感覺會更強(qiáng)烈。比如我們用一個(gè)公眾號頁面舉例,使用純黑底白字,采用相同字號的中英文,看看顯示的效果。
還有,純黑背景色和白色的對比度,會根據(jù)屏幕的類型和參數(shù)不同而有不一樣的感受,比如蘋果從 X 后旗艦機(jī)型使用 OLED 屏幕,純黑色區(qū)域?qū)⒉粫l(fā)光,進(jìn)一步擴(kuò)大對比度,使得文字變得更尖銳,更讓人難以接受。
如果不是使用 OLED 屏幕的同學(xué)光看一個(gè)案例可能很難受,所以我們用純黑的案例來對比一下現(xiàn)在的狀態(tài)。
是不是發(fā)現(xiàn)明顯在夜間的情況下黑白模式并不如另一個(gè)模式看起來舒適?而這種不舒適的差別并不會隨著屏幕亮度降低而變化。
所以色彩并沒有符合官方的原因,我的判斷就在大環(huán)境中無法割裂夜間和深色模式的區(qū)別,同時(shí)也要讓深色模式更適應(yīng)夜間環(huán)境,做出了調(diào)整。而又因?yàn)樗皇钦嬲囊归g模式,所以對比度也不能像 QQ 之前的夜間版本一樣將整體環(huán)境完全壓暗。
你看,真是一個(gè)讓人矛盾的過程……
最后再簡單討論一個(gè)問題,為什么深色模式來得這么晚。很多用戶一直嘲諷,不就是換一套皮膚的事嘛,為什么就是不上線。
外行可以看熱鬧,但是如果是從業(yè)人員就應(yīng)該知道,微信這種體量的應(yīng)用,上線深色模式絕對不是一個(gè)非常容易的事情。
適配黑暗模式首先需要使用蘋果新的 iOS 13 SDK(開發(fā)者工具)進(jìn)行編譯,等于應(yīng)用中有大量的代碼需要調(diào)整,而這種升級調(diào)整的結(jié)果還會導(dǎo)致沉重的測試壓力。有經(jīng)歷過 Darkmode 開發(fā)的團(tuán)隊(duì)?wèi)?yīng)該都知道這絕對不是改改顏色就能上線的皮膚。
再看到知乎另一個(gè)回答中提到的:
另一方面點(diǎn)大家隨便聽聽。使用 iOS 13 SDK 之后,Apple 要求 VoIP 推送必須使用 CallKit,否則應(yīng)用程序會被終止。而由于眾所周知的原因,CallKit 在中國大陸是無法使用的,這樣的改動會降低微信語音電話的體驗(yàn)。
原文地址:https://www.zhihu.com/question/378027349/answer/1069072154
再者,拋去大量用戶體驗(yàn)調(diào)研相關(guān)的工作,微信整個(gè)生態(tài)對于暗黑模式的不友好可以說是無解的。比如說公眾號,有大量公眾號內(nèi)部的標(biāo)題、分割線、引用語句是用圖片做上去的,而圖片還用的是白底(透明底黑字的也有),于是現(xiàn)在就產(chǎn)生了災(zāi)難性的閱讀體驗(yàn)。
比如我的公眾號:超人的電話亭,其中文章展示的截圖。
而且因?yàn)楣娞柊l(fā)出去是不能修改的,只能刪除,那么這部分存量文章將無法更改,體驗(yàn)也無法扭轉(zhuǎn)。而且公眾號還支持文字色彩等自定義,那么你在白色背景下添加的顏色,可不會直接適配深色模式,尤其是官方也不可能輕易直接給你們 「適配」 掉。
而在夜間模式,正常訪問的文章網(wǎng)頁,也和公眾號會很像,但是打開以后是白色背景的話,統(tǒng)一的體驗(yàn)在哪里?
再者還有小程序,小程序雖然也可以通過微信官方提供小程序的深色模式適配文檔,對應(yīng)的 SDK,但是小程序不是 APP,其中有大量小程序開發(fā)后是缺少維護(hù)的。
因?yàn)榫€下門店通過外包方做好一個(gè)小程序上線以后,沒特殊的原因不會直接去更新它,那么這部分小程序的升級適配無從談起,會出現(xiàn)打開小程序一個(gè)白一個(gè)黑的窘境。
最后,再講一個(gè)微信里最高頻使用的功能 —— 發(fā)表情。深色模式直接造成大量自定義表情報(bào)廢,無法正常顯示的問題,比如看看下面我自己發(fā)的表情。
前面提到的,都是不能解決的問題,這就是做深色模式的挑戰(zhàn),因?yàn)橛脩?UGC 內(nèi)容是不可控的,官方不可能通過算法直接幫用戶強(qiáng)行 「適配」。
而這些,就是做深色版的難點(diǎn)。
以上總結(jié)內(nèi)容多數(shù)為主觀分析,純粹站在 UI 設(shè)計(jì)師角度進(jìn)行專業(yè)解讀,不帶入個(gè)人立場。而一定要我自己評價(jià)的話,那就是 :趕緊把這模式給我移除?。?
再順便提一點(diǎn)小感想,一個(gè)有數(shù)億用戶的產(chǎn)品,每一個(gè)小調(diào)整分量都不輕,都要慎之又慎。同時(shí),你做的每一個(gè)決策,都意味著要站在其中一部分用戶的對立面,因?yàn)槟銤M足不了所有用戶的需求。所以,這就是設(shè)計(jì)師的壓力與挑戰(zhàn)。
文章來源:優(yōu)設(shè) 作者:超人的電話亭
網(wǎng)上對于這兩個(gè)的區(qū)別解釋都是統(tǒng)一口徑的,一個(gè)是開發(fā)依賴,一個(gè)是線上依賴,打包發(fā)布需要用到的要添加到線上依賴,一模一樣的回答,誤導(dǎo)了很多人。今天自己測試一下這兩個(gè)命令,記錄一下。
–save-dev,會在devDependencies里面添加依賴
-D,會在devDependencies里面添加依賴
–save,會在dependencies里面添加依賴
-S,會在dependencies里面添加依賴
devDependencies和dependencies可以同時(shí)存在同一個(gè)包的依賴。
如果npm install xxx后面沒有輸入要保存到哪個(gè)里面,devDependencies和dependencies都沒有。
我這邊直接npm install jquery,node_modules下有jQuery。然后我刪除node_modules,執(zhí)行npm install,node_modules下并沒有下載jQuery。
所以,安裝依賴的時(shí)候如果沒有加上要依賴到開發(fā)還是線上,只是臨時(shí)的在node_modules里面幫你下載,而devDependencies和dependencies的依賴都會幫你下載。
然后我在devDependencies下安裝依賴:
"devDependencies": {
"html-webpack-plugin": "^4.0.3",
"jquery": "^3.4.1",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11"
}
在入口文件引用和打印jQuery:
import $ from 'jquery'
console.log($)
打包之后,可以使用jQuery。
然后我在dependencies下安裝依賴:
"dependencies": {
"html-webpack-plugin": "^4.0.3",
"jquery": "^3.4.1",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11"
}
在入口文件引用和打印jQuery:
import $ from 'jquery'
console.log($)
打包之后,可以使用jQuery。
測試的結(jié)果就是,無論是–save還是–save-dev,對于打包都沒有任何影響。devDependencies和dependencies兩種情況,打包出來的main.js都把jQuery打包進(jìn)去。這兩種情況,如果都沒有引用jQuery的情況下,也都不會把jQuery打包。
接著在一個(gè)空白的項(xiàng)目里面下載axios,npm install axios -S,打開node_modules文件夾:
發(fā)現(xiàn)多出了另外三個(gè)依賴,查看axios下的package.json:
"dependencies": {
"follow-redirects": "1.5.10"
}
查看follow-redirects下的package.json:
"dependencies": {
"debug": "=3.1.0"
}
查看debugs下的package.json:
"dependencies": {
"ms": "2.0.0"
}
最后ms的package.json沒有dependencies。
而這幾個(gè)包的devDependencies依賴的包沒有一個(gè)下載。
接著我在node_modules把follow-redirects、debugs、ms都刪了,把a(bǔ)xios里面的package.js的dependencies給刪了,然后執(zhí)行npm install,發(fā)現(xiàn)沒有下載follow-redirects、debugs、ms這幾個(gè),也證明了如果node_modules里面有下載的包,是不會重新去下載的。我把node_modules刪除,執(zhí)行npm install,這幾個(gè)包又都下載下來了。
最后得出 的結(jié)論是,–save-dev和–save在平時(shí)開發(fā)的時(shí)候,對于打包部署上線是沒有任何影響的。如果你是發(fā)布一個(gè)包給別人用,而你開發(fā)的包依賴第三方的包,那么你如果是–save,那么別人安裝你開發(fā)的包,會默認(rèn)下載你依賴的包,如果你是–save-dev,那么別人安裝你開發(fā)的包,是不會默認(rèn)幫忙下載你依賴的包。
其實(shí)發(fā)布的包如果沒有必要,很少會默認(rèn)幫你下載,比如bootstrap,依賴jQuery,怕你原本就下載了引起沖突,也不會在dependencies里面安裝jQuery而是:
"peerDependencies": {
"jquery": "1.9.1 - 3",
"popper.js": "^1.16.0"
}
表示bootstrap依賴于這兩個(gè)包,你必須安裝,版本不固定,但是一定要安裝這兩個(gè)包,安裝的時(shí)候會有警告:
peerDependencies WARNING bootstrap@ requires a peer of jquery@1.9.1 - 3 but none was installed
peerDependencies WARNING bootstrap@ requires a peer of popper.js@^1.16.0 but none was installed
當(dāng)你引用了然后打包,報(bào)錯(cuò):
ERROR in ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js
Module not found: Error: Can't resolve 'jquery' in 'C:\Users\wade\Desktop\savedev\node_modules_bootstrap@4.4.1@bootstrap\dist\js'
@ ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js 7:82-99
@ ./src/index.js
ERROR in ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js
Module not found: Error: Can't resolve 'popper.js' in 'C:\Users\wade\Desktop\savedev\node_modules_bootstrap@4.4.1@bootstrap\dist\js'
@ ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js 7:101-121
@ ./src/index.js
以上就是對–save和–save-dev的一些測試,想更快的得出結(jié)論其實(shí)是自己發(fā)布一個(gè)包。至于本人的答案是不是存在錯(cuò)誤,歡迎指出,因?yàn)橹皇亲约汉唵螠y試的結(jié)果。
用戶體驗(yàn)地圖(Customer Journey Map)是什么?
用戶體驗(yàn)地圖是從用戶的視角出發(fā),去理解用戶、產(chǎn)品或者服務(wù)交互的一個(gè)重要的設(shè)計(jì)工具。
也可以說是以可視化的形式,來表現(xiàn)一個(gè)用戶使用產(chǎn)品或者接受服務(wù)的體驗(yàn)情況,從體驗(yàn)的過程中來發(fā)現(xiàn)用戶在整個(gè)體驗(yàn)過程中的問題點(diǎn)與情緒點(diǎn),以此來從中提取出產(chǎn)品的優(yōu)化點(diǎn),方便對產(chǎn)品進(jìn)行迭代,從而保證良好的用戶體驗(yàn)。
經(jīng)典案例
Chris Risdon繪制的歐洲鐵路購票的體驗(yàn)地圖
上圖中是歐洲鐵路公司整個(gè)體驗(yàn)地圖的一部分。歐洲鐵路公司是一家美國經(jīng)銷商,為北美旅客提供一個(gè)獨(dú)立預(yù)訂火車票去歐洲各地的平臺,而無需用戶去網(wǎng)站預(yù)定。他們已經(jīng)擁有了一個(gè)良好體驗(yàn)的網(wǎng)站和一個(gè)屢獲殊榮的咨詢中心,但他們希望通過所有接觸點(diǎn)來優(yōu)化用戶使用過程,這樣可以讓他們更全面地了解,他們應(yīng)該專注的投資,設(shè)計(jì)和技術(shù)資源。整體的“診斷”評價(jià)系統(tǒng),包含一系列的重點(diǎn)舉措,體驗(yàn)地圖只是其中派生的一部分。體驗(yàn)地圖幫助建立同理心圖,來理解隨著時(shí)間和空間的推移,用戶與歐洲鐵路公司服務(wù)系統(tǒng)交互時(shí)接觸點(diǎn)的變化。
在這張?bào)w驗(yàn)地圖中采用了五個(gè)關(guān)鍵組成一個(gè)體驗(yàn)地圖,一個(gè)體驗(yàn)地圖可以直觀的表示用戶操作流、期望、特定的目標(biāo)、用戶情緒狀態(tài)和整體的體驗(yàn)點(diǎn),做到整體把控和評估產(chǎn)品體驗(yàn)。
作用 :
用戶體驗(yàn)地圖能幫助我們創(chuàng)造出一個(gè)有大局觀的用戶體驗(yàn),更好的幫助我們理解用戶的痛點(diǎn)和需求,幫助Team達(dá)成共識,非常有利于跨團(tuán)隊(duì)合作。
用戶體驗(yàn)地圖包含的內(nèi)容 :
其中包括,人群(產(chǎn)品的用戶是哪一類人)、 用戶的需求(用戶想得到什么)、 路徑(在某特定的場景下體驗(yàn)的整體過程) 、接觸點(diǎn) (產(chǎn)品與人或人與服務(wù)接觸的關(guān)鍵點(diǎn))、行為(用戶的行為是什么樣的?)、情緒 (體驗(yàn)過程中的感受心情) 、機(jī)會點(diǎn) (過程中可以突破的點(diǎn),可以成為特色的地方)、 解決方案 (解決用戶在體驗(yàn)過程的痛點(diǎn))、 問題 (解決用戶在體驗(yàn)過程的痛點(diǎn))。
用戶畫像 :
在準(zhǔn)備開始繪制用戶體驗(yàn)地圖的時(shí)候,我們應(yīng)該要確立用戶群體 / 確定產(chǎn)品目標(biāo) / 了解用戶目標(biāo),并作出用戶畫像。
視覺設(shè)計(jì)師怎么使用
舉例(一):
那我們看看作為一名視覺設(shè)計(jì)師應(yīng)該關(guān)注哪部分的流程。
視覺設(shè)計(jì)師的用戶體驗(yàn)地圖 :
我們的聚焦點(diǎn)應(yīng)在上圖的這幾個(gè)部分。
所以當(dāng)繪制完用戶體驗(yàn)地圖后,應(yīng)該再繪制一份視覺設(shè)計(jì)師看的版本,我們設(shè)計(jì)師主要關(guān)注的視覺的觸點(diǎn)。
定量方法(產(chǎn)品方向):
我們在行為和情緒上一般會使用問卷法、后臺數(shù)據(jù)分析法;而在需求和問題上一般會使用焦點(diǎn)小組、訪談法、觀察法、日志法和田野調(diào)查,下面就為大家來解釋下這些方法。
焦點(diǎn)小組:是指從研究產(chǎn)品中所確定的全部用戶群(總體)中抽取一定數(shù)量的用戶來組成樣本,根據(jù)樣本信息推斷用戶群總體特征的一種調(diào)查方法。
訪談法:訪談,就是以口頭形式向用戶進(jìn)行詢問,根據(jù)被詢用戶的答復(fù)搜集客觀的、不帶偏見的事實(shí)信息,以準(zhǔn)確地說明樣本所要代表的總體的一種方式。
觀察法:觀察法是指研究者根據(jù)一定的研究目的、研究提綱或觀察表,用自己的感官和輔助工具去直接觀察用戶,從而獲得資料的一種方法。
日志法:是由用研人員按時(shí)間順序,詳細(xì)記錄自己在一段時(shí)間內(nèi)使用產(chǎn)品的過程,經(jīng)過歸納、分析,達(dá)到分析產(chǎn)品目的的一種工作分析方法。
田野調(diào)查:在日常生活中,在一個(gè)有一個(gè)嚴(yán)格定義的空間和時(shí)間的范圍內(nèi),體驗(yàn)特定用戶群的日常生活與思想境界,通過記錄自己的生活的方方面面,來展示不同階段用戶群的基本需求。
注意事項(xiàng)(5要點(diǎn))
1. 在制作地圖前,應(yīng)理清楚產(chǎn)品的前期規(guī)劃和需求,并且與同事達(dá)成共識。
2. 避免以自己的經(jīng)驗(yàn)或者認(rèn)知來確定用戶體驗(yàn)地圖中的接觸點(diǎn),應(yīng)當(dāng)真正的從用戶的行為中去提取。
3. 不要將一些落后的信息加入到用戶體驗(yàn)地圖中。
4. 最好先在Team內(nèi)部腦暴一份地圖,再去與所制作的地圖進(jìn)行對比。
5. 用戶體驗(yàn)地圖不會涉及到實(shí)現(xiàn)方案和現(xiàn)實(shí)機(jī)制,只涉及用戶的體驗(yàn)。
團(tuán)隊(duì)人員的合理搭配 :
將公司或者團(tuán)隊(duì)的PM、RD、運(yùn)營、Leader等過來,詳細(xì)的描述這一份用戶體驗(yàn)地圖,聆聽他們的反饋。
在分析用戶問題上 :
分為四個(gè)等級:ABCD,在對優(yōu)先級進(jìn)行排列的同時(shí)應(yīng)該,考慮到產(chǎn)品在每個(gè)階段的側(cè)重點(diǎn),根據(jù)不同的進(jìn)度和情況,來對優(yōu)先級進(jìn)行排列,幫助我們整理問題和提煉最核心的一些體驗(yàn)問題,區(qū)分問題還能幫助我們更好的把握產(chǎn)品的優(yōu)化方向。
視覺設(shè)計(jì)師應(yīng)該關(guān)注的點(diǎn) :
視覺設(shè)計(jì)師的任務(wù)是什么?是有效的傳達(dá)出產(chǎn)品的信息、簡潔并且優(yōu)雅的傳達(dá)、通過視覺設(shè)計(jì)制造出愉悅的用戶體驗(yàn)。用戶在很多的場景下都可能接觸到企業(yè)的產(chǎn)品或者是服務(wù),這個(gè)服務(wù)接觸帶給用戶的感受更多是偏向于視覺感知方面的。所以我們需要盡可能的列舉出企業(yè)的產(chǎn)品或者服務(wù)與用戶可能產(chǎn)生接觸的場景、服務(wù)觸點(diǎn),再根據(jù)服務(wù)觸點(diǎn)延伸出相關(guān)的“視覺觸點(diǎn)”,用來梳理出我們需要輸出的視覺產(chǎn)物,做出相對應(yīng)的查漏補(bǔ)缺和優(yōu)化,輸出指導(dǎo)企業(yè)的品牌建設(shè)工作。而用戶體驗(yàn)地圖就很適合作為這樣的工具。
“體驗(yàn)地圖”對于優(yōu)化視覺體驗(yàn)的意義 :
整體性:系統(tǒng)性地規(guī)劃品牌的視覺統(tǒng)一化工作,提升品牌建設(shè)工作的全面性和完整度。也可以避免未來工作中不同的品牌 / UI / 運(yùn)營設(shè)計(jì)師對于品牌概念的理解不同而帶來的設(shè)計(jì)出入。
品牌設(shè)計(jì),是用戶對于公司產(chǎn)品的直接印象,所以在品牌設(shè)計(jì)的要求就是:建立特征、保持特征、推廣特征、美化特征、對于以上的要求,來提供完整且匹配的設(shè)計(jì)方案。
運(yùn)營設(shè)計(jì),運(yùn)營設(shè)計(jì)的目標(biāo)就是讓用戶盡可能的感知到產(chǎn)品的好,把產(chǎn)品的特點(diǎn)通過設(shè)計(jì)包裝傳遞給用戶,一個(gè)好的運(yùn)營設(shè)計(jì),應(yīng)該是在用戶看到你的設(shè)計(jì)作品后,會產(chǎn)生足夠好的興趣和好感,并愿意去關(guān)注你的產(chǎn)品。
UI設(shè)計(jì),這是產(chǎn)品與用戶接觸過程中,頻率最高、最直觀的部分,目的是為了讓用戶認(rèn)識到產(chǎn)品的相貌和氣質(zhì),UI設(shè)計(jì)需要注意界面視覺層次的強(qiáng)弱、信息劃分、用戶的視線軌跡、色彩的表達(dá)、質(zhì)感、舒適度等,來讓用戶覺得這個(gè)產(chǎn)品設(shè)計(jì)真好。
例如 :
OFO,以年輕人為主的共享騎行產(chǎn)品,無論是在品牌/運(yùn)營/UI的設(shè)計(jì)上,都能讓人感覺時(shí)尚、年輕、陽光、且有親和力。
品牌設(shè)計(jì) :
UI設(shè)計(jì) :
運(yùn)營設(shè)計(jì) :
UI設(shè)計(jì) :
運(yùn)營設(shè)計(jì) :
設(shè)計(jì)師的進(jìn)階 :
在一開始的初級設(shè)計(jì)師階段(也就是1.0階段),我們需要從交互設(shè)計(jì)師手中接過交互設(shè)計(jì)稿,來對它進(jìn)行氣質(zhì)進(jìn)行改造,做出獨(dú)特的視覺設(shè)計(jì),也就是將其翻譯為高保真稿,然后再與開發(fā)同學(xué)進(jìn)行對接,也要保持視覺走查,以防實(shí)際效果與預(yù)期效果的不符;在這個(gè)1.0階段我們的表現(xiàn)力和創(chuàng)造力,是最為主要的,如何去做出差異化?這是這個(gè)階段的設(shè)計(jì)師需要考慮的,在這個(gè)APP設(shè)計(jì)趨同的大浪潮下,你如果能夠做出不一樣的設(shè)計(jì),那么你則可以一鳴驚人,從眾多水平相當(dāng)?shù)脑O(shè)計(jì)師中脫穎而出,這時(shí)你便可以考慮進(jìn)入下一個(gè)階段,也就是2.0。
在高級設(shè)計(jì)階段(即2.0階段),這時(shí)候你就需要擁有更好的產(chǎn)品思維和邏輯能力,不僅僅只是從交互設(shè)計(jì)師拿到交互設(shè)計(jì)稿,直接上手開做,在這之前,你需要開始了解產(chǎn)品的業(yè)務(wù)定位、用戶人群、產(chǎn)品目標(biāo)、當(dāng)前的問題、未來的迭代等,需求方這時(shí)候就成你的主要對接對象,需要你具備拆解需求、采集用戶的需求、擴(kuò)展業(yè)務(wù)、能進(jìn)行設(shè)計(jì)驗(yàn)證的能力,能將產(chǎn)品的氣質(zhì)和品牌貫穿于整個(gè)產(chǎn)品(UI/運(yùn)營/品牌),設(shè)計(jì)是怎么推導(dǎo)的,現(xiàn)在就不是僅僅只在停留在好看的層面上了,畢竟設(shè)計(jì)師不是畫師,而是解決問題的,我們在做了某個(gè)設(shè)計(jì)后,就要去關(guān)注它的變化了,看看用戶的反饋、商業(yè)轉(zhuǎn)化率等等,這都是為你的下一次設(shè)計(jì)迭代做的參考。
從
分享到脈脈
轉(zhuǎn)自:脈脈
原文鏈接:https://maimai.cn/article/detail?fid=988630001&efid=N-uHKNnf7vXGBmaFd3lZHA&use_rn=1
本文講述,在使用VUE的移動端實(shí)現(xiàn)類似于iPhone的懸浮窗的效果。
相關(guān)知識點(diǎn)
touchstart 當(dāng)在屏幕上按下手指時(shí)觸發(fā)
touchmove 當(dāng)在屏幕上移動手指時(shí)觸發(fā)
touchend 當(dāng)在屏幕上抬起手指時(shí)觸發(fā)
mousedown mousemove mouseup對應(yīng)的是PC端的事件
touchcancel 當(dāng)一些更高級別的事件發(fā)生的時(shí)候(如電話接入或者彈出信息)會取消當(dāng)前的touch操作,即觸發(fā)touchcancel。一般會在touchcancel時(shí)暫停游戲、存檔等操作。
效果圖
實(shí)現(xiàn)步驟
1.html
總結(jié)了一下評論,好像發(fā)現(xiàn)大家都碰到了滑動的問題。就在這里提醒一下吧。可將該懸浮 DIV 同你的 scroller web 同級。 —- (log: 2018-08-21)
html結(jié)構(gòu): <template> <div>你的web頁面</div> <div>懸浮DIV</div> </template>
<template> <div id="webId"> ... <div>你的web頁面</div> <!-- 如果碰到滑動問題,1.1 請檢查這里是否屬于同一點(diǎn)。 --> <!-- 懸浮的HTML --> <div v-if="!isShow" class="xuanfu" id="moveDiv" @mousedown="down" @touchstart="down" @mousemove="move" @touchmove="move" @mouseup="end" @touchend="end" > <div class="yuanqiu"> {{pageInfo.totalPage}} </div> </div> ... </div> </template>
2.JS
<script> data() { return { flags: false, position: { x: 0, y: 0 }, nx: '', ny: '', dx: '', dy: '', xPum: '', yPum: '', } } methods: { // 實(shí)現(xiàn)移動端拖拽 down(){ this.flags = true; var touch; if(event.touches){ touch = event.touches[0]; }else { touch = event; } this.position.x = touch.clientX; this.position.y = touch.clientY; this.dx = moveDiv.offsetLeft; this.dy = moveDiv.offsetTop; }, move(){ if(this.flags){ var touch ; if(event.touches){ touch = event.touches[0]; }else { touch = event; } this.nx = touch.clientX - this.position.x; this.ny = touch.clientY - this.position.y; this.xPum = this.dx+this.nx; this.yPum = this.dy+this.ny; moveDiv.style.left = this.xPum+"px"; moveDiv.style.top = this.yPum +"px"; //阻止頁面的滑動默認(rèn)事件;如果碰到滑動問題,1.2 請注意是否獲取到 touchmove document.addEventListener("touchmove",function(){ event.preventDefault(); },false); } }, //鼠標(biāo)釋放時(shí)候的函數(shù) end(){ this.flags = false; }, } </script>
3.CSS
<style> .xuanfu { height: 4.5rem; width: 4.5rem; /* 如果碰到滑動問題,1.3 請檢查 z-index。z-index需比web大一級*/ z-index: 999; position: fixed; top: 4.2rem; right: 3.2rem; border-radius: 0.8rem; background-color: rgba(0, 0, 0, 0.55); } .yuanqiu { height: 2.7rem; width: 2.7rem; border: 0.3rem solid rgba(140, 136, 136, 0.5); margin: 0.65rem auto; color: #000000; font-size: 1.6rem; line-height: 2.7rem; text-align: center; border-radius: 100%; background-color: #ffffff; } </style>
實(shí)現(xiàn)好JS邏輯,基本上,問題不大。
本文鏈接 http://www.luyixian.cn/javascript_show_166242.aspx
再加一點(diǎn)
css之display:inline-block布局
1.解釋一下display的幾個(gè)常用的屬性值,inline , block, inline-block
兩個(gè)圖可以看出,display:inline-block后塊級元素能夠在同一行顯示,有人這說不就像浮動一樣嗎。沒錯(cuò),display:inline-block的效果幾乎和浮動一樣,但也有不同,接下來講一下inline-block和浮動的比較。
2.inline-block布局 vs 浮動布局
a.不同之處:對元素設(shè)置display:inline-block ,元素不會脫離文本流,而float就會使得元素脫離文本流,且還有父元素高度坍塌的效果
b.相同之處:能在某程度上達(dá)到一樣的效果
我們先來看看這兩種布局:
圖一:display:inline-block
圖二:
對兩個(gè)孩子使用float:left,我在上一篇浮動布局講過,這是父元素會高度坍塌,所以要閉合浮動,對box使用overflow:hidden,效果如下:
>>乍一看兩個(gè)都能做到幾乎相同的效果,(仔細(xì)看看display:inline-block中有間隙問題,這個(gè)留到下面再講)
c.浮動布局不太好的地方:參差不齊的現(xiàn)象,我們看一個(gè)效果:
圖三:
圖四:
>>從圖3,4可以看出浮動的局限性在于,若要元素排滿一行,換行后還要整齊排列,就要子元素的高度一致才行,不然就會出現(xiàn)圖三的效果,而inline-block就不會。
3.inline-block存在的小問題:
a.上面可以看到用了display:inline-block后,存在間隙問題,間隙為4像素,這個(gè)問題產(chǎn)生的原因是換行引起的,因?yàn)槲覀儗憳?biāo)簽時(shí)通常會在標(biāo)簽結(jié)束符后順手打個(gè)回車,而回車會產(chǎn)生回車符,回車符相當(dāng)于空白符,通常情況下,多個(gè)連續(xù)的空白符會合并成一個(gè)空白符,而產(chǎn)生“空白間隙”的真正原因就是這個(gè)讓我們并不怎么注意的空白符。
b.去除空隙的方法:
1.對父元素添加,{font-size:0},即將字體大小設(shè)為0,那么那個(gè)空白符也變成0px,從而消除空隙
現(xiàn)在這種方法已經(jīng)可以兼容各種瀏覽器,以前chrome瀏覽器是不兼容的
圖一:
c.瀏覽器兼容性:ie6/7是不兼容 display:inline-block的所以要額外處理一下:
在ie6/7下:
對于行內(nèi)元素直接使用{dislplay:inline-block;}
對于塊級元素:需添加{display:inline;zoom:1;}
4.總結(jié):
display:inline-block的布局方式和浮動的布局方式,究竟使用哪個(gè),我覺得應(yīng)該根據(jù)實(shí)際情況來決定的:
a.對于橫向排列東西來說,我更傾向與使用inline-block來布局,因?yàn)檫@樣清晰,也不用再像浮動那樣清除浮動,害怕布局混亂等等。
b.對于浮動布局就用于需要文字環(huán)繞的時(shí)候,畢竟這才是浮動真正的用武之地,水平排列的是就交給inline-block了。
Node 的os模塊是操作系統(tǒng)的
Node 的內(nèi)置模塊 fs
內(nèi)置模塊在下載node的時(shí)候就自帶的,使用 require()方法來導(dǎo)入
語法 :require(‘模塊fs’)
在內(nèi)置模塊中的方法
1 fs.readFile() —》用來專門 異步 讀取文件的方法 三個(gè)參數(shù)
語法 :fs.readFile(‘要讀取的文件’,讀取文件的格式,讀取成功的回調(diào)函數(shù))
Eg : fs.readFIle(‘a(chǎn)’,’utf8’,’function(err,data){ })
2 fs.readFileSync()-– 專門用來 同步 讀取的方法, 兩個(gè)參數(shù)
語法: fs.readFileSync(‘要讀取的文件’,讀取格式)
3 fs.writeFIle() —>用來寫入 異步 文件的方法 三個(gè)參數(shù)
語法: fs.writeFile(‘寫入到哪個(gè)文件’,寫入的內(nèi)容,成功的回調(diào)函數(shù))
Eg: fs.writeFile(‘./text.tex’,”內(nèi)容”, function(){ })
注意:再次寫入的內(nèi)容會完全覆蓋 。如果文件夾沒有 會自動創(chuàng)建一個(gè)文件夾
4 fs.writeFileSync() --> 同步寫入的方法
語法: fs.writeFileSync(‘寫入到文件’,“寫入的內(nèi)容”)
Node的http模塊
這個(gè)模塊專門用來創(chuàng)建服務(wù)的
只能支持http協(xié)議。
也是使用require()方法
Const http= require(“http”)
方法
1 http.createServer(function(req,res){ }) 兩個(gè)形參
Req=request 代表每次的請求信息
Res=response 代表每次請求的響應(yīng)
返回值是一個(gè)服務(wù),當(dāng)服務(wù)監(jiān)聽端口號的時(shí)候,就變成了服務(wù)器。
2 監(jiān)聽端口號
創(chuàng)建的服務(wù).listen(監(jiān)聽的端口號,監(jiān)聽成功的回調(diào)函數(shù)(選填))
server.listen(8080,function(){ 端口號0-65535 建議0-1023不使用 })
此時(shí)瀏覽器就可以執(zhí)行l(wèi)ocalhost進(jìn)行訪問了
自定義模塊
每一個(gè)js文件都是一個(gè)獨(dú)立的模塊,他們都自帶一個(gè) module 是一個(gè)對象,
其中 module里面的 exports,是一個(gè)對象 這個(gè) module.exports 就是這個(gè)文件向外導(dǎo)出的內(nèi)容,也就是說,只有導(dǎo)出,才能導(dǎo)入
Eg: function fn1(){console.log() }
Module.exports.fn1=fn1
這樣,才能是另一個(gè)js文件到入這個(gè)文件 同樣也是require(‘./js’)方法
想要學(xué)會這個(gè)漂亮的煙花嗎?快來跟著學(xué)習(xí)吧~
<div class="container"></div>
我們只需要一個(gè)盒子表示煙花爆炸范圍就可以了
fire是煙花 注意添加絕對定位
<style> .container{ margin: 0 auto; height: 500px; width: 1200px; background: black; position: relative; overflow: hidden; } .fire{ width: 10px; background: white; height: 10px; /* border-radius: 50%; */ position: absolute; bottom: 0; } </style>
需要用到一個(gè)鼠標(biāo)點(diǎn)擊的位置,一個(gè)div選擇器,一個(gè)爆炸樣式
function Firework(x,y,selector,type){ //此處獲取對象的方式為單例的思想,避免重復(fù)獲取相同的元素 if(Firework.box && selector === Firework.box.selector){ this.box = Firework.box.ele; }else{ Firework.box = { ele:document.querySelector(selector), selector:selector } this.box = Firework.box.ele; } this.type = type; this.init(x,y) }
function animation(ele,attroptions,callback){ for(var attr in attroptions){ attroptions[attr] ={ target:attroptions[attr], inow:parseInt(getComputedStyle(ele)[attr]) } } clearInterval(ele.timer); ele.timer = setInterval(function(){ for(var attr in attroptions ){ var item = attroptions[attr] var target = item.target; var inow = item.inow; var speed = (target - inow)/10; speed = speed>0?Math.ceil(speed):Math.floor(speed); if(Math.abs(target - inow) <= Math.abs(speed)){ ele.style[attr] = target+"px"; delete attroptions[attr]; for(var num in attroptions){ return false; } clearTimeout(ele.timer); if(typeof callback === "function")callback(); }else{ attroptions[attr].inow += speed; ele.style[attr] = attroptions[attr].inow+"px"; } } },30) }
Firework.prototype = { constructor:Firework, //初始化 init:function(x,y){ //創(chuàng)建一個(gè)煙花 this.ele = this.createFirework(); //xy為鼠標(biāo)落點(diǎn) this.x = x ; this.y = y; //maxXy為最大運(yùn)動范圍 this.maxX = this.box.offsetWidth - this.ele.offsetWidth; this.maxY = this.box.offsetHeight - this.ele.offsetHeight; //初始化結(jié)束后 煙花隨機(jī)顏色 this.randomColor(this.ele); //煙花升空 this.fireworkUp(this.ele); }, //創(chuàng)造煙花 createFirework:function(){ var ele = document.createElement("div"); ele.className = "fire"; this.box.appendChild(ele); return ele; }, //煙花升空 fireworkUp:function(ele){ ele.style.left = this.x + "px"; //此處用到剛剛封裝的運(yùn)動方法 animation(ele,{top:this.y},function(){ ele.remove(); this.fireworkBlast() }.bind(this)); }, //煙花爆炸 fireworkBlast:function(){ for(var i = 0 ; i < 20; i++){ var ele = document.createElement("div"); ele.className = "fire"; ele.style.left = this.x + "px"; ele.style.top = this.y + "px"; this.box.appendChild(ele); ele.style.borderRadius = "50%"; this.randomColor(ele); //判定一下輸入的爆炸方式是原型煙花 還是散落煙花 由此更改獲取的煙花位置 animation(ele,this.type === "circle"?this.circleBlast(i,20): this.randomPosition(),function(cale){ cale.remove(); }.bind(this,ele)) } }, //圓形爆炸位置 circleBlast:function(i,total){ var r = 200; var reg = 360 / total *i; var deg = Math.PI / 180 *reg; return { left:r * Math.cos(deg) + this.x , top:r * Math.sin(deg) + this.y } }, //隨機(jī)顏色 randomPosition:function(){ return { left : Math.random()*this.maxX, top : Math.random()*this.maxY } }, randomColor:function(ele){ var color = "#" + parseInt(parseInt("ffffff",16)*Math.random()).toString(16).padStart(6,0); return ele.style.backgroundColor = color; } }
document.querySelector(".container").addEventListener("click",function(evt){ var e = evt||event; new Firework(e.offsetX,e.offsetY,".container","circle") new Firework(e.offsetX,e.offsetY,".container") })
全部代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .container{ margin: 0 auto; height: 500px; width: 1200px; background: black; position: relative; overflow: hidden; } .fire{ width: 10px; background: white; height: 10px; /* border-radius: 50%; */ position: absolute; bottom: 0; } </style> </head> <body> <div class="container"></div> <script src="./utils.js"></script> <script> function animation(ele,attroptions,callback){ for(var attr in attroptions){ attroptions[attr] ={ target:attroptions[attr], inow:parseInt(getComputedStyle(ele)[attr]) } } clearInterval(ele.timer); ele.timer = setInterval(function(){ for(var attr in attroptions ){ var item = attroptions[attr] var target = item.target; var inow = item.inow; var speed = (target - inow)/10; speed = speed>0?Math.ceil(speed):Math.floor(speed); if(Math.abs(target - inow) <= Math.abs(speed)){ ele.style[attr] = target+"px"; delete attroptions[attr]; for(var num in attroptions){ return false; } clearTimeout(ele.timer); if(typeof callback === "function")callback(); }else{ attroptions[attr].inow += speed; ele.style[attr] = attroptions[attr].inow+"px"; } } },30) } function Firework(x,y,selector,type){ if(Firework.box && selector === Firework.box.selector){ this.box = Firework.box.ele; }else{ Firework.box = { ele:document.querySelector(selector), selector:selector } this.box = Firework.box.ele; } this.type = type; this.init(x,y) } Firework.prototype = { constructor:Firework, //初始化 init:function(x,y){ this.ele = this.createFirework(); this.x = x ; this.y = y; this.maxX = this.box.offsetWidth - this.ele.offsetWidth; this.maxY = this.box.offsetHeight - this.ele.offsetHeight; this.randomColor(this.ele); this.fireworkUp(this.ele); }, //創(chuàng)造煙花 createFirework:function(){ var ele = document.createElement("div"); ele.className = "fire"; this.box.appendChild(ele); return ele; }, fireworkUp:function(ele){ ele.style.left = this.x + "px"; animation(ele,{top:this.y},function(){ ele.remove(); this.fireworkBlast() }.bind(this)); }, //煙花爆炸 fireworkBlast:function(){ for(var i = 0 ; i < 20; i++){ var ele = document.createElement("div"); ele.className = "fire"; ele.style.left = this.x + "px"; ele.style.top = this.y + "px"; this.box.appendChild(ele); ele.style.borderRadius = "50%"; this.randomColor(ele); animation(ele,this.type === "circle"?this.circleBlast(i,20): this.randomPosition(),function(cale){ cale.remove(); }.bind(this,ele)) } }, circleBlast:function(i,total){ var r = 200; var reg = 360 / total *i; var deg = Math.PI / 180 *reg; return { left:r * Math.cos(deg) + this.x , top:r * Math.sin(deg) + this.y } }, randomPosition:function(){ return { left : Math.random()*this.maxX, top : Math.random()*this.maxY } }, randomColor:function(ele){ var color = "#" + parseInt(parseInt("ffffff",16)*Math.random()).toString(16).padStart(6,0); return ele.style.backgroundColor = color; } } document.querySelector(".container").addEventListener("click",function(evt){ var e = evt||event; new Firework(e.offsetX,e.offsetY,".container","circle") new Firework(e.offsetX,e.offsetY,".container") }) </script> </body> </html>
———————————————— 版權(quán)聲明:本文為CSDN博主「SpongeBooob」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/qq_41383900/article/details/105026768
許多人都有這樣一種映像,NodeJS比較快; 但是因?yàn)槠涫菃尉€程,所以它不穩(wěn)定,有點(diǎn)不安全,不適合處理復(fù)雜業(yè)務(wù); 它比較適合對并發(fā)要求比較高,而且簡單的業(yè)務(wù)場景。
在Express的作者的TJ Holowaychuk的 告別Node.js一文中列舉了以下罪狀:
Farewell NodeJS (TJ Holowaychuk)
? you may get duplicate callbacks
? you may not get a callback at all (lost in limbo)
? you may get out-of-band errors
? emitters may get multiple “error” events
? missing “error” events sends everything to hell
? often unsure what requires “error” handlers
? “error” handlers are very verbose
? callbacks suck
其實(shí)這幾條主要吐嘈了兩點(diǎn): node.js錯(cuò)誤處理很扯蛋,node.js的回調(diào)也很扯蛋。
事實(shí)上NodeJS里程確實(shí)有“脆弱”的一面,單線程的某處產(chǎn)生了“未處理的”異常確實(shí)會導(dǎo)致整個(gè)Node.JS的崩潰退出,來看個(gè)例子, 這里有一個(gè)node-error.js的文件:
var http = require('http'); var server = http.createServer(function (req, res) { //這里有個(gè)錯(cuò)誤,params 是 undefined var ok = req.params.ok; res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World '); }); server.listen(8080, '127.0.0.1'); console.log('Server running at http://127.0.0.1:8080/');
啟動服務(wù),并在地址欄測試一下發(fā)現(xiàn) http://127.0.0.1:8080/ 不出所料,node崩潰了
$ node node-error Server running at http://127.0.0.1:8080/ c:githubscript ode-error.js:5 var ok = req.params.ok; ^ TypeError: Cannot read property 'ok' of undefined at Server.<anonymous> (c:githubscript ode-error.js:5:22) at Server.EventEmitter.emit (events.js:98:17) at HTTPParser.parser.onIncoming (http.js:2108:12) at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:121:23) at Socket.socket.ondata (http.js:1966:22) at TCP.onread (net.js:525:27)
其實(shí)Node.JS發(fā)展到今天,如果連這個(gè)問題都解決不了,那估計(jì)早就沒人用了。
我們可以uncaughtException來全局捕獲未捕獲的Error,同時(shí)你還可以將此函數(shù)的調(diào)用棧打印出來,捕獲之后可以有效防止node進(jìn)程退出,如:
process.on('uncaughtException', function (err) { //打印出錯(cuò)誤 console.log(err); //打印出錯(cuò)誤的調(diào)用棧方便調(diào)試 console.log(err.stack); });
這相當(dāng)于在node進(jìn)程內(nèi)部進(jìn)行守護(hù), 但這種方法很多人都是不提倡的,說明你還不能完全掌控Node.JS的異常。
我們還可以在回調(diào)前加try/catch,同樣確保線程的安全。
var http = require('http'); http.createServer(function(req, res) { try { handler(req, res); } catch(e) { console.log(' ', e, ' ', e.stack); try { res.end(e.stack); } catch(e) { } } }).listen(8080, '127.0.0.1'); console.log('Server running at http://127.0.0.1:8080/'); var handler = function (req, res) { //Error Popuped var name = req.params.name; res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello ' + name); };
這種方案的好處是,可以將錯(cuò)誤和調(diào)用棧直接輸出到當(dāng)前發(fā)生的網(wǎng)頁上。
標(biāo)準(zhǔn)的HTTP響應(yīng)處理會經(jīng)歷一系列的Middleware(HttpModule),最終到達(dá)Handler,如下圖所示:
這 些Middleware和Handler在NodeJS中都有一個(gè)特點(diǎn),他們都是回調(diào)函數(shù),而回調(diào)函數(shù)中是唯一會讓Node在運(yùn)行時(shí)崩潰的地方。根據(jù)這個(gè) 特點(diǎn),我們只需要在框架中集成一處try/catch就可以相對完美地解決異常問題,而且不會影響其它用戶的請求request。
事實(shí)上現(xiàn)在的NodeJS WEB框架幾乎都是這么做的,如 OurJS開源博客所基于的 WebSvr
就有這么一處異常處理代碼:
Line: 207 try { handler(req, res); } catch(err) { var errorMsg = ' ' + 'Error ' + new Date().toISOString() + ' ' + req.url + ' ' + err.stack || err.message || 'unknow error' + ' ' ; console.error(errorMsg); Settings.showError ? res.end('<pre>' + errorMsg + '</pre>') : res.end(); }
那么不在回調(diào)中產(chǎn)生的錯(cuò)誤怎么辦?不必?fù)?dān)心,其實(shí)這樣的node程序根本就起不起來。
此外node自帶的 cluster 也有一定的容錯(cuò)能力,它跟nginx的worker很類似,但消耗資源(內(nèi)存)略大,編程也不是很方便,OurJS并沒有采用此種設(shè)計(jì)。
現(xiàn) 在已經(jīng)基本上解決了Node.JS因異常而崩潰的問題,不過任何平臺都不是100%可靠的,還有一些錯(cuò)誤是從Node底層拋出的,有些異常 try/catch和uncaughtException都無法捕獲。之前在運(yùn)行ourjs的時(shí)侯,會偶爾碰到底層拋出的文件流讀取異常,這就是一個(gè)底層 libuv的BUG,node.js在0.10.21中進(jìn)行了修復(fù)。
面對這種情況,我們就應(yīng)該為nodejs應(yīng)用添加守護(hù)進(jìn)程,讓NodeJS遭遇異常崩潰以后能馬上復(fù)活。
另外,還應(yīng)該把這些產(chǎn)生的異常記錄到日志中,并讓異常永遠(yuǎn)不再發(fā)生。
node-forever 提供了守護(hù)的功能和LOG日志記錄功能。
安裝非常容易
[sudo] npm install forever
使用也很簡單
$ forever start simple-server.js $ forever list [0] simple-server.js [ 24597, 24596 ]
還可以看日志
forever -o out.log -e err.log my-script.js
使用node來守護(hù)的話資源開銷可能會有點(diǎn)大,而且也會略顯復(fù)雜,OurJS直接在開機(jī)啟動腳本來進(jìn)程線程守護(hù)。
如在debian中放置的 ourjs 開機(jī)啟動文件: /etc/init.d/ourjs
這個(gè)文件非常簡單,只有啟動的選項(xiàng),守護(hù)的核心功能是由一個(gè)無限循環(huán) while true; 來實(shí)現(xiàn)的,為了防止過于密集的錯(cuò)誤阻塞進(jìn)程,每次錯(cuò)誤后間隔1秒重啟服務(wù)
WEB_DIR='/var/www/ourjs' WEB_APP='svr/ourjs.js' #location of node you want to use NODE_EXE=/root/local/bin/node while true; do { $NODE_EXE $WEB_DIR/$WEB_APP config.magazine.js echo "Stopped unexpected, restarting " } 2>> $WEB_DIR/error.log sleep 1 done
錯(cuò)誤日志記錄也非常簡單,直接將此進(jìn)程控制臺當(dāng)中的錯(cuò)誤輸出到error.log文件即可: 2>> $WEB_DIR/error.log 這一行, 2 代表 Error。
藍(lán)藍(lán)設(shè)計(jì)的小編 http://www.yvirxh.cn