日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

React全家桶构建一款Web音乐App实战(九):皮肤切换

發布時間:2023/12/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 React全家桶构建一款Web音乐App实战(九):皮肤切换 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這一節是這款React Web音樂App實戰的最后一節:皮膚切換功能。皮膚切換是Web音樂App中一個與核心無關的功能,加入這個功能可以為應用增添不少趣味性

實現思路

實現皮膚切換功能的大致原理就是將樣式提取出來作為一份單獨的樣式,給需要做皮膚切換的dom元素添加上這些樣式,切換樣式的時候替換指定樣式屬性值,再動態插入到DOM中,使CSS樣式生效

準備

在做這個功能前,需要新加入一些小圖標,新增的小圖標在下圖紅色方框中標出

在第一節已經制作了一份字體圖標文件和樣式,這次加入新的圖標需要使用svg圖片重新來制作新的字體圖標文件和樣式,和第二節一樣使用icomoon這個網站來制作,詳細步驟見第一節字體圖標制作

字體圖標制作完成后會有一份icomoon.zip包,解壓后將里面fonts目錄下的4個文件重命名為icomusic,然后進入到項目中將src/assets/stylus/fonts目錄下面的4個文件替換成剛剛重命名的4個字體圖標文件。回到src/assets/stylus下面的font.styl中,打開解壓后的目錄中的style.css,復制里面的.icon-開頭的所有樣式,替換掉font.styl中.icon-開頭的樣式

提取樣式

在做皮膚切換功能前先將需要切換的樣式提取出來。這里因為是所有功能做完之后再做皮膚切換功能,所以提取樣式是非常繁瑣的過程。以下是列舉出所有需要提取樣式的地方

文件位置樣式名稱
app.styl.app,.app-header,.music-tab,.active
recommend/recommend.styl.title,.album-wrapper,.album-name
album/album.styl.music-album,.album-wrapper,.song-name,.song-singer,.album-title
ranking/ranking.styl.ranking-wrapper,.ranking-title,.index,.singer
ranking/rankinginfo.styl.ranking-info,.ranking-wrapper,.song-name,.song-singer,.ranking-title
singer/singer.styl.music-singer,.singer-wrapper,.song-name,.song-singer
singer/singerlist.styl.nav,a.choose,.singer-name
search/search.styl.search-box,.search-input,.title,.hot-item,.album-wrapper .song,.singer,.song-wrapper .song
play/miniplayer.styl.mini-player,.player-img,.singer,.player-right,.filter:after

將以上樣式中的colorbackground-color屬性注釋掉

實現皮膚切換的做法有很多種。可能你見過將不同的樣式寫在一份css文件里面,多少種皮膚就有多少份css文件,使用某種皮膚的時候將其引入,這種做法重復定義的屬性太多,大量的css屬性冗余,復用性太差。還有可能你見過寫一份樣式文件,樣式屬性的值使用指定字符占位,需要切換皮膚的時候請求這個文件,拿到樣式文本后,替換掉占位的字符為實際的屬性值,然后插入到HTML DOM中,這種做法每次切換皮膚的時候都要發送一次請求

這里將利用字符串模板返回一個樣式文本字符串,樣式屬性值使用對象的屬性占位,將css屬性值定義成對象屬性值,在切換皮膚的時候傳入指定的對象。在util目錄下新建skin.js,先定義用來保存樣式屬性值的對象skin,和一個返回樣式文本的方法getSkinStyle

const skin = {};skin.coolBlack = {appColor: "#DDDDDD",appBgColor: "#212121",/* 首頁header */appHeaderColor: "#FFD700",appHeaderBgColor: "transparent",/* 首頁tab */tabColor: "#DDDDDD",tabBgColor: "transparent",/* 最新專輯 */albumColor: "rgba(221, 221, 221, 0.7)",albumNameColor: "#FFFFFF",/* 排行榜 */rankingWrapperBgColor: "#333333",rankingSingerColor: "rgba(221, 221, 221, 0.7)",/* 搜索 */searchBgColor: "#212121",searchBoxBgColor: "#333333",searchBoxWrapperBgColor: "#212121",searchTitleColor: "#FFD700",searchHotColor: "#DDDDDD",searchHotBorderColor: "transparent",searchResultBorderColor: "transparent",/* 詳情 */detailBgColor: "#212121",detailSongColor: "#FFFFFF",detailSingerColor: "rgba(221, 221, 221, 0.7)",/* mini播放器 */miniPlayerBgColor: "#333333",miniImgBorderColor: "rgba(221, 221, 221, 0.3)",miniProgressBarBgColor: "rgba(0, 0, 0, 0.3)",miniRightColor: "#FFD700",miniSongColor: "#FFFFFF",activeColor: "#FFD700" };let getSkinStyle = (skin) => {if (!skin) {return "";}return `.skin-app {color: ${skin.appColor};background-color: ${skin.appBgColor};}.skin-app-header {color: ${skin.appHeaderColor};background-color: ${skin.appHeaderBgColor};}.skin-music-tab {color: ${skin.tabColor};background-color: ${skin.tabBgColor};}.skin-recommend-title {color: ${skin.activeColor};}.skin-album-wrapper {color: ${skin.albumColor};}.skin-album-wrapper .album-name {color: ${skin.albumNameColor}}.skin-ranking-wrapper {background-color: ${skin.rankingWrapperBgColor};}.skin-ranking-wrapper .ranking-title {color: ${skin.albumNameColor};}.skin-ranking-wrapper .singer {color: ${skin.rankingSingerColor};}.skin-music-singers .choose {color: ${skin.activeColor} !important;border: 1px solid ${skin.activeColor} !important;}.skin-search {background-color: ${skin.searchBgColor};}.skin-search .title {color: ${skin.searchTitleColor};}.skin-search .hot-item {border: 1px solid ${skin.searchHotBorderColor};color: ${skin.searchHotColor};background-color: ${skin.searchBoxBgColor};}.skin-search-box {background-color: ${skin.searchBoxBgColor};}.skin-search-box input {color: ${skin.appColor};}.skin-search-box-wrapper {background-color: ${skin.searchBoxWrapperBgColor};}.skin-search-result .singer {color: ${skin.albumColor};}.skin-search-result .singer-wrapper .singer {color: ${skin.appColor};}.skin-search-result .singer-wrapper .info {color: ${skin.albumColor};}.skin-detail-wrapper {background-color: ${skin.detailBgColor};}.skin-detail-wrapper .song-name {color: ${skin.detailSongColor};}.skin-detail-wrapper .song-singer {color: ${skin.detailSingerColor};}.skin-mini-player {background-color: ${skin.miniPlayerBgColor};}.skin-mini-player .player-img {border: 2px solid ${skin.miniImgBorderColor};}.skin-mini-player .progress-bar {background-color: ${skin.miniProgressBarBgColor} !important;}.skin-mini-player .progress {background-color: ${skin.miniRightColor} !important;}.skin-mini-player .player-right {color: ${skin.miniRightColor};}.skin-mini-player .song {color: ${skin.miniSongColor};}.skin-mini-player .singer {color: ${skin.detailSingerColor};}.music-album, .ranking-info, .music-singer {background-color: ${skin.detailBgColor};}.nav-link.active {color: ${skin.activeColor} !important;border-bottom: 2px solid ${skin.activeColor};}`; }; 復制代碼

skin.coolBlack中的顏色值是從上述表格中的樣式中提取出來的

編寫一個把樣式插入到HTML DOM中的方法setSkinStyle

let setSkinStyle = (skin) => {let styleText = getSkinStyle(skin);let oldStyle = document.getElementById("skin");const style = document.createElement("style");style.id = "skin";style.type = "text/css";style.innerHTML = styleText;oldStyle ? document.head.replaceChild(style, oldStyle) : document.head.appendChild(style); }; 復制代碼

在skin.js中調用setSkinStyle并傳入skin.coolBlack

// 設置皮膚 setSkinStyle(skin.coolBlack); 復制代碼

最后導出skin對象和setSkinStyle,后續使用

export {skin, setSkinStyle} 復制代碼

在程序運行的時候需要把這些樣式插入到HTML DOM中,所以在Root.js中導入skin.js

import "../util/skin" 復制代碼

接下來把提取出來的樣式添加到各個組件中的標簽上,下表列出來樣式添加的位置

組件位置元素需要添加的樣式
App.jsdiv.app
div.app-header
div.music-tab
.skin-app
.skin-app-header
.skin-music-tab
recommend/Recommend.jsdiv.album-wrapper
h1.title
.skin-album-wrapper
album/Album.jsdiv.album-wrapper.skin-detail-wrapper
ranking/Ranking.jsdiv.ranking-wrapper.skin-ranking-wrapper
ranking/RankingInfo.jsdiv.ranking-wrapper.skin-detail-wrapper
singer/SingerList.jsdiv.music-singers.skin-music-singers
singer/Singer.jsdiv.singer-wrapper.skin-detail-wrapper
search/Search.jsdiv.music-search
div.search-box-wrapper
div.search-box
div.search-result
.skin-search
.skin-search-box-wrapper
.skin-search-box
.skin-search-result
play/MiniPlayer.jsdiv.mini-player

應用上以上樣式后,如果沒有問題整體外觀和之前會相差無幾

自定義皮膚

除了以上的默認皮膚外,再定義幾種樣式。先定義一個芒果顏色做皮膚色,另外再使用酷狗、網易、QQ音樂三大音樂播放器的主色做皮膚色,接下來擴展skin對象,在skin.js中加入以下代碼

  • 芒果黃
  • skin.mangoYellow = {appColor: "#333333",appBgColor: "#F8F8FF",appHeaderColor: "#FFFFF0",appHeaderBgColor: "#FFA500",tabColor: "rgba(0, 0, 0, .7)",tabBgColor: "#FFFFFF",albumColor: "rgba(0, 0, 0, 0.6)",albumNameColor: "#333333",rankingWrapperBgColor: "#FFFFFF",rankingSingerColor: "rgba(0, 0, 0, 0.5)",searchBgColor: "#FFFFFF",searchBoxBgColor: "#FFFFFF",searchBoxWrapperBgColor: "#F8F8FF",searchTitleColor: "rgba(0, 0, 0, .7)",searchHotColor: "#000000",searchHotBorderColor: "rgba(0, 0, 0, .7)",searchResultBorderColor: "#E5E5E5",detailBgColor: "#F8F8FF",detailSongColor: "#000000",detailSingerColor: "rgba(0, 0, 0, 0.6)",miniPlayerBgColor: "#FFFFFF",miniImgBorderColor: "#EEEEEE",miniProgressBarBgColor: "rgba(0, 0, 0, 0.1)",miniRightColor: "#FFD700",miniSongColor: "#333333",activeColor: "#FFA500" }; 復制代碼
  • 酷狗藍
  • skin.kuGouBlue = Object.assign({}, skin.mangoYellow, {appHeaderBgColor: "#2CA2F9",activeColor: "#2CA2F9",searchTitleColor: "#2CA2F9",miniRightColor: "#2CA2F9" }); 復制代碼
  • 網易紅
  • skin.netBaseRed = Object.assign({}, skin.mangoYellow, {appHeaderBgColor: "#D43C33",activeColor: "#D43C33",searchTitleColor: "#D43C33",miniRightColor: "#D43C33" }); 復制代碼
  • QQ綠
  • skin.qqGreen = Object.assign({}, skin.mangoYellow, {appHeaderBgColor: "#31C27C",activeColor: "#31C27C",searchTitleColor: "#31C27C",miniRightColor: "#31C27C" }); 復制代碼

    以上對象中,后三個繼承自mangoYellow對象,然后將不同屬性值覆蓋,可以很好的減少了相同樣式的冗余

    皮膚切換實現

    為了實現皮膚切換,需要編寫一個皮膚中心組件,皮膚中心從App組件中的菜單列表進入

    在App.js中添加constructor,并初始化控制顯示菜單的state屬性menuShow

    constructor(props) {super(props);this.state = {menuShow: false};} 復制代碼

    header.app-header元素中添加圖標i.icon-et-more,并添加點擊事件處理,點擊后將menuShow設置為true

    <header className="app-header skin-app-header"><i className="icon-et-more app-more" onClick={() => {this.setState({menuShow: true});}}></i><img src={logo} className="app-logo" alt="logo" /><h1 className="app-title">Mango Music</h1> </header> 復制代碼

    樣式如下

    App.styl

    .app-headerheight: 55pxline-height: 55px/*color: #FFD700*/text-align: centerposition: relative.app-moreposition: absolutetop: 15pxleft: 15pxfont-size: 20px 復制代碼

    在components目錄下新建setting目錄,然后新建Menu.jsmenu.styl

    Menu.js

    import React from "react" import {CSSTransition} from "react-transition-group"import "./menu.styl"class Menu extends React.Component {constructor(props) {super(props);}close = () => {this.props.closeMenu();}render() {return (<div><CSSTransition in={this.props.show} timeout={300} classNames="fade"onEnter={() => {this.refs.bottom.style.display = "block";}}onExited={() => {this.refs.bottom.style.display = "none";}}><div className="bottom-container" onClick={this.close} ref="bottom"><div className="bottom-wrapper"><div className="item">皮膚中心</div><div className="item-close" onClick={this.close}>關閉</div></div></div></CSSTransition></div>);} }export default Menu 復制代碼

    menu.styl請在源碼中查看

    在App.js中導入Menu組件

    import MusicMenu from "./setting/Menu" 復制代碼

    放置在如下位置,并傳遞showcloseMenu兩個props,其中show用來控制Menu組件的顯示和隱藏動畫,closeMenu傳遞給Menu,當點擊取消或背景遮罩時關閉自身

    <Router><div className="app skin-app">...<MusicPlayer/><MusicMenu show={this.state.menuShow}closeMenu={() => {this.setState({menuShow: false});}} /></div> </Router> 復制代碼

    我們把當前皮膚的key值交給Redux,在Skin組件中列出所有的皮膚,將Redux中保存的key對應的皮膚打上對鉤的標記,點擊單個皮膚可以設置當前皮膚。給皮膚添加Redux屬性skin,actionType,action和reducer

    actionTypes.js

    export const SET_SKIN = "SET_SKIN"; 復制代碼

    actions.js

    export function setSkin(skin) {return {type:ActionTypes.SET_SKIN, skin}; } 復制代碼

    reducers.js

    const initialState = {skin: "coolBlack",... };//設置皮膚 function skin(skin = initialState.skin, action) {switch (action.type) {case ActionTypes.SET_SKIN:return action.skin;default:return skin;} }...const reducer = combineReducers({skin,... }); 復制代碼

    在setting目錄下新建Skin.js和skin.styl

    import React from "react" import {CSSTransition} from "react-transition-group"import "./skin.styl"class Skin extends React.Component {constructor(props) {super(props);this.skins = [{key: "mangoYellow", name: "芒果黃", color: "#FFD700"},{key: "coolBlack", name: "炫酷黑", color: "#212121"},{key: "kuGouBlue", name: "酷狗藍", color: "#2CA2F9"},{key: "netBaseRed", name: "網易紅", color: "#D43C33"},{key: "qqGreen", name: "QQ綠", color: "#31C27C"}]}render() {return (<CSSTransition in={this.props.show} timeout={300} classNames="pop"onEnter={() => {this.refs.skin.style.display = "block";}}onExited={() => {this.refs.skin.style.display = "none";}}><div className="music-skin" ref="skin"><div className="header">皮膚中心<span className="cancel" onClick={() => {this.props.close();}}>取消</span></div><div className="skin-title">推薦皮膚</div><div className="skin-container">{this.skins.map(skin => (<div className="skin-wrapper" key={skin.key}><div className="skin-color" style={{backgroundColor: skin.color, boxShadow: `0 0 3px ${skin.color}`}}><i className="icon-right" style={{display: skin.key === this.props.currentSkin ? "" : "none"}}></i></div><div>{skin.name}</div></div>))}</div></div></CSSTransition>);} }export default Skin 復制代碼

    skin.styl代碼請在源碼中查看

    上訴代碼在constructor中定義5中皮膚對象,用key屬性標識某一個皮膚,這個key值對應到Redux中的skin,name和color對應皮膚名稱和皮膚主色。在containers目錄下新建Skin.js將Skin包裝成容器組件

    import {connect} from "react-redux" import {setSkin} from "../redux/actions" import Skin from "../components/setting/Skin"const mapStateToProps = (state) => ({currentSkin: state.skin });const mapDispatchToProps = (dispatch) => ({setSkin: (skin) => {dispatch(setSkin(skin));} });export default connect(mapStateToProps, mapDispatchToProps)(Skin) 復制代碼

    在Menu組件中導入Skin容器組件,然后添加一個state屬性skinShow控制Skin顯示或隱藏,同時編寫一個改變skinShow的方法showSetting

    導入Skin

    import Skin from "../../containers/Skin" 復制代碼

    constructor初始化skinShow

    constructor(props) {super(props);this.state = {skinShow: false}; } 復制代碼

    showSetting方法中先關閉當前頁面,然后將skinShow設置為true或false

    showSetting = (status) => {this.close();// menu關閉后打開設置setTimeout(() => {this.setState({skinShow: status});}, 300); } 復制代碼

    SKin組件放置在如下位置,傳入show控制顯示和隱藏,close方法用來關閉皮膚中心頁面

    <div><CSSTransition in={this.props.show} timeout={300} classNames="fade"...</CSSTransition><Skin show={this.state.skinShow} close={() => {this.showSetting(false);}} /> </div> 復制代碼

    給皮膚中心添加點擊事件,點擊后調用showSetting,顯示皮膚中心頁面

    <div className="bottom-wrapper"><div className="item" onClick={() => {this.showSetting(true);}}>皮膚中心</div>... </div> 復制代碼

    回到Skin組件中,給皮膚添加點擊事件,點擊后將當前皮膚的key傳入,調用util下skin.js中導出的setSkinStyle方法設置皮膚,然后將皮膚設置到Redux的狀態屬性中保存,再調用props中的close方法關閉頁面

    skin.js

    import {skin, setSkinStyle} from "../../util/skin" 復制代碼setCurrentSkin = (key) => {// 設置皮膚setSkinStyle(skin[key]);this.props.setSkin(key);// 關閉當前頁面this.props.close(); } 復制代碼<div className="skin-wrapper" onClick={() => {this.setCurrentSkin(skin.key);}} key={skin.key}>... </div> 復制代碼

    皮膚持久化保存

    做完皮膚切換功能后,每次刷新或重新進入頁面上次設置的皮膚會切換為默認的黑色,我們想讓上一次設置的皮膚在刷新或重新進入時都是一樣的,這時需要將皮膚的key值保存到本地

    在util目錄下的storage.js中添加兩個方法

    let localStorage = {setSkin(key) {window.localStorage.setItem("skin", key);},getSkin() {let skin = window.localStorage.getItem("skin");return !skin ? "coolBlack" : skin;},... } 復制代碼

    在reducers.js將skin寫死的默認值從localStorage中獲取,設置skin的reducer方法中將皮膚的key保存到localStorage中

    const initialState = {skin: localStorage.getSkin(), //皮膚... };//設置皮膚 function skin(skin = initialState.skin, action) {switch (action.type) {case ActionTypes.SET_SKIN:localStorage.setSkin(action.skin);return action.skin;default:return skin;} } 復制代碼

    然后在util下的skin.js中將調用setSkinStyle(skin.coolBlack)中的skin.coolBlack換成從localStorage中獲取

    import localStorage from "./storage"...setSkinStyle(skin[localStorage.getSkin()]); 復制代碼

    效果

    總結

    本節主要內容是切換皮膚的功能,實現的原理總的來說就是提取樣式,切換的時候替換樣式,再插入到HTML DOM中,實現的方式有多種,這里主要選取一種比較合適的方式來做。

    本系列所有章節到此結束

    本章節代碼在chapter9分支

    完整項目地址:github.com/code-mcx/ma…

    體驗地址:code-mcx.github.io/mango-music

    總結

    以上是生活随笔為你收集整理的React全家桶构建一款Web音乐App实战(九):皮肤切换的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。