移動設備與PC設備最大的差別在於屏幕,這主要體如今屏幕尺寸和屏幕分辨率兩個方面。javascript
一般咱們所指的屏幕尺寸,實際上指的是屏幕對角線的長度(通常用英寸來度量)。css
而分辨率則通常用像素來度量 px
,表示屏幕水平和垂直方向的像素數,例如 1920*1080 指的是屏幕垂直方向和水平方向分別有1920和1080個像素點而構成。html
在Web開發中可使用px
(像素)、em
、pt
(點)、in
(英寸)、cm
(釐米)作爲長度單位,咱們最經常使用px
(像素)作爲長度單位。前端
咱們能夠將上述的幾種長度單位劃分紅相對長度單位和絕對長度單位。java
例如:iPhone3G/S和iPhone4/S的屏幕尺寸都爲 3.5 英寸(in)可是屏幕分辨率卻分別爲 480x320px、960x480px,由此咱們能夠得出英寸是一個絕對長度單位,而像素是一個相對長度單位(像素並無固定的長度)。git
DPI(Dots Per Inch)是印刷行業中用來表示打印機每英寸能夠噴的墨汁點數,計算機顯示設備從打印機中借鑑了DPI的概念,因爲計算機顯示設備中的最小單位不是墨汁點而是像素,因此用PPI(Pixels Per Inch)值來表示屏幕每英寸的像素數量,咱們將PPI、DPI都稱爲像素密度,但PPI應用更普遍,DPI在Android設備比較常見。github
利用屏幕分辨率計算 PPI :web
隨着技術發展,設備不斷更新,出現了不一樣PPI的屏幕共存的狀態(如iPhone3G/S爲163PPI,iPhone4/S爲326PPI),像素再也不是統一的度量單位,這會形成一樣尺寸的圖像在不一樣PPI設備上的顯示大小不同。數組
以下圖,假設你設計了一個163x163的藍色方塊,在PPI爲163的屏幕上,那這個方塊看起來正好就是1x1寸大小,在PPI爲326的屏幕上,這個方塊看起來就只有0.5x0.5寸大小了。瀏覽器
可是作爲用戶是不會關心這些細節的,他們只是但願在不一樣PPI的設備上看到的圖像內容差很少大小,因此這時咱們須要一個新的單位,這個新的單位可以保證圖像內容在不一樣的PPI設備看上去大小應該差很少,這就是獨立像素,在IOS設備上叫PT(Point),Android設備上叫DIP(Device independent Pixel)或DP。
舉例說明就是iPhone 3G(PPI爲163)1pt = 1px,iPhone 4(PPI爲326)1pt = 2px。
經過上面例子咱們不難發現 pt 同px是有一個對應(比例)關係的,這個對應(比例)關係是操做系統肯定並處理,目的是確保不一樣PPI屏幕所能顯示的圖像大小是一致的,經過 window.devicePixelRatio
能夠得到該比例值。
因此,咱們如何處理在不一樣 pt/px 比例上使得顯示相同大小的圖片呢?
很簡單,在美工設計圖片的時候,多設計幾種尺寸的圖片。
物理像素指的是屏幕渲染圖像的最小單位,屬於屏幕的物理屬性,不可人爲進行改變,其值大小決定了屏幕渲染圖像的品質,咱們以上所討論的都指的是物理像素。
獲取屏幕的物理像素尺寸:
window.screen.width;
window.screen.height;
CSS像素,與設備無關像素,指的是經過CSS進行網頁佈局時用到的單位,其默認值(PC端)是和物理像素保持一致的(1個單位的CSS像素等於1個單位的物理像素),可是咱們可通縮放來改變CSS像素的大小。
咱們須要理解的是物理像素和CSS像素的一個關係,1個物理像素並不老是等於一個CSS像素,經過縮放,一個CSS像素可能大於1個物理像素,也可能小於1個物理像素。
現代主流瀏覽器均支持移動開發模擬調試,一般按F12能夠調起,其使用也比較簡單,能夠幫咱們方便快捷定位問題。
模擬調試能夠知足大部分的開發調試任務,可是因爲移動設備種類繁多,環境也十分複雜,模擬調試容易出現差錯,因此真機調試變的很是必要。
有兩種方法能夠實現真機調試:
一、將作好的網頁上傳至服務器或者本地搭建服務器,而後移動設備經過網絡來訪問。(重點)
二、藉助第三方的調試工具,如weinre、debuggap、ghostlab(推薦) 等。
真機調試必須保證移動設備同服務器間的網絡是相通的。
視口(viewport)是用來約束網站中最頂級塊元素<html>的,即它決定了<html>的大小。
在PC設備上viewport的大小取決於瀏覽器窗口的大小,以CSS像素作爲度量單位。
經過以往CSS的知識,咱們都能理解<html>的大小是會影響到咱們的網頁佈局的,而viewport又決定了<html>的大小,因此viewport間接的決定並影響了咱們網頁的佈局。
/* 獲取viewport的大小 */ document.documentElement.clientWidth; document.documentElement.clientHeight;
在PC端,咱們經過調整瀏覽器窗口能夠改變 viewport 的大小,爲了保證網頁佈局不發生錯亂,須要給元素設定較大固定寬度。
移動設備屏幕廣泛都是比較小的,可是大部分的網站又都是爲PC設備來設計的,要想讓移動設備也能夠正常顯示網頁,移動設備不得不作一些處理,經過上面的例子咱們能夠知道只要viewport足夠大,就能保證本來爲PC設備設計的網頁也能在移動設備上正常顯示,移動設備廠商也的確是這樣來處理的。
在移動設備上viewport再也不受限於瀏覽器的窗口,而是容許開發人員自由設置viewport的大小,一般瀏覽 器會設置一個默認大小的 viewport,爲了可以正常顯示那些專爲PC設計的網頁,通常這個值的大小會大於屏幕的尺寸。
以下圖爲常見默認viewport大小(僅供參考):
從圖中統計咱們得知不一樣的移動廠商分別設置了一個默認的viewport的值,這個值保證大部分網頁能夠正常在移動設備下瀏覽。
可是因爲咱們手機的屏幕很小,而 viewport 的值卻很大,因此頁面全部的內容就會縮小以適應屏幕,因此用手機看起來,這些字體和圖片就會特別小,這就像手機設置裏面有個電腦版顯示同樣。
要解釋上面的緣由,須要進一步對移動設備的 viewport 進行分析,移動設備上有2個viewport(爲了方便講解人爲定義的),分別是 layout viewport
和 ideal viewport
。
一、layout viewport
(佈局視口)指的是咱們能夠進行網頁佈局區域的大小,一樣是以CSS像素作爲計量單位,能夠經過下面方式獲取
/* 獲取layout viewport */ document.documentElement.clientWidth; document.documentElement.clientHeight;
經過前面介紹咱們知道,若是要保證爲PC設計的網頁在移動設備上佈局不發生錯亂,移動設備會默認設置一個較大的viewport(如IOS爲980px),這個viewport實際指的是layout viewport。
二、ideal viewport
(理想視口)設備屏幕區域,(以設備獨立像素PT、DP作爲單位)以CSS像素作爲計量單位,其大小是不可能被改變,經過下面方式能夠獲取。
/* 獲取ideal viewport有兩種情形 */ /* 新設備 */ window.screen.width; window.screen.height; /* 老設備 */ window.screen.width / window.devicePixelRatio; window.screen.height / window.devicePixelRatio;
理解兩個viewport後咱們來解釋爲何網頁會被縮放或出現水平滾動條:
其緣由在於移動設備瀏覽器會默認設置一個layout viewport,而且這個值會大於ideal viewport,那麼咱們也知道ideal viewport就是屏幕區域,layout viewport是咱們佈局網頁的區域,那麼最終layout viewport是要顯示在ideal viewport裏的,而layout viewport大於ideal viewport時,因而就出現滾動條了,那麼爲何有的移動設備網頁內容被縮放了呢?移動設備廠商認爲將網頁完整顯示給用戶才最合理,而不應出現滾動條,因此就將layout viewport進行了縮放,使其剛好完整顯示在ideal viewport(屏幕)裏,其縮放比例爲ideal viewport / layout viewport。
移動端開發主要是針對IOS和Android兩個操做系統平臺的,除此以外還有Windows Phone。
移動端主要能夠分紅三大類,系統自帶瀏覽器、應用內置瀏覽器、第三方瀏覽器。
指跟隨移動設備操做系統一塊兒安裝的瀏覽器,通常不能卸載。好比 iPhone 的 safari 瀏覽器。
一般在移動設備上都會安裝一些APP例如 QQ、微信、微博、淘寶等,這些APP裏每每會內置一個瀏覽器,咱們稱這個瀏覽器爲應用內置瀏覽器(也叫WebView),這個內置的瀏覽器通常功能比較簡單,而且客戶端開發人員能夠更改這個瀏覽器的某些設置。
指安裝在手機的瀏覽器如FireFox、Chrome、360等等。
在IOS 和 Android 操做系統上自帶瀏覽器、應用內置瀏覽器都是基於Webkit內核的。
通過分析咱們獲得,移動頁面最理想的狀態是,避免滾動條且不被默認縮放處理,咱們能夠經過設置 <meta name="viewport" content="">
來進行控制,並改變瀏覽器默認的layout viewport的寬度。
viewport 是由蘋果公司爲了解決移動設備瀏覽器渲染頁面而提出的解決方案,後來被其它移動設備廠商採納,其使用參數以下:
經過設置屬性 content=""
實現,中間以逗號分隔。
示例:
<meta name="viewport" content="width=device-width, initital-scale=1.0, user-scalable=no">
width
:設置 layout viewport 寬度,其取值可爲數值或者device-width。
height
:設置layout viewport 高度,其取值可爲數值或者device-height
initital-scale
:設置頁面的初始縮放值,爲一個數字,能夠帶小數。
maximum-scale
:容許用戶的最大縮放值,爲一個數字,能夠帶小數。
minimum-scale
:容許用戶的最小縮放值,爲一個數字,能夠帶小數。
user-scalable
:是否容許用戶進行縮放,值爲"no"(不能縮放)或"yes"(能夠縮放)。
注:device-width 和 device-height 就是 ideal viewport 的寬和高。
設置 <meta name="viewport" content="initial-scale=1">
,這時咱們發現網頁沒有被瀏覽器設置縮放。
設置 <meta name="viewport" content="width=device-width">
,這時咱們發現網頁也沒有被瀏覽器設設置縮放。
當咱們設置 width=device-width
,也達到了 initial-scale=1
的效果,得知其實 initial-scale = ideal viewport / layout viewport
。
兩種方式均可以控制縮放,開發中通常同時設置 width=device-width 和 initial-scale=1.0(爲了解決一些兼容問題)參見 移動前端開發之viewport深刻理解 (http://www.cnblogs.com/2050/p...,即:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
關於 em 和 rem
em 是相對長度單位(參照父元素),其參照當前元素字號大小,若是當前元素未設置字號則會繼承其祖先元素字號大小。
例如: .box {font-size: 16px;}
則 1em = 16px
.box {font-size: 32px;}
則 1em = 32px,0.5em = 16px
rem 相對長度單位(參照 html 元素),其參照根元素(html)字號大小。
例如 :html {font-size: 16px;}
則 1rem = 16px
html {font-size: 32px;}
則 1rem = 32px,0.5rem = 16px.
vw:viewport width,視口寬度 (1vw = 1%視口寬度)
vh:viewport height 視口高度 (1vh = 1%視口高度)
網易淘寶適配方案:https://www.manster.me/?p=311
相關源碼已放置 Github:https://github.com/Daotin/Web...
html 源碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link rel="stylesheet" href="./css/base.css"> <link rel="stylesheet" href="./css/index.css"> <script src="./js/index.js"></script> </head> <body> <div class="jd"> <!-- 搜索欄開始 --> <div class="search"> <a href="javascript:;" class="search-logo"></a> <form action="" class="search-text"> <input type="text" placeholder="請輸入商品名稱"> </form> <a href="javascript:;" class="search-login">登陸</a> </div> <!-- 搜索欄結束 --> <!-- 輪播圖開始 --> <div class="slideshow"> <ul class="slideshow-img clearfix"> <li> <a href="javascript:;"> <img src="./uploads/l1.jpg" alt=""> </a> </li> <li> <a href="javascript:;"> <img src="./uploads/l2.jpg" alt=""> </a> </li> <li> <a href="javascript:;"> <img src="./uploads/l3.jpg" alt=""> </a> </li> <li> <a href="javascript:;"> <img src="./uploads/l4.jpg" alt=""> </a> </li> <li> <a href="javascript:;"> <img src="./uploads/l5.jpg" alt=""> </a> </li> <li> <a href="javascript:;"> <img src="./uploads/l6.jpg" alt=""> </a> </li> <li> <a href="javascript:;"> <img src="./uploads/l7.jpg" alt=""> </a> </li> <li> <a href="javascript:;"> <img src="./uploads/l8.jpg" alt=""> </a> </li> </ul> <ul class="slideshow-dot"> <li class="select"></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </div> <!-- 輪播圖結束 --> <!-- 導航欄開始 --> <div class="nav"> <ul class="nav-ul clearfix"> <li> <a href="javascript:;"> <img src="./uploads/nav0.png"> </a> <p>商品分類</p> </li> <li> <a href="javascript:;"> <img src="./uploads/nav1.png"> </a> <p>商品分類</p> </li> <li> <a href="javascript:;"> <img src="./uploads/nav2.png"> </a> <p>商品分類</p> </li> <li> <a href="javascript:;"> <img src="./uploads/nav3.png"> </a> <p>商品分類</p> </li> <li> <a href="javascript:;"> <img src="./uploads/nav4.png"> </a> <p>商品分類</p> </li> <li> <a href="javascript:;"> <img src="./uploads/nav5.png"> </a> <p>商品分類</p> </li> <li> <a href="javascript:;"> <img src="./uploads/nav6.png"> </a> <p>商品分類</p> </li> <li> <a href="javascript:;"> <img src="./uploads/nav7.png"> </a> <p>商品分類</p> </li> </ul> </div> <!-- 導航欄結束 --> <!-- 主體內容開始 --> <div class="content"> <div class="content-box clearfix content-box-sk"> <div class="content-title"> <span class="content-title-left-clock"></span> <span class="content-title-left-text fl">掌上秒殺</span> <div class="content-title-left-time fl"> <span>0</span> <span>0</span> <span>:</span> <span>0</span> <span>0</span> <span>:</span> <span>0</span> <span>0</span> </div> <span class="content-title-right fr">更多秒殺...</span> </div> <lu class="content-ul clearfix"> <li> <a href="javascript:;"> <img src="./uploads/detail01.jpg" alt="" class="br"> </a> <p>¥10.00</p> <p class="content-ul-delete">¥20.00</p> </li> <li> <a href="javascript:;"> <img src="./uploads/detail01.jpg" alt="" class="br"> </a> <p>¥10.00</p> <p class="content-ul-delete">¥20.00</p> </li> <li> <a href="javascript:;"> <img src="./uploads/detail01.jpg" alt=""> </a> <p>¥10.00</p> <p class="content-ul-delete">¥20.00</p> </li> </lu> </div> <div class="content-box clearfix"> <div class="content-title"> <h3>京東超市</h3> </div> <lu class="content-ul"> <li class="fl"> <a href="javascript:;"> <img src="./uploads/cp1.jpg" alt=""> </a> </li> <li class="fl bl bb"> <a href="javascript:;"> <img src="./uploads/cp2.jpg" alt=""> </a> </li> <li class="fl bl"> <a href="javascript:;"> <img src="./uploads/cp3.jpg" alt=""> </a> </li> </lu> </div> <div class="content-box clearfix"> <div class="content-title"> <h3>京東超市</h3> </div> <lu class="content-ul"> <li class="fr"> <a href="javascript:;"> <img src="./uploads/cp4.jpg" alt=""> </a> </li> <li class="fl bl bb"> <a href="javascript:;"> <img src="./uploads/cp5.jpg" alt=""> </a> </li> <li class="fl bl"> <a href="javascript:;"> <img src="./uploads/cp6.jpg" alt=""> </a> </li> </lu> </div> </div> <!-- 主體內容結束 --> </div> </body> </html>
CSS 源碼:
CSS 初始化代碼:
@charset "UTF-8"; /*css 初始化 */ html, body, ul, li, ol, dl, dd, dt, p, span, h1, h2, h3, h4, h5, h6, form, fieldset, legend, img, input, div { margin: 0; padding: 0; /* 盒模型 */ box-sizing: border-box; /* 去掉移動端特有的點擊高亮效果: transparent 透明色*/ -webkit-tap-highlight-color: transparent; } body { width: 100%; font: 12px/150% "Microsoft YaHei", sans-serif; color: #666; background: #fff } fieldset, img, input, button { /*fieldset組合表單中的相關元素*/ border: none; padding: 0; margin: 0; outline-style: none; /*去除藍色邊框*/ } ul, ol, li { list-style: none; /*清除列表風格*/ } a, a:hover { text-decoration: none; } a:active { color: red; } select, input { vertical-align: middle; /*圖片文字垂直居中*/ } select, input, textarea { font-size: 12px; margin: 0; } textarea { resize: none; /*不能改變多行文本框的大小*/ } img { border: 0; vertical-align: middle; /* 去掉圖片低測默認的3像素空白縫隙*/ } table { border-collapse: collapse; /*合併外邊線*/ } .clearfix::before, .clearfix::after { content: ""; display: block; height: 0; line-height: 0; clear: both; visibility: hidden; } .clearfix { *zoom: 1; /*IE/7/6*/ } h1, h2, h3, h4, h5, h6 { text-decoration: none; font-weight: normal; font-size: 100%; } s, i, em { font-style: normal; text-decoration: none; } /*公共類*/ .w { /*版心 提取 */ width: 1225px; margin: 0 auto; } .fl { float: left } .fr { float: right } .al { text-align: left } .ac { text-align: center } .ar { text-align: right } .hide { display: none } .bl { border-left: 1px solid #ccc; } .br { border-right: 1px solid #ccc; } .bb { border-bottom: 1px solid #ccc; }
CSS 主要代碼:
.jd { width: 100%; /* height: 1000px; */ /* 最大寬度 */ max-width: 640px; /* 最小寬度 */ min-width: 320px; margin: 0 auto; background-color: #eee; } /* 搜索欄開始 */ .search { width: 100%; max-width: 640px; height: 40px; background-color: rgba(233, 35, 34, 0); position: fixed; z-index: 10; } .search-logo { width: 56px; height: 20px; background-image: url("../images/jd-sprites.png"); background-size: 200px 200px; background-position: left -109px; position: absolute; left: 10px; top: 10px; } .search-login { color: #fff; font-size: 14px; line-height: 40px; position: absolute; right: 10px; top: 0; } .search-login:visited { color: #fff; } .search-text { width: 100%; height: 100%; padding-left: 76px; padding-right: 50px; } .search-text>input { width: 100%; height: 30px; margin-top: 5px; border-radius: 15px; color: #666; padding-left: 30px; font-size: 14px; } .search-text::before { content: ""; width: 19px; height: 20px; background-image: url("../images/jd-sprites.png"); background-size: 200px 200px; background-position: -60px -109px; position: absolute; left: 83px; top: 10px; } /* 搜索欄結束 */ /* 輪播圖開始 */ .slideshow { width: 100%; overflow: hidden; position: relative; } .slideshow-img { width: 800%; } .slideshow-img>li { width: 12.5%; float: left; } .slideshow-img>li img { width: 100%; } .slideshow-dot { width: 80px; height: 10px; position: absolute; left: 50%; bottom: 0; transform: translateX(-50%); } .slideshow-dot>li { width: 6px; height: 6px; float: left; border-radius: 50%; border: 1px solid #fff; margin: 0 2px; } .slideshow-dot>li.select { background-color: #fff; } /* 輪播圖結束 */ /* 導航欄開始 */ .nav { width: 100%; box-shadow: 0 2px 2px #ccc; } .nav-ul { width: 100%; background-color: #fff; } .nav-ul>li { width: 25%; float: left; text-align: center; margin-top: 10px; } .nav-ul>li img { width: 60px; height: 60px; } .nav-ul>li p { margin: 5px 0; } /* 導航欄結束 */ /* 主體內容開始 */ .content { width: 100%; } .content-box { width: 100%; margin-top: 10px; background-color: #fff; box-shadow: 0 2px 2px #ccc; } .content-title { border-bottom: 1px solid #ccc; position: relative; } .content-box>.content-title { height: 30px; line-height: 30px; } .content-title>h3 { margin-left: 20px; position: relative; font-weight: 700; } .content-title>h3::before { content: ""; width: 3px; height: 10px; background-color: #e92322; position: absolute; left: -8px; top: 10px; } .content-ul { width: 100%; } .content-ul>li { width: 50%; } .content-ul>li img { width: 100%; display: block; } .content-title-left-clock { display: block; width: 16px; height: 20px; background-image: url("../images/jd-sprites.png"); background-size: 200px 200px; background-position: -85px -111px; position: absolute; left: 3px; top: 4px; } .content-title-left-text { padding: 0 10px 0 25px; color: #e92322; } .content-title-left-time>span { display: inline-block; width: 12px; height: 18px; line-height: 18px; background-color: #363634; color: #e5d790; text-align: center; font-weight: bold; } .content-title-left-time>span:nth-of-type(3n) { background-color: transparent; width: 3px; } .content-title-right { margin-right: 10px; } .content-box-sk { padding: 10px; } .content-box-sk>.content-title { border-bottom: 0; margin-bottom: 10px; } .content-box-sk .content-ul { padding: 10px 0; display: flex; justify-content: space-around; } .content-box-sk .content-ul>li { /* width: 33.33%; */ text-align: center; } .content-box-sk .content-ul>li img { width: 100%; display: inline-block; padding: 0 30px; margin-bottom: 5px; } .content-ul-delete { text-decoration: line-through; color: #aaa; } /* 主體內容結束 */
javaScript 代碼:
window.onload = function () { bannerEffect(); timeCount(); slideshowEffect(); }; // 搜索欄上下滾動時改變透明度 function bannerEffect() { var bannerObj = document.querySelector(".search"); var slideshowObj = document.querySelector(".slideshow") // 獲取搜索欄的高度 var bannerHeight = bannerObj.offsetHeight; //40 // 獲取輪播圖高度 var slideshowHeight = slideshowObj.offsetHeight; //311 window.addEventListener("scroll", function () { // 頁面向上滾動的距離(兼容代碼) var scrolllen = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; if (scrolllen < (slideshowHeight - bannerHeight)) { var setopacity = scrolllen / (slideshowHeight - bannerHeight); bannerObj.style.backgroundColor = "rgba(233, 35, 34, " + setopacity + ")"; } }, false); } // 主體內容秒殺欄目的倒計時 function timeCount() { var spanObjs = document.querySelector(".content-title-left-time").children; var titleCount = 2 * 60 * 60; // 2小時倒計時 var timeId = setInterval(function () { titleCount--; var hour = Math.floor(titleCount / 3600); var minute = Math.floor((titleCount % 3600) / 60); var second = titleCount % 60; if (titleCount >= 0) { // 下面的true實際想表達的是不執行任何操做,可是必需要寫個語句,因此用true代替。 spanObjs[0].innerHTML == Math.floor(hour / 10) ? true : spanObjs[0].innerHTML = Math.floor(hour / 10); spanObjs[1].innerHTML == hour % 10 ? (true) : spanObjs[1].innerHTML = hour % 10; spanObjs[3].innerHTML == Math.floor(minute / 10) ? true : spanObjs[3].innerHTML = Math.floor(minute / 10); spanObjs[4].innerHTML == minute % 10 ? true : spanObjs[4].innerHTML = minute % 10; spanObjs[6].innerHTML == Math.floor(second / 10) ? true : spanObjs[6].innerHTML = Math.floor(second / 10); spanObjs[7].innerHTML == second % 10 ? true : spanObjs[7].innerHTML = second % 10; } else { titleCount = 0; clearInterval(timeId); return; } }, 1000); } // 輪播圖 function slideshowEffect() { // 1. 自動輪播圖 // 思路:1.一、使用js在圖片開頭動態添加本來最後一張圖片 // 1.二、使用js在圖片結尾動態添加本來第一張圖片 // 獲取輪播圖 var slideshowObj = document.querySelector(".slideshow"); // 獲取ul var ulObj = document.querySelector(".slideshow-img"); // 獲取全部li var liObjs = ulObj.children; // 設置li的索引值 var index = 1; // 要添加的第一個li和最後一個li var first = liObjs[0].cloneNode(true); var last = liObjs[liObjs.length - 1].cloneNode(true); // 在li開頭結尾添加克隆圖片 ulObj.appendChild(first); ulObj.insertBefore(last, ulObj.firstElementChild); // 設置ul寬度 ulObj.style.width = liObjs.length + "00%"; // 設置每一個li的寬度 for (var i = 0; i < liObjs.length; i++) { liObjs[i].style.width = slideshowObj.offsetWidth + "px"; } // 默認顯示第一張圖 ulObj.style.transform = "translateX(" + -(slideshowObj.offsetWidth) + "px)" // 改變窗口大小的時候自動調節輪播圖的寬度 window.addEventListener("resize", function () { ulObj.style.width = liObjs.length + "00%"; for (var i = 0; i < liObjs.length; i++) { liObjs[i].style.width = slideshowObj.offsetWidth + "px"; } // 改變窗口的大小的時候,不能僅僅回到第一張,要回到第index張 ulObj.style.transform = "translateX(" + -(slideshowObj.offsetWidth * index) + "px)" }, false); // 1. 實現自動輪播效果 var timerId; var timerStart = function () { timerId = setInterval(function () { index++; ulObj.style.transition = "transform 0.5s ease-in-out"; ulObj.style.transform = "translateX(" + -(slideshowObj.offsetWidth * index) + "px)"; // 設置小白點 if(index >= liObjs.length - 1) { setDot(1); return; } setDot(index); // 因爲過渡效果,使得過渡的時候,就進行if、判斷,沒法顯示最後一張圖片。 // 因此進行延時過渡的時候,等全部過渡效果完成後再進行判斷是否到達最後一張。 setTimeout(function () { if (index >= liObjs.length - 1) { index = 1; // 從最後一張調到第一張的時候,取消過渡效果 ulObj.style.transition = "none"; ulObj.style.transform = "translateX(" + -(slideshowObj.offsetWidth * index) + "px)"; } }, 500); }, 1500); }; timerStart(); // 2. 輪播圖的手動滑動效果 // 2.一、記錄手指的起始位置 // 2.二、記錄手指滑動時與起始位置水平軸的偏移距離 // 2.三、設置當手指鬆開後,判斷偏移距離的大小,決定回彈仍是翻頁。 var startX, diffX; // 設置節流閥,避免手動滑動過快,在過渡過程當中也有滑動,形成的最後圖片會有空白的操做,也就是index越界了,沒有執行相應的 webkitTransitionEnd 事件。 var isEnd = true; ulObj.addEventListener("touchstart", function (e) { // 手指點擊輪播圖時,中止自動輪播效果 clearInterval(timerId); startX = e.targetTouches[0].clientX; }, false); // 最開始的時候不觸發,緣由ul的高度爲0 ulObj.addEventListener("touchmove", function (e) { if (isEnd) { // 手指移動的距離 diffX = e.targetTouches[0].clientX - startX; // 關閉過渡效果,不然手指滑動困難 ulObj.style.transition = "none"; ulObj.style.transform = "translateX(" + -(slideshowObj.offsetWidth * index - diffX) + "px)"; } }, false); ulObj.addEventListener("touchend", function (e) { isEnd = false; // 判斷當前滑動的距離是否超過必定的距離,則翻頁 if (Math.abs(diffX) > 100) { if (diffX > 0) { index--; } else { index++; } // 翻頁 ulObj.style.transition = "transform 0.5s ease-in-out"; ulObj.style.transform = "translateX(-" + slideshowObj.offsetWidth * index + "px)"; } else if (Math.abs(diffX) > 0) { // 回彈 ulObj.style.transition = "transform 0.5s ease-in-out"; ulObj.style.transform = "translateX(-" + slideshowObj.offsetWidth * index + "px)"; } // 每次離開手指清除startX, diffX的值 startX = 0; diffX = 0; if(index == liObjs.length-1) { setDot(1); return; } else if(index == 0) { setDot(liObjs.length-2); return; } setDot(index); // 手指離開從新開啓定時器 timerStart(); }, false); // 咱們發如今第一張往右滑動,或者最後一張往左滑動時,會形成空白 /*webkitTransitionEnd:能夠監聽當前元素的過渡效果執行完畢,當一個元素的過渡效果執行完畢的時候,會觸發這個事件*/ ulObj.addEventListener("webkitTransitionEnd", function () { // 若是到了第一張(index=0),讓index=count-2 // 若是到了最後一張(index=count-1),讓index=1; if (index == 0) { index = liObjs.length - 2; // 從第一張到最後一張的時候,取消過渡效果 ulObj.style.transition = "none"; ulObj.style.transform = "translateX(" + -(slideshowObj.offsetWidth * index) + "px)"; } else if (index >= liObjs.length - 1) { index = 1; // 從最後一張調到第一張的時候,取消過渡效果 ulObj.style.transition = "none"; ulObj.style.transform = "translateX(" + -(slideshowObj.offsetWidth * index) + "px)"; } // 設置過渡效果完成後,才能夠繼續滑動 setTimeout(function () { isEnd = true; }, 100); }, false); // 3. 設置輪播圖小白點 function setDot(index) { var dotliObjs = document.querySelector(".slideshow-dot").children; // 建議不使用className,由於class屬性可能有多個,使用dotliObjs[i].className = "";可能將其餘的類樣式一塊兒清除。 for (var i = 0; i < dotliObjs.length; i++) { // 清除全部select樣式 dotliObjs[i].classList.remove("select"); } // 設置當前索引的樣式爲select dotliObjs[index-1].classList.add("select"); } }
這裏面的難點和重點就是輪播圖部分:
一、思路:
要實現輪播圖,必須在首尾添加圖片,若是直接在 html 代碼直接添加圖片的話,因爲圖片的數量是不固定的,那麼每次圖片的數量發生改變的話,不只須要設置 html 的源碼,並且還要設置對應的 css 代碼,因此,爲了從後臺獲取的圖片數量不固定的狀況下,也可以實現輪播效果,咱們可使用 js 來動態的添加圖片。
輪播圖要首尾鏈接,關鍵是先後加圖。
二、在手動輪播的時候,必定記得將自動輪播時的過渡效果清除。還要關閉定時器,在手指離開的時候再次設置是定時器。關於手動輪播的相關觸摸事件知識點在下面介紹。
touchstart
: 手指觸摸屏幕時觸發
touchmove
: 手指在屏幕上移動時觸發
touchend
: 手指離開屏幕時觸發
touchcancel
: 手指要移動的事件中,touchmove事件被打斷的時候觸發(好比忽然來了個彈框等)
細節:
touch 事件的觸發,必須保證元素有大於0的寬高值,不然沒法觸發。(好比 ul 下 li 有寬高,ul 會被撐開,有了寬高,可是當 li 浮動起來後,ul 的寬還在, 高爲0,此時沒法對 ul 觸發 touch 事件。)
event 事件對象是手指觸摸屏幕時觸發的事件對象,在這個對象中,咱們主要關注三個對象數組。
touches
:指屏幕上全部的觸摸的手指列表
targetTouched
:當前目標上全部的觸摸的手指列表
changedTouches
:指當前屏幕上變換的手指對象。在 touchstart 時爲新接觸屏幕的手指,在 touchend 時爲新離開屏幕的手指。
PS:沒有對比出 touches 同 targetTouches 的差別,推薦使用 targetTouches。
示例:手指拖動小球box
targetTouches 對象中有幾個座標值值得咱們關注:
screenX/screenY
:手指的觸摸點相對屏幕左上角的距離。
clientX/clientY
:手指的觸摸點相對視口(移動端屏幕左上角)的距離。
pageX/pageY
:手指的觸摸點相對當前頁面的左上角的距離(當前頁面可能有滾動條,因此距離包含滾動的距離)。
而,通常當咱們在移動端設置了 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
以後,clientX/clientY 的距離和 pageX/pageY 的距離是相同的了。因此通常使用 clientX/clientY。
還有2件事拜託你們一:求贊 求收藏 求分享 求留言,讓更多的人看到這篇內容
二:歡迎添加個人我的微信
備註「資料」, 300多篇原創技術文章,海量的視頻資料便可得到
備註「加羣」,我會拉你進技術交流羣,羣裏大牛學霸具在,哪怕您作個潛水魚也會學到不少東西