第十一課:useRef與gsap介紹與應用,hover彈跳comment視窗與Slider 滑動banner特效
useRef一樣跟useState等等是React Hooks其中一個,所以使用上也要先
import React, { useRef } from 'react'
而useRef的概念也跟useState有點像,都屬hook系列,hook出一整個div。一開始都會給他一個初始值,比如說,下方設立一個變數comments為null值
let comments = useRef(null)
useRef的用途很廣可以是為了做一些網頁的互動特效或是功能,所以就跟useState來設置打開視窗等等的一樣,useRef也可以專門設置跳出視窗,但這兩個差別是useState在做出的特效上是根據useState得初始值,如按鈕會有onClick並通過點擊來更改boolean的值,紀錄開與關去做轉換,詳細的部分在前面實作上我們也練習過很多次了,而useRef可以把div拉成一個變數去做運算跟特效,在操作上配合很多函數可以比useState更活用,也可以想成useState不是專門在做互動特效處理的,如果需要大量的開關特效或是互動,useRef直接拉出區塊作用比較快,而不是useState下去做存取、修改資料等等的,總之這兩個方式一定都可以運用,只是要看情況用哪個比較適當,而下面就會實作用useRef配合gsap來做hover特效(hover就是不用點擊,滑鼠過去就有就顯示了),跟useState的點擊開啟視窗不一樣的操作。
而gsap則是一個可以配合useRef來專門處理動畫與特效的套件,所以一樣必須都先導入
import { gsap } from "gsap";
因為他是專門對網頁特效做處理的套件,所以這邊會來介紹他的gsap.from跟
gsap.to,做動畫一定都會有時間序的問題,所以這邊很有趣的gsap都能幫你做到,
下方就是混用後常見的樣子
變數這邊也可以直接提classname的部分不一定要配合用useRef
這邊有更詳細得官網解釋
https://greensock.com/docs/v3/GSAP/gsap.from()
所以簡單來說我們要用這個方式來做彈跳留言視窗
首先要先製作沒有彈跳動作的視窗設計,剛好我們有預留hotelImgWrapper的空間可以放popupcomment.div
popupcomment.div
<div className="popupcomment" >
<div className='commentInfo' ref={e => (comments = e)} >
<button className='commentRate'>
9.5
</button>
傑出<br />
1,223則評論
</div>
</div>
然後我這邊的設計目的我想要讓popupcomment這個div是一個感知區域,當他被碰到時會跳出評論,然後commentInfo才是實際的顯示視窗。
然後再設計scss, 然後popupcomment一樣可以配合
position: absolute;與.hotelImgWrapper {position: relative;}讓他用絕對位置定位,跟之前的headerSearchBar一樣做法
.hotelImgWrapper {position: relative;}
.popupcomment {
position: absolute; //觸碰區
top: 0px;
left: 0px;
z-index: 2; //這個代表圖層至少比下面img照片都還要多一層
//就是最上方圖層才不會被蓋住看不見
height: 300px;
width: 300px;
padding: 10px;
background-color: rgba(31, 30, 30, 0.559);
.commentInfo {
display: flex;
height: 60px;
width: 250px;
border-radius: 2px;
background-color: #fff;
align-items: center;
justify-content: flex-start;
gap: 5px;
.commentRate {
margin-left: 5px;//左邊間距空一下
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
font-weight: 700;
height: 40px;
width: 40px;
border: none;
background-color: var(--primary-color);
border-radius: 5px 5px 5px 0px;
}
}
}
好了應該就會看到觸控區的遮罩了,到時候會把那個底色刪除,留下他的區域可以觸控,所以終於可以進到hover功能部分
首先先了解整個hover過程,原本要先消失後來hover進去 視窗出現 hover出去 視窗消失 這一套過程,所以先將原本的popupcomment等先隱藏 他沒有底色,跟讓commentInfo整個消失 所以用display:none,來表示觸控區一直都在,但彈跳視窗會消失與出現。
handleHover functions
const handleHover = e => {
gsap.to(comments, {
css: {
display: "flex",
opacity: 1,
},
ease: "power3.inOut"//拉出來跟css並行
})
}
//離開特效
const handleHoverExit = e => {
gsap.to(comments, {
css: {
display: "none",
opacity: 0,
},
ease: "power3.inOut"//拉出來跟css並行
})
}
ease: "power3.inOut" 圖上面得 ease: "power3.inOut"放在css內會產生warning,所以製圖上忘記改,但實際上記得以上面這種示範將它拉出來。
配合好函數後,再將它寫入div popupcomment 觸控區 這邊使用onMouseEnter與onMouseOut預設函數來連結到我們的hover函數
<div className="popupcomment" onMouseEnter={e => handleHover(e)} onMouseOut={e => handleHoverExit(e)} >
<div className='commentInfo' onMouseEnter={e => handleHover(e)} ref={e => (comments = e)} >
commentInfo這邊也寫onMouseEnter是因為 這樣防止滑鼠從popupcomment觸控區滑到commentInfo時會觸發handleHoverExit,而讓視窗消失,因為對popupcomment來說我們滑鼠的確是離開了他這一層到較高一層的commentInfo上,所以離開自己區域都會觸發。
附上github連結
hotel.jsx
hotel.scss
首先先來這次要做的跟前面練習過的useState開啟彈跳視窗一樣,只是這次會更進階,我們要來做可以左右點選換照片的彈跳視窗,也就是常見的slider
如:w3shool的基本slider練習
https://www.w3schools.com/w3css/w3css_slideshow.asp
介面UI分析
首先我們可以將slider.div寫在hotel.jsx 的navabar下方,但在hotelContainer的上方內,如圖因為是彈跳視窗以不要影響到其他component為主,slider到時候也會是滿版,所以也要直接寫在hotel.div內,如圖
附上silder.div
<div className="slider">
<div className="sliderWrapper">
<div className="wrapperTitle">
<div className='TitleName'>台南微醺文旅</div>
<span className="CloseSign">關閉
<FontAwesomeIcon icon={faXmark} /></span>
</div>
<div className="wrapperBody">
<FontAwesomeIcon icon={faAngleLeft} className="arrow" />
<img src={photos[0].src} alt="" />
<FontAwesomeIcon icon={faAngleRight} className="arrow" />
</div>
</div>
</div>
中間photos[0]是先放一張照片準備,並之後會改成變數,當左右滑動箭頭被點擊時,換成該照片的index,所以原理十分的簡單,就是點擊右鍵icon,照片的img Index+1 就換成下一張照片了,點擊左鍵icon,照片的img Index-1 就換成上一張照片了,
並簡單的排版一下
附上slider.scss
.slider {
position: fixed; //要讓他黏在整個頁面上
top: 0;
left: 0;//並重新定義黏在頁面上的起點才能讓下面滿版
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.613); //後面黑色遮罩的部分
z-index: 999; //使他圖層數最高,這樣一定會是在最上面
display: flex;
align-items: center;
justify-content: center;//在水平上下置中一下
.sliderWrapper {
border-radius: 2px;
width: 90%;
height: 80%;//簡單設置一下視窗的長寬
background-color: #fff;
}
}
.wrapperTitle {
width: 100%;
height: 10%;
display: flex;
justify-content: space-between; //將飯店名與 關閉左右分開
align-items: center;
border-bottom: 1px solid lightgray; //底線分隔線
.TitleName {
font-size: 20px;//排版一下飯店名
margin: 0 20px;
}
.CloseSign {//排版一下關閉icon等等的
margin: 0 20px;
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
svg {
font-size: 30px;
}
&:hover {
color: var(--primary-color);
}
}
}
.wrapperBody {
height: 90%;
display: flex;
align-items: center;
justify-content: center;
img {
width: 600px;
height: 500px;
border-radius: 5px;
user-select: none; //防止拖拉也防止到時候點擊的選取效果
}
.arrow {
margin: 0 20px;
font-size: 40px;
color: gray;
cursor: pointer;
&:hover {
color: var(--primary-color);
}
}
}
這樣好了以後應該就有上面彈跳視窗的樣子了,再來就要處理特效部分,這邊大家都可以繼續優化hotelPage 彈跳視窗UI設計或是加上特效。
上面有提到簡單原理,所以第一個我們可以先處理開關彈跳視窗的部分,一樣使用useState配搭boolean的true和false
const [openSlider, setOpenSlider] = useState(false);
跟我們有稍微提到,點擊時,換成該照片的index以顯示,跟左右點擊滑動,所以我們也必須紀錄Slider的index為主要目地,所以一樣使用useState紀錄
const [sliderIndex, setSiderIndex] = useState(0);
預設為0就第一張照片的意思,所以大致上ok後,原本開啟彈跳視窗的方式是使用onClick是搭配(!onSlider)來改動開與關,如之前做過的calendar方式
onClick={() => setOpenCalendar(!openCalendar)}
但這邊我們不這樣做,因為我們要更進階在點擊的時候,再加入新的傳入變數slider的index,因為要同時做兩件事,開啟與傳入index變數
所以這邊設置了一個新的函數clickSilde把他們都包起來,如圖
const clickSlider = (index) => {
setOpenSlider(true);
setSiderIndex(index);
}
onClick={() => clickSlider(i)}
這邊不使用setOpenSlider(!openSlider);是因為開關處不是同一個,打開是點擊照片,但關起是按視窗的關閉,所以用不到!
轉換boolean
接下來我們將先完整開關視窗,所以將整個slider先用{ && } 一樣如同calendar的方式,忘記可以去複習,將整個div包裹起來
那因為應該是useState初始值是false的所以等於隱藏整個slider,然後可以點點看照片,看有沒有跑出視窗,並小心不要點到popupcomment的觸控區域上應該是都可以打開slider視窗,或是也可以一樣onClick放入popupcomment上讓他點擊也有反應都可以,並再將原本div.wrapperBody裡面的{photos[0].src}
換成{photos[sliderIndex].src}
讓他可以點擊照片打開就是這張照片,還有點擊關閉或是icon X 可以關閉視窗
onClick={()=>setOpenSlider(false)}
完成後要開始處理點右鍵與點左鍵視窗移動照片的函數,所以一樣我們要有觸發左鍵右鍵的函數,首先這個函數會紀錄新的index,所以我宣告newSliderInde與紀錄lastPicutre是照片array資料個數-1的原因是有兩種情況,當今天往右點擊到最後一張照片了理當來說就不能往下了,要跳回最後一張,所以今天往左點擊到第一張照片也是,必須跳到第一張,讓照片可以是無限點擊的迴圈
const slideDirection = (direction) => {
let newSliderIndex;
let lastPicutre = photos.length - 1
if (direction == "left") {
sliderIndex == 0 ? newSliderIndex = lastPicutre : newSliderIndex = sliderIndex - 1
setSiderIndex(newSliderIndex)
} else {
sliderIndex == lastPicutre ? newSliderIndex = 0 : newSliderIndex = sliderIndex + 1
setSiderIndex(newSliderIndex)
}
}
所以這邊使用了兩個 ? : (if esle) 第一個是當今天點擊的是左鍵direction == "left",那如果是這種sliderIndex == 0情況,代表點到第一張照片了必須跳回最後一張,所以回傳新的index 為lastPicutre ,newSliderIndex = lastPicutre,如果不是就繼續減index並最後紀錄進去,如下圖
所以完成的div應該長這樣
{ openSlider &&
<div className="slider">
<div className="sliderWrapper">
<div className="wrapperTitle">
<div className='TitleName'>台南微醺文旅</div>
<span className="CloseSign" onClick={()=>setOpenSlider(false)}>關閉
<FontAwesomeIcon icon={faXmark} /></span>
</div>
<div className="wrapperBody">
<FontAwesomeIcon icon={faAngleLeft} className="arrow" onClick={()=>slideDirection("left")} />
<img src={photos[sliderIndex].src} alt="" />
<FontAwesomeIcon icon={faAngleRight} className="arrow" onClick={()=>slideDirection("right")}/>
</div>
</div>
</div>
}
恭喜你這邊就差不多完成了,最後的最後我們來做個login/register做結尾
這邊就不特別講解了,因為相信太簡單不太需要講解XD
附上完成圖
但這邊值得講的是navbar的改動會比較大,因為跟之前做的navbar在註冊與登入這邊很明顯的不用登入與註冊按鈕這些,所以一樣要用component傳props的方式來做,就跟那時候在做HomePage在做
<Announcement type={"Upper half"}/>
時一樣,忘記的話可以去前面複習,我們要做一個專屬login與auth的navbar,首先先看減少那些部分
所以需要讓navbar一樣有兩種呈現的方式,一個是原本的,一個是type=auth的形式,專為login與register的簡化版navbar
<Navbar type={"auth"}/>
所以loginPage與registerPage導入Navbar時要輸入上面這一樣,用props導入來區分
最後一步是login與register的大量link連接,包括了app.jsx的route與navbar的註冊登入按鈕,跟loginPage與RegisterPage的各自"註冊&創建一個張號"與"已有張號?按這裡登入"等連結,附上各自的連結
loginPage.div
loginPage.scss
registerPage.div
registerPage.scss
這邊我都將這個login/register放在page頁面,屬於分頁的一種
整體的UI完整版github連結
challenge.day11.version
今天終於結束了長達11天的UI網站版面設計,並開始學了一些網站炫技的部分,這部分也非常實用很適合深入研究加入各種motion與套件,然後玩出一套最吸睛的版面,彈跳式視窗、hoverIn還是click特效,都是讓你的網站與別人與眾不同的開始,明天將會進入新篇章nodejs的Api連接,要來介紹如何使用mongoDB與後端連接,將會網站是否能連動最重要的課題!