復制代碼
查看效果
代碼注釋:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>海報畫廊 - 何問起</title>
<style type="text/css">
* {
padding: 0;
margin: 0;
}
body {
background-color: #fff;
color: #555;
font-family: 'Avenir Next', 'Lantinghei SC';
font-size: 14px;
/*-webkit-font-smoothing:none | subpixel-antialiased | antialiased
*none:對低像素的文本比較好
*subpixel-antialiased:默認值
*antialiased:抗鋸齒
*-moz-osx-font-smoothing是mozilla給特定操作系統推出的特性增強
*設置grayscale對於圖標字體表現更清晰,減輕次像素渲染帶來的相鄰像素色彩污染問題
*/
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.wrap {
width: 100%;
height: 600px;
position: absolute;
top: 50%;
margin-top: -300px;
background-color: #333;
overflow: hidden;
/*perspective 是 CSS3 屬性,目前浏覽器都不支持 perspective 屬性,
*Chrome 和 Safari 支持替代的 -webkit-perspective 屬性。
*perspective 屬性定義 3D 元素距視圖的距離,以像素計,其值越小,用戶與3D空間Z平面距離越近,視覺效果更令人印象深刻;
*反之,值越大,用戶與3D空間Z平面距離越遠,視覺效果就很小
*當為元素定義 perspective 屬性時,其子元素會獲得透視效果,而不是元素本身,perspective 屬性只影響 3D 轉換元素
*與 perspective-origin 屬性一同使用該屬性,這樣您就能夠改變 3D 元素的底部位置
*perspective: number | none;
*number:元素距離視圖的距離,以像素計(px 可以不寫)
*none:默認值,與 0 相同,不設置透視
*在3D變形中,除了perspective屬性可以激活一個3D空間之外,在3D變形的函數中的perspective()也可以激活3D空間。
*他們不同的地方是:perspective用在舞台元素上(變形元素們的共同父元素);perspective()就是用在當前變形元素上,
*並且可以與其他的transform函數一起使用。例如,我們可以把:
*.stage {perspective: 600px;} 寫成 .stage .box {transform: perspective(600px);}
*perspective()函數取值只能大於0,如果取值為0或比0小的值,將無法激活3D空間
*/
perspective: 800px;
-webkit-perspective: 800px;
}
/* 海報樣式 s*/
.photo {
position: absolute;
width: 260px;
height: 320px;
z-index: 1;
box-shadow: 0 0 1px rgba(0, 0, 0, .01);
/*IE 10、Firefox、Opera 和 Chrome 支持 transition 屬性
*Safari 支持替代的 -webkit-transition 屬性
*Internet Explorer 9 以及更早版本的浏覽器不支持 transition 屬性
*transition 屬性是一個簡寫屬性(默認值:all 0 ease 0),用於設置四個過渡屬性:
*transition-property,定義應用過渡效果的 CSS 屬性名稱列表,列表以逗號分隔;all 則所有屬性都將獲得過渡效果
*transition-duration,規定完成過渡效果需要多少秒或毫秒,必須始終設置 transition-duration 屬性,否則時長為 0,就不會產生過渡效果
*transition-timing-function,規定速度效果的速度曲線(linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n))
*transition-delay,定義過渡效果何時開始
*/
transition: all .5s;
-moz-transition: all .5s; /* Firefox 4 */
-webkit-transition: all .5s; /* Safari 和 Chrome */
-o-transition: all .5s; /* Opera */
/*初始化時每張海報都居中顯示,然後通過 JavaScript 設置除中間海報外的隨機位置,會產生從中間發散的效果時候時候,
*切換中間海報的時候,由於中間海報瞬間失去了 .photo_center 的樣式,但仍然還有 .photo 的樣式,再被設置隨機樣式後會有平滑的過渡效果
*/
left: 50%;
top: 50%;
margin: -160px 0 0 -130px;
}
.photo .photo-wrap .side {
position: absolute;
width: 100%;
height: 100%;
background-color: #eee;
top: 0;
right: 0;
padding: 20px;
/*IE8+、Opera 以及 Chrome 支持 box-sizing 屬性,Firefox 支持替代的 -moz-box-sizing 屬性。
*box-sizing: content-box | border-box | inherit
*content-box:默認值,按W3C盒模型進行處理 (element width = border + padding + border + content)
*border-box:按IE6盒模型進行處理 (element width = content)
*/
box-sizing: border-box;
-moz-box-sizing:border-box;
/*
*IE 10+ 和 Firefox 支持 backface-visibility 屬性,Opera 15+、Safari 和 Chrome 支持替代的 -webkit-backface-visibility 屬性
*backface-visibility 屬性定義當元素不面向屏幕時是否可見
*backface-visibility: visible | hidden
*visible:背面是可見的
*hidden:背面是不可見的
*/
backface-visibility: hidden;
-webkit-backface-visibility: hidden; /* Chrome 和 Safari */
-moz-backface-visibility: hidden; /* Firefox */
-ms-backface-visibility: hidden; /* Internet Explorer */
}
.photo .photo-wrap .side-front {
/*
*IE 10、Firefox、Opera 支持 transform 屬性,IE 9 支持替代的 -ms-transform 屬性(僅適用於 2D 轉換)
*Safari 和 Chrome 支持替代的 -webkit-transform 屬性(3D 和 2D 轉換),Opera 只支持 2D 轉換
*transform: none | rotate | scale | skew | translate | matrix
*如果有多個變換函數的時候,以空格分開
*none: 表示不進行變換
*rotate:旋轉。rotate(<angle>) :通過指定的角度參數對原元素指定一個 2D rotation,需先有 transform-origin 屬性的定義
*scale:縮放。元素的縮放中心點是元素的中心位置,縮放基數為1,如果其值大於1元素就放大,反之其值小於1,元素縮小;
*scale(x,y)使元素水平方向和垂直方向同時縮放;如果第二個參數未提供,則取與第一個參數一樣的值;
*scaleX(x)元素僅水平方向縮放;scaleY(y)元素僅垂直方向縮放;其中 x, y 為數字
*skew:扭曲。默認以元素中心為基點,也可以通過transform-origin來改變元素的基點位置;
*skew(x,y)使元素在水平和垂直方向同時扭曲,如果沒有設置第二個參數,那麼Y軸為0deg;
*skewX(x)僅使元素在水平方向扭曲變形;skewY(y)僅使元素在垂直方向扭曲變形,其中 x, y 為角度
*translate:移動。移動物體時基點默認為元素中心點,也可以根據 transform-origin 進行改變基點;
*translate(x,y)水平方向和垂直方向同時移動,如果第二個參數未提供,則以 0 作為其值;
*translateX(x)僅水平方向移動;translateY(y)僅垂直方向移動;其中 x, y 為像素值
*matrix:矩陣。
*/
transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
-moz-transform: rotateY(0deg);
-o-transform: rotateY(0deg);
-ms-transform: rotateY(0deg);
}
.photo .photo-wrap .side-front .image {
width: 100%;
height: 250px;
line-height: 250px;
overflow: hidden;
}
.photo .photo-wrap .side-front .image img {
width: 100%;
vertical-align: middle; /*使高度不夠的圖片居中顯示*/
}
.photo .photo-wrap .side-front .caption {
text-align: center;
font-size: 16px;
line-height: 50px;
}
/* 初始化時使 side-back 沿Y軸旋轉180度,即在背面顯示 */
.photo .photo-wrap .side-back {
transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
-moz-transform: rotateY(180deg);
-o-transform: rotateY(180deg);
-ms-transform: rotateY(180deg);
}
.photo .photo-wrap .side-back .desc {
color: #666;
font-size: 14px;
line-height: 1.5em;
}
/*當前選中的海報樣式*/
.photo_center {
top: 50%;
left: 50%;
margin: -160px 0 0 -130px;
z-index: 2;
}
/*負責翻轉*/
.photo .photo-wrap {
position: absolute;
width: 100%;
height: 100%;
/*
*IE不支持,Firefox 支持 transform-style 屬性,Chrome、Safari 和 Opera 支持替代的 -webkit-transform-style 屬性
*transform-style 屬性規定如何在 3D 空間中呈現被嵌套的元素。
*transform-style: flat | preserve-3d
*flat: 子元素將不保留其 3D 位置
*preserve-3d: 子元素將保留其 3D 位置
*一般而言,該聲明應用在3D變換的兄弟元素們的父元素上,也就是舞台元素
*/
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
transition: all .6s ease-in-out;
-webkit-transition: all .6s ease-in-out; /* Safari 和 Chrome */
-moz-transition: all .5s; /* Firefox 4 */
-o-transition: all .5s; /* Opera */
/*
*IE 10、Firefox、Opera 支持 transform-origin 屬性,IE 9 支持替代的 -ms-transform-origin 屬性(僅適用於 2D 轉換),
*Safari 和 Chrome 支持替代的 -webkit-transform-origin 屬性(3D 和 2D 轉換),Opera 只支持 2D 轉換
*transform-origin: x-axis y-axis z-axis; 設置旋轉元素的基點位置
*x-axis:定義視圖被置於 X 軸的何處。可能的值:left | center | right | length | %
*y-axis:定義視圖被置於 Y 軸的何處。可能的值:top | center | bottom | length | %
*z-axis:定義視圖被置於 Z 軸的何處。可能的值:length
*/
transform-origin: 0% 50% 0px;
-ms-transform-origin: 0% 50% 0px; /* IE 9 */
-o-transform-origin: 0% 50% 0px; /* Opera */
-webkit-transform-origin: 0% 50% 0px; /* Safari 和 Chrome */
-moz-transform-origin: 0% 50% 0px; /* Firefox */
}
.photo_front .photo-wrap { /* .photo_front 是添加到 div.photo 的類 */
transform: translate(0px, 0px) rotateY(0deg);
-webkit-transform: translate(0px, 0px) rotateY(0deg);
-moz-transform: translate(0px, 0px) rotateY(0deg);
-o-transform: translate(0px, 0px) rotateY(0deg);
-ms-transform: translate(0px, 0px) rotateY(0deg);
}
.photo_back .photo-wrap { /* .photo_back 是添加到 div.photo 的類*/
transform: translate(260px, 0px) rotateY(180deg);
-webkit-transform: translate(260px, 0px) rotateY(180deg);
-moz-transform: translate(260px, 0px) rotateY(180deg);
-o-transform: translate(260px, 0px) rotateY(180deg);
-ms-transform: translate(260px, 0px) rotateY(180deg);
}
/* 添加控制按鈕的樣式 */
@font-face {
font-family: 'icomoon';
src: url('images/icomoon.woff') format('woff');
font-weight: normal;
font-size: normal;
}
.nav {
position: absolute;
width: 80%;
left: 10%;
height: 30px;
line-height: 30px;
bottom: 20px;
z-index: 3;
text-align: center;
}
/* 普通樣式 */
.nav .i {
display: inline-block;
width: 30px;
height: 30px;
cursor: pointer;
background-color: #aaa;
text-align: center;
border-radius: 50px;
transform: scale(.5);
-webkit-transform: scale(.5);
-moz-transform: scale(.5);
-o-transform: scale(.5);
-ms-transform: scale(.5);
transition: all .5s;
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
}
/* 設置並顯示字體圖標 */
.nav .i:after {
content: '\e965';
font-family: 'icomoon';
font-size: 80%;
display: inline-block;
line-height: 30px;
text-align: center;
color: #fff;
opacity: 0;
}
/* 選中樣式 */
.nav .i_current {
transform: scale(1);
-webkit-transform: scale(1);
-moz-transform: scale(1);
-o-transform: scale(1);
-ms-transform: scale(1);
}
.nav .i_current:after {
opacity: 1;
}
/* 背面樣式 */
.nav .i_back {
background-color: #555;
transform: rotateY(180deg);
-webkit-transform: rotateY(180deg);
-moz-transform: rotateY(180deg);
-o-transform: rotateY(180deg);
-ms-transform: rotateY(180deg);
}
</style>
</head>
<!-- 禁止用戶選中網頁上的內容,IE及Chrome下的方法一樣,使用 onselectstart="return false"
-- Firefox 下,控制 css: body {-moz-user-select: none;}
-->
<body onselectstart="return false;" style="-moz-user-select:none;">
<div class="wrap" id="wrap"></div>
<script type="text/javascript" src="images/data.js"></script>
<script type="text/javascript">
// 3.通用函數,返回被選擇的元素或元素集合
function g(selector){
return selector.substring(0, 1) == '.' ? document.getElementsByClassName(selector.substring(1)) : document.getElementById(selector.substring(1));
}
/*隨機數生成函數,在給定的范圍內(random([min, max]))隨機生成一個值,
*因為此案例要在 20 張海報中隨機選取一張居中顯示,以及在左右兩個分區內隨機擺放
*剩余海報,因為海報數量和左右區域都是有限制的,所以必須在給定范圍內生成隨機數。
*傳入的參數 range 是一個包含兩個數值的數組,這兩個數值即是給定范圍的最小值(包含)和最大值(包含)
*Math.random() 隨機生成介於 0.0 ~ 1.0 之間的一個偽隨機數
*/
function random(range){
var min = Math.min(range[0], range[1]);
var max = Math.max(range[0], range[1]);
var diff = max - min;
/*
*例如 [1, 20],則 diff = 19 --> 0 <= Math.round(Math.random() * diff) <= 19
*然後再加上最小值,即可隨機生成 1 ~ 20 之間的任意數,如果使用 Math.floor() 則
*生成 1 ~ 19 之間的任意數,使用 Math.ceil() 則生成 2 ~ 20 之間的任意數
*/
var number = Math.round(Math.random() * diff) + min;
return number;
}
// 4.輸出所有的海報
/*
*也可以使用模板替換的方法:
<div class="wrap" id="wrap">
<div class="photo photo_front" onclick="turn(this)" id="photo_{{index}}"> <!-- div.photo負責位移、旋轉(平面上xy旋轉)-->
<div class="photo-wrap"> <!-- div.photo-wrap負責3D翻轉(正反面切換)-->
<div class="side side-front">
<p class="image"><img src="images/{{img}}"></p>
<p class="caption">{{caption}}</p>
</div>
<div class="side side-back">
<p class="desc">{{desc}}</p>
</div>
</div>
</div>
<div class="nav">
<span id="nav_{{index}}" onclick="turn(g('#photo_{{index}}'))" class="i"></span>
</div>
</div>
*以上是靜態模板,然後在 JavaScript 中對其中的 {{}} 關鍵字進行替換:
function addPhotos(){
var template = g('#wrap').innerHTML;
var html = [];
var nav=[];
for(var i = 0; i < data.length; i++){//for in循環中的循環計數器是字符串,而不是數字它包含當前屬性的名稱或當前數組元素的索引
var _html = template.replace('{{index}}', i + 1).replace('{{img}}', data[i].img).replace('{{caption}}', data[i].caption).replace('{{desc}}', data[i].desc);
html.push(_html);
nav.push('<span id="nav_' + (i + 1) + '" onclick="turn(g(\'#photo_' + (i + 1) + '\'))" class="i"></span>');
}
html.push('<div class="nav">' + nav.join('') + '</div>');
g('#wrap').innerHTML = html.join('');
rsort(random([1, data.length]));
}
*這樣也可以實現輸出所有海報,但是在刷新頁面的一瞬間,文字部分會顯示 {{}} 關鍵字沒有被替換之前的樣子,
*因此我修改為純 JavaScript 寫入的方式
*/
function addPhotos(){
var _wrap = '';
var _nav = '';
for(var i = 0; i < data.length; i++){//for in 循環中的循環計數器是字符串,而不是數字它包含當前屬性的名稱或當前數組元素的索引
_wrap += '<div class="photo photo_front" onclick="turn(this)" id="photo_' + (i + 1) + '"><div class="photo-wrap"><div class="side side-front"><p class="image"><img src="' + data[i].img + '"></p><p class="caption">' + data[i].caption + '</p></div><div class="side side-back"><p class="desc">' + data[i].desc + '</p></div></div></div></div>';
_nav += '<span id="nav_' + (i + 1) + '" onclick="turn(g(\'#photo_' + (i + 1) + '\'))" class="i"></span>';
}
var navigation = '<div class="nav">' + _nav + '</div>'
g('#wrap').innerHTML = _wrap + navigation;
rsort(random([1, data.length]));
}
addPhotos();
// 6.計算左右分區的范圍,使其他海報的顯示不會超出此范圍
function range(){
/*{left: {x: [左側區域 left 的最小值, 左側區域 left 的最大值], y: [左側區域 top 的最小值, 左側區域 top 的最大值]},
*right: {x: [右側區域 left 的最小值, 右側區域 left 的最大值], y: [右側區域 top 的最小值, 右側區域 top 的最大值]}}
*/
var range = {
left: {
x: [],
y: []
},
right: {
x: [],
y: []
}
};
//獲取最外圍容器的寬度和高度
var wrap = {
width: g('#wrap').clientWidth,
height: g('#wrap').clientHeight
};
//獲取每一張海報的寬度和高度,因為海報的大小都是一樣的,所以取第一張
var photo = {
width: g('.photo')[0].clientWidth,
height: g('.photo')[0].clientHeight
};
//按照自己想要顯示的區域進行計算,左右區域的高度 (top) 范圍是一樣的
range.left.x = [0 - photo.width / 4 + 130, wrap.width / 2 - photo.width * 5 / 4 + 130];
range.left.y = [0 - photo.height / 4 + 160, wrap.height - photo.height * 3 / 4 + 160];
range.right.x = [wrap.width / 2 + photo.width / 4 + 130, wrap.width - photo.width / 4 + 130];
range.right.y = range.left.y;
return range;
}
// 5.排序海報
function rsort(n){
var _photo = g('.photo');
var photos = [];
for(var i = 0; i < _photo.length; i++){
_photo[i].className = 'photo photo_front';
/*重排序之前去除所有圖片樣式*/
_photo[i].style.left = '';
_photo[i].style.top = '';
/*一般來說,訪問對象屬性時使用的都是點表示法,不過,在 JavaScript 中也可以使用方括號表示法來訪問
*對象的屬性。在使用方括號語法時,應該將要訪問的屬性以字符串的形式放在方括號中,如下面的例子所示:
alert(person["name"]); //"Nicholas"
alert(person.name); //"Nicholas"
*方括號語法的主要優點是可以通過變量來訪問屬性,如果屬性名中包含會導致語法錯誤的字符,或者屬性名
*使用的是關鍵字或保留字,也可以使用方括號表示法。例如:
person["first name"] = "Nicholas";
*通常,除非必須使用變量來訪問屬性,否則建議使用點表示法。(《JavaScript 高級程序設計》P85)
*/
_photo[i].style['transform'] = _photo[i].style['-webkit-transform'] = 'scale(1.3)';
photos.push(_photo[i]);
}
var photo_center = g('#photo_' + n);
photo_center.className += ' photo_center';
photo_center = photos.splice(n-1, 1);//把photo_center從數組裡刪掉,splice() 方法會改變原始數組
// 把剩下的海報分為左右兩部分
var photos_left = photos.splice(0, Math.ceil(photos.length / 2));
var photos_right = photos;
var ranges = range();
// 對左右區域的海報位置進行隨機賦值
for(var j = 0; j < photos_left.length; j++){
var photo = photos_left[j];
photo.style.left = random(ranges.left.x) + 'px';
photo.style.top = random(ranges.left.y) + 'px';
photo.style['transform'] = photo.style['-webkit-transform'] = 'rotate(' + random([-150, 150]) + 'deg) scale(1)';
}
for(var s = 0; s < photos_right.length; s++){
var photo = photos_right[s];
photo.style.left = random(ranges.right.x) + 'px';
photo.style.top = random(ranges.right.y) + 'px';
photo.style['transform'] = photo.style['-webkit-transform'] = 'rotate(' + random([-150, 150]) + 'deg) scale(1)';
}
// 控制按鈕處理
var navs = g('.i');
for(var k = 0; k < navs.length; k++){
navs[k].className = 'i';
}
g('#nav_' + n).className += ' i_current';
}
/*1.翻面控制,每個元素的 onclick() 事件都綁定此函數,如果點擊的元素不是中間的海報,則取得其
* id 進行重新排序,使其在中間顯示;如果點擊的是中間的海報則讓它翻面,同時控制按鈕也要翻面
*/
function turn(elem){
var cls = elem.className;
var n = elem.id.split('_')[1];//var n = elem.id.substr(-1, 1),但是不推薦 substr;
if(!/photo_center/.test(cls)){//判斷當前點擊的元素是不是 photo_center,不是的話不執行後面的翻轉而進行海報排序
return rsort(n);
}
if(/photo_front/.test(cls)){
cls = cls.replace(/photo_front/, 'photo_back');
g('#nav_' + n).className += ' i_back';//同時處理控制按鈕
} else{
cls = cls.replace(/photo_back/, 'photo_front');
g('#nav_' + n).className = g('#nav_' + n).className.replace(/\s*i_back\s*/, ' ');//同時處理控制按鈕
}
elem.className = cls;
}
</script>
</body>
</html>