HTTP keep-alive参考博文
socketio跨域 socketio跨域问题自己去了解
WebSocket
websocket可以实现客户端与服务端之间的数据实时通信。(长连接)
网络通信过程中的长连接与短连接
短连接: 客户端发起连接请求,请求建立连接,服务端接受请求,完成连接的创建,而后客户端通过该连接发送数据,服务端处理请求,返回响应,最后连接断开。这种通讯模式称为短连接。
特点:这种通讯模式将会及时的释放服务端资源,所以可以为更多的客户端提供服务。但是也有缺点:无状态。(每个请求都是独立的,无法保存客户端状态)
**长连接:**客户端发起连接请求,请求建立连接,服务端接受请求,完成连接的创建,而后客户端通过该连接发送数据,服务端处理请求,返回响应;服务端也可以主动的向客户端发送数据,客户端接收。这个过程中持续连接不断开。这种通讯模式称为长连接。
特点:支持客户端与服务端之间的实时通信。服务端可以随时随地向客户端发消息。缺点就是浪费服务端资源。
什么是websocket?
websocket是一种在单TCP连接上进行的支持全双工通信(通信的过程中允许两个方向上同时完成数据传输)的一种协议。是一款长连接协议。
如何在网页中建立websocket连接?
服务端接收接收该连接?
客户端与服务端如何通过该websocket连接进行通信呢?
socket.io
https://socketio.bootcss.com/
socket.io是一个为浏览器与服务端之间提供实时的、双向的、基于事件通信的网络通信库。底层封装了websocket协议。提供了一些简单的API接口,方便的建立连接,收发消息,抹平了一些技术细节与浏览器兼容性问题。
建立连接的伪代码:
socket(套接字)就是一个javascript对象,对象的本质就是封装了属性与方法。socket中就封装了用于与对方通信时所需要的资源。
// 客户端 导入socket.io后
let socket = io('ws://localhost:3000/');
// 服务端 安装socket.io后
let socketio = require('socket.io');
socketio.on('connection', (socket)=>{
连接建立成功后执行
});
建立websocket连接
服务端代码:
- 新建项目(
socketserver文件夹)安装
express。安装socket.io。cd socketserver npm init // 一路回车 生成package.json npm install --save express // 安装express npm install --save socket.io // 安装socket.io
- 基于
socket.io提供的API,接收客户端的连接请求,建立websocket连接。// index.js 服务端核心js文件 const express = require('express'); const server = express(); // 调用server.get() server.post() 注册路由 接收请求 // 配置websocket // 获取express底层所使用的http模块 const http = require('http').createServer(server); // websocket需要把通信相关路由注册到该底层http模块 const socketio = require('socket.io')(http); socketio.on('connection', (socket)=>{ console.log('有客户端进来了:'+socket.id) }); // 设置静态资源托管目录 public server.use(express.static('public')); // 启动服务 不能再使用server.listen 而应该调用http.listen // 因为socketio将通信相关路由注册到了http模块,而非express // 所以http才可以接收到websocket请求,express则只用于处理http服务 http.listen(3000, function(){ console.log('server is running...') });
客户端代码:
为了避免跨域问题,设置静态资源托管目录public,把网页写在public中即可。
<script src="socket.io.js"></script>
<script>
btnConn.addEventListener('click', ()=>{
let socket = io('ws://localhost:3000/');
console.log(socket);
});
</script>
客户端与服务端之间进行通信
客户端发消息给服务端
客户端:
let socket = io('ws://localhost:3000/');
// 发消息 (emit 翻译为触发)
socket.emit('textmsg', '你瞅啥?!');
服务端:
socketio.on('connection', (socket)=>{
// 监听客户端发过来的消息
socket.on('textmsg', (data)=>{
console.log(data)
});
});
服务端发消息给客户端
服务端代码:
socket.on('textmsg', (data)=>{
console.log(data)
// 回复
socket.emit("textmsg", '瞅你咋地~~');
});
客户端代码:
let socket = io('ws://localhost:3000/');
socket.on('textmsg', (data)=>{
alert('xxxx');
});
服务端向所有客户端广播消息
socketio.on('connection', (socket)=>{
socket.emit(); // 针对当前客户端发消息
socketio.emit('textmsg', '兄弟们!'); // 向所有客户端都发消息
});
例子:文件夹目录为
客户端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>websockt示例</h2>
<button id="btnConn">建立连接</button>
<button id="btnSend">向服务器端发消息</button>
<button id="btnSend3">向服务器端发消息:请服务器端准备群发</button>
<script src="socket.io.js"></script>
<script>
let socket;
// 建立连接
btnConn.addEventListener("click", ()=>{
// socket = io('ws//localhost:3000/');
socket = io('ws://localhost:3000/');
console.log(socket);
// 因为接收信息是在建立完连接之后就监听,所以接收服务器的信息是在连接之后就开启接收
socket.on("textmsg", (data)=>{
window.alert(data);
});
});
// 客户端主动发消息
btnSend.addEventListener("click", ()=>{
// textmsg 是代表在服务器端接收时要用该textmsg去接收,只跟此次发送和接收相关,
// 即每次接收和发送的要匹配即可
socket.emit("textmsg", "向服务器发送信息,单发");
});
btnSend3.addEventListener("click", ()=>{
// textmsg 是代表在服务器端接收时要用该textmsg去接收,只跟此次发送和接收相关,
// 即每次接收和发送的要匹配即可
socket.emit("textmsg", "向服务器端发消息:请服务器端准备群发");
});
</script>
</body>
</html>
服务端:
//index.js 服务端核心js文件
const express = require('express');
const server = express();
// 调用server.get() serve.post()注册路由,接收请求
// const socketio = require("socket.io");
// 建立服务端和socketio连接
// 获取express底层所使用的http模块,因为要通过express底层的http去发送请求,建立连接是用http,所以要获得http
const http = require('http').createServer(server);
// 要将express底层的http和socketio建立联系
const socketio = require('socket.io')(http);
// 监听连接事件,连接就触发回调函数
socketio.on("connection", (socket)=>{
console.log(socket.id);
// 1.服务器端主动发送消息
// 1.1针对当前客户端发送消息
// socket.emit("textmsg", "连接建立成功");
// 1.2向所有客户端发送消息,通过socktio发送
// 2.服务器端接收消息并发送消息
// 通过socketio连接的是长连接,所以接收信息要在长链接接听中接收和处理
socket.on('textmsg', (data)=>{//接收textmsg类型的消息,即可以理解为监听textmsg的信息,有该信息则进行回调函数调用
console.log(data);
// 回复客户端
if(/向服务器发送信息,单发/.test(data)){
socket.emit("textmsg", "回复客户端信息");
}else{
socketio.emit("textmsg", "给客户端们都群发信息");
}
});
});
// 如果之后要写接口,那么是用serve.get来接收,因为serve上绑定了socketio,所以可以直接用
// 页面有自动重连的功能,如果没有连接成功
// 设置静态资源托管目录 public,即可以通过服务器地址直接访问该文件夹内的资源
server.use(express.static('public'));
// 启动服务 不能再使用server.listen,而应该调用http.listen
// 因为sokectio将通信相关的路由注册到了http模块,而非express
// 所以http才可以接收到websockt请求,express则用于处理http服务
http.listen(3000, function(){
console.log("serve is running");
})
实现群聊天室
需求如下:
在聊天界面中建立
websocket连接。当客户端点击发送按钮时,使用已经建立好的连接向服务端发送消息。服务端接收该消息后,将这个消息内容向所有已经连接的客户端再发一遍即可。
每个客户端需要监听服务端发回来的消息,控制台打印。
消息上屏。把接收到的消息显示在聊天记录区。
优化业务细节。
实时更新聊天室的人数。
- 在服务端实时维护一个表达在线人数的变量。
count - 一旦有客户端连接,
count++ - 一旦有客户端断开连接,
count-- - 无论
count是递增了还是递减了,只要count有变化,就需要给所有客户端发消息emit('count_updated', count)。 - 客户端监听并接收该消息类型,更新页面中的在线人数即可。
- 在服务端实时维护一个表达在线人数的变量。
实现登录业务。
在
index.html点击登录时,需要获取昵称(文本框里输入)与头像(随机生成文件名)。带着这两个参数一起跳转到chart.html// window.location.href = "chart.html" location = "chart.html?name=xxx&avatar=xx.jpg"在
chart.html中获取name与avatar,更新用户信息。发消息时,不能只发送内容,而是需要发送一个对象:
{content:'xxxx', name:'xx', avatar:'xxx.jpg'}服务端接收后将原封不动的把对象发给所有客户端。一旦客户端接收到返回回来的对象,需要解析对象的属性,上屏。
聊天室文件夹结构为:

群聊完成了,私聊该如何解决?
每当客户端与服务端建立了一个websocket连接,客户端将会得到一个socket对象,服务端也会得到一个socket对象。这些socket对象用于与对方进行数据通信。所以服务端保存了与每个客户端通信所需要的的socket对象。
所以如何实现私聊?
当亮亮发消息时,不仅需要发送消息内容,还需要发送对方的ID(也可以是name,总之得传递对方的唯一标识符)。服务端接收到该消息后,需要通过ID(也可以是name)找到与对方建立起来的socket连接对象,直接调用socket.emit()方法发消息即可。
但是这里依然存在着不足,这里在用户退出之后,没有清除对应的用户socket,所以这里依然要去不断完善。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微信聊天室</title>
<link rel="stylesheet" href="styles/normalize.css">
<link rel="stylesheet" href="styles/reset.css">
<link rel="stylesheet" href="styles/chart.css">
</head>
<body>
<div id="login-container">
<div class="login-title">微信聊天室</div>
<div class="login-user-icon">
<img src="images/login.png" alt="">
</div>
<div>
<input type="text" id="username" class="login-item login-username" placeholder="请输入聊天昵称">
</div>
<div>
<input type="button" id="login" class="login-item login-button" value="登录">
</div>
</div>
<script>
// 为登录按钮绑定事件,点击之后跳转到chart.html页面,并且要把用户名和头像带过去
// 通过location.href带过去
login.addEventListener("click", ()=>{
// 拿到用户名
let name = username.value;
// 头像的随机值
let avatar = Math.floor(Math.random()*100);
// 这里是相对路径,所以直接替换最后一个斜杆的内容
let url = `chart.html?name=${name}&navatar=${avatar}.jpg`;
console.log(url);
// window.location = location
// location == location.href
location.href = url;
});
</script>
</body>
</html>
chart.html
chart.html
```cpp
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微信聊天室</title>
<link rel="stylesheet" href="styles/normalize.css">
<link rel="stylesheet" href="styles/reset.css">
<link rel="stylesheet" href="styles/chart.css">
</head>
<body>
<div id="chart-container">
<div class="chart-user-list" id="chart-user-list">
<div class="user-item" id="userinfo">
<!-- <img src="images/avatar/15.jpg" alt="">
未知名 -->
</div>
</div>
<div class="chart-main-area">
<div class="chart-main-title">
<h1>微信聊天室(<span id="userNumber"></span>人)-<span id="currentUser"></span></h1>
</div>
<div class="chart-list" id="chart-list">
<div class="user-logined" id="user-logined"><span id="logined-user"></span>上线了</div>
<!-- <div class="chart-item">
<div class="user-face"><img src="images/avatar/11.jpg" alt=""></div>
<div class="user-message">111</div>
</div> -->
</div>
<div class="chart-form">
<div><textarea class="chart-form-message" id="message" onkeydown="sendto(event.keyCode)"></textarea></div>
<div><input type="button" id="send" class="chart-form-send" value="发表"></div>
</div>
</div>
</div>
<script src="scripts/socket.io.js"></script>
<script>
let str = window.location.search;
if(!str){//如果没有参数,则不能跳转到chart页面
location = "index.html";
}
// 这里要接收index传过来的参数,并且用于更新界面
// 解析请求资源路径,获取name与avator
let name = str.split("&")[0].split("=")[1];
let avatar = str.split("&")[1].split("=")[1];
name = decodeURI(name);
// 更新页面中的头像与昵称:下面要注意头像的随机不是每个头像都有,因为文件夹内不连续
userinfo.innerHTML = `
<img src="images/avatar/${avatar}" alt="">
${name}
`;
// 如果客户端和服务端的socket版本不一样。一个新旧,则会导致俩者无法正常交涉,会断开再连接
let socket = io('ws://localhost:3000/');
console.log(socket);
// 登录并将用户名发送到服务端,让客户端根据名字将每个用户的socket保存起来
socket.emit("login", name);
// 更新群聊人数
// 连接建立成功后,直接开始监听服务端发过来的count_updated消息
socket.on("count_updated", (data)=>{
// 把data更新到span中即可
userNumber.innerHTML = data;
});
// 服务端上线,则界面通知
socket.on("person", (data)=>{
var person = document.getElementById("logined-user");
person.innerHTML = data;
// 过了一段时间之后,让通知消失
var notice = document.getElementById("user-logined");
setTimeout(()=>{
notice.style.display = "none";
}, 2000);
});
//版本一: 点击按钮发消息。头像是固定的
// send.addEventListener("click", function(){
// // 获取textarea中输入的文本
// let msg = document.getElementById("message").value;
// // 发送给客户端消息
// if(msg.trim() && msg.length<30){//符合发送信息的要求,才可以发送给服务器
// // string.trim()是去除字符串左右俩端的字符串
// socket.emit("textmsg", msg);
// // 发送完毕之后让消息清空
// document.getElementById("message").value = "";
// }
// });
// 版本一:监听发送的消息
// 连接建立成功后就直接开始监听服务端发过来的消息
// socket.on("textmsg", (data)=>{
// console.log(data);
// // 将收到的消息添加到显示屏上
// // 构造一个聊天记录列表div,追加到聊天记录列表
// let div = document.createElement('div');
// div.className = 'chart-item';
// div.innerHTML = `<div class="user-face">
// <img src="images/avatar/11.jpg" alt="">
// </div>
// <div class="user-message">${data}</div>`;
// // 把div追加到chart-list子元素的末尾位置
// let list = document.getElementById("chart-list");
// list.appendChild(div);
// // 将list元素的滚动条始终定位在最底部
// list.scrollTop = list.scrollHeight;
// });
//版本二: 点击按钮发消息。头像是变化的
// send.addEventListener("click", function(){
// // 获取textarea中输入的文本
// let msg = document.getElementById("message").value;
// // 发送给客户端消息
// if(msg.trim() && msg.length<30){//符合发送信息的要求,才可以发送给服务器
// // string.trim()是去除字符串左右俩端的字符串
// // 这里将用户的发送内容和头像发送过服务端
// let allmsg = {content: msg, name: name, avatar: avatar};
// socket.emit("textmsg", allmsg);
// // 发送完毕之后让消息清空
// document.getElementById("message").value = "";
// }
// });
// 版本二(动态头像):监听发送的消息
// 连接建立成功后就直接开始监听服务端发过来的消息
socket.on("textmsg", (data)=>{
console.log(data);
// 将收到的消息添加到显示屏上
// 构造一个聊天记录列表div,追加到聊天记录列表
let div = document.createElement('div');
div.className = 'chart-item';
div.innerHTML = `<div class="user-face">
<img src="images/avatar/${data.avatar}" alt="">
</div>
<p style="font-size: 15px">${data.name}</p>
<div class="user-message">${data.content}</div>`;
// 把div追加到chart-list子元素的末尾位置
let list = document.getElementById("chart-list");
list.appendChild(div);
// 将list元素的滚动条始终定位在最底部
list.scrollTop = list.scrollHeight;
});
// 封装发送信息的函数
function sendmsg(){
// 获取textarea中输入的文本
let msg = document.getElementById("message").value;
// 发送给客户端消息
if(msg.trim() && msg.length<30){//符合发送信息的要求,才可以发送给服务器
// string.trim()是去除字符串左右俩端的字符串
// 这里将用户的发送内容和头像发送过服务端 这里先写死私发给亮亮
let allmsg = {content: msg, name: name, avatar: avatar, to:"亮亮"};
socket.emit("textmsg", allmsg);
// 发送完毕之后让消息清空
document.getElementById("message").value = "";
}
}
//版本三: 点击按钮发消息。头像是变化的,且回车会触发发送事件
send.addEventListener("click", function(){
// 点击按钮发送信息
sendmsg();
});
// 回车触发的事件函数
function sendto(key){
if(key == 13){//回车
sendmsg();
}
}
</script>
</body>
</html>
app.js
const express = require('express');
const server = express();
// 取出serve里的http底层
const http = require('http').createServer(server);
// serve和io建立联系
const socketio = require('socket.io')(http);
// 用于保存实时在线人数
let count = 0;
// 保存用户们的{名字:socket}
let clients = {};
socketio.on("connection", (socket)=>{
console.log(`客户端${socket.id}访问`);
// 有人连接服务器,就更新在线人数,把最新的count值发送给所有客户端
count++;
socketio.emit('count_updated', count);
// 登录消息,通知上线
// socket.emit('person', socket.id);
// 捕获断开连接事件,count--
socket.on("disconnect", ()=>{//因为是事件
count--;
socketio.emit('count_updated', count);
});
// 因为是事件处理函数,所以不能保证放在外面一定会执行,所以要放到事件的回调函数中
// socketio.emit('count_updated', count);
// 监听客户端发过来的消息做不同的处理
socket.on("textmsg", (data)=>{//监听textmsg类型的数据,把数据放到data中
// 1.群聊:将收到的信息群发给每个用户
// socketio.emit("textmsg", data);//向所有客户端发送接收的信息
// 2.私聊:将收到的信息,发送给前端指定发送的用户,这里的指定用户是写死的
clients[data.to].emit("textmsg", data);
});
// 这里在每个用户连接之后,根据用户名保存每个socket对象
socket.on("login", (data)=>{
// 保存每个用户
clients[data] = socket;
console.log(clients);
// 登录成功,通知上线
socket.emit("person", data);
});
});
// 何时建立好连接,当客户端加载完页面之后就建立连接
// 群里思路是每个人发送给服务端,服务端将每个人发送的信息都转发给全部客户端
// 设置公共目录
server.use(express.static('public'));
// 监听
http.listen(3000, function(){
console.log("serve is running");
})
style/chart.css
body{
background-color: #fff;
font:14px/1.5 'Microsoft Yahei';
}
::-webkit-scrollbar {
width:12px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
-webkit-box-shadow:inset006pxrgba(0,0,0,0.3);
border-radius:10px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius:10px;
background:rgba(0,0,0,0.1);
-webkit-box-shadow:inset006pxrgba(0,0,0,0.5);
}
::-webkit-scrollbar-thumb:window-inactive {
background:rgba(255,0,0,0.4);
}
#login-container{
width:285px;
min-height:400px;
border-radius: 4px;;
margin:50px auto 0 auto;
background-color: #F5F5F5;
border:1px solid #ccc;
}
#login-container .login-title{
height: 40px;
line-height: 40px;
padding-left:10px;
border-bottom:1px solid #ddd;
}
#login-container .login-user-icon{
width:120px;
height: 120px;
margin: 50px auto;;
}
#login-container .login-item{
display: block;
margin:0 auto 10px auto;
}
#login-container .login-username{
width:190px;
height: 30px;
line-height: 30px;
padding:0 5px;
text-align: center;
}
#login-container .login-button{
width:204px;
background-color:#1AAD19;
border: none;
line-height: 40px;
color:#fff;
}
#chart-container::before,#chart-container::after{
content: '.';
display: block;
height: 0;
visibility: hidden;
clear: both;
}
#chart-container{
width:900px;
height: 500px;
margin: 50px auto;
border:1px solid #ccc;
}
#chart-container .chart-user-list{
float:left;
width:250px;
height: 500px;
background-color: #EAE8E7;
}
#chart-container .chart-user-list .user-item{
padding:10px;
border-bottom:1px solid #cccc;
}
#chart-container .chart-user-list .user-item img{
margin-right: 5px;
width: 40px;
height: 40px;
border-radius: 5px;
vertical-align: middle;
}
#chart-container .chart-main-area{
float:left;
width:650px;
height: 500px;
background-color: #F5F5F5;
}
#chart-container .chart-main-area .chart-main-title{
margin-bottom: 10px;;
height: 60px;
border-bottom:1px solid #cccc;
}
#chart-container .chart-main-area .chart-main-title h1{
margin-left:20px;
line-height: 60px;
font-size:18px;
}
#chart-container .chart-list{
position: relative;
height: 295px;
overflow-Y: auto;
overflow-X:hidden;
}
#chart-container .chart-list .user-logined{
position: absolute;
bottom: 10px;
width: 250px;
padding: 10px;
text-align: center;
background-color: #DADADA;
color: #000;
left:190px;
transition:all 0.8s ease-in-out;
}
#chart-container .chart-list .chart-item::after,#chart-container .chart-list .chart-item::before{
content: '.';
display: block;
height: 0;
visibility: hidden;
clear: both;
}
#chart-container .chart-list .chart-item{
margin-bottom: 10px;
}
#chart-container .chart-list .chart-item .user-face{
float:left;
margin:0 15px;
}
#chart-container .chart-list .chart-item .user-face img{
width:40px;
height: 40px;
border-radius: 5px;
}
#chart-container .chart-list .chart-item .user-message{
position: relative;
float:left;
padding:10px;
min-width: 100px;
max-width: 520px;
background: #fff;
font-size: 12px;
border-radius: 3px;
}
#chart-container .chart-list .chart-item .user-message::before{
content: ' ';
position: absolute;
width:10px;
height:10px;
background: #fff;
top:10px;
left:-5px;
z-index: 222;
transform: rotate(45deg);
}
#chart-container .chart-form{
height: 134px;
background: #fff;
}
#chart-container .chart-form .chart-form-message{
width:630px;
padding:5px 10px;
height: 95px;
line-height: 1.5;
resize:none;
border: none;
}
#chart-container .chart-form .chart-form-send{
float: right;
width: 70px;
height: 24px;
margin-right: 10px;
background-color: #F5F5F5;
color: #606060;
border: 1px solid #ccc;
cursor: pointer;
}
style/normalize.css
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
style/reset.css
/* http://meyerweb.com/eric/tools/css/reset/
v2.0-modified | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* make sure to set some focus styles for accessibility */
:focus {
outline: 0;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
input[type=search]::-webkit-search-cancel-button,
input[type=search]::-webkit-search-decoration,
input[type=search]::-webkit-search-results-button,
input[type=search]::-webkit-search-results-decoration {
-webkit-appearance: none;
-moz-appearance: none;
}
input[type=search] {
-webkit-appearance: none;
-moz-appearance: none;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
textarea {
overflow: auto;
vertical-align: top;
resize: vertical;
}
/**
* Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
max-width: 100%;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
* Known issue: no IE 6 support.
*/
[hidden] {
display: none;
}
/**
* 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
* `em` units.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-size: 100%; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/**
* 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
* 2. Improve image quality when scaled in IE 7.
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/**
* Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
*/
figure {
margin: 0;
}
/**
* Correct margin displayed oddly in IE 6/7.
*/
form {
margin: 0;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct color not being inherited in IE 6/7/8/9.
* 2. Correct text not wrapping in Firefox 3.
* 3. Correct alignment displayed oddly in IE 6/7.
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/**
* 1. Correct font size not being inherited in all browsers.
* 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
* and Chrome.
* 3. Improve appearance and consistency in all browsers.
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/**
* Address Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
* 4. Remove inner spacing in IE 7 without affecting normal text inputs.
* Known issue: inner spacing remains in IE 6.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
*overflow: visible; /* 4 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to content-box in IE 8/9.
* 2. Remove excess padding in IE 8/9.
* 3. Remove excess padding in IE 7.
* Known issue: excess padding remains in IE 6.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
*height: 13px; /* 3 */
*width: 13px; /* 3 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 6/7/8/9.
* 2. Improve readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
html,
button,
input,
select,
textarea {
color: #222;
}
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
::selection {
background: #b3d4fc;
text-shadow: none;
}
img {
vertical-align: middle;
}
fieldset {
border: 0;
margin: 0;
padding: 0;
}
textarea {
resize: vertical;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
图片文件夹里面的图片为多张命名为数字的图片,0-99命名,其中有些缺少了。