js實(shí)現(xiàn)3D照片墻效果
聊一下心得:CSS寫(xiě)得好,真的可以省很多js代碼哈,寫(xiě)起來(lái)也簡(jiǎn)單很多,所以要好好掌握js哈,所以這里也提供了css代碼,如果您覺(jué)得您的css寫(xiě)得不錯(cuò),可以直接看js代碼哦
效果:
1、點(diǎn)擊Start View進(jìn)入照片墻
2、只有一張圖片是在中間顯示,其他圖片在中間的圖片兩側(cè)隨機(jī)排序,并且隨機(jī)旋轉(zhuǎn)一定的角度,層級(jí)也是隨機(jī)的哦
3、點(diǎn)擊上面的導(dǎo)航條,可以讓對(duì)應(yīng)的圖片在中間顯示
4、點(diǎn)擊中間的圖片該照片翻轉(zhuǎn),顯示背面(照片的描述信息)
實(shí)現(xiàn)過(guò)程:
1、用數(shù)據(jù)生成結(jié)構(gòu)(模擬的數(shù)據(jù),此處不再提供)
2、對(duì)所有圖片進(jìn)行排序
3、計(jì)算兩側(cè)圖片的隨機(jī)范圍
4、控制圖片翻轉(zhuǎn)
5、控制導(dǎo)航按鈕切換圖片
6、遮罩層動(dòng)畫(huà)實(shí)現(xiàn)
HTML代碼:
<body>
<div class="photo_wall">
<div class="photo">
<!-- 每張圖片的最外層,用來(lái)控制圖片的旋轉(zhuǎn)和位移 -->
<div class="photo_i front" id="photo_{{id}}">
<!-- 內(nèi)層用來(lái)控制圖片的3D翻轉(zhuǎn) -->
<div class="photo_3d">
<!-- 每個(gè)照片的正面 -->
<div class="photo_side photo_front">
<p><img {{src}}="{{img}}"></p>
<h3>{{caption}}</h3>
</div>
<!-- 每個(gè)照片的反面 -->
<div class="photo_side photo_back">
<p class="desc">{{desc}}</p>
</div>
</div>
</div>
{{split}}
<div class="nav"><span class="nav_i" id="nav_{{id}}"></span></div>
</div>
<div class="shade">
<div class="start">Start View</div>
</div>
</div>
</body>
CSS代碼:
/*最外層樣式*/
.photo_wall{
width: 100%;
height: 600px;
position: relative;
background: url(../imgs/bg.jpg) no-repeat center center;
background-size: cover;
overflow: hidden;
}
/*照片區(qū)域的樣式*/
.photo {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
opacity: 0;
transition: 1s;
}
/*每個(gè)照片的樣式*/
.photo .photo_i,.photo .photo_3d,.photo .photo_side {
width: 336px;
height: 392px;
position: absolute;
left: 0;
top: 0;
}
.photo .photo_i {
transition: 800ms;
perspective: 750px;
left: 50%;
top: 50%;
transform: translate(-50%,-50%) scale(.5) rotate(0);
}
.photo .photo_3d {
transition: 500ms;
transform-style: preserve-3d;
transform-origin: 0 50%;
}
/*正面和反面的公共樣式*/
.photo .photo_side {
border-radius: 6px;
background: #fff;
padding: 26px 24px;
box-sizing: border-box;
backface-visibility: hidden;
}
/*照片的正面樣式*/
.photo .photo_front {
transform: rotateY(0);
}
.photo .photo_front p {
width: 286px;
height: 286px;
border: 2px solid #d8536d;
overflow: hidden;
display: flex;
/*align-items: center;*/
}
.photo .photo_front p img{
width: 100%;
align-self: center;
}
.photo .photo_front h3{
width: 166px;
height: 44px;
background: #d8536d;
border-radius: 0 0 6px 6px;
margin: 0 auto;
text-align: center;
font: 16px/44px Arial;
color: #fff;
}
/*照片的反面樣式*/
.photo .photo_back {
transform: rotateY(-180deg);
}
.photo .photo_back .desc {
font-size: 14px;
line-height: 20px;
color: #d8536d;
}
.photo .photo_back a {
color: #d8356d;
}
/*照片的居中樣式*/
.photo .center {
z-index: 9999;
left: 50%;
top: 50%;
transform: translate(-50%,-50%) scale(1) rotate(0);
}
/*照片正面的class*/
.photo .front .photo_3d {
transform: translateX(0) rotateY(0);
}
/*照片反面的calss*/
.photo .back .photo_3d {
transform: translateX(100%) rotateY(-180deg);
}
/*導(dǎo)航欄的樣式*/
.nav {
position: absolute;
left: 0;
top: 0;
z-index: 888;
width: 100%;
height: 200px;
padding-top: 10px;
box-sizing: border-box;
text-align: center;
background: -webkit-linear-gradient(top,rgba(0,0,0,.5),transparent);
}
@font-face {
font-family: "icont";
src: url(../font/iconfont.woff) format("woff");
}
.nav .nav_i {
display: inline-block;
width: 30px;
height: 30px;
border-radius: 50%;
background: rgba(255,255,255,.5);
font-family: "icont";
text-align: center;
line-height: 30px;
color: rgba(255,255,255,0);
cursor: pointer;
transform: scale(.5);
transition: 500ms;
}
.nav .active {
color: rgba(255,255,255,1);
transform: scale(.9) rotateY(0);
}
.nav .back {
transform: scale(.8) rotateY(-180deg);
}
/*遮罩層*/
.photo_wall .shade {
position: absolute;
left: 0;
top: 0;
z-index: 2;
width: 100%;
height: 100%;
background: rgba(255,255,255,.7);
display: flex;
justify-content: center;
align-items: center;
}
.photo_wall .hide {
transition: 1s;
opacity: 0;
transform: scale(0) rotateY(360deg);
}
.photo_wall .shade .start {
width: 200px;
height: 60px;
border: 2px solid #d8536d;
border-radius: 10px;
background: rgba(248,229,227,.5);
text-align: center;
font: 22px/60px Arial;
cursor: pointer;
}
js代碼:用到了我昨天在博客上寫(xiě)的工具函數(shù):
// 用來(lái)獲取元素
// 用來(lái)判斷某個(gè)元素是否有某個(gè)class
// 如果沒(méi)有添加
// 如果有就刪除
//獲取元素id class tag all
function M(sele) {
var first = sele.substr(0,1),
isArr = sele.split(' ');//id class tag
if(first==="#"&&isArr.length==1){//id
return document.getElementById(sele.substr(1));
}else{
var arr = Array.from(document.querySelectorAll(sele));
return arr.length == 1?arr[0] :arr;
}
}
//判斷某個(gè)元素是否包含某個(gè)class
function hasClass(obj,cls){
var re = new RegExp(`\\b${cls}\\b`);
if(re.test(obj.className)){
return true;
}else{
return false;
}
}
//給某個(gè)元素添加class
function addClass(obj,cls){
if(!hasClass(obj,cls)){
obj.className += ` ${cls}`;//不要忘了前面的空格哈
}
obj.className = obj.className.trim();//去掉前后空格
}
//給某個(gè)元素刪除class
function rmClass(obj,cls){
var re = new RegExp(`\\b${cls}\\b`);
if(hasClass(obj,cls)){
obj.className = obj.className.replace(re,'')
.replace(/\s{2}/,' ').trim();//去掉前后空格
}
}
提供主要的實(shí)現(xiàn)步驟的js代碼:
(function () {
//---------------------------------------------------------
// 初始化數(shù)據(jù)
var data = dataList,len = data.length;
createPhotos(data);
var n = 0;
//---------------------------------------------------------
// 基本邏輯
M('.shade .start').addEventListener('click',function() {
addClass(M('.shade'),'hide');
M('.photo').style.opacity = 1;
addClass(M(`#photo_0`),'center');
setTimeout(function(){
sortImgs(n);
}, 200);
});
M('.nav_i').forEach((item,i)=>{
item.onclick = function(){
turnImg(M(`#photo_${i}`));
};
});
//---------------------------------------------------------
// 需求函數(shù)化
// 需求1:利用數(shù)據(jù)生成所有html結(jié)構(gòu)
function createPhotos(data) {
var photo_html = M('.photo').innerHTML.split('{{split}}')[0].trim(),
nav_html = M('.nav').innerHTML.trim();
var photos = [],nav = [];
data.forEach((item,i)=>{
var photoTemp = photo_html.replace(/{{id}}/,i)
.replace(/{{src}}/,'src')
.replace(/{{img}}/,item.img)
.replace(/{{caption}}/,item.caption)
.replace(/{{desc}}/,item.desc),
navTemp = nav_html.replace(/{{id}}/,i);
photos.push(photoTemp);
nav.push(navTemp);
});
photos.push(`<div class="nav">${nav.join('')}</div>`);
M('.photo').innerHTML = photos.join('');
}
// 需求2:給所有的圖片排序
function sortImgs(n) {
var photos = M('.photo_i');
initPhotos(photos);
var center = photos.splice(n,1)[0];
addClass(center,'center');
addClass(M(`#nav_${n}`),'active');
// center.addEventListener('click', function(e){
// turnImg(this);
// });
center.onclick = function () {
turnImg(this);
};
// 對(duì)剩余的圖片進(jìn)行隨機(jī)排序
photos.sort(()=>{
return 0.5 - Math.random();
})
var rP = scope(); //返回左右兩側(cè)范圍 從 x - y
// 分成左側(cè)和右側(cè)兩部分
var left = photos.splice(0,Math.ceil((len-1)/2)),
right = photos;
left.forEach((item,i)=>{
item.style.zIndex = rn([0,len]);
item.style.left = rn(rP.L.x) + 'px';
item.style.top = rn(rP.L.y) + 'px';
item.style.transform = `translate(0,0) scale(.9) rotate(${rn([-2160,2160])}deg)`;
});
right.forEach((item,i)=>{
item.style.zIndex = rn([0,len]);
item.style.left = rn(rP.R.x) + 'px';
item.style.top = rn(rP.R.y) + 'px';
item.style.transform = `translate(0,0) scale(.9) rotate(${rn([-2160,2160])}deg)`;
});
}
// 需求3 編寫(xiě)某個(gè)區(qū)間的隨機(jī)整數(shù)
function rn(arr) {
var max = Math.max.apply(null,arr),
min = Math.min.apply(null,arr);
var p = Math.round(Math.random() * (max - min) + min);
//?;
return p;
}
// 需求4 計(jì)算隨機(jī)的范圍
function scope() {
var outer = M('.photo_wall');
var pic = M(`#photo_${rn([0,len-1])}`);
var W = outer.clientWidth,
H = outer.clientHeight,
w = pic.offsetWidth,
h = pic.offsetHeight;
console.log(W,w);
var data = {
L:{
x:[-w/3,W/2 - w/2 - w],
y:[-h/3,H - h*2/3]
},
R:{
x:[W/2 + w/2,W - w*2/3],
y:[-h/3,H - h*2/3]
}
}
return data;
}
// 需求5:控制圖片翻轉(zhuǎn)
function turnImg(ele) {
var cur = ele.id.split('_')[1];
var nav = M(`#nav_${cur}`);
if(!hasClass(ele,'center')){ //如果點(diǎn)的不是當(dāng)前對(duì)應(yīng)的按鈕就重新排序
return sortImgs(cur)
}
if(hasClass(ele,'front')){
//翻轉(zhuǎn)到背面
console.log('現(xiàn)在是正面準(zhǔn)備移除front');
addClass(ele,'back');
console.log(ele.className);
rmClass(ele,'front');
console.log(ele.className);
addClass(nav,'back');
}else{
//翻轉(zhuǎn)到正面
console.log('現(xiàn)在是反面準(zhǔn)備移除back');
addClass(ele,'front');
console.log(ele.className);
rmClass(ele,'back');
console.log(ele.className);
rmClass(nav,'back')
}
}
// 需求6 初始化所有樣式
function initPhotos(objs) {
objs.forEach((item,i)=>{
if(hasClass(item,'center')){
var nav = M(`#nav_${i}`);
rmClass(item,'center');
rmClass(item,'back');
addClass(item,'front');
rmClass(nav,'active');
rmClass(nav,'back');
item.onclick = null;
}
item.style.left = '';
item.style.top = '';
item.style.zIndex = '';
item.style.transform = `translate(-50%,-50%) scale(1.1) rotate(0deg)`;
});
}
})()
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript組合拼接字符串的效率對(duì)比測(cè)試
這篇文章主要介紹了JavaScript組合拼接字符串的效率對(duì)比測(cè)試,本文測(cè)試了IE6、Firefox、Mozilla、Netscape、Opera等瀏覽器,需要的朋友可以參考下2014-11-11
JS實(shí)現(xiàn)判斷圖片是否加載完成的方法分析
這篇文章主要介紹了JS實(shí)現(xiàn)判斷圖片是否加載完成的方法,結(jié)合實(shí)例形式分析了javascript常見(jiàn)的圖片加載完成判斷方法與相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
js如何查找json數(shù)據(jù)中的最大值和最小值方法
這篇文章主要介紹了js如何查找json數(shù)據(jù)中的最大值和最小值方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
小議Function.apply() 之一------(函數(shù)的劫持與對(duì)象的復(fù)制)
小議Function.apply() 之一------(函數(shù)的劫持與對(duì)象的復(fù)制)...2006-11-11
JS實(shí)現(xiàn)點(diǎn)擊按鈕后框架內(nèi)載入不同網(wǎng)頁(yè)的方法
這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊按鈕后框架內(nèi)載入不同網(wǎng)頁(yè)的方法,涉及javascript點(diǎn)擊按鈕載入頁(yè)面的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-05-05
JavaScript基于setTimeout實(shí)現(xiàn)計(jì)數(shù)的方法
這篇文章主要介紹了JavaScript基于setTimeout實(shí)現(xiàn)計(jì)數(shù)的方法,涉及javascript中setTimeout方法的使用技巧,需要的朋友可以參考下2015-05-05

