Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 99视频这里有精品,国产对白91色拍高清精品,亚洲国产人久久久成人精品网站

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          探秘網(wǎng)頁性能提升利器之CSS硬件加速

          天我們將為大家介紹一個令網(wǎng)頁性能大幅提升的神奇技術(shù)——CSS硬件加速。隨著移動互聯(lián)網(wǎng)的蓬勃發(fā)展和網(wǎng)頁設(shè)計(jì)越發(fā)復(fù)雜,如何優(yōu)化網(wǎng)頁性能成為了前端開發(fā)者們亟待解決的問題。在這篇文章中,我們將深入了解CSS硬件加速的原理,并通過一個生動的案例來展示它如何幫助我們改善網(wǎng)頁的渲染性能。

          一、什么是CSS硬件加速

          在傳統(tǒng)的網(wǎng)頁渲染中,瀏覽器使用中央處理器(CPU)來處理CSS樣式和頁面渲染。然而,隨著網(wǎng)頁變得越來越復(fù)雜,例如包含大量動畫、過渡效果或復(fù)雜的變換,CPU可能會承擔(dān)較重的負(fù)擔(dān),導(dǎo)致頁面加載緩慢或卡頓。CSS硬件加速是一種解決方案,它充分利用了計(jì)算機(jī)的圖形處理單元(GPU)來加快CSS樣式的處理和渲染,從而提高頁面性能和流暢度。

          1.1 CPU

          CPU 即中央處理器。

          CPU是計(jì)算機(jī)的大腦,它提供了一套指令集,我們寫的程序最終會通過 CPU 指令來控制的計(jì)算機(jī)的運(yùn)行。它會對指令進(jìn)行譯碼,然后通過邏輯電路執(zhí)行該指令。整個執(zhí)行的流程分為了多個階段,叫做流水線。指令流水線包括取指令、譯碼、執(zhí)行、取數(shù)、寫回五步,這是一個指令周期。CPU會不斷的執(zhí)行指令周期來完成各種任務(wù)。

          1.2 GPU

          GPU 即圖形處理器。

          GPU,是Graphics ProcessingUnit的簡寫,是現(xiàn)代顯卡中非常重要的一個部分,其地位與CPU在主板上的地位一致,主要負(fù)責(zé)的任務(wù)是加速圖形處理速度。GPU是顯卡的“大腦”,它決定了該顯卡的檔次和大部分性能,同時也是2D顯示卡和3D顯示卡的區(qū)別依據(jù)。2D顯示芯片在處理3D圖像和特效時主要依賴CPU的處理能力,稱為“軟加速”。3D顯示芯片是將三維圖像和特效處理功能集中在顯示芯片內(nèi),也即所謂的“硬件加速”功能。

          二、CSS硬件加速原理

          CSS硬件加速的原理涉及到瀏覽器的渲染引擎、GPU以及優(yōu)化渲染的過程。

          2.1 瀏覽器的渲染流程

          一個完整的渲染步驟大致可總結(jié)為如下:

          • 渲染進(jìn)程將HTML內(nèi)容轉(zhuǎn)換為能夠讀懂的DOM樹結(jié)構(gòu)。
          • 渲染引擎將CSS樣式表轉(zhuǎn)化為瀏覽器可以理解的 styleSheets ,計(jì)算出DOM節(jié)點(diǎn)的樣式。
          • 創(chuàng)建布局樹,并計(jì)算元素的布局信息。
          • 對布局樹進(jìn)行分層,并生成分層樹。
          • 為每個圖層生成繪制列表,并將其提交到合成線程。
          • 合成線程將圖層分成圖塊,并在光柵化線程池中將圖塊轉(zhuǎn)換成位圖。
          • 合成線程發(fā)送繪制圖塊命令DrawQuad給瀏覽器進(jìn)程。
          • 瀏覽器進(jìn)程根據(jù)DrawQuad消息生成頁面,并顯示到顯示器上。

          2.2 CSS硬件加速觸發(fā)

          在傳統(tǒng)的渲染過程中,布局和繪制是由CPU來完成的,而在CSS硬件加速下,GPU參與了渲染的處理,從而提高了性能。

          CSS 中的以下幾個屬性能觸發(fā)硬件加速:

          1.transform屬性:該屬性用于應(yīng)用2D或3D變換效果,如旋轉(zhuǎn)、縮放、平移等。當(dāng)使用transform屬性時,瀏覽器會將變換任務(wù)交給GPU處理,從而實(shí)現(xiàn)硬件加速。

          2.opacity屬性:該屬性用于設(shè)置元素的不透明度。雖然它主要用于控制透明度,但是一個不為1的值(例如0.99)也可以觸發(fā)硬件加速。

          3.will-change屬性:will-change屬性用于提示瀏覽器一個元素將要發(fā)生的變化,以便瀏覽器在渲染過程中做出優(yōu)化。

          一旦CSS硬件加速被觸發(fā),相關(guān)的渲染任務(wù)將被GPU處理。GPU在處理圖形和動畫方面通常比CPU更快和更高效。對于復(fù)雜的CSS動畫和變換,GPU可以并行處理多個任務(wù),從而提高性能和流暢度。

          請注意,CSS硬件加速并不是適用于所有情況。雖然它在許多情況下可以帶來顯著的性能提升,但有時也可能導(dǎo)致額外的GPU資源占用,從而影響其他應(yīng)用程序的性能。因此,在使用CSS硬件加速時,我們應(yīng)該進(jìn)行性能測試和優(yōu)化,確保在特定情況下確實(shí)能獲得性能的提升。

          三、CSS硬件加速案例

          現(xiàn)在,我們來看一個實(shí)際的案例,通過啟用CSS硬件加速來改善網(wǎng)頁性能。

          <!DOCTYPE html>

          <html lang="en">

          <head>

          <meta charset="UTF-8" />

          <meta name="viewport" content="width=device-width, initial-scale=1.0" />

          <title>Document</title>

          <style>

          .app {

          position: relative;

          width: 400px;

          height: 400px;

          }

          .box {

          position: absolute;

          left: 0;

          top: 0;

          width: 100px;

          height: 100px;

          background-color: yellowgreen;

          }

          .box-run1 {

          -webkit-animation: run1 4s infinite;

          animation: run1 4s infinite;

          }

          .box-run2 {

          -webkit-animation: run2 4s infinite;

          animation: run2 4s infinite;

          }

          @keyframes run1 {

          0% {

          top: 0;

          left: 0;

          }

          25% {

          top: 0;

          left: 200px;

          }

          50% {

          top: 200px;

          left: 200px;

          }

          75% {

          top: 200px;

          left: 0;

          }

          }

          @keyframes run2 {

          0% {

          transform: translate(0, 0);

          }

          25% {

          transform: translate(200px, 0);

          }

          50% {

          transform: translate(200px, 200px);

          }

          75% {

          transform: translate(0, 200px);

          }

          }

          </style>

          </head>

          <body>

          <div class="app">

          <div class="box"></div>

          </div>

          <button class="btn1">循環(huán)轉(zhuǎn)換</button>

          <button class="btn2">硬件加速</button>

          <script>

          let box = document.querySelector(".box");

          let btn1 = document.querySelector(".btn1");

          let btn2 = document.querySelector(".btn2");

          btn1.addEventListener("click", function (e) {

          box.classList.remove("box-run2");

          box.classList.add("box-run1");

          });

          btn2.addEventListener("click", function (e) {

          box.classList.remove("box-run1");

          box.classList.add("box-run2");

          });

          </script>

          </body>

          </html>

          此時我們可以運(yùn)行代碼,在頁面上可以看到,2個按鈕均能使box在app當(dāng)中循環(huán)移動。但對于這兩種方式的移動,他們的效率卻有著很大的差異。我們可以使用開發(fā)者工具里的Performance去查看。

          當(dāng)我們點(diǎn)擊btn1時,此時box盒子通過定位的left和top進(jìn)行循環(huán)移動時。

          此時我們可以看到細(xì)節(jié)模塊的記錄詳情。

          藍(lán)色(Loading):網(wǎng)絡(luò)通信和HTML解析

          黃色(Scripting):Javascript執(zhí)行

          紫色(Rendering):樣式計(jì)算和布局,即重排

          綠色(Painting):重繪

          灰色(Other):其他事件花費(fèi)的時間

          白色(Idle):空閑時間

          細(xì)節(jié)模塊有4個面板,Summary面板每個事件都會有,其他三個只針對特定事件會有。

          當(dāng)我們點(diǎn)擊btn2時,此時box盒子通過transform屬性進(jìn)行css硬件加速后進(jìn)行循環(huán)移動時。

          通過對比我們不難發(fā)現(xiàn),當(dāng)啟用硬件加速時,方塊的變換會更加流暢,其樣式計(jì)算和布局、重繪的時間都會減少。因?yàn)镚PU參與了渲染過程。

          總結(jié)

          CSS硬件加速是一個強(qiáng)大的前端技術(shù),可以顯著提高網(wǎng)頁的性能和流暢度。通過啟用硬件加速,我們可以將一些渲染任務(wù)交給GPU來處理,減輕CPU的負(fù)擔(dān),從而優(yōu)化網(wǎng)頁的渲染性能。然而,我們需要注意不要濫用硬件加速,避免觸發(fā)不必要的GPU渲染,以確保真正獲得性能提升。在日常的網(wǎng)頁開發(fā)中,我們可以靈活運(yùn)用CSS硬件加速,為用戶帶來更好的瀏覽體驗(yàn)。

          篇主要講websocket的服務(wù)端實(shí)現(xiàn),主要基于Tomcat9.0,關(guān)于登錄,用戶列表下篇再講,

          前端可以借鑒上篇文章,地址如下:

          自己動手實(shí)現(xiàn)基于websocket的聊天web聊天功能高仿仿qq

          基于Tomcat9的websocket服務(wù)實(shí)現(xiàn)

          首先創(chuàng)建一個javaweb項(xiàng)目(此處以eclipse開發(fā)工具為例)

          然后選中項(xiàng)目build path

          假如tomcat的依賴jar包

          websocket-api.jar

          tomcat-websocket.jar

          這樣就可以了

          下面是websocket的簡單的實(shí)現(xiàn):

          此處“imchat”為訪問路徑,“{id}”為需傳遞的參數(shù),是OnOpen時接收的參數(shù)這兩處變量名需要寫一樣,如果是多參數(shù)可以后面繼續(xù)追加如/imchat/{id}/{name}

          @ServerEndpoint(value="/imchat/{id}")

          public class ImSocket {

          //靜態(tài)變量,用來記錄當(dāng)前在線連接數(shù)。應(yīng)該把它設(shè)計(jì)成線程安全的。

          private static int onlineCount = 0;

          //concurrent包的線程安全Set,用來存放每個客戶端對應(yīng)的MyWebSocket對象。若要實(shí)現(xiàn)服務(wù)端與單一客戶端通信的話,可以使用Map來存放,其中Key可以為用戶標(biāo)識

          private static CopyOnWriteArraySet<ImSocket> webSocketSet = new CopyOnWriteArraySet<ImSocket>();

          //與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)

          private Session session;

          /**

          * 連接建立成功調(diào)用的方法,只在建立連接時調(diào)用

          * @param session 可選的參數(shù)。session為與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)

          */

          @OnOpen

          public void onOpen(@PathParam("id") String id,Session session){

          this.session = session;

          webSocketSet.add(this); //加入set中

          addOnlineCount(); //在線數(shù)加1

          System.out.println("有新連接加入!當(dāng)前在線人數(shù)為" + getOnlineCount());

          }

          /**

          * 連接關(guān)閉調(diào)用的方法

          */

          @OnClose

          public void onClose(){

          webSocketSet.remove(this); //從set中刪除

          subOnlineCount(); //在線數(shù)減1

          System.out.println("有一連接關(guān)閉!當(dāng)前在線人數(shù)為" + getOnlineCount());

          }

          /**

          * 收到客戶端消息后調(diào)用的方法,連接后所有交互數(shù)據(jù)都在此處理

          * @param message 客戶端發(fā)送過來的消息

          * @param session 可選的參數(shù)

          */

          @OnMessage

          public void onMessage(String message, Session session) {

          System.out.println("來自客戶端的消息:" + message);

          //群發(fā)消息

          for(ImSocket item: webSocketSet){

          try {

          item.sendMessage(message);

          } catch (IOException e) {

          e.printStackTrace();

          continue;

          }

          }

          }

          /**

          * 發(fā)生錯誤時調(diào)用

          * @param session

          * @param error

          */

          @OnError

          public void onError(Session session, Throwable error){

          System.out.println("發(fā)生錯誤");

          error.printStackTrace();

          }

          /**

          * 這個方法與上面幾個方法不一樣。沒有用注解,是根據(jù)自己需要添加的方法。

          * @param message

          * @throws IOException

          */

          public void sendMessage(String message) throws IOException{

          this.session.getBasicRemote().sendText(message);

          //this.session.getAsyncRemote().sendText(message);

          }

          public static synchronized int getOnlineCount() {

          return onlineCount;

          }

          public static synchronized void addOnlineCount() {

          ImSocket.onlineCount++;

          }

          public static synchronized void subOnlineCount() {

          ImSocket.onlineCount--;

          }

          }

          前端測試代碼

          <!DOCTYPE html>

          <html>

          <head>

          <title>Java后端WebSocket的Tomcat實(shí)現(xiàn)</title>

          </head>

          <body>

          Welcome<br/><input id="text" type="text"/>

          <button onclick="send()">發(fā)送消息</button>

          <hr/>

          <button onclick="closeWebSocket()">關(guān)閉WebSocket連接</button>

          <hr/>

          <div id="message"></div>

          </body>

          <script type="text/javascript">

          var websocket = null;

          //判斷當(dāng)前瀏覽器是否支持WebSocket

          if ('WebSocket' in window) {

          websocket = new WebSocket("ws://localhost:8080/項(xiàng)目名/imchat/id123");

          }

          else {

          alert('當(dāng)前瀏覽器 Not support websocket')

          }

          //連接發(fā)生錯誤的回調(diào)方法

          websocket.onerror = function () {

          setMessageInnerHTML("WebSocket連接發(fā)生錯誤");

          };

          //連接成功建立的回調(diào)方法

          websocket.onopen = function () {

          setMessageInnerHTML("WebSocket連接成功");

          }

          //接收到消息的回調(diào)方法

          websocket.onmessage = function (event) {

          setMessageInnerHTML(event.data);

          }

          //連接關(guān)閉的回調(diào)方法

          websocket.onclose = function () {

          setMessageInnerHTML("WebSocket連接關(guān)閉");

          }

          //監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會拋異常。

          window.onbeforeunload = function () {

          closeWebSocket();

          }

          //將消息顯示在網(wǎng)頁上

          function setMessageInnerHTML(innerHTML) {

          document.getElementById('message').innerHTML += innerHTML + '<br/>';

          }

          //關(guān)閉WebSocket連接

          function closeWebSocket() {

          websocket.close();

          }

          //發(fā)送消息

          function send() {

          var message = document.getElementById('text').value;

          websocket.send(message);

          }

          </script>

          </html>

          以上是簡單的websocket后端與前端的連接與交互,以上測試通過后咱們寫一下我們聊天的簡單實(shí)現(xiàn)

          基于上面的認(rèn)識我們已經(jīng)能夠?qū)崿F(xiàn)簡單的前端與websocket服務(wù)端的交互,下面我們接著上篇的自己動手實(shí)現(xiàn)基于websocket的聊天web聊天功能高仿仿qq

          首先我們看一下qq的邏輯,簡單的講就這 三步,登錄---》獲取好友列表---》發(fā)送消息給好友;

          第一步登錄和獲取好友列表,這個我們有兩種方式去實(shí)現(xiàn),一種是http請求,一種是websocket去實(shí)現(xiàn),考慮到這樣請求websocket會使處理流程變的復(fù)雜,所以我們采用http的方式實(shí)現(xiàn),這樣我們得websocket主要用來處理消息轉(zhuǎn)發(fā) 服務(wù),

          首先我們創(chuàng)建一個實(shí)體作為消息的承載體

          public class ImMsgModel {
          	 private boolean system;//消息類型
          	 private String key;//事件類型// offline離線消息 online在線消息
          	 private String avatar;
          	 private String id;
          	 private String sign;
          	 private String status;
          	 private String username;
          	 private String name;
          	 private String type;
          	 private String content;
          	 private long timestamp;
          	 private String fromid;
          	 	
          		public boolean isSystem() {
          			return system;
          		}
          		public void setSystem(boolean system) {
          			this.system = system;
          		}
          		public String getKey() {
          			return key;
          		}
          		public void setKey(String key) {
          			this.key = key;
          		}
          		public String getId() {
          			return id;
          		}
          		public void setId(String id) {
          			this.id = id;
          		}
          		public long getTimestamp() {
          			return timestamp;
          		}
          		public void setTimestamp(long timestamp) {
          			this.timestamp = timestamp;
          		}
          		public String getFromid() {
          			return fromid;
          		}
          		public void setFromid(String fromid) {
          			this.fromid = fromid;
          		}
          		public void setAvatar(String avatar) {
          	 this.avatar = avatar;
          	 }
          	 public String getAvatar() {
          	 return avatar;
          	 }
          	 public void setSign(String sign) {
          	 this.sign = sign;
          	 }
          	 public String getSign() {
          	 return sign;
          	 }
          	 public void setStatus(String status) {
          	 this.status = status;
          	 }
          	 public String getStatus() {
          	 return status;
          	 }
          	 public void setUsername(String username) {
          	 this.username = username;
          	 }
          	 public String getUsername() {
          	 return username;
          	 }
          	 public void setName(String name) {
          	 this.name = name;
          	 }
          	 public String getName() {
          	 return name;
          	 }
          	 public void setType(String type) {
          	 this.type = type;
          	 }
          	 public String getType() {
          	 return type;
          	 }
          	 public void setContent(String content) {
          	 this.content = content;
          	 }
          	 public String getContent() {
          	 return content;
          	 }
          

          第二步我們對websocket進(jìn)行封裝,此處消息實(shí)體根據(jù)layim前端封裝,需要了解詳情的請移步layui官網(wǎng)訪問layim模塊

          @ServerEndpoint(value="/imchat/{id}")
          public class WebsocketsListener {
          	
          	 private static final Set<WebsocketsListener> connections = new CopyOnWriteArraySet<WebsocketsListener>();
          	 private Session session;
          	 private String userid;
          	
          	Logger log = null;
          	public WebsocketsListener() {
          		log = Logger.getGlobal();
          	}
          	
          	 @OnOpen
          	public void start(@PathParam(value="id") String id,Session session) {
          		// TODO Auto-generated method stub
          		//log.log(Level.INFO, "打開監(jiān)聽onOpen");
          		this.session=session;
          		this.userid=id;
          		connections.add(this);
          //		Redis.use().hmset("userid="+id, hash);
          		System.out.println(id+"用戶session:"+session);
          	}
          	@OnMessage
           public void incoming(String message) {
          		 log.info("------------------"+message); 
          	 try { 
          	 	 ImMsgModel m = JSON.parseObject(message, ImMsgModel.class);
          	 	 System.out.println(m.getFromid()+"發(fā)送給"+m.getId());
          	 	
          	 	 Session s = getSessionByID(m.getId());//接收者id
          	 	
          	 	 //消息接收方掉線
          				if(s==null){
          					 Session s1 = getSessionByID(m.getFromid());
          					 //發(fā)送方也不在線
          					 if(s1==null) {
          						 log.info( "用戶已掉線線");
          					 }else {
          						 //發(fā)送消息給消息發(fā)送方提示消息接收方掉線
          						 ImMsgModel msg = new ImMsgModel();
          						 msg.setKey("offline");
          						 msg.setSystem(true);
          						 msg.setId(m.getId());
          						 msg.setType("friend");
          						 msg.setContent("對方已掉線");
          						 log.info( "對方已掉線");
          						 send2user(JSON.toJSONString(msg), s1);
          					 }
          				}else{
          					//發(fā)送消息給消息接收方
          					 m.setId(m.getFromid());
          					 m.setKey("online");
          					 send2user(JSON.toJSONString(m), s);
          				}
          				
          	 } catch (Exception e) { 
          	 // TODO Auto-generated catch block 
          	 e.printStackTrace(); 
          	 } 
          	 
          	 } 
          	private Session getSessionByID(String id) {
          		System.out.println("連接用戶數(shù)"+connections.size());
          		for (WebsocketsListener wb : connections) {
          			 //接收對象id對應(yīng)的session
           		if(id.equals(wb.userid)) {
           			return wb.session;
           		}
           	}
          		return null;
          	}
          	@OnClose
           public void end() {
          		connections.remove(this);
          	 }
          	
          	@OnError
          	 public void onError(Throwable t) throws Throwable {
          		log.info( "發(fā)生錯誤onError");
          		t.printStackTrace();
          	}
          	
          	private void send2user(String msg,Session session){
          			 try {
          					 session.getBasicRemote().sendText(msg); 
          			} catch (IOException e) {
          				e.printStackTrace();
          			}
          		
          	}
          	 
          	 public static void sendAll(String string) {
          			 for (WebsocketsListener wb : connections) {
          	 	try {
          	 			wb.session.getBasicRemote().sendText(string);
          					} catch (Exception e) {
          						e.printStackTrace();
          						 connections.remove(wb);
          						try {
          						 wb.session.close();
          						} catch (IOException e1) {
          							e1.printStackTrace();
          						}
          					}
          	 	
          	 }
          		}
          	}
          

          以上完成了websocket的服務(wù)前端消息轉(zhuǎn)發(fā)代碼(數(shù)據(jù)持久化與 redis后面繼續(xù)進(jìn)行補(bǔ)充)

          下面我貼一下上篇中講的前端代碼的完整版

          前端代碼

           <!DOCTYPE html>
          <html>
          <head>
           <meta charset="utf-8">
           <title>layui</title>
           <meta name="renderer" content="webkit">
           <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
           <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
           <link rel="stylesheet" href="js/css/modules/layui.css" media="all">
           <!-- 注意:如果你直接復(fù)制所有代碼到本地,上述css路徑需要改成你本地的 -->
          </head>
          <body>
           
           
          <script src="js/layui.js" charset="utf-8"></script>
          <!-- 注意:如果你直接復(fù)制所有代碼到本地,上述js路徑需要改成你本地的 -->
          <style>
          /* img */
          i{font-style:normal;}
          .qq-login{width:430px;height:330px;margin:0 0 -165px -215px;bottom:50%;left:50%;position:fixed;z-index:9999;border-radius:3px;overflow:hidden;box-shadow:0 0 5px #333;background:#ebf2f9 url(js/images/bj/qq-login-bg.jpg) center top no-repeat;display:block;}
          .login-menu{width:90px;height:30px;top:0;right:0;position:absolute;}
          .login-menu span{float:left;width:30px;height:30px;background-image:url(js/images/bj/qq-login-bg.jpg);}
          .login-menu span:hover{background-color:#3a95de;}
          .login-menu span:nth-child(1){background-position:left center;}
          .login-menu span:nth-child(2){background-position:-30px center;}
          .login-menu span:nth-child(3){background-position:-90px center;}
          .login-menu span:nth-child(3):hover{background-color:#ea4848;}
          .login-ner{margin-top:182px;float:left;width:100%;height:148px;}
          .login-left{float:left;width:133px;height:148px;}
          .login-head{float:left;width:80px;height:80px;border-radius:50%;border:1px solid #ccc;overflow:hidden;margin:12px 11px 0 40px;}
          .login-head img{width:80px;height:80px;}
          .login-on{width:194px;height:148px;float:left;}
          .login-txt{float:left;margin-top:12px;height:60px;width:100%;}
          .login-txt input{border:1px solid #d1d1d1;float:left;height:30px;padding:0 7px;font-size:12px;width:100%;}
          .login-txt input:nth-child(1){border-radius:4px 4px 0 0;}
          .login-txt input:nth-child(2){border-radius:0 0 4px 4px;margin-top:-1px;}
          .login-xuan{width:100%;float:left;height:14px;line-height:14px;margin-top:8px;}
          .login-xuan input{width:14px;height:14px;float:left;}
          .login-xuan i{float:left;padding-left:4px;}
          .login-right{width:103px;height:60px;float:left;margin-top:12px;}
          .login-right a{float:left;padding-left:10px;width:90%;color:#2786e4;line-height:30px;text-indent:10px;}
          .login-but{width:100%;height:30px;margin:13px 0;float:left;background:#09a3dc;color:#fff;text-align:center;line-height:30px;border-radius:4px;font-size:14px; cursor:context-menu;}
          .login-menu span {
           float: left;
           width: 30px;
           height: 30px;
           background-image: url(js/images/bj/wins.png);
          }
          .login-tips{line-height:40px;width:300px;padding:10px;color: white;top:0;left:0;position:absolute;}
          </style>
          <script id="login_html" type="text/html">
          <div class="qq-login">
           <div class="login-tips" id="login-tips"></div>
          <div class="login-menu">
           <span></span><span></span><span class="login-close"></span>
          </div>
          <div class="login-ner">
           <div class="login-left">
           <div class="login-head"><img src="js/css/modules/layim/skin/4.jpg"></div>
           </div>
           <div class="login-on">
           <div class="login-txt"><input type="text" id="username" placeholder="QQ號碼/手機(jī)/郵箱"><input id="password" type="password" placeholder="密碼"></div>
           <div class="login-xuan"><span class="fl"><input type="checkbox"><i>記住密碼</i></span><span class="fr"><input type="checkbox"><i>自動登錄</i></span></div>
           <div class="login-but" id="login-but">安全登錄</div>
           </div>
           <div class="login-right">
           <a  target="_blank">注冊賬號</a><a  target="_blank">找回密碼</a>
           </div>
          </div>
          </div>
          </script>
          <script>
          layui.use('layim', function(){
           var layim = layui.layim,id;
           $ = layui.jquery,
           
           layer.open({
          	 title:false
           ,type: 1
           ,offset: 'auto' //具體配置參考:http://www.layui.com/doc/modules/layer.html#offset
           ,content: login_html.innerHTML
           ,btn: false
           ,shadeClose: false
           ,closeBtn: 0
           ,moveType: 0
           ,move: '.login-head'
           ,btnAlign: 'r' //按鈕居中
           ,shade: 0 //不顯示遮罩
           });
           //綁定登陸事件
           $(document).on('click', '#login-but', function(data) {
           login();
           });
           
           var tips= $('#login-tips');	
           
           function login(){
          	 tips.html("正在登陸……");
          	 var un= $("#username").val();
          	 var ps= $("#password").val();
          	 var d={"m":"login","username":un,"password":ps}
          	 $.ajax({
           type:"POST",
           url:"../chat",
           dataType:"json",
           data:d,
           success:function(data){
           if(data.code==20000){
           	 id=data.data.id
           	 tips.html("登陸成功");
           	 chartSetting();
           	 connection();
           }else{
           	 tips.html("登陸失敗請重試!"+data.msg); 
           }
           },
           error:function(jqXHR){
           	 tips.html("發(fā)生錯誤"+jqXHR.status); 
           }
           });
           }
           
           function chartSetting(){
          	//基礎(chǔ)配置
          	 layim.config({
          	 //初始化接口
          	 init: {
          	 url: 'chat?m=list&id='+id
          	 ,data: {}
          	 }
          	 //查看群員接口
          	 ,members: {
          	 url: 'chat?m=getMembers&id='+id
          	 ,data: {}
          	 }
          	 
          	 ,uploadImage: {
          	 url: 'uploadv2?filepath=' //(返回的數(shù)據(jù)格式見下文)
          	 ,type: '' //默認(rèn)post
          	 }
          	 ,uploadFile: {
          	 url: 'uploadv2?filepath=' //(返回的數(shù)據(jù)格式見下文)
          	 ,type: '' //默認(rèn)post
          	 }
          	 
          	 ,isAudio: true //開啟聊天工具欄音頻
          	 ,isVideo: true //開啟聊天工具欄視頻
          	 
          	 //擴(kuò)展工具欄
          	 ,tool: [{
          	 alias: 'code'
          	 ,title: '代碼'
          	 ,icon: ''
          	 }]
          	 
          	 ,brief: false //是否簡約模式(若開啟則不顯示主面板)
          	 
          	 ,title: '消息' //自定義主面板最小化時的標(biāo)題
          	 ,right: '10px' //主面板相對瀏覽器右側(cè)距離
          	 ,minRight: '90px' //聊天面板最小化時相對瀏覽器右側(cè)距離
          	 ,initSkin: '3.jpg' //1-5 設(shè)置初始背景
          	 ,skin: ['js/css/modules/layim/skin/6.jpg',
          	 'js/css/modules/layim/skin/1.jpg'] //新增皮膚
          	 ,isfriend: true //是否開啟好友
          	 ,isgroup: true //是否開啟群組
          	 ,min: false //是否始終最小化主面板,默認(rèn)false
          	 ,notice: true //是否開啟桌面消息提醒,默認(rèn)false
          	 ,voice: true //聲音提醒,默認(rèn)開啟,聲音文件為:default.mp3
          	 ,msgbox: 'msgbox.html' //消息盒子頁面地址,若不開啟,剔除該項(xiàng)即可
          	 ,find: 'find.html' //發(fā)現(xiàn)頁面地址,若不開啟,剔除該項(xiàng)即可
          	 ,chatLog: 'chatlog.html' //聊天記錄頁面地址,若不開啟,剔除該項(xiàng)即可
          	 });
           }
           //監(jiān)聽在線狀態(tài)的切換事件
           layim.on('online', function(status){
           layer.msg(status);
           });
           //演示自動回復(fù)
           var autoReplay = [
           '您好,我現(xiàn)在有事不在,一會再和您聯(lián)系。', 
           '你沒發(fā)錯吧?face[微笑] ',
           '洗澡中,請勿打擾,偷窺請購票,個體四十,團(tuán)體八折,訂票電話:一般人我不告訴他!face[哈哈] ',
           '你好,我是主人的美女秘書,有什么事就跟我說吧,等他回來我會轉(zhuǎn)告他的。face[心] face[心] face[心] ',
           'face[威武] face[威武] face[威武] face[威武] ',
           '<(@ ̄︶ ̄@)>',
           '你要和我說話?你真的要和我說話?你確定自己想說嗎?你一定非說不可嗎?那你說吧,這是自動回復(fù)。',
           'face[黑線] 你慢慢說,別急……',
           '(*^__^*) face[嘻嘻] ,是賢心嗎?'
           ];
           
           
           //監(jiān)聽在線狀態(tài)的切換事件
           layim.on('online', function(status){
           layer.msg(status);
           });
           
           //監(jiān)聽簽名修改
           layim.on('sign', function(value){
           layer.msg(value);
           });
           //監(jiān)聽自定義工具欄點(diǎn)擊,以添加代碼為例
           layim.on('tool(code)', function(insert){
           layer.prompt({
           title: '插入代碼 - 工具欄擴(kuò)展示例'
           ,formType: 2
           ,shade: 0
           }, function(text, index){
           layer.close(index);
           insert('[pre class=layui-code]' + text + '[/pre]'); //將內(nèi)容插入到編輯器
           });
           });
           
           //監(jiān)聽layim建立就緒
           layim.on('ready', function(res){
           //console.log(res.mine);
           layim.msgbox(5); //模擬消息盒子有新消息,實(shí)際使用時,一般是動態(tài)獲得
           });
           //監(jiān)聽發(fā)送消息
           layim.on('sendMessage', function(data){
           var To = data.to;
           var Me = data.mine;
           if(To.type === 'friend'){
           layim.setChatStatus('<span style="color:#FF5722;">對方正在輸入。。。</span>');
           }
           if(To.id==Me.id){
           	alert("無法和自己發(fā)起聊天");
           	return;
           }else{
           	
           var data={
          			 username: Me.username //消息來源用戶名
          				 ,avatar: Me.avatar //消息來源用戶頭像
          				 ,id: To.id //消息的來源ID(如果是私聊,則是用戶id,如果是群聊,則是群組id)
          				 ,type:To.type //聊天窗口來源類型,從發(fā)送消息傳遞的to里面獲取
          				 ,content: Me.content //消息內(nèi)容
          				 ,cid: 0 //消息id,可不傳。除非你要對消息進(jìn)行一些操作(如撤回)
          				 ,mine: false //是否我發(fā)送的消息,如果為true,則會顯示在右方
          				 ,fromid:Me.id //消息的發(fā)送者id(比如群組中的某個消息發(fā)送者),可用于自動解決瀏覽器多窗口時的一些問題
          				 ,timestamp:new Date().getTime() //服務(wù)端時間戳毫秒數(shù)。注意:如果你返回的是標(biāo)準(zhǔn)的 unix 時間戳,記得要 *1000
          	 };
           //模擬系統(tǒng)消息
          	 websocket.send(JSON.stringify(data));
          	 layim.setChatStatus('<span style="color:#FF5722;">在線</span>');
           }
           
           });
           //監(jiān)聽查看群員
           layim.on('members', function(data){
           //console.log(data);
           });
           
           //監(jiān)聽聊天窗口的切換
           layim.on('chatChange', function(res){
           var type = res.data.type;
           console.log(res.data.id)
           if(type === 'friend'){
           //模擬標(biāo)注好友狀態(tài)
           layim.setChatStatus('<span style="color:#FF5722;">在線</span>');
           } else if(type === 'group'){
           //模擬系統(tǒng)消息
           layim.getMessage({
           system: true
           ,id: res.data.id
           ,type: "group"
           ,content: '模擬群員'+(Math.random()*100|0) + '加入群聊'
           });
           }
           });
           
           
           
           
           function connection(){
          	 tips.html("開始連接服務(wù)……");
           	 if('WebSocket' in window){
           websocket = new WebSocket("ws://"+sy()+"/imchat/"+id);
           
           }else{
           	tips.html("不支持websocket");
           
           }
           //連接發(fā)生錯誤的回調(diào)方法
           websocket.onerror = function(ev,data){
           	
           	tips.html("連接發(fā)生錯誤的回調(diào)方法");
           };
           	 //連接成功建立的回調(diào)方法
           websocket.onopen = function(e){
           	
           	 tips.html("");
           };
           
           //接收到消息的回調(diào)方法
           websocket.onmessage = function(event){
           	// layim.getMessage(event.data);
           	var json=JSON.parse(event.data);
           	 console.log("接收信息:");
           	 console.log(event.data);
           	 if(json.key=="offline"){
           		 //用戶離線 
           		 layim.setFriendStatus(json.id, 'offline');
           		 layim.setChatStatus('<span style="color:gray;">離線</span>');
           layer.msg(json.content+"無法接收到消息", {
           icon: 1
           });
           	 }else if(json.key=="online"){ //接收在線消息
           		 //制造好友消息
           		 layim.setFriendStatus(json.id, 'online');
           		 layim.setChatStatus('<span style="color:#FF5722;">在線</span>');
           layim.getMessage(json);	 
           	 }
           	 
           		 
           	
           };
           //連接關(guān)閉的回調(diào)方法
           websocket.onclose = function(event){
           	 //alert('連接關(guān)閉的回調(diào)方法');
           	 tips.html("連接已關(guān)閉,嘗試重連……");
           disConnect();
           };
           //監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會拋異常。
           window.onbeforeunload = function(){
           websocket.close();
           };
           }
          //檢查鏈接,短線重連
           var disConnect = function(){
           setTimeout(function(){
           	connection();
           },5000);
           }
           //關(guān)閉連接
           function closeWebSocket(){
          	 tips.html("關(guān)閉closeWebSocket");
           websocket.close();
           }
           
          	function sy(){
          		var curWwwPath = window.document.location.href;
          		var pathName = window.document.location.pathname;
          		var pos = curWwwPath.indexOf(pathName);
          		var localhostPaht = curWwwPath.substring(0,pos);
          		var projectName = pathName.substring(0,pathName.substr(1).indexOf('/')+1);
          		var ip=window.location.host;
          		var prot=window.location.port;
          		return (ip + projectName);
          		}
          	
          });
          </script>
          </body>
          </html>
          

          結(jié)合以上服務(wù)端代碼和前端代碼可以實(shí)現(xiàn)基本的聊天功能,本篇沒涉及到登錄接口和用戶列表接口,下篇再做補(bǔ)充。需要的同學(xué)關(guān)注一下下篇。有問題歡迎留言指正

          門的HTML - tabindex 的作用

          HTML 的 tabindex 屬性開發(fā)過程中一般不會使用到,最近開發(fā)中有個需求兼顧富交互,便總結(jié)了一下。本篇文章同時收錄在我的【前端知識點(diǎn)】中,Github鏈接請點(diǎn)擊閱讀原文直達(dá),歡迎 Star

          兼容性:Safari不支持!

          閱讀本文您將收獲

          • tabindex的作用
          • tabindex的使用
          • 如何利用 tabindex 創(chuàng)造更好的用戶體驗(yàn)

          前言

          在我們?nèi)粘J褂镁W(wǎng)頁的過程中,可以通過鍵盤控制一些元素的聚焦,從而達(dá)到便捷訪問的目的

          element 分為 focusable 和 非focusable ,如果使用了tabindex就可以改變相關(guān)的行為

          在HTML中有6個元素默認(rèn)支持聚焦:

          • 帶 href 屬性的 <a> 標(biāo)簽
          • 帶 href 屬性的 <link> 標(biāo)簽
          • <button></button> 標(biāo)簽
          • <input /> 標(biāo)簽 (排除帶有 type="hidden" 屬性的)
          • <select></select> 標(biāo)簽
          • <textarea></textarea> 標(biāo)簽

          以上的元素默認(rèn)都可以使用 Tab 鍵,以及 JS focus() 方法聚焦

          document.querySelector("a").focus();

          使用 tab鍵 進(jìn)行聚焦元素時,聚焦的順序等于元素在代碼中的出現(xiàn)先后順序,當(dāng)我們進(jìn)行富交互優(yōu)化時,就需要用到 tabindex 這個屬性來幫助我們進(jìn)行更好用戶體驗(yàn)的優(yōu)化了

          tabindex的作用

          ①元素是否能聚焦:通過鍵盤這類輸入設(shè)備,或者通過 JS focus() 方法

          ②元素什么時候能聚焦:在用戶通過鍵盤與頁面交互時

          通俗來說:就是當(dāng)用戶使用鍵盤時,tabindex用來定位html元素,即使用tab鍵時焦點(diǎn)的順序。

          tabindex的范圍

          tabindex理論上可以使用在幾乎所有元素上

          • tabindex 理論上可以用在幾乎所有元素上,不管這個元素默認(rèn)是否支持聚焦

          tabindex 有三個值:0,-N(通常是-1),N(正值)

          • tabindex=0,該元素可以用tab鍵獲取焦點(diǎn)
            • 且訪問的順序是按照元素在文檔中的順序來focus,即使采用了浮動改變了頁面中顯示的順序,依然是按照html文檔中的順序來定位
          • tabindex<=-1,該元素用tab鍵獲取不到焦點(diǎn),但是可以通過js獲取
            • 這樣就便于我們通過js設(shè)置上下左右鍵的響應(yīng)事件來focus
            • 取值 -1~-999 之間沒有區(qū)別,但為了可讀性和一致性考慮,推薦使用 -1
          • tabindex>=1,該元素可以用tab鍵獲取焦點(diǎn),而且優(yōu)先級大于tabindex=0
            • 不過在tabindex>=1時,數(shù)字越小,越先定位到;
            • 如果多個元素?fù)碛邢嗤?tabindex ,他們的相對順序按照他們在當(dāng)前DOM中的先后順序決定

          tabindex的使用

          tabindex 決定聚焦順序

          • 可聚焦元素中,正整數(shù)數(shù)值越大,順序越往后,正整數(shù)數(shù)值的節(jié)點(diǎn)順序比0值的節(jié)點(diǎn)靠前
          • 代碼:
          // HTML
          <button type="button" tabindex="1">tabindex === 1</button>
          <button type="button" tabindex="999">tabindex === 999</button>
          <button type="button" tabindex="0">tabindex === 0</button>
          • 效果:
          • 可聚焦元素中,相同 tabindex 數(shù)值的節(jié)點(diǎn),根據(jù) DOM節(jié)點(diǎn) 先后順序決定聚焦順序
          • 代碼:
          // HTML
          <button type="button" tabindex="0">tabindex === 0</button>
          <button type="button" tabindex="1">tabindex === 1</button>
          <button type="button" tabindex="999">tabindex === 999</button>
          <button type="button" tabindex="0">tabindex === 0</button>
          • 效果:

          tabindex 決定是否聚焦

          • 節(jié)點(diǎn)的 tabindex 設(shè)置為 -1 時,當(dāng)前節(jié)點(diǎn)使用 tab鍵 不能聚焦
          • 代碼:
          // HTML
          <button type="button">未設(shè)置tabindex</button>
          <button type="button" tabindex="-1">tabindex === -1</button>
          <button type="button" tabindex="0">tabindex === 0</button>
          <button type="button" tabindex="1">tabindex === 1</button>
          • 效果:

          tabindex 與JS編程聚焦

          • 通過 tabindex 結(jié)合JS可以讓默認(rèn)不支持聚焦的節(jié)點(diǎn)進(jìn)行聚焦,tabindex 為不超出范圍的任何整數(shù)值都可以
          • 代碼:
          // HTML
          <button type="button" @click="clickBtn()">點(diǎn)擊讓DIV聚焦</button>
          <div id="FocusDiv" ref="FocusDiv" tabindex="-1">這是一個div</div>
          
          // JS
          clickBtn: function() {
              document.getElementById('FocusDiv').focus();
          }
          • 效果:

          如何利用 tabindex 創(chuàng)造更好的用戶體驗(yàn)

          針對自定義標(biāo)簽進(jìn)行富交互優(yōu)化

          • 我們在創(chuàng)建一個自定義的標(biāo)簽時,如果默認(rèn)行為中不包含聚焦事件,我們可以使用 tabindex 為它增加聚焦功能,從而可以像很多可聚焦節(jié)點(diǎn)一樣進(jìn)行順次焦點(diǎn)聚焦了

          針對特定節(jié)點(diǎn)禁止聚焦操作

          • 某些浮層及上層節(jié)點(diǎn),如 toast組件、模態(tài)框、側(cè)邊彈出信息等,我們不希望節(jié)點(diǎn)被用戶聚焦捕獲,可以將節(jié)點(diǎn)的 tabindex 設(shè)置為 -1,就能避免這一問題

          復(fù)雜列表控制聚焦順序

          • 一些復(fù)雜的樹形結(jié)構(gòu)或者列式結(jié)構(gòu),如果需要用戶操作順序按照我們預(yù)想的書序進(jìn)行聚焦,可以利用tabindex 值的大小來進(jìn)行處理。

          主站蜘蛛池模板: 一区视频免费观看| 国内精自品线一区91| 一本大道在线无码一区| 日韩福利视频一区| 久久国产午夜一区二区福利| 日韩三级一区二区三区| AV鲁丝一区鲁丝二区鲁丝三区| 中文字幕Av一区乱码| 国产91精品一区| 国产成人精品视频一区| 日韩亚洲AV无码一区二区不卡| 狠狠色婷婷久久一区二区| 色视频综合无码一区二区三区| 国产精品美女一区二区三区| 无码囯产精品一区二区免费| 亚洲福利视频一区| 无码aⅴ精品一区二区三区浪潮| 中文字幕一区二区免费| 国产精品亚洲不卡一区二区三区| 无码视频一区二区三区| 久久精品无码一区二区三区免费 | 九九久久99综合一区二区| 色视频综合无码一区二区三区| 日韩一区二区三区视频久久| 无码人妻AⅤ一区二区三区水密桃| 国产一区二区三区美女| 日本一区二区三区在线视频观看免费| 国产中文字幕一区| 无码毛片一区二区三区中文字幕 | 精品少妇人妻AV一区二区三区| 日韩人妻不卡一区二区三区| 亚洲第一区二区快射影院| 亚洲av日韩综合一区久热| 日本一区二三区好的精华液| 亚洲一区二区观看播放| 国产精品无码一区二区三区免费| 色系一区二区三区四区五区| 久久综合一区二区无码| 性无码免费一区二区三区在线| 男插女高潮一区二区| 国产AV一区二区精品凹凸|