製作簡單的側邊欄side bar

更新於 發佈於 閱讀時間約 34 分鐘
這次的side bar不一樣了,上一次我好像做的太簡單了,所以完讀率只有14%,我好難過啊(;´д`)ゞ,所以我這準備的內容有多一點。



這次的目標

  • 按鈕的排版
  • 按鈕滑過去會有顏色變化
  • Side bar能夠展開
  • 箭頭能夠移動至被選擇的物件上


宣告

這次我創了兩個檔案 SideBar.tsx 跟 SideBarItem.tsx 並放在SideBar的資料夾

SideBarItem.tsx

const SideBarItem = ({ icon, handleClick, name, }: { icon: React.ReactNode, handleClick: Function, name: string}) => {

return (
<div className={`grid grid-cols-[80px-auto] bg-slate-900 rounded-lg`} onClick={() => { handleClick() }}>

<div className="col-start-1 flex justify-center items-center size-20">
{icon}
</div>
<p className={`col-start-2 hidden text-4xl`}>{name}</p>
</div>
)
}
export default SideBarItem

SideBarItem會有圖片以及展開後的文字,所以我用grid切出兩個區域,接著設定這些tag的顏色、大小

P.S. 展開後的文字等等再處理,所以我目前先讓他hidden

SideBar.tsx

import { IoIosMenu, IoIosCalendar, IoIosHome, IoIosSettings, IoIosHelp } from "react-icons/io";
import { MdOutlineTask } from "react-icons/md";
import SideBarItem from "./SideBarItem";
const SideBar = () => {
return (
<div className={`bg-black text-white p-3`}>
<SideBarItem handleClick={() => { }} icon={<IoIosMenu size="40" />} name=" " />

<SideBarItem handleClick={() => { }} icon={<IoIosHome size="40" />} name='Home' />
<SideBarItem handleClick={() => { }} icon={<MdOutlineTask size="40" />} name='Task' />
<SideBarItem handleClick={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' />
<SideBarItem handleClick={() => { }} icon={<IoIosHelp size="70" />} name='Help' />
<SideBarItem handleClick={() => { }} icon={<IoIosSettings size="40" />} name='Settings' />


<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>

</div>
)
}
export default SideBar

接著在SideBar裡,把所需要的Icon import進來,並將資料都丟進去SideBarItem裡,

最後加入分隔線

<div className={` h-[3px] bg-slate-200 rounded-sm`}></div>

完成後會長這樣

raw-image


排版

按鈕我分成三種

raw-image

所以我讓最外層是grid,分出四格

<div className={`grid grid-rows-[auto_auto_1fr_auto] gap-5 bg-black text-white p-3`}>
<div className="row-start-1">
{/* 第一種 */}
<SideBarItem handleClick={() => { }} icon={<IoIosMenu size="40" />} name=" " />
</div>

<div className="row-start-4">
{/* 分隔線 */}
<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>
</div>

<div className={`row-start-3 space-y-3`}>
{/* 第二種 */}
<SideBarItem handleClick={() => { }} icon={<IoIosHome size="40" />} name='Home' />
<SideBarItem handleClick={() => { }} icon={<MdOutlineTask size="40" />} name='Task' />
<SideBarItem handleClick={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' />
</div>

<div className="row-start-4 space-y-3">
{/* 第三種 */}
<SideBarItem handleClick={() => { }} icon={<IoIosHelp size="70" />} name='Help' />
<SideBarItem handleClick={() => { }} icon={<IoIosSettings size="40" />} name='Settings' />
</div>

</div>

:hover

為了讓滑鼠滑過去要有反應,我加了:hover跟cursor-pointer

在SideBarItem最外層的div上

hover:bg-slate-300 hover:text-black  //顏色(背景、圖片)
hover:rounded-[25px]                 //邊框      
cursor-pointer // 滑鼠鼠標

但這樣一瞬間變化太大,螢幕閃爍會害眼睛很不舒服,所以要在多加這一行

transition-all duration-200 ease-linear

Side Bar 展開

這是整篇最麻煩的,首先先創一個變數isOpen以及func控制Side Bar 的開關。

const [isOpen, setisOpen] = useState<boolean>(false)
function toggleIsOpen(): void{
setisOpen(!isOpen);
}

將toggleIsOpen()丟進去handleClicked

<SideBarItem handleClick={() => { toggleIsOpen() }} icon={<IoIosMenu size="40" />} name=" " />


接著把IsOpen傳入SideBarItem的props

並加入判斷是裡控制寬度

transition-all duration-200 ease-linear //這個也順便丟進去
<p className={`col-start-2 flex items-center text-white text-4xl ${isOpen ? 'w-52 ' : 'w-0'}
overflow-hidden
            transition-all duration-200 ease-linear`}>{name}</p>


<p> overflow

歐對了,如果沒加overflow-hidden,SideBar會變這樣

raw-image

menu也展開

但這展開時,menu也伸長了

raw-image


為了避免這個情況

要給裝menu的div flex屬性之外,還要讓SideBarItem判斷是否需要<p>

SideBarItem.tsx

{name === '' ? <></> :  <p className={`col-start-2 flex items-center text-4xl ${isOpen ? 'w-52 ' : 'w-0'} 
overflow-hidden
transition-all duration-200 ease-linear`}>{name}</p>}


目前的程式

SideBarItem.tsx

const SideBarItem = ({ icon, handleClicked, name, isOpen}: { icon: React.ReactNode, handleClicked: Function, name: string, isOpen: boolean}) => {

return (
<div className={`grid grid-cols-[80px-auto] bg-slate-900 rounded-lg
hover:bg-slate-300 hover:text-black hover:rounded-[25px]
transition-all duration-200 ease-linear cursor-pointer`} onClick={() => { handleClicked() }}>

<div className="col-start-1 flex justify-center items-center size-20">
{icon}
</div>
{name === '' ? <></> : <p className={`col-start-2 flex items-center text-4xl ${isOpen ? 'w-52 ' : 'w-0'}
overflow-hidden
transition-all duration-200 ease-linear`}>{name}</p>}

</div>
)
}
export default SideBarItem

SideBar.tsx

import { IoIosMenu, IoIosCalendar, IoIosHome, IoIosSettings, IoIosHelp } from "react-icons/io";
import { MdOutlineTask } from "react-icons/md";
import SideBarItem from "./SideBarItem";
import { useState } from "react";

const SideBar = () => {
const [isOpen, setisOpen] = useState<boolean>(false)
function toggleIsOpen(): void {
setisOpen(!isOpen);
}

return (
<div className={`grid grid-rows-[auto_auto_1fr_auto] gap-5 bg-black text-white p-3`}>
<div className="row-start-1 flex">
{/* 第一種 */}
<SideBarItem handleClicked={() => { toggleIsOpen();}} icon={<IoIosMenu size="40" />} name="" isOpen={isOpen}/>

</div>

<div>
{/* 分隔線 */}
<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>
</div>


<div className={`row-start-3 space-y-3`}>
{/* 第二種 */}
<SideBarItem handleClicked={() => { }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen}/>
</div>

<div className="row-start-4 space-y-3">
{/* 第三種 */}
<SideBarItem handleClicked={() => { }} icon={<IoIosHelp size="70" />} name='Help' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { }} icon={<IoIosSettings size="40" />} name='Settings' isOpen={isOpen}/>
</div>

</div>
)
}
export default SideBar

箭頭

這個部份我是用position: absolute去做到的,所以我需要一個變數去儲存箭頭目的地的y座標

為此,我用了useState

const [arrowPosition, setArrowPosition] = useState<string>('[8px]')

接著

import箭頭到SideBar.tsx裡

import { FaCaretRight } from "react-icons/fa";

加進去div裡

<div className={`row-start-3 space-y-3 relative`}>

{/* 第二種 */}

<SideBarItem handleClicked={() => { }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen}/>

<SideBarItem handleClicked={() => { }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen}/>

<SideBarItem handleClicked={() => { }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen}/>
<div className={`absolute top-${arrowPosition} left-[-20px] transition-all duration-200 ease-linear`}>
{/* 箭頭 */}
<FaCaretRight size="40"/>
</div>

</div>

P.S.怕會有人不知道,如果子項用了position: absolute且想以父項作為基準點的話,則父項也要有position: relative 或 absolute 或...


目前應該會做到這

raw-image


function onNavItemClicked(position: string): void {
if(arrowPosition === position){
toggleIsOpen() //順便加的功能
return
}
setArrowPosition(position)
//打開某個頁面,還沒寫
}

把func加進去

<div className={`row-start-3 space-y-3 relative`}>
{/* 第二種 */}
<SideBarItem handleClicked={() => { onNavItemClicked('[8px]') }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { onNavItemClicked('[100px]') }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen}/>
<SideBarItem handleClicked={() => { onNavItemClicked('[192px]') }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen}/>

<div className={`absolute top-${arrowPosition} left-[-20px] transition-all duration-200 ease-linear`}>
{/* 箭頭 */}
<FaCaretRight size="40"/>
</div>
</div>


後記

我知道top-[ooopx]這東西其實可以變成數學算式,好像廢話,畢竟我每個Item的大小以及padding都是固定的。但我好懶歐。可能以後多一些功能像是能自由更改icon大小,或是動態增減SideBarItem的時候,在更改好了。


我也知道我一開始的圖的menu是置中的,但後來的是在旁邊,這是因為,這個程式是我重寫的(我一開始寫的時候寫的很亂),結果我後來比較喜歡這種放邊邊的樣式,反正如果想要置中,就多打一個 justify-center在menu的div的flex旁邊

反正就先這樣了,希望你們會喜歡。

P.S. 喜歡的話拜託拜託按個愛心,我會很開心

這是我這次整個的程式

SideBarItem.tsx

const SideBarItem = ({ icon, handleClicked, name, isOpen}: { icon: React.ReactNode, handleClicked: Function, name: string, isOpen: boolean}) => {

return (
<div className={`grid grid-cols-[80px-auto] bg-slate-900 rounded-lg
hover:bg-slate-300 hover:text-black hover:rounded-[25px]
transition-all duration-200 ease-linear cursor-pointer`} onClick={() => { handleClicked() }}>

<div className="col-start-1 flex justify-center items-center size-20">
{icon}
</div>
{name === '' ? <></> : <p className={`col-start-2 flex items-center text-4xl ${isOpen ? 'w-52 ' : 'w-0'}
overflow-hidden
transition-all duration-200 ease-linear`}>{name}</p>}

</div>
)
}
export default SideBarItem


SideBar.tsx

import { IoIosMenu, IoIosCalendar, IoIosHome, IoIosSettings, IoIosHelp } from "react-icons/io";
import { MdOutlineTask } from "react-icons/md";
import SideBarItem from "./SideBarItem";
import { useState } from "react";
import { FaCaretRight } from "react-icons/fa";

const SideBar = () => {
const [isOpen, setisOpen] = useState<boolean>(false)
function toggleIsOpen(): void {
setisOpen(!isOpen);
}

const [arrowPosition, setArrowPosition] = useState<string>('[8px]')
function onNavItemClicked(position: string): void {
if (arrowPosition === position) {
toggleIsOpen()
return
}
setArrowPosition(position)
}

return (
<div className={`grid grid-rows-[auto_auto_1fr_auto] gap-5 bg-black text-white p-3`}>
<div className="row-start-1 flex">
{/* 第一種 */}
<SideBarItem handleClicked={() => { toggleIsOpen(); }} icon={<IoIosMenu size="40" />} name="" isOpen={isOpen} />
</div>

<div>
{/* 分隔線 */}
<div className={` h-[3px] bg-slate-200 rounded-lg`}></div>
</div>

<div className={`row-start-3 space-y-3 relative`}>
{/* 第二種 */}
<SideBarItem handleClicked={() => { onNavItemClicked('[8px]') }} icon={<IoIosHome size="40" />} name='Home' isOpen={isOpen} />
<SideBarItem handleClicked={() => { onNavItemClicked('[100px]') }} icon={<MdOutlineTask size="40" />} name='Task' isOpen={isOpen} />
<SideBarItem handleClicked={() => { onNavItemClicked('[192px]') }} icon={<IoIosCalendar size="40" />} name='Calendar' isOpen={isOpen} />

<div className={`absolute top-${arrowPosition} left-[-20px] transition-all duration-200 ease-linear`}>
{/* 箭頭 */}
<FaCaretRight size="40" />
</div>
</div>

<div className="row-start-4 space-y-3">
{/* 第三種 */}
<SideBarItem handleClicked={() => { }} icon={<IoIosHelp size="70" />} name='Help' isOpen={isOpen} />
<SideBarItem handleClicked={() => { }} icon={<IoIosSettings size="40" />} name='Settings' isOpen={isOpen} />
</div>

</div>
)
}
export default SideBar


留言
avatar-img
留言分享你的想法!
avatar-img
Web開發日誌
2會員
5內容數
這是一篇帶新手快速入門的Web教學。會帶你製作第一個todo-list,雖然看起來很簡單,但其實蘊含很多觀念。
你可能也想看
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
有時候回顧我部落格的文章,發現往往自己嘔心瀝血之作點閱數少的可憐... 反而隨意寫、在我眼中跟個版廢文沒兩樣的作品點閱率很高,一直感到很疑惑。我想知道怎樣的文章受讀者喜愛? 搜尋引擎又會推薦給讀者什麼樣子的文章?
Thumbnail
有時候回顧我部落格的文章,發現往往自己嘔心瀝血之作點閱數少的可憐... 反而隨意寫、在我眼中跟個版廢文沒兩樣的作品點閱率很高,一直感到很疑惑。我想知道怎樣的文章受讀者喜愛? 搜尋引擎又會推薦給讀者什麼樣子的文章?
Thumbnail
前言與本文適合對象 本篇電子書是在幫目前已經有在經營的自媒體在更往上提升,所以不會教如何架設網站、主機操作與設定等。 如果真的想要自己架設網站的可以參考:網站帶路姬 想要找部落格主機代管的可以找:金城事務所 我自己的建議是架站跟主機選擇都不是創作者需要學習的,可以交給專業的服務商幫你
Thumbnail
前言與本文適合對象 本篇電子書是在幫目前已經有在經營的自媒體在更往上提升,所以不會教如何架設網站、主機操作與設定等。 如果真的想要自己架設網站的可以參考:網站帶路姬 想要找部落格主機代管的可以找:金城事務所 我自己的建議是架站跟主機選擇都不是創作者需要學習的,可以交給專業的服務商幫你
Thumbnail
這次的side bar不一樣了,上一次我好像做的太簡單了,所以完讀率只有14%,我好難過啊(;´д`)ゞ,所以我這準備的內容有多一點。 這次的目標 按鈕的排版 按鈕滑過去會有顏色變化 Side bar能夠展開 箭頭能夠移動至被選擇的物件上 宣告 這次我創了兩個檔案 SideBa
Thumbnail
這次的side bar不一樣了,上一次我好像做的太簡單了,所以完讀率只有14%,我好難過啊(;´д`)ゞ,所以我這準備的內容有多一點。 這次的目標 按鈕的排版 按鈕滑過去會有顏色變化 Side bar能夠展開 箭頭能夠移動至被選擇的物件上 宣告 這次我創了兩個檔案 SideBa
Thumbnail
這篇文章教你如何製作側邊欄,包括準備工作、HTML和CSS的部分,還有一些互動效果。文章涵蓋了連結、圖片、超連結、大小、顏色、排版、flex和滑鼠互動等內容。
Thumbnail
這篇文章教你如何製作側邊欄,包括準備工作、HTML和CSS的部分,還有一些互動效果。文章涵蓋了連結、圖片、超連結、大小、顏色、排版、flex和滑鼠互動等內容。
Thumbnail
本文介紹如何使用UINavigationBarAppearance調整四種場景下的UI外觀,並探討客製化返回鍵UI又保留返回手勢的做法,可以有效地客製化NavigationBar的外觀,並避免一些NG作法。
Thumbnail
本文介紹如何使用UINavigationBarAppearance調整四種場景下的UI外觀,並探討客製化返回鍵UI又保留返回手勢的做法,可以有效地客製化NavigationBar的外觀,並避免一些NG作法。
Thumbnail
這幾天我一直沒有發布新文章,是因為我一直在研究電子報訂閱的事情。其實架設一個網站不難! 佈置好首頁的版面也不難! 對我來說反而是[電子報訂閱]難倒我了。 光是找一個外掛看人家推薦一堆電子報程式,我光選就消耗掉我2天時間了! 總算選好要使用的程式下在完畢,才是接下來困難的地方。 我好死不死選到了
Thumbnail
這幾天我一直沒有發布新文章,是因為我一直在研究電子報訂閱的事情。其實架設一個網站不難! 佈置好首頁的版面也不難! 對我來說反而是[電子報訂閱]難倒我了。 光是找一個外掛看人家推薦一堆電子報程式,我光選就消耗掉我2天時間了! 總算選好要使用的程式下在完畢,才是接下來困難的地方。 我好死不死選到了
Thumbnail
礙於我的個人網站二月跟三月都讓我去跟主機商詢問問題,外加未來等過了優惠價,年度的支出會增加,還有每次看到旅遊、美食部落客都有使用,上次還有店家問我怎麼沒有想用pixnet,讓我覺得有點無法回嘴。
Thumbnail
礙於我的個人網站二月跟三月都讓我去跟主機商詢問問題,外加未來等過了優惠價,年度的支出會增加,還有每次看到旅遊、美食部落客都有使用,上次還有店家問我怎麼沒有想用pixnet,讓我覺得有點無法回嘴。
Thumbnail
剛剛檢視我的文章 追蹤人數 好難看 沒有打中大眾心 反而是交友軟體 點擊率最高 那我接下來一系列 狂分享交友軟體 骯髒事? 為了流量? 好的交友軟體 是個趨勢 因為現代人 太寂寞 但太多交友軟體 充滿情色 亂 愛滋? 軟體只是個媒介 識人 需要時間 觀
Thumbnail
剛剛檢視我的文章 追蹤人數 好難看 沒有打中大眾心 反而是交友軟體 點擊率最高 那我接下來一系列 狂分享交友軟體 骯髒事? 為了流量? 好的交友軟體 是個趨勢 因為現代人 太寂寞 但太多交友軟體 充滿情色 亂 愛滋? 軟體只是個媒介 識人 需要時間 觀
Thumbnail
產出一篇 SEO 文章時應該要注意什麼?如何下筆? 此篇文章以我過去的經驗,分享有關 SEO 文章內容產出的架構與邏輯。
Thumbnail
產出一篇 SEO 文章時應該要注意什麼?如何下筆? 此篇文章以我過去的經驗,分享有關 SEO 文章內容產出的架構與邏輯。
Thumbnail
好的網站應該要有直覺性網站版面配置與迅速的網頁載入速度,需要具備好的使用者體驗,進而提升SEO關鍵字排名。隨著時代演進,網站設計經歷了五個世代的變革,並逐漸注重UI/UX的整合。本文說明網站版面的重要觀念、配色的重要性、動畫對網站行銷的影響,以及改善網站PSI分數的優化方法。
Thumbnail
好的網站應該要有直覺性網站版面配置與迅速的網頁載入速度,需要具備好的使用者體驗,進而提升SEO關鍵字排名。隨著時代演進,網站設計經歷了五個世代的變革,並逐漸注重UI/UX的整合。本文說明網站版面的重要觀念、配色的重要性、動畫對網站行銷的影響,以及改善網站PSI分數的優化方法。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News