首頁

開發(fā)中我們需要遵循的幾個設計原則!

周周

出處:https://www.cnblogs.com/pengdai


一、開發(fā)原則
S:單一職責SRP
O:開放封閉原則OCP
L:里氏替換原則LSP
I:接口隔離法則
D:依賴倒置原則DIP
合成/聚合復用原則
迪米特法則
在軟件開發(fā)中,前人對軟件系統(tǒng)的設計和開發(fā)總結(jié)了一些原則和模式, 不管用什么語言做開發(fā),都將對我們系統(tǒng)設計和開發(fā)提供指導意義。本文主要將總結(jié)這些常見的原則和具體闡述意義。
面向?qū)ο蟮幕驹瓌t(solid)是五個,但是在經(jīng)常被提到的除了這五個之外還有迪米特法則和合成復用原則等,所以在常見的文章中有表示寫六大或七大原則的; 除此之外我還將給出一些其它相關(guān)書籍和互聯(lián)網(wǎng)上出現(xiàn)的原則;

二、S單一職責SRP

Single-Responsibility Principle,一個類,最好只做一件事,只有一個引起它的變化。單一職責原則可以看做是低耦合、高內(nèi)聚在面向?qū)ο笤瓌t的引申,將職責定義為引起變化的原因,以提高內(nèi)聚性減少引起變化的原因。

1、定義

一個對象應該只包含單一的職責,并且該職責被完整地封裝在一個類中。(Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.),即又定義有且僅有一個原因使類變更。

2、原則分析

一個類或者大到模塊,小到方法,承擔的職責越多,它被復用的可能性越小,而且如果一個類承擔的職責過多,就相當于將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作。
類的職責主要包括兩個方面:數(shù)據(jù)職責和行為職責,數(shù)據(jù)職責通過其屬性來體現(xiàn),而行為職責通過其方法來體現(xiàn)。
單一職責原則是實現(xiàn)高內(nèi)聚、低耦合的指導方針,在很多代碼重構(gòu)手法中都能找到它的存在,它是最簡單但又最難運用的原則,需要設計人員發(fā)現(xiàn)類的不同職責并將其分離,而發(fā)現(xiàn)類的多重職責需要設計人員具有較強的分析設計能力和相關(guān)重構(gòu)經(jīng)驗。

3、優(yōu)點

降低類的復雜性,類的職責清晰明確。比如數(shù)據(jù)職責和行為職責清晰明確;
提高類的可讀性和維護性;
變更引起的風險減低,變更是必不可少的,如果接口的單一職責做得好,一個接口修改只對相應的類有影響,對其他接口無影響,這對系統(tǒng)的擴展性、維護性都有非常大的幫助。
注意:單一職責原則提出了一個編寫程序的標準,用“職責”或“變化原因”來衡量接口或類設計得是否合理,但是“職責”和“變化原因”都是沒有具體標準的,一個類到底要負責那些職責?這些職責怎么細化?細化后是否都要有一個接口或類?這些都需從實際的情況考慮。因項目而異,因環(huán)境而異。

4、例子

SpringMVC中Entity、DAO、Service、Controller、Util等的分離。

三、O開放封閉原則OCP

Open - ClosedPrinciple,OCP對擴展開放,對修改關(guān)閉(設計模式的核心原則)

1、定義

一個軟件實體(如類、模塊和函數(shù))應該對擴展開放,對修改關(guān)閉。意思是在一個系統(tǒng)或者模塊中,對于擴展是開放的,對于修改是關(guān)閉的。一個 好的系統(tǒng)是在不修改源代碼的情況下,可以擴展你的功能。而實現(xiàn)開閉原則的關(guān)鍵就是抽象化。

2、原則分析

當軟件實體因需求要變化時, 盡量通過擴展已有軟件實體,可以提供新的行為,以滿足對軟件的新的需求,而不是修改已有的代碼,使變化中的軟件有一定的適應性和靈活性 。已有軟件模塊,特別是最重要的抽象層模塊不能再修改,這使變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。
實現(xiàn)開閉原則的關(guān)鍵就是抽象化 :在"開-閉"原則中,不允許修改的是抽象的類或者接口,允許擴展的是具體的實現(xiàn)類,抽象類和接口在"開-閉"原則中扮演著極其重要的角色..即要預知可能變化的需求.又預見所有可能已知的擴展..所以在這里"抽象化"是關(guān)鍵!
可變性的封閉原則:找到系統(tǒng)的可變因素,將它封裝起來。這是對"開-閉"原則最好的實現(xiàn)。不要把你的可變因素放在多個類中,或者散落在程序的各個角落。你應該將可變的因素,封套起來..并且切忌不要把所用的可變因素封套在一起。最好的解決辦法是,分塊封套你的可變因素!避免超大類、超長類、超長方法的出現(xiàn)!!給你的程序增加藝術(shù)氣息,將程序藝術(shù)化是我們的目標!

3、例子

設計模式中模板方法模式和觀察者模式都是開閉原則的極好體現(xiàn)。

四、L里氏替換原則LSP

Liskov Substitution Principle,LSP:任何基類可以出現(xiàn)的地方,子類也可以出現(xiàn);這一思想表現(xiàn)為對繼承機制的約束規(guī)范,只有子類能夠替換其基類時,才能夠保證系統(tǒng)在運行期內(nèi)識別子類,這是保證繼承復用的基礎。

1、定義

第一種定義方式相對嚴格:如果對每一個類型為S的對象o1,都有類型為T的對象o2,使得以T定義的所有程序P在所有的對象o1都代換成o2時,程序P的行為沒有變化,那么類型S是類型T的子類型。
第二種更容易理解的定義方式:所有引用基類(父類)的地方必須能透明地使用其子類的對象。即子類能夠必須能夠替換基類能夠從出現(xiàn)的地方。子類也能在基類 的基礎上新增行為。
里氏代換原則由2008年圖靈獎得主、美國第一位計算機科學女博士、麻省理工學院教授BarbaraLiskov和卡內(nèi)基.梅隆大學Jeannette Wing教授于1994年提出。其原文如下:Let q(x) be a property provableabout objects x of type T. Then q(y) should be true for objects y of type Swhere S is a subtype of T.

2、原則分析

講的是基類和子類的關(guān)系,只有這種關(guān)系存在時,里氏代換原則才存在。正方形是長方形是理解里氏代換原則的經(jīng)典例子。
里氏代換原則可以通俗表述為:在軟件中如果能夠使用基類對象,那么一定能夠使用其子類對象。把基類都替換成它的子類,程序?qū)⒉粫a(chǎn)生任何錯誤和異常,反過來則不成立,如果一個軟件實體使用的是一個子類的話,那么它不一定能夠使用基類。

里氏代換原則是實現(xiàn)開閉原則的重要方式之一,由于使用基類對象的地方都可以使用子類對象,因此在程序中盡量使用基類類型來對對象進行定義,而在運行時再確定其子類類型,用子類對象來替換父類對象。

五、I接口隔離法則

(Interface Segregation Principle,ISL):客戶端不應該依賴那些它不需要的接口。(這個法則與迪米特法則是相通的)

1、定義

客戶端不應該依賴那些它不需要的接口。
另一種定義方法:一旦一個接口太大,則需要將它分割成一些更細小的接口,使用該接口的客戶端僅需知道與之相關(guān)的方法即可。
注意,在該定義中的接口指的是所定義的方法。例如外面調(diào)用某個類的public方法。這個方法對外就是接口。

2、原則分析:

(1)接口隔離原則是指使用多個專門的接口,而不使用單一的總接口。每一個接口應該承擔一種相對獨立的角色,不多不少,不干不該干的事,該干的事都要干。
? 一個接口就只代表一個角色,每個角色都有它特定的一個接口,此時這個原則可以叫做“角色隔離原則”。
? 接口僅僅提供客戶端需要的行為,即所需的方法,客戶端不需要的行為則隱藏起來,應當為客戶端提供盡可能小的單獨的接口,而不要提供大的總接口。
(2)使用接口隔離原則拆分接口時,首先必須滿足單一職責原則,將一組相關(guān)的操作定義在一個接口中,且在滿足高內(nèi)聚的前提下,接口中的方法越少越好。
(3)可以在進行系統(tǒng)設計時采用定制服務的方式,即為不同的客戶端提供寬窄不同的接口,只提供用戶需要的行為,而隱藏用戶不需要的行為。

六、D依賴倒置原則DIP

Dependency-Inversion Principle 要依賴抽象,而不要依賴具體的實現(xiàn), 具體而言就是高層模塊不依賴于底層模塊,二者共同依賴于抽象。抽象不依賴于具體,具體依賴于抽象。

1、定義

高層模塊不應該依賴低層模塊,它們都應該依賴抽象。抽象不應該依賴于細節(jié),細節(jié)應該依賴于抽象。簡單的說,依賴倒置原則要求客戶端依賴于抽象耦合。原則表述:
(1)抽象不應當依賴于細節(jié);細節(jié)應當依賴于抽象;
(2)要針對接口編程,不針對實現(xiàn)編程。

2、原則分析

(1)如果說開閉原則是面向?qū)ο笤O計的目標,依賴倒轉(zhuǎn)原則是到達面向設計"開閉"原則的手段..如果要達到最好的"開閉"原則,就要盡量的遵守依賴倒轉(zhuǎn)原則. 可以說依賴倒轉(zhuǎn)原則是對"抽象化"的最好規(guī)范! 我個人感覺,依賴倒轉(zhuǎn)原則也是里氏代換原則的補充..你理解了里氏代換原則,再來理解依賴倒轉(zhuǎn)原則應該是很容易的。
(2)依賴倒轉(zhuǎn)原則的常用實現(xiàn)方式之一是在代碼中使用抽象類,而將具體類放在配置文件中。
(3)類之間的耦合:零耦合關(guān)系,具體耦合關(guān)系,抽象耦合關(guān)系。依賴倒轉(zhuǎn)原則要求客戶端依賴于抽象耦合,以抽象方式耦合是依賴倒轉(zhuǎn)原則的關(guān)鍵。

3、例子1

理解這個依賴倒置,首先我們需要明白依賴在面向?qū)ο笤O計的概念:
依賴關(guān)系(Dependency):是一種使用關(guān)系,特定事物的改變有可能會影響到使用該事物的其他事物,在需要表示一個事物使用另一個事物時使用依賴關(guān)系。(假設A類的變化引起了B類的變化,則說名B類依賴于A類。)大多數(shù)情況下,依賴關(guān)系體現(xiàn)在某個類的方法使用另一個類的對象作為參數(shù)。在UML中,依賴關(guān)系用帶箭頭的虛線表示,由依賴的一方指向被依賴的一方。
4、例子2
某系統(tǒng)提供一個數(shù)據(jù)轉(zhuǎn)換模塊,可以將來自不同數(shù)據(jù)源的數(shù)據(jù)轉(zhuǎn)換成多種格式,如可以轉(zhuǎn)換來自數(shù)據(jù)庫的數(shù)據(jù)(DatabaseSource)、也可以轉(zhuǎn)換來自文本文件的數(shù)據(jù)(TextSource),轉(zhuǎn)換后的格式可以是XML文件(XMLTransformer)、也可以是XLS文件(XLSTransformer)等。
由于需求的變化,該系統(tǒng)可能需要增加新的數(shù)據(jù)源或者新的文件格式,每增加一個新的類型的數(shù)據(jù)源或者新的類型的文件格式,客戶類MainClass都需要修改源代碼,以便使用新的類,但違背了開閉原則?,F(xiàn)使用依賴倒轉(zhuǎn)原則對其進行重構(gòu)。
當然根據(jù)具體的情況,也可以將AbstractSource注入到AbstractStransformer,依賴注入的方式有以下三種:

[img]https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_png/ ... rFZQ/640?wx_fmt=png[/img]

七、合成/聚合復用原則

(Composite/Aggregate ReusePrinciple ,CARP):要盡量使用對象組合,而不是繼承關(guān)系達到軟件復用的目的。

1、定義

經(jīng)常又叫做合成復用原則(Composite ReusePrinciple或CRP),盡量使用對象組合,而不是繼承來達到復用的目的。
就是在一個新的對象里面使用一些已有的對象,使之成為新對象的一部分;新對象通過向這些對象的委派達到復用已有功能的目的。簡而言之,要盡量使用合成/聚合,盡量不要使用繼承。

2、原則分析

(1)在面向?qū)ο笤O計中,可以通過兩種基本方法在不同的環(huán)境中復用已有的設計和實現(xiàn),即通過組合/聚合關(guān)系或通過繼承。
繼承復用:實現(xiàn)簡單,易于擴展。破壞系統(tǒng)的封裝性;從基類繼承而來的實現(xiàn)是靜態(tài)的,不可能在運行時發(fā)生改變,沒有足夠的靈活性;只能在有限的環(huán)境中使用。(“白箱”復用)
組合/聚合復用:耦合度相對較低,選擇性地調(diào)用成員對象的操作;可以在運行時動態(tài)進行。(“黑箱”復用)
(2)組合/聚合可以使系統(tǒng)更加靈活,類與類之間的耦合度降低,一個類的變化對其他類造成的影響相對較少,因此一般首選使用組合/聚合來實現(xiàn)復用;其次才考慮繼承,在使用繼承時,需要嚴格遵循里氏代換原則,有效使用繼承會有助于對問題的理解,降低復雜度,而濫用繼承反而會增加系統(tǒng)構(gòu)建和維護的難度以及系統(tǒng)的復雜度,因此需要慎重使用繼承復用。
(3)此原則和里氏代換原則氏相輔相成的,兩者都是具體實現(xiàn)"開-閉"原則的規(guī)范。違反這一原則,就無法實現(xiàn)"開-閉"原則,首先我們要明白合成和聚合的概念:
注意:聚合和組合的區(qū)別是什么?
合成(組合):表示一個整體與部分的關(guān)系,指一個依托整體而存在的關(guān)系(整體與部分不可以分開);比如眼睛和嘴對于頭來說就是組合關(guān)系,沒有了頭就沒有眼睛和嘴,它們是不可分割的。在UML中,組合關(guān)系用帶實心菱形的直線表示。
聚合:聚合是比合成關(guān)系的一種更強的依賴關(guān)系,也表示整體與部分的關(guān)系(整體與部分可以分開);比如螺絲和汽車玩具的關(guān)系,螺絲脫離玩具依然可以用在其它設備之上。在UML中,聚合關(guān)系用帶空心菱形的直線表示。

八、迪米特法則

(Law of Demeter,LoD:系統(tǒng)中的類,盡量不要與其他類互相作用,減少類之間的耦合度。

1、定義

又叫最少知識原則(Least Knowledge Principle或簡寫為LKP)幾種形式定義:
不要和“陌生人”說話。英文定義為:Don't talk to strangers.
只與你的直接朋友通信。英文定義為:Talk only to your immediate friends.
每一個軟件單位對其他的單位都只有最少的知識,而且局限于那些與本單位密切相關(guān)的軟件單位。
簡單地說,也就是,一個對象應當對其它對象有盡可能少的了解。一個類應該對自己需要耦合或調(diào)用的類知道得最少,你(被耦合或調(diào)用的類)的內(nèi)部是如何復雜都和我沒關(guān)系,那是你的事情,我就知道你提供的public方法,我就調(diào)用這么多,其他的一概不關(guān)心。

2、法則分析

朋友類:在迪米特法則中,對于一個對象,其朋友包括以下幾類:
(1) 當前對象本身(this);
(2) 以參數(shù)形式傳入到當前對象方法中的對象;
(3) 當前對象的成員對象;
(4) 如果當前對象的成員對象是一個集合,那么集合中的元素也都是朋友;
(5) 當前對象所創(chuàng)建的對象。
任何一個對象,如果滿足上面的條件之一,就是當前對象的“朋友”,否則就是“陌生人”。
3、狹義法則和廣義法則:
在狹義的迪米特法則中,如果兩個類之間不必彼此直接通信,那么這兩個類就不應當發(fā)生直接的相互作用,如果其中的一個類需要調(diào)用另一個類的某一個方法的話,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用。
狹義的迪米特法則:可以降低類之間的耦合,但是會在系統(tǒng)中增加大量的小方法并散落在系統(tǒng)的各個角落,它可以使一個系統(tǒng)的局部設計簡化,因為每一個局部都不會和遠距離的對象有直接的關(guān)聯(lián),但是也會造成系統(tǒng)的不同模塊之間的通信效率降低,使得系統(tǒng)的不同模塊之間不容易協(xié)調(diào)。
廣義的迪米特法則:指對對象之間的信息流量、流向以及信息的影響的控制,主要是對信息隱藏的控制。信息的隱藏可以使各個子系統(tǒng)之間脫耦,從而允許它們獨立地被開發(fā)、優(yōu)化、使用和修改,同時可以促進軟件的復用,由于每一個模塊都不依賴于其他模塊而存在,因此每一個模塊都可以獨立地在其他的地方使用。一個系統(tǒng)的規(guī)模越大,信息的隱藏就越重要,而信息隱藏的重要性也就越明顯。
4、迪米特法則的主要用途:在于控制信息的過載。
在類的劃分上,應當盡量創(chuàng)建松耦合的類,類之間的耦合度越低,就越有利于復用,一個處在松耦合中的類一旦被修改,不會對關(guān)聯(lián)的類造成太大波及;
在類的結(jié)構(gòu)設計上,每一個類都應當盡量降低其成員變量和成員函數(shù)的訪問權(quán)限;
在類的設計上,只要有可能,一個類型應當設計成不變類;
在對其他類的引用上,一個對象對其他對象的引用應當降到。

5、例子

外觀模式Facade(結(jié)構(gòu)型)
迪米特法則與設計模式Facade模式、Mediator模式
系統(tǒng)中的類,盡量不要與其他類互相作用,減少類之間的耦合度,因為在你的系統(tǒng)中,擴展的時候,你可能需要修改這些類,而類與類之間的關(guān)系,決定了修改的復雜度,相互作用越多,則修改難度就越大,反之,如果相互作用的越小,則修改起來的難度就越小..例如A類依賴B類,則B類依賴C類,當你在修改A類的時候,你要考慮B類是否會受到影響,而B類的影響是否又會影響到C類. 如果此時C類再依賴D類的話,呵呵,我想這樣的修改有的受了。

九、Q&A1、面向?qū)ο笤O計其他原則?

封裝變化;
少用繼承多用組合;
針對接口編程、不針對實現(xiàn)編程;
為交互對象之間的松耦合設計而努力;
類應該對擴展開發(fā)、對修改封閉(開閉OCP原則);
依賴抽象,不要依賴于具體類(依賴倒置DIP原則);
密友原則:只和朋友交談(最少知識原則,迪米特法則);
說明:一個對象應當對其他對象有盡可能少的了解,將方法調(diào)用保持在界限內(nèi),只調(diào)用屬于以下范圍的方法: 該對象本身(本地方法)對象的組件 被當作方法參數(shù)傳進來的對象 此方法創(chuàng)建或?qū)嵗娜魏螌ο螅?/span>
別找我(調(diào)用我) 我會找你(調(diào)用你)(好萊塢原則);
一個類只有一個引起它變化的原因(單一職責SRP原則);

2、你能解釋一下里氏替換原則嗎?

嚴格定義:如果對每一個類型為S的對象o1,都有類型為T的對象o2,使得以T定義的所有程序P在所有的對象用o1替換o2時,程序P的行為沒有變化,那么類型S是類型T的子類型。
通俗表述:所有引用基類(父類)的地方必須能透明地使用其子類的對象。也就是說子類可以擴展父類的功能,但不能改變父類原有的功能。它包含以下4層含義:
  • 子類可以實現(xiàn)父類的抽象方法,但不能覆蓋父類的非抽象方法。
  • 子類中可以增加自己特有的方法。
  • 當子類的方法重載父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入?yún)?shù)更寬松。
  • 當子類的方法實現(xiàn)父類的抽象方法時,方法的后置條件(即方法的返回值)要比父類更嚴格。

3、什么情況下會違反迪米特法則?為什么會有這個問題?

迪米特法則建議“只和朋友說話,不要陌生人說話”,以此來減少類之間的耦合。

4、給我一個符合開閉原則的設計模式的例子?

開閉原則要求你的代碼對擴展開放,對修改關(guān)閉。這個意思就是說,如果你想增加一個新的功能,你可以很容易的在不改變已測試過的代碼的前提下增加新的代碼。有好幾個設計模式是基于開閉原則的,如策略模式,如果你需要一個新的策略,只需要實現(xiàn)接口,增加配置,不需要改變核心邏輯。一個正在工作的例子是 Collections.sort() 方法,這就是基于策略模式,遵循開閉原則的,你不需為新的對象修改 sort() 方法,你需要做的僅僅是實現(xiàn)你自己的 Comparator 接口。

5、什么時候使用享元模式(蠅量模式)?

享元模式通過共享對象來避免創(chuàng)建太多的對象。為了使用享元模式,你需要確保你的對象是不可變的,這樣你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。




談談移動端布局

周周

移動端推廣速度快,效果好,越來越多的企業(yè),商家開始重視移動站的建設和移動頁面(h5)的制作。隨著移動頁面的玩法越來越多,對前端技術(shù)的要求也會越來越高。

選擇合適的布局,是寫好移動頁面的第一步。今天我們就來談談移動端的布局問題。
為什么移動端布局如此混亂?這是由多方的原因造成的。
1. css這套技術(shù)系統(tǒng)本身十分混亂,基本上可以說毫無規(guī)律可言,依賴于技術(shù)人員的熟練程度而不是邏輯更多一些;
2.css歷經(jīng)了多個時代的升級,每一次升級之后,新的技術(shù)標準和舊的基本上沒有任何關(guān)聯(lián)。比如:table布局,div+css布局,flex布局,grid布局等;
3. 手機終端市場的混亂。當前市場上手機的尺寸五花八門;加上由iphone的retina技術(shù)帶來的dpr的混亂;

關(guān)于移動設備一些基本概念的理解。



一. 物理設備像素。
思考:為什么手電筒只能發(fā)出一種顏色的光,而我們的屏幕能發(fā)出這么多種顏色的光?
因為我們的屏幕是由無數(shù)個小的手電筒組成的,每個點可以發(fā)不同顏色的光,最后就組成了我們看到的彩色的效果。
每張圖片都是由色點組成的,每個色點稱為一個像素。一張圖片由30萬個色點組成,這個圖片的像素就是30W。我們常說相機是多少像素,這個像素實際就是在說這款照相機的感器件有多少個,有100W個感光器件的相機就是100W像素的相機,有4000W個感光器件的相機就是4000W像素,以此類推。一臺100W像素的相機拍攝的照片洗成5寸的照片會比洗成6寸清晰一點。
二. 屏幕分辨率
屏幕分辨率是屏幕每行的像素點數(shù)*每列的像素點數(shù),每個屏幕有自己的分辨率。屏幕分辨率越高,所呈現(xiàn)的色彩越多,清晰度越高。
結(jié)論:
1. 像素的單位本質(zhì)上是:個數(shù),100像素你可以理解成你有100個手電筒;
2. 同樣大小(比如1cm*1cm大小的矩形),里面的像素越多,畫面越清晰;
三.css像素
在pc端1css像素相當于1物理設備像素。
思考:
我們的手機分辨率是640*1136(iphone 5和iphone 5s的物理設備分辨率),如果我們打開一個純粹pc端的網(wǎng)站會出現(xiàn)什么情況?
(比如jumei.com,min-width是1090px,在pc端的我的電腦的設備寬度是1280,通過screen.width進行檢測)
我們會發(fā)現(xiàn)網(wǎng)站會縮小到我們可以看到整個網(wǎng)站(www.jubi.com)
則會發(fā)現(xiàn),有滾動條了,因為禁止縮放了
四. dpr
1個css像素占多少物理設備像素
思考:iphone 5或者iphone 5s一屏幕能看到的極限是多少寬度?
應該是320(這是默認的可視區(qū)的css寬度) * 2 = 640px
以上,我們學習完了所有關(guān)于移動端布局相關(guān)的概念,接下來,我們來聊一聊布局的思路。
假如我們有640px的設計稿,我們?nèi)绾尾拍茏層脩羧靠吹侥?
思路一:百分比布局
把尺寸除以2,比如我們量出來的是640px ---> 實際上我們只寫320px;
如果是iphone 6怎么辦? iphone 6的寬度是375px;
由于320和375的寬度其實差別不大,我們可以不定寬度,也就是把整體寬度設定為100%,然后其他的全部量出來是多少。
布局方法
- 拿到設計師給我們的設計稿之后(推薦640px),把所有量出來的尺寸除以2即可
- 遇到等分就用百分比
- 左浮動 + 右浮動(導航部分實現(xiàn)、折扣推薦導航部分) --> 適合于所有的元素寬度固定的
- 左浮動 + padding擠(見超值折扣推薦內(nèi)容部分) 本質(zhì)上元素大小在任何尺寸下面都是一致,改變的其實是元素與元素之間的間距大小 --> 適合一個元素寬度固定,另一個寬度自適應;

網(wǎng)站示例

http://m.duba.com/

http://m.lagou.com/

百分比布局的缺點
在大屏幕的手機下顯示效果會變成有些頁面元素寬度被拉的很長,但是高度還是和原來一樣,實際顯示非常的不協(xié)調(diào),這就是流式布局的最致命的缺點,往往只有幾個尺寸的手機下看到的效果是令人滿意的,其實很多視覺設計師應該無法接受這種效果,因為他們的設計圖在大屏幕手機下看到的效果相當于是被橫向拉長來一樣。流式布局并不是最理想的實現(xiàn)方式,通過大量的百分比布局,會經(jīng)常出現(xiàn)許多兼容性的問題,還有就是對設計有很多的限制,因為他們在設計之初就需要考慮流式布局對元素造成的影響,只能設計橫向拉伸的元素布局,設計的時候存在很多局限性。
思路二:rem布局
如何理解rem布局?
思考一個問題,假如我們的設計稿是750px,我們量出來一個盒子的寬度是75px,那么在640px下面,它應該是多少合適呢? 答案是:64
問題,如果才能保證你寫的css的尺寸只需要寫一次,在不同的屏幕尺寸下面不用改?
假如我們在750px下面,我們讓html的font-size為75,則這個盒子的寬度是1rem,在640px下面我們讓html的font-size為64,則這個盒子的寬度也是1rem,問題就這樣解決了。
那么實際開發(fā)中,該用什么樣等布局思路?
我們打開m.jd.com,m.vip.com,會發(fā)現(xiàn),實際上沒有一個網(wǎng)站用了純粹的百分比或者rem布局,經(jīng)常會發(fā)現(xiàn)各種布局思路混在一起,因為沒有一套布局思路能夠通用保證不出問題
為什么rem不是萬能的?
比如1px,如果我們在dpr是2的情況下就會變得很粗,我們知道那并不是真正的1像素。
推薦布局思路——使用由阿里出品的lib-flexible庫。

網(wǎng)址:https://github.com/amfe/lib-flexible;


該如何使用呢?
1. 引入布局用的flexible.js要注意的是不要再寫meta:viewport標簽了,因為flexible.js會自動幫你創(chuàng)建;
2. 引入base.css;
3. 把設計師的設計稿拿過來,標注稿基準字體大小 = 標注稿寬度 / 10,如標注稿寬為750,標注稿基準字體大小為75;標注稿寬為640,標注稿基準字體大小為64;
4. 除了字體大小以外,其他所有的均按rem來,比如你的設計稿是750px的,那么,假如你量出來的是75px,則是1rem;
字體除外,要根據(jù)不同的dpr設置不同的大小,比如如果是750的設計稿,那么字體假如是24px,則在dpr為1的情況下是16px,dpr2的情況下是24px,dpr3的情況下是32px(這塊涉及到字體專業(yè)知識,總結(jié)一句話就是沒有人會考慮用奇數(shù)字體,https://www.zhihu.com/question/20440679,所以不能讓工具幫我們自動算,得寫死。
以上是我個人關(guān)于移動端布局的一些總結(jié)。如有不妥的地方,還請指正。
最后附上關(guān)于移動端常見問題當網(wǎng)址:



js json的格式、存儲與發(fā)送

周周

1.Json的格式

其實json就是對象。源生的js代碼并沒有類的概念。對象救就是object。對象有自己的屬性,也可以有自己的方法。json是一種輕量級的存儲和交換信息的語言。他有自己的格式。

較為簡單的json。里面只有簡單的對象,key+value的形式:

  • var CellInfo = {


  •                 "CellId":         document.getElementById("CellId").value,


  •                 "UEAmount":         document.getElementById("UE value").innerText,


  •                 "BearAddDel":         document.getElementById("bearvalue").innerText,


  •                 "UEAttachDe":         document.getElementById("attachvalue").innerText,


  •                 "TotalDLTP":         document.getElementById("dlvalue").innerText,

  •               "TotalULTP":         document.getElementById("ulvalue").innerText,


  •                 };



每個元素之間用逗號隔開。調(diào)用每個key的值可用語句。例如:CellInfo.UEAmunt,就可取出其中的值。

較為復雜的json。里面包含了對象。


  • var UEGroup1 = {



  •                 "UEAmount": ua[1],



  •                 "DBR1": {



  •                         "DLPackageSize": DS[1],



  •                         "ULPackageSize": US[1],



  •                         "DLTP": DP[1],



  •                         "ULTP": UP[1],



  •                         "QCI": QCI[0]



  •                 },



  •                 "DBR2": {



  •                         "DLPackageSize": DS[2],



  •                         "ULPackageSize": US[2],



  •                         "DLTP": DP[2],



  •                         "ULTP": UP[2],



  •                         "QCI": QCI[1]



  •                 },



  •                 "DBR3": {



  •                         "DLPackageSize": DS[3],



  •                         "ULPackageSize": US[3],



  •                         "DLTP": DP[3],



  •                         "ULTP": UP[3],



  •                         "QCI": QCI[2]



  •                 }



  •         };




例如這個UEGroup1,里面的元素不僅有簡單的key+value,還包含了三個對象。對象里的元素用{}括起來,彼此之間用逗號隔開。想具體訪問某個元素的值也是通過逐層key,例如:UEGrooup1.DBR1.DLPackageSize

動態(tài)的往json只增加元素,增加對象。

前面說的幾個都是靜態(tài)的,提前寫好的。那如果臨時想加一個元素,例如在Cellinfo這個json中相加一個number的元素:

CellInfo.number=10;

對于往json中添加對象。例如我們想把Cellinfo和UEGroup1這兩個object作為兩個元素加入到另外一個大的json中:

  • var PETInfo = {};//聲明了一個空的對象



  • var CellInfo = {



  •                 "CellId":         document.getElementById("CellId").value,



  •                 "UEAmount":         document.getElementById("UE value").innerText,



  •                 "BearAddDel":         document.getElementById("bearvalue").innerText,



  •                 "UEAttachDe":         document.getElementById("attachvalue").innerText,



  •                 "TotalDLTP":         document.getElementById("dlvalue").innerText,



  •                 "TotalULTP":         document.getElementById("ulvalue").innerText,



  •                 };



  • str_CellInfo = JSON.stringify(CellInfo);//將CellInfo轉(zhuǎn)為字符串對象



  • PETInfo.CellInfo=str_CellInfo;//在PETInfo中添加名為Cellinfo的屬性,并賦值


2.json的發(fā)送

json寫好后,發(fā)送給后臺。至于后臺怎么處理數(shù)據(jù)我們不關(guān)心。發(fā)送json的函數(shù)如下:

  • function post(path, params, method) {



  •         method = method || "post";



  •         var form = document.createElement("form");



  •         form.setAttribute("method", method);



  •         form.setAttribute("action", path);





  •         for (var key in params) {



  •                 if (params.hasOwnProperty(key)) {



  •                         var hiddenField = document.createElement("input");



  •                         hiddenField.setAttribute("type", "hidden");



  •                         hiddenField.setAttribute("name", key);



  •                         hiddenField.setAttribute("value", params[key]);



  •                         form.appendChild(hiddenField);



  •                 }



  •         }



  •         document.body.appendChild(form);



  •         form.submit();



  • }

    參數(shù)分別是后臺的地址,變量,方法。變量就是我們自己寫好的json,方法默認為post。例如我們想發(fā)剛剛的PETInfo

    $.post('http://10.140.160.64:3012/users/ueinfo', PETInfo);

    數(shù)據(jù)的發(fā)送、并獲取結(jié)果的實例:

    需求描述:用戶填寫一系列的輸入框,前端獲取數(shù)據(jù),封裝成json并發(fā)送給服務器,服務器會返回一個返回值,表示狀態(tài)。前端需要展示這個內(nèi)容提示客戶。

    • function sendBook(){



    •         var Book={



    •                 "openstackIP":document.getElementById("openstackIP").value,



    •                 "RAPName":document.getElementById("RAPName").value,



    •                 "RAPVer":document.getElementById("ver").value,



    •                 "OAMIP":document.getElementById("OAMIP").value



    •         };//json封裝用戶輸入的數(shù)據(jù)



    •         $.post('http://10.140.160.64:3012/servers/env/book', Book)//調(diào)用post傳輸數(shù)據(jù)



    •         .done((resp) => {//傳輸后獲取服務器的返回值



    •         alert(resp);//展示返回值



    •        // window.location.href = 'Environment-List.html';//選擇性界面跳轉(zhuǎn)



    •     });



    • }

    3.json在本地的存儲

    存儲數(shù)據(jù)有很多方法。這里我用的是localStorage。localStorage與cookie的區(qū)別如下:

    ① cookie在瀏覽器與服務器之間來回傳遞。
    sessionStorage和localStorage不會把數(shù)據(jù)發(fā)給服務器,僅在本地保存

    ②數(shù)據(jù)有效期不同:
    cookie只在設置的cookie過期時間之前一直有效,即使窗口或瀏覽器關(guān)閉。
    sessionStorage:僅在當前瀏覽器窗口關(guān)閉前有效。
    localStorage  始終有效,長期保存。

    ③cookie數(shù)據(jù)還有路徑的概念,可以限制cookie只屬于某個路徑下。
    存儲大小也不同,cookie數(shù)據(jù)不能超過4k,sessionStorage和localStorage 雖然也有存儲大小的限制,但比cookie大得多,可以達到5M或更大。

    ④ 作用域不用
    sessionStorage不在不同的瀏覽器窗口中共享;
    localStorage在所有同源窗口中都是共享的;
    cookie也是在所有同源窗口中都是共享的;

    WebStorage 支持事件通知機制,可以將數(shù)據(jù)更新的通知發(fā)送給監(jiān)聽者。Web Storage 的 api 接口使用更方便。

    用localstage存儲json的實例:

    • str_PETInfo=JSON.stringify(PETInfo);//將json轉(zhuǎn)為字符串對象



    • window.localStorage.setItem("PET",str_PETInfo);//存入本地,該json的key為PET


    將json取出來:



    • var PET=JSON.parse(window.localStorage.getItem("PET"));//將字符串轉(zhuǎn)化為json



    • var CellInfo=JSON.parse(PET.CellInfo);//json中的Cellinfo對象轉(zhuǎn)化為json



手機端頁面常見的問題

周周

1.解決頁面使用overflow: scroll在iOS上滑動卡頓的問題?



首先你可能會給頁面的html和body增加了height: 100%, 然后就可能造成IOS上頁面滑動的卡頓問題。解決方案是:



(1) 看是否能把body和html的height: 100%去除掉。

(2) 在滾動的容器中增加:-webkit-overflow-scrolling: touch或者給body增加:body {overflow-x: hidden}。



2.ios頁面橡皮彈回效果遮擋頁面選項卡?



(1) 有時body和html的height: 100%去除掉問題可能就沒有了。

(2) 到達臨界值的時候在阻止事件默認行為

var startY,endY;
//記錄手指觸摸的起點坐標
$('body').on('touchstart',function (e) {
     startY = e.touches[0].pageY;
});
$('body').on('touchmove',function (e) {
     endY = e.touches[0].pageY;  //記錄手指觸摸的移動中的坐標
     //手指下滑,頁面到達頂端不能繼續(xù)下滑
     if(endY>startY&& $(window).scrollTop()<=0){
         e.preventDefault();
     }
   //手指上滑,頁面到達底部能繼續(xù)上滑
     if(endY<startY&& $(window).scrollTop()+ 
         $(window).height()>=$('body')[0].scrollHeight){
         e.preventDefault();
     }
})
有時也會碰見彈窗出來后兩個層的橡皮筋效果出現(xiàn)問題,我們可以在彈出彈出時給底層頁面加上一個類名,類名禁止頁面滑動這樣下層的橡皮筋效果就會被禁止,就不會影響彈窗層。 3.IOS機型margin屬性無效問題? (1) 設置html body的高度為百分比時,margin-bottom在safari里失效 (2) 直接padding代替margin 4.Ios綁定點擊事件不執(zhí)行?  (1)添加樣式cursor :pointer。點擊后消除背景閃一下的css:-webkit-tap-highlight-color:transparent;  5.Ios鍵盤換行變?yōu)樗阉鳎?/span> 首先,input 要放在 form里面。 這時 "換行" 已經(jīng)變成 “前往”。 如果想變成 “搜索”,input 設置 type="search"。 6.Jq對a標簽點擊事件不生效? 出現(xiàn)這種情況的原因不明,有的朋友解釋:我們平時都是點擊的A標簽中的文字了。 所以要想用JS模擬點擊A標簽事件,就得先往A標簽中的文字添加能被JS捕獲的元素,然后再用JS模擬點擊該元素即可。但是我覺得不合理,雖然找不到原因但是解決辦法還是有的。 (1)document.getElementById("abc ").click(); (2)$("#abc ")[0].click(); 7.有時因為服務器或者別的原因?qū)е马撁嫔系膱D片沒有找到? 這是我們想需要用一個本地的圖片代替沒有找的的圖片
<script type="text/javascript"> 
function nofind(){ 
var img=event.srcElement; 
img.src="images/logoError.png"; 
img.onerror=null; 控制不要一直跳動 
} 
</script> 
<img src="images/logo.png" />
8.transform屬性影響position:fixed?

(1)規(guī)范中有規(guī)定:如果元素的transform值不為none,則該元素會生成包含塊和層疊上下文。CSS Transforms Module Level 1不只在手機上,電腦上也一樣。除了fixed元素會受影響之外,z-index(層疊上下文)值也會受影響。絕對定位元素等和包含塊有關(guān)的屬性都會受到影響。當然如果transform元素的display值為inline時又會有所不同。最簡單的解決方法就是transform元素內(nèi)部不能有absolute、fixed元素.

9.ios對position: fixed不太友好,有時我們需要加點處理?

在安卓上面,點擊頁面底部的輸入框,軟鍵盤彈出,頁面移動上移。
而ios上面,點擊頁面底部輸入框,軟鍵盤彈出,輸入框看不到了。。。查資料說什么的都有,iscroll,jquery-moblie,absolute,fixe,static都非常復雜,要改很多。。。
讓他彈出時讓滾動條在部
var u = navigator.userAgent, app = navigator.appVersion;
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios終端
if (isiOS) {
    $('textarea').focus(function () {
        window.setTimeout('scrollBottom()', 500);
    });
}
function scrollBottom() {
    window.scrollTo(0, $('body').height());
}
10.jq validate插件驗證問題?

(1)所以的input必須有name不然會出錯

11.有時手機會出現(xiàn)斷網(wǎng)的情況,我沒可能會對斷網(wǎng)的情況做一些處理?

(1)navigator.onLine可判斷是否是脫機狀態(tài).

12.判斷對象的長度?

(1)用Object.keys,Object.keys方法返回的是一個數(shù)組,數(shù)組里面裝的是對象的屬性
var person = {
    "name" : "zhangshan",
    "sex" : "man",
    "age" : "50",
    "height" : "180",
    "phone" : "1xxxxxxxxxx",
    "email" : "xxxxxxxxx@xxx.com"
};
var arr = Object.keys(person);
console.log(arr.length);
(2)Object.getOwnPropertyNames(obj).length

13.上一題我們用到了Object.keys與Object.getOwnPropertyNames他們的區(qū)別?

Object.keys定義:返回一個對象可枚舉屬性的字符串數(shù)組;
Object.getOwnPropertyNames定義:返回一個對象可枚舉、不可枚舉屬性的名稱;
屬性的可枚舉性、不可枚舉性:定義:可枚舉屬性是指那些內(nèi)部 “可枚舉” 標志設置為 true 的屬性,對于通過直接的賦值和屬性初始化的屬性,該標識值默認為即為 true,對于通過 Object.defineProperty 等定義的屬性,該標識值默認為 false。
var obj = { "prop1": "v1" };
Object.defineProperty(obj, "prop2", { value: "v2", enumerable: false });
console.log(Object.keys(obj).length);           //output:1
console.log(Object.getOwnPropertyNames(obj).length);    //output:2
console.log(Object.keys(obj));           //output:Array[1] => [0: "prop1"]
console.log(Object.getOwnPropertyNames(obj));    //output:Array[2] => [0: "prop1", 1: "prop2"]

綜合實例


var obj = { "prop1": "v1" };
Object.defineProperty(obj, "prop2", { value: "v2", enumerable: false});
console.log(obj.hasOwnProperty("prop1")); //output: true
console.log(obj.hasOwnProperty("prop2")); //output: true
console.log(obj.propertyIsEnumerable("prop1")); //output: true
console.log(obj.propertyIsEnumerable("prop2")); //output: false
console.log('prop1' in obj);    //output: true
console.log('prop2' in obj);    //output: true
for (var item in obj) {
    console.log(item);
}
//output:prop1
for (var item in Object.getOwnPropertyNames(obj)) {
    console.log(Object.getOwnPropertyNames(obj)[item]);
}
//ouput:[prop1,prop2]

14.移動開發(fā)不同手機彈出數(shù)字鍵盤問題?



(1)type="tel"

iOS和Android的鍵盤表現(xiàn)都差不多

(2)type="number"

優(yōu)點是Android下實現(xiàn)的一個真正的數(shù)字鍵盤

缺點一:iOS下不是九宮格鍵盤,輸入不方便

缺點二:舊版Android(包括微信所用的X5內(nèi)核)在輸入框后面會有超級雞肋的小尾巴,好在Android 4.4.4以后給去掉了。

不過對于缺點二,我們可以用webkit私有的偽元素給fix掉:


input[type=number]::-webkit-inner-spin-button,  
input[type=number]::-webkit-outer-spin-button { 
        -webkit-appearance: none; 
        appearance: none; 
        margin: 0; 
}

(3)pattern屬性



pattern用于驗證表單輸入的內(nèi)容,通常HTML5的type屬性,比如email、tel、number、data類、url等,已經(jīng)自帶了簡單的數(shù)據(jù)格式驗證功能了,加上pattern后,前端部分的驗證更加簡單了。

顯而易見,pattern的屬性值要用正則表達式。

實例 簡單的數(shù)字驗證

數(shù)字的驗證有兩個:

<input type="number" pattern="d"> 

<input type="number" pattern="[0-9]*">



15.input[number]類型輸入非數(shù)字字符



js獲取的值是空;比如-12,+123等



16.Javascript:history.go()和history.back()的用法與區(qū)別?




簡單的說就是:go(-1):返回上一頁,原頁面表單中的內(nèi)容會丟失;back():返回上一頁,原頁表表單中的內(nèi)容會保留。history.go(-1):后退+刷新history.back():后退

之所以注意到這個區(qū)別,是因為不同的瀏覽器后退行為也是有區(qū)別的,而區(qū)別就跟javascript:history.go()和history.back()的區(qū)別類似。

Chrome和ff瀏覽器后退頁面,會刷新后退的頁面,若有數(shù)據(jù)請求也會提交數(shù)據(jù)申請。類似于history.go(-1);

而safari(包括桌面版和ipad版)的后退按鈕則不會刷新頁面,也不會提交數(shù)據(jù)申請。類似于javascript:history.back();



17.Meta基礎知識:



<meta name="viewport"content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />

// width    設置viewport寬度,為一個正整數(shù),或字符串‘device-width’

// height   設置viewport高度,一般設置了寬度,會自動解析出高度,可以不用設置

// initial-scale    默認縮放比例,為一個數(shù)字,可以帶小數(shù)

// minimum-scale    允許用戶最小縮放比例,為一個數(shù)字,可以帶小數(shù)

// maximum-scale    允許用戶最大縮放比例,為一個數(shù)字,可以帶小數(shù)

// user-scalable    是否允許手動縮放 

空白頁基本meta標簽

<!-- 設置縮放 -->

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" />

<!-- 可隱藏地址欄,僅針對IOS的Safari(注:IOS7.0版本以后,safari上已看不到效果) -->

<meta name="apple-mobile-web-app-capable" content="yes" />

<!-- 僅針對IOS的Safari頂端狀態(tài)條的樣式(可選default/black/black-translucent ) -->

<meta name="apple-mobile-web-app-status-bar-style" content="black" />

<!-- IOS中禁用將數(shù)字識別為電話號碼/忽略Android平臺中對郵箱地址的識別 -->

<meta name="format-detection"content="telephone=no, email=no" />

其他meta標簽

<!-- 啟用360瀏覽器的極速模式(webkit) -->

<meta name="renderer" content="webkit">

<!-- 避免IE使用兼容模式 -->

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

<!-- 針對手持設備優(yōu)化,主要是針對一些老的不識別viewport的瀏覽器,比如黑莓 -->

<meta name="HandheldFriendly" content="true">

<!-- 微軟的老式瀏覽器 -->

<meta name="MobileOptimized" content="320">

<!-- uc強制豎屏 -->

<meta name="screen-orientation" content="portrait">

<!-- QQ強制豎屏 -->

<meta name="x5-orientation" content="portrait">

<!-- UC強制全屏 -->

<meta name="full-screen" content="yes">

<!-- QQ強制全屏 -->

<meta name="x5-fullscreen" content="true">

<!-- UC應用模式 -->

<meta name="browsermode" content="application">

<!-- QQ應用模式 -->

<meta name="x5-page-mode" content="app">

<!-- windows phone 點擊無高光 -->

<meta name="msapplication-tap-highlight" content="no">



18.移動端如何定義字體font-family?



@ --------------------------------------中文字體的英文名稱

@ 宋體 SimSun

@ 黑體 SimHei

@ 微信雅黑 Microsoft Yahei

@ 微軟正黑體 Microsoft JhengHei

@ 新宋體 NSimSun

@ 新細明體 MingLiU

@ 細明體 MingLiU

@ 標楷體 DFKai-SB

@ 仿宋 FangSong

@ 楷體 KaiTi

@ 仿宋_GB2312 FangSong_GB2312

@ 楷體_GB2312 KaiTi_GB2312 

@

@ 說明:中文字體多數(shù)使用宋體、雅黑,英文用Helvetica



body { font-family: Microsoft Yahei,SimSun,Helvetica; }



19.打電話發(fā)短信寫郵件怎么實現(xiàn)?


// 一、打電話
<a href="tel:0755-10086">打電話給:0755-10086</a>
//  二、發(fā)短信,winphone系統(tǒng)無效
<a href="sms:10086">發(fā)短信給: 10086</a>
// 三、寫郵件
<a href="mailto:863139978@qq.com">點擊我發(fā)郵件</a>
//2.收件地址后添加?cc=開頭,可添加抄送地址(Android存在兼容問題)
<a href="mailto:863139978@qq.com?cc=zhangqian0406@yeah.net">點擊我發(fā)郵件</a>
//3.跟著抄送地址后,寫上&bcc=,可添加密件抄送地址(Android存在兼容問題)
<a href="mailto:863139978@qq.com?cc=zhangqian0406@yeah.net&bcc=384900096@qq.com">點擊我發(fā)郵件</a>
//4.包含多個收件人、抄送、密件抄送人,用分號(;)隔開多個郵件人的地址
<a href="mailto:863139978@qq.com;[url=mailto:384900096@qq.com]384900096@qq.com[/url]">點擊我發(fā)郵件</a>
//5.包含主題,用?subject=
<a href="mailto:863139978@qq.com?subject=郵件主題">點擊我發(fā)郵件</a>
//6.包含內(nèi)容,用?body=;如內(nèi)容包含文本,使用%0A給文本換行 
<a href="mailto:863139978@qq.com?body=郵件主題內(nèi)容%0A騰訊誠信%0A期待您的到來">點擊我發(fā)郵件</a>
//7.內(nèi)容包含鏈接,含http(s)://等的文本自動轉(zhuǎn)化為鏈接
<a href="mailto:863139978@qq.com?body=http://www.baidu.com">點擊我發(fā)郵件</a>
//8.內(nèi)容包含圖片(PC不支持)
<a href="mailto:863139978@qq.com?body=<img src='images/1.jpg' />">點擊我發(fā)郵件</a>
//9.完整示例
<a href="mailto:863139978@qq.com;[url=mailto:384900096@qq.com]384900096@qq.com[/url]?cc=zhangqian0406@yeah.net&bcc=993233461@qq.com&subject=[郵件主題]&body=騰訊誠邀您參與%0A%0A[url=http://www.baidu.com]http://www.baidu.com[/url]%0A%0A<img src='images/1.jpg' />">點擊我發(fā)郵件</a>
20.移動端touch事件(區(qū)分webkit和winphone)?

// 以下支持webkit
touchstart——當手指觸碰屏幕時候發(fā)生。不管當前有多少只手指
touchmove——當手指在屏幕上滑動時連續(xù)觸發(fā)。通常我們再滑屏頁面,會調(diào)用event的preventDefault()可以阻止默認情況的發(fā)生:阻止頁面滾動
touchend——當手指離開屏幕時觸發(fā)
touchcancel——系統(tǒng)停止跟蹤觸摸時候會觸發(fā)。例如在觸摸過程中突然頁面alert()一個提示框,此時會觸發(fā)該事件,這個事件比較少用

//TouchEvent說明:
touches:屏幕上所有手指的信息
targetTouches:手指在目標區(qū)域的手指信息
changedTouches:最近一次觸發(fā)該事件的手指信息
touchend時,touches與targetTouches信息會被刪除,changedTouches保存的最后一次的信息,最好用于計算手指信息

//參數(shù)信息(changedTouches[0])
clientX、clientY在顯示區(qū)的坐標
target:當前元素

//事件響應順序
ontouchstart > ontouchmove > ontouchend > onclick


前端工程師必備實用網(wǎng)站

周周

素材類網(wǎng)站


千庫網(wǎng),一個免費下載圖片素材的網(wǎng)站:http://588ku.com/ 
千庫網(wǎng).jpg.png


Unsplash是一個分享免費高質(zhì)量照片的網(wǎng)站,照片分辨率都挺大,而且都是真實的攝影師作品,圖片多是風景和靜物:https://unsplash.com/ 

Unsplash.jpg.pngUnsplash2.jpg.png

插件類網(wǎng)站

jq22,分享jQuery插件和提供各種jQuery的詳細使用方法,在線預覽,jQuery插件下載及教程http://www.jq22.com/

jq22.jpg.png


http://www.htmleaf.com/ 這個網(wǎng)站與上一個網(wǎng)站類似,也提供了大量的jQuery插件。

                htmleaf.jpg.png

layui這是一個強大的模塊化前端框架http://www.layui.com/

             layui1.jpg.png

layui2.jpg.png

H-ui,端框架,一個輕量級前端框架,簡單免費,兼容性好,服務中國網(wǎng)站:http://www.h-ui.net/index.shtml

H-ui.jpg.png 

字體類網(wǎng)站

有字庫,一個免下載字體,直接在線引用字體的網(wǎng)站http://www.youziku.com/onlinefont/index

有字庫.jpg.png



PS字體庫,包含了幾乎所有類型的字體,下載好安裝,PS中就可以使用了:http://www.psjia.com/pssc/fontxz/list_18_3.html


               PS字體庫.png

圖標類網(wǎng)站

iconfont,這是阿里巴巴旗下的圖標庫網(wǎng)站,直接搜索關(guān)鍵詞就可以找到大批的圖標。下載圖標的時候我們還可以選擇顏色、大小、格式,根據(jù)自己的需要下載就好了:http://www.iconfont.cn/plus

                   iconfont.jpg.png

easyicon,這也是一個非常有名的圖標庫,與上面那個不同的是,這里的圖標不是單一顏色的,而是設計好的顏色。下載圖標也很簡單,直接點擊對應圖標上面的格式就可以下載:http://www.easyicon.net/iconsearch/ios/

                  easyicon.jpg.png

奧森圖標(Font Awesome),提供豐富的矢量字體圖標—通過CSS可以任意控制所有圖標的大小 ,顏色,陰影:http://www.thinkcmf.com/font/search.html

                奧森圖標.jpg.png

                 奧森圖標1.jpg.png

配色類網(wǎng)站

http://colorhunt.co這個網(wǎng)站給我們提供了很多的配色方案,我們直接使用就OK了。

使用方法也很簡單,鼠標移動到對應的顏色上,我們就可以看到顏色的十六進制碼,復制這個顏色到工具里就可以使用了。

                colorhunt.jpg.png

https://webgradients.com/, 180種漸變方案供你選擇,還可以直接復制CSS樣式應用到網(wǎng)頁中。

              webgradients.jpg.png

adobe,這個是Adobe公司出的,他提供了多種配色方案。我們點擊圓盤中間的點,就可以調(diào)整出我們想要的配色方案:https://color.adobe.com/zh/create/color-wheel

                Adobe.jpg.png

http://www.colorhunter.com/,這是一個提取現(xiàn)有圖片配色方案的工具。我們上傳一張圖片,它就會幫我們把圖片的配色提取出來供我們使用。

                colorhunt.jpg.png

bootcss,這個網(wǎng)站是為WEB設計,開發(fā)中經(jīng)常用到的安全色。網(wǎng)站內(nèi)列出了顏色的十六進制碼和RGB碼,復制粘貼就可以了:http://www.bootcss.com/p/websafecolors/

               bootcss..jpg.png

sioe,這是一個在線RGB和十六進制顏色碼轉(zhuǎn)換工具。在對應的位置填入十六進制代碼,點擊轉(zhuǎn)換,我們就可以獲取到RGB顏色的代碼了http://www.sioe.cn/yingyong/yanse-rgb-16/

              sioe.jpg.png




由‘==’和‘===’引出的js的隱式轉(zhuǎn)換問題

周周

‘==’和‘===’都是Javascript中的比較運算符,都是比較運算符兩邊是否相等。對于‘==’和‘===’的區(qū)別,大家也都知道:

  ‘==’僅僅是比較運算符兩邊的數(shù)值是否相等,如果數(shù)值相等則返回true;‘===’不僅會判斷運算符兩邊的數(shù)值是否相等,并且還會判斷兩邊的類型是否相等,只有數(shù)值和類型都相等才會返回true。雖然知道以上的判斷依據(jù)已經(jīng)能解決絕大數(shù)此類問題,但是如果往其中深究來說,會有同學問:在比較的時候‘===’先判斷類型,如果類型不同就直接返回false,這個沒什么問題。但是如果是‘==’比較兩個不同類型的數(shù)據(jù)時,具體是怎么進行計算判斷的呢?

 既然是不同類型進行比較,肯定最終參與比較的結(jié)果必須是同一個類型的,因此JS會存在一個隱式轉(zhuǎn)換的問題,并且很多JS的隱式轉(zhuǎn)換很難通過console.log()等方法直觀的觀察到,因此很多初學者會對JS的隱式轉(zhuǎn)換感到疑惑。

 首先讓我們回憶一下,咱們的JS中一共有哪些數(shù)據(jù)類型?

       六大數(shù)據(jù)類型
       基本數(shù)據(jù)類型(簡單數(shù)據(jù)類型)
       number 數(shù)值型(NaN)
       string 字符串
       boolean 布爾型
       undefined 未定義
       null 空引用
       引用數(shù)據(jù)類型(復雜數(shù)據(jù)類型)
       object

       JS基礎中,我們學習到咱們的JS中一共有六種數(shù)據(jù)類型,分為基本數(shù)據(jù)類型(簡單數(shù)據(jù)類型)和引用數(shù)據(jù)類型(復雜數(shù)據(jù)類型),不同類型的值進行比較的時候,存在隱式轉(zhuǎn)換的問題,咱們通過‘==’來驗證一下JS隱式轉(zhuǎn)換的情況。

       1.我們首先來看看下列的語句計算結(jié)果:

console.log(NaN==true);//false
console.log(NaN==false);//false
console.log(NaN==0);//false
console.log(NaN==1);//false
console.log(NaN==NaN);//false

       由上面的例子可以看出,NaN屬于Number數(shù)據(jù)類型中一個特殊情況,如果‘==’兩邊同為Number數(shù)據(jù)類型的數(shù)字,很直觀的可以看出值是否相同一眼就可以看出結(jié)果,但是作為Number類型的特殊情況,NaN在進行比較的時候,也會有特殊的結(jié)果:如果 x 或 y 中有一個為 NaN,則返回 false;

       2.我們繼續(xù)看看下列的語句計算結(jié)果:

console.log(null == undefined); //true(特殊情況)---------------------------------
console.log(null == ''); //false
console.log(undefined == ''); //false

      在上述例子中,引出了一個null,null是一個簡單數(shù)據(jù)類型,它的意義就是一個空應用,但是你如果通過console.log(typeof null) 來打印結(jié)果的時候卻發(fā)現(xiàn),結(jié)果竟然是object?此時你可能會懷疑人生,然后瘋狂的翻閱之前學習的資料,因為object明明是一個復雜數(shù)據(jù)類型,怎么會在判斷null這個簡單數(shù)據(jù)類型的類型時打印出來呢?其實,這個問題屬于一個歷史問題。咱們學習的JS在發(fā)展過程中是通過ECMAScript來確定規(guī)范的,每年都會有新的規(guī)定和規(guī)范提出,在JS的發(fā)展過程中,null一開始的作用就是用來指向一個空地址,讓開發(fā)者在創(chuàng)建數(shù)據(jù)的時候,先用null賦值給還未給值的對象用于標準初始化。但是其實咱們開發(fā)過程中很少用到,但是這個仍作為規(guī)范留了下來。又因為typeof是根據(jù)數(shù)據(jù)的前幾位判斷數(shù)據(jù)類型的,null相當于空指針,前幾位是地址的格式,所以判斷結(jié)果就為object。又因為undefined值是派生自null值的,因此ECMA-262規(guī)定對他們的相等測試要返回true。所以這一情況判斷的條件為:如果 x 與 y 皆為 null 或 undefined 中的一種類型,則返回 true(null == undefined // true);否則返回 false(null == 0 // false);

       3.請看下列例子:

console.log(true == '123'); //false
console.log(true == '1'); //true
console.log(false == '0'); //true
 
console.log(true == !0); //true
 
console.log([] == []); //false
console.log([] == ![]); //true 比較地址 ------------------------------------------------
var a = c = [];
var b = [];
console.log(a == b); //false
console.log(a == !b); //true
console.log(a == c); //true
 
console.log(Boolean([]) == true); //true
console.log(Number([]) == 0); //true
console.log(Number(false) == 0); //true

       其實比較的邏輯為:如果 x,y 類型不一致,且 x,y 為 String、Number、Boolean 中的某一類型,則將 x,y 使用 Number 函數(shù)轉(zhuǎn)化為 Number 類型再進行比較;

      使用Number函數(shù)可以將其他的數(shù)據(jù)類型轉(zhuǎn)變?yōu)镹umber類型,這一同為Number類型的數(shù)據(jù),對比起來就會變得十分簡單。值得注意的是在上述的例子中,兩個空數(shù)組進行比較,結(jié)果返回的結(jié)果仍然為false,這個是怎么回事呢?其實這個很好理解,因為數(shù)組也是對象的一種,是復雜數(shù)據(jù)類型,所以用變量儲存對象時儲存的其實是地址。對象的內(nèi)容相同,但是儲存在堆區(qū)的位置不同,所以地址也是不同的,所以在判斷的時候返回的是false。

      其實在JS中還有很多的隱式轉(zhuǎn)換情況,以上只是針對于‘==’的隱式轉(zhuǎn)換情況,對于這些問題,在實際開發(fā)過程中,需要作為開發(fā)者不斷的學習和積累,這也是咱們作為開發(fā)者的一個要求之一。

jquery原理的簡單分析

周周

       jquery是一個輕量級的JS框架,這點相信大部分人都聽過,而jquery之所以有這樣一個稱呼,就是因為它悄悄披了一件外衣,將自己給隱藏了起來。

      /以下截取自jquery源碼片段
      (function( window, undefined ) {
      /*    源碼內(nèi)容    */
       })( window );

      上面這一小段代碼來自于1.9.0當中jquery的源碼,它是一個無污染的JS插件的標準寫法,專業(yè)名詞叫閉包??梢园阉唵蔚目醋鍪且粋€函數(shù),與普通函數(shù)不同的是,這個函數(shù)沒有名字,而且會立即執(zhí)行,就像下面這樣,會直接彈出字符串。

      (function( window, undefined ) {
         alert("Hello World!");
       })( window );

       可以看出來這樣寫的直接效果,就相當于我們直接彈出一個字符串。但是不同的是,我們將里面的變量變成了局域變量,這不僅可以提高運行速度,更重要的是我們在引用jquery的JS文件時,不會因為jquery當中的變量太多,而與其它的JS框架的變量命名產(chǎn)生沖突。對于這一點,我們拿以下這一小段代碼來說明。

     var temp = "Hello World!";
        (function( window, undefined ) {
         var temp = "ByeBye World!";
        })( window );
        alert(temp);

       這段代碼的運行結(jié)果是Hello而不是ByeBye,也就是說閉包中的變量聲明沒有污染到外面的全局變量,倘若我們?nèi)サ糸]包,則最終的結(jié)果會是ByeBye,就像下面這樣。

      var temp = "Hello World!";
        //    (function( window, undefined ) {
         var temp = "ByeBye World!";
      //    })( window );
       alert(temp);

       由此就可以看出來,jquery的外衣就是這一層閉包,它是很重要的一個內(nèi)容,是編寫JS框架必須知道的知識,它可以幫助我們隱藏我們的臨時變量,降低污染。

       剛才我們說了,jquery將自己聲明的變量全部都用外衣遮蓋起來了,而我們平時使用的Jquery和$,卻是真真實實的全局變量,這個是從何而來,謎底就在jquery的某一行代碼,一般是在文件的末尾。

window.jQuery = window.$ = jQuery;
       這一句話將我們在閉包當中定義的jQuery對象導出為全局變量jQuery和$,因此我們才可以在外部直接使用jQuery和$。window是默認的JS上下文環(huán)境,因此將對象綁定到window上面,就相當于變成了傳統(tǒng)意義上的全局變量,就像下面這一小段代碼的效果一樣。

      var temp = "Hello World!";
      (function( window, undefined ) {
         var temp = "ByeBye World!";
         window.temp = temp;
       })( window );
       alert(temp);

       很明顯,它的結(jié)果應該是ByeBye,而不是Hello。因為我們在閉包中導出了temp局部變量為全局變量,從而覆蓋了第一行聲明的全局變量temp。

        jquery最核心的功能,就是選擇器。而選擇器簡單理解的話,其實就是在DOM文檔中,尋找一個DOM對象的工具。

        首先我們進入jquery源碼中,可以很容易的找到jquery對象的聲明,看過以后會發(fā)現(xiàn),原來我們的jquery對象就是init對象。

         jQuery = function( selector, context ) {
          return new jQuery.fn.init( selector, context, rootjQuery );
         }

         jQuery.fn = jQuery.prototype;

         jQuery.fn.init.prototype = jQuery.fn;
        這兩句話,第一句把jQuery對象的原型賦給了fn屬性,第二句把jQuery對象的原型又賦給了init對象的原型。也就是說,init對象和jQuery具有相同的原型,因此我們在上面返回的init對象,就與jQuery對象有一樣的屬性和方法。
很多時候,我們在jQuery和DOM對象之間切換時需要用到[0]這個屬性。從截圖也可以看出,jQuery對象其實主要就是把原生的DOM對象存在了[0]的位置,并給它加了一系列簡便的方法。這個索引0的屬性我們可以從一小段代碼簡單的看一下它的由來,下面是init方法中的一小段對DOMElement對象作為選擇器的源碼。

      // Handle $(DOMElement)
       if ( selector.nodeType ) {
            /*     可以看到,這里將DOM對象賦給了jQuery對象的[0]這個位置  */
            this.context = this[0] = selector;
            this.length = 1;
           return this;
        }

       這一小段代碼可以在jquery源碼中找到,它是處理傳入的選擇參數(shù)是一個DOM對象的情況??梢钥吹剑锩婧苊黠@的將jQuery對象索引0的位置以及context屬性,都賦予了DOM對象。代碼不僅說明了這一點,也同時說明了,我們使用$(DOMElement)可以將一個DOM對象轉(zhuǎn)換為jQuery對象,從而通過轉(zhuǎn)換獲得jQuery對象的簡便方法。

css的多行省略號處理

周周

      在我們的頁面布局的時候,經(jīng)常會有這樣的需求,超過指定行文本的時候會進行(省略號...)的處理,那么我們改怎么設置css呢?如下:

設置盒子的css為:

  • overflow:hidden;;
  • text-overflow:ellipsis;
  • white-space:nowrap;

       但是這樣只能顯示一行而不能實現(xiàn)指定行,所以還要其他的方法來實現(xiàn)指定行處理的。



     WebKit瀏覽器或移動端的頁面(大部分移動端都是webkit)

        可以直接使用WebKit的CSS擴展屬性(WebKit是私有屬性)-webkit-line-clamp ;注意:這是一個不規(guī)范的屬性,它沒有出現(xiàn)在 CSS 規(guī)范草案中。



        -webkit-line-clamp用來限制在一個塊元素顯示的文本的行數(shù)。 為了實現(xiàn)該效果,它需要組合其他的WebKit屬性。
       常見結(jié)合屬性:
  • display: -webkit-box; 必須結(jié)合的屬性 ,將對象作為彈性伸縮盒子模型顯示 。
  • -webkit-box-orient 必須結(jié)合的屬性 ,設置或檢索伸縮盒對象的子元素的排列方式 。
  • text-overflow: ellipsis;,可以用來多行文本的情況下,用省略號“…”隱藏超出范圍的文本 。

     css 代碼:

  • overflow:hidden;
  • text-overflow: ellipsis;
  • display: -webkit-box;
  • -webkit-line-clamp:2;/*這里控制著顯示多少行*/
  • -webkit-box-orient:vertical;


原生js的ajax請求

周周

傳統(tǒng)方法的缺點:

      傳統(tǒng)的web交互是用戶觸發(fā)一個http請求服務器,然后服務器收到之后,在做出響應到用戶,并且返回一個新的頁面,,每當服務器處理客戶端提交的請求時,客戶都只能空閑等待,并且哪怕只是一次很小的交互、只需從服務器端得到很簡單的一個數(shù)據(jù),都要返回一個完整的HTML頁,而用戶每次都要浪費時間和帶寬去重新讀取整個頁面。這個做法浪費了許多帶寬,由于每次應用的交互都需要向服務器發(fā)送請求,應用的響應時間就依賴于服務器的響應時間。這導致了用戶界面的響應比本地應用慢得多。

什么是ajax?

       ajax的出現(xiàn),剛好解決了傳統(tǒng)方法的缺陷。AJAX 是一種用于創(chuàng)建快速動態(tài)網(wǎng)頁的技術(shù)。通過在后臺與服務器進行少量數(shù)據(jù)交換,AJAX 可以使網(wǎng)頁實現(xiàn)異步更新。這意味著可以在不重新加載整個網(wǎng)頁的情況下,對網(wǎng)頁的某部分進行更新。

XMLHttpRequest 對象

       XMLHttpRequest對象是ajax的基礎,XMLHttpRequest 用于在后臺與服務器交換數(shù)據(jù)。這意味著可以在不重新加載整個網(wǎng)頁的情況下,對網(wǎng)頁的某部分進行更新。目前所有瀏覽器都支持XMLHttpRequest

方法
描述
abort()
停止當前請求
getAllResponseHeaders() 
 把HTTP請求的所有響應首部作為鍵/值對返回
getResponseHeader("header")
返回指定首部的串值
open("method","URL",[asyncFlag],["userName"],["password"])
建立對服務器的調(diào)用。method參數(shù)可以是GET、POST或PUT。url參數(shù)可以是相對URL或絕對URL。這個方法還包括3個可選的參數(shù),是否異步,用戶名,密碼
send(content)
向服務器發(fā)送請求
setRequestHeader("header", "value") 
把指定首部設置為所提供的值。在設置任何首部之前必須先調(diào)用open()。設置header并和請求一起發(fā)送 ('post'方法一定要 )
五步使用法:

       1.創(chuàng)建XMLHTTPRequest對象
       2.使用open方法設置和服務器的交互信息
       3.設置發(fā)送的數(shù)據(jù),開始和服務器端交互
       4.注冊事件
       5.更新界面

下面給大家列出get請求和post請求的例子

get請求:      

       //步驟一:創(chuàng)建異步對象
       var ajax = new XMLHttpRequest();
       //步驟二:設置請求的url參數(shù),參數(shù)一是請求的類型,參數(shù)二是請求的url,可以帶參數(shù),動態(tài)的傳遞參數(shù)starName到服務端
       ajax.open('get','getStar.php?starName='+name);
       //步驟三:發(fā)送請求
        ajax.send();
       //步驟四:注冊事件 onreadystatechange 狀態(tài)改變就會調(diào)用
        ajax.onreadystatechange = function () {
       if (ajax.readyState==4 &&ajax.status==200) {
       //步驟五 如果能夠進到這個判斷 說明 數(shù)據(jù) 完美的回來了,并且請求的頁面是存在的
       console.log(xml.responseText);//輸入相應的內(nèi)容
         }
        } 

post請求:

       //創(chuàng)建異步對象  
       var xhr = new XMLHttpRequest();
       //設置請求的類型及url
       //post請求一定要添加請求頭才行不然會報錯
       xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
       xhr.open('post', '02.post.php' );
       //發(fā)送請求
       xhr.send('name=fox&age=18');
       xhr.onreadystatechange = function () {
       // 這步為判斷服務器是否正確響應
       if (xhr.readyState == 4 && xhr.status == 200) {
              console.log(xhr.responseText);
             }
        };    

為了方便使用,我們可以把他封裝進方法里面,要用的時候,直接調(diào)用就好了

       function ajax_method(url,data,method,success) {
       // 異步對象
       var ajax = new XMLHttpRequest();

      // get 跟post  需要分別寫不同的代碼
      if (method=='get') {
          // get請求
          if (data) {
              // 如果有值
              url+='?';
              url+=data;
          }else{

         }      

       // 設置 方法 以及 url
            ajax.open(method,url);

           // send即可
           ajax.send();
        }else{
             // post請求
             // post請求 url 是不需要改變
             ajax.open(method,url);

            // 需要設置請求報文
           ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");

           // 判斷data send發(fā)送數(shù)據(jù)
          if (data) {
            // 如果有值 從send發(fā)送
                ajax.send(data);
          }else{
               // 木有值 直接發(fā)送即可
              ajax.send();
            }
         }     

       // 注冊事件
       ajax.onreadystatechange = function () {
       // 在事件中 獲取數(shù)據(jù) 并修改界面顯示
            if (ajax.readyState==4&&ajax.status==200) {
                // console.log(ajax.responseText);

                // 將 數(shù)據(jù) 讓 外面可以使用
               // return ajax.responseText;

               // 當 onreadystatechange 調(diào)用時 說明 數(shù)據(jù)回來了
              // ajax.responseText;

              // 如果說 外面可以傳入一個 function 作為參數(shù) success
              success(ajax.responseText);
             }
         }
      }

WebVR大潮來襲 ---前端開發(fā)能做些什么?

周周

        去年谷歌和火狐針對WebVR提出了WebVR API的標準,顧名思義,WebVR即web + VR的體驗方式,我們可以戴著頭顯享受沉浸式的網(wǎng)頁,新的API標準讓我們可以使用js語言來開發(fā)。今天,約克先森將介紹如何開發(fā)一個WebVR網(wǎng)頁,在此之前,我們有必要了解WebVR的體驗方式。

WebVR體驗模式

WebVR的體驗方式可以分為VR模式和裸眼模式

一、VR模式

?滑配式HMD + 移動端瀏覽器

        如使用cardboard眼鏡來體驗手機瀏覽器的webVR網(wǎng)頁,瀏覽器將根據(jù)水平陀螺儀的參數(shù)來獲取用戶的頭部傾斜和轉(zhuǎn)動的朝向,并告知頁面需要渲染哪一個朝向的場景。

?分離式HMD + PC端瀏覽器

        通過佩戴Oculus Rift的分離式頭顯瀏覽連接在PC主機端的網(wǎng)頁,現(xiàn)支持WebVR API的瀏覽器主要是火狐的 Firefox Nightly和設置VR enabled的谷歌chrome beta。

二、裸眼模式

        除了VR模式下的體驗方式,這里還考慮了裸眼下的體驗瀏覽網(wǎng)頁的方式,在PC端如果探測的用戶選擇進入VR模式,應讓用戶可以使用鼠標拖拽場景,而在智能手機上則應讓用戶可以使用touchmove或旋轉(zhuǎn)傾斜手機的方式來改變場景視角。

        WebVR的概念大概就如此,這次我們將采用cardboard + mobile的方式來測試我們的WebVR場景,現(xiàn)在踏上我們的開發(fā)之旅。

準備工作

        技術(shù)和框架:three.js for WebGL

         Three.js是構(gòu)建3d場景的框架,它封裝了WebGL函數(shù),簡化了創(chuàng)建場景的代碼成本,利用three.js我們可以更優(yōu)雅地創(chuàng)建出三維場景和三維動畫。

        測試工具:智能手機 + 滑配式頭顯

        推薦使用cardboard或者某寶上三十塊錢的高仿貨。當然,如果你練就了裸眼就能將手機雙屏畫面看成單屏的能力也可以忽略。

       需要引入的js插件:

  • three.min.js
  • webvr-polyfill.js
  • VRcontrols.js
  • VReffect.js
  • webvr-manager.js

webvr-polyfill.js

        由于WebVR API還沒被各大主流瀏覽器支持,因此需要引入webvr-polyfill.js來支持WebVR網(wǎng)頁,它提供了大量VR相關(guān)的API,比如Navigator.getVRDevices()獲取VR頭顯信息的方法。

VRControls.js

        VR控制器,是three.js的一個相機控制器對象,引入VRcontrols.js可以根據(jù)用戶在空間的朝向渲染場景,它通過調(diào)用WebVR API的orientation值控制camera的rotation屬性。

VREffect.js

        VR分屏器,這是three.js的一個場景分屏的渲染器,提供戴上VR頭顯的顯示方式,VREffect.js重新創(chuàng)建了左右兩個相機,對場景做二次渲染,產(chǎn)生雙屏效果。

webvr-manager.js

         這是WebVR的方案適配插件,它提供PC端和移動端的兩種適配方式,通過new WebVRManager()可以生成一個VR圖標,提供VR模式和裸眼模式的不同體驗,當用戶在移動端點擊按鈕進入VR模式時,WebVRManager便會調(diào)用VREffect分屏器進行分屏,而退出VR模式時,WebVRManager便用回renderer渲染器進行單屏渲染。

        具體使用方法我們將在下文說明。

        3D場景構(gòu)建

        首先我們創(chuàng)建一個HTML文件

       <!DOCTYPE html>

       <html lang="en">

       <head> 

              <meta charset="UTF-8">

                  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">

              <title>webVR-helloworld</title>

                   <style type="text/css">

                         * { 

                           margin: 0;

                           padding: 0;

                            }

                     html,body {

                                 height: 100%;

                                overflow: hidden;

                     }

               </style>

         </head>

         <body>

         </body>

        <script src="./vendor/three.min.js"></script>

          <script src="./vendor/webvr-polyfill.js"></script>

          <script src="./vendor/VRControls.js"></script>

       <script src="./vendor/VREffect.js"></script>

       <script src="./vendor/webvr-manager.js"></script>

       <script src="./main.js"></script>

       </html>

         接下來編寫js腳本,開始創(chuàng)建我們的3d場景。

1、創(chuàng)建場景

        Three.js中的scene場景是繪制我們3d對象的整個容

       1.var scene = new THREE.Scene();

2、添加相機

Three.js的相機

Three.js中的camera相機代表用戶的眼睛,我們通過設置FOV確定視野范圍,

  • //定義一個60°的視角,視線范圍在1到1000的透視相機
  • var camera = new THREE. new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
  • scene.add(camera);

3、添加渲染器

  • Three.js的渲染器用來渲染camera所看到的畫面


  • //初始化渲染器 antialias參數(shù)為ture表示開啟抗鋸齒策略
  • var renderer = new THREE.WebGLRenderer({ antialias: true } );
  • //設置渲染器渲染尺寸
  • renderer.setSize(window.innerWidth,window.innerHeight);
  • //設置渲染背景為白色
  • renderer.setClearColor(0xeeeeee);
  • //將渲染場景的canvas放入body標簽里
  • document.body.appendChild(renderer.domElement);

  • 添加一個立方體網(wǎng)格

  • // 創(chuàng)建立方體
  • var geometry = new THREE.CubeGeometry( 10,10,10);
  • var cubematerial = new THREE.MeshLambertMaterial( { color: 0xef6500,needsUpdate: true,opacity:1,transparent:true} );
  • var cube = new THREE.Mesh( geometry, Cubematerial );
  • cube.position.set(0,100,-50);
  • cube.rotation.set(Math.PI/6,Math.PI/4,0);
  • scene.add(cube);

4、啟動動畫

  • 產(chǎn)生動畫的原理就是讓camera持續(xù)連拍,同時每一次改變物體的屬性,通過requestAnimationFrame()方法遞歸的方式來持續(xù)更新場景對象屬性,你可以將它理解為setTimeout的優(yōu)化版。相比setTimeout函數(shù),requestAnimationFrame可以保證動畫渲染不會因為主線程的阻塞而造成跳幀。


  • function animate() {
  •     //讓立方體旋轉(zhuǎn)
  •     cube.rotation.y += 0.01;
  •     //渲染器渲染場景,等同于給相機按下快門
  •     renderer.render(scene, camera);
  •     //遞歸運行該函數(shù)
  •     requestAnimationFrame( animate );
  • }
  • animate();//啟動動畫

      至此,我們已經(jīng)繪制了一個簡單的3d場景并且讓它動了起來,接下來,我們需要讓我們的場景可以支持WebVR模式。

WebVR場景開發(fā)

       WebVR網(wǎng)頁的基本原理其實是通過瀏覽器的WebVR API獲取用戶輸入,進而控制相機的視角,在VR模式下通過VR控制器和VR分屏器以二分屏+gyroscope(使用水平陀螺儀)的方式顯示畫面,裸眼情況下提供全屏+touchmove/gyroscope。

       現(xiàn)在我們開始分別創(chuàng)建上文所說的VR控制器和VR分屏器

  • //初始化VR控制器需要傳入場景相機
  • var vrControls = new THREE.VRControls(camera);
  • //初始化VR渲染器需要傳入場景渲染器
  • var vrEffect = new THREE.VREffect(renderer);
  • //初始化VR適配器,傳入渲染器和分屏器
  • var vrManager = new WebVRManager(renderer, vrEffect);

      然后在前面創(chuàng)建的場景渲染函數(shù)里調(diào)用

  • function animate() {
  •     cube.rotation.y += 0.01;
  •     //實時更新相機的位置和轉(zhuǎn)角
  •     vrControls.update();
  •     vrManager.render(scene, camera);
  •     //遞歸運行該函數(shù)
  •     requestAnimationFrame( animate );
  • }

       至此,我們已經(jīng)完成了一個基本的webVR網(wǎng)頁,不過少了點交互效果好像,敬請期待Web開發(fā)的新世界---WebVR之交互事件。

  • 完整代碼:在文章基礎上添加了天空和地面相關(guān)代碼,以及下篇文章將講到VR凝視交互事件。
  • demo演示地址 :手機瀏覽需設置允許橫屏。

結(jié)語

        目前,國外的谷歌、火狐、Facebook和國內(nèi)百度已推出支持WebVR瀏覽器的版本,微軟也宣布將推出自己的VR瀏覽器,隨著后期5g網(wǎng)絡極速時代的到來以及HMD頭顯的價格和平臺的成熟,WebVR的體驗方式將是革命性的,用戶通過WebVR瀏覽網(wǎng)上商店,線上教學可進行“面對面”師生交流等,基于這種種應用場景,我們可以找到一個更好的動力去學習WebVR。






日歷

鏈接

個人資料

藍藍設計的小編 http://www.yvirxh.cn

存檔