React Family Bucket создает приложение для веб-музыки Actual Combat (IX): переключение скинов

React.js

Этот раздел является последним разделом реальной битвы приложения React Web Music: функция переключения скинов. Переключение скинов — это функция, которая не имеет ничего общего с ядром приложения Web Music. Добавление этой функции может добавить приложению массу удовольствия.

Реализовать идеи

Общий принцип реализации функции переключения скинов заключается в том, чтобы извлечь стили как отдельный стиль, добавить эти стили в DOM-элементы, которые нужно скинировать, заменить заданные значения атрибутов стиля при переключении стилей, а затем динамически вставить их в DOM, чтобы стили CSS вступили в силу

Подготовить

Прежде чем использовать эту функцию, вам нужно добавить несколько маленьких значков.Новые маленькие значки отмечены в красной рамке на рисунке ниже.

В первом разделе был создан файл значка шрифта и стиль.На этот раз для добавления нового значка необходимо использовать изображение svg для создания нового файла значка шрифта и стиля, который совпадает со вторым разделом.icomoonЭтот веб-сайт создан, подробные шаги показаны в Разделе 1.Создание иконки шрифта

После того, как иконка шрифта будет сделана, будет копияicomoon.zipпакет, после распаковки будет внутриfontsПереименуйте 4 файла в каталоге в icomusic, а затем войдите в проект, чтобыsrc/assets/stylus/fonts4 файла ниже каталога заменяются 4 файлами значков шрифтов, только что переименованными. назадsrc/assets/stylusпоследующийfont.styl, откройте распакованный каталог вstyle.css, скопируйте в него все стили в начале .icon-, и замените стили в начале .icon- в font.styl

Извлечь стиль

Прежде чем выполнять функцию переключения скинов, извлеките стили, которые необходимо переключить. Поскольку функция переключения скинов выполняется после завершения всех функций, извлечение стиля является очень утомительным процессом. Ниже приведен список всех мест, где необходимо извлечь стили.

местоположение файла имя стиля
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

в вышеуказанном стилеcolorилиbackground-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);
};

Вызовите setSkinStyle в skin.js и передайте в skin.coolBlack.

// 设置皮肤
setSkinStyle(skin.coolBlack);

Наконец, экспортируйте объект скина и setSkinStyle для последующего использования.

export {skin, setSkinStyle}

Эти стили необходимо вставить в HTML DOM во время работы программы, поэтому импортируйте skin.js в Root.js.

import "../util/skin"

Затем добавьте извлеченные стили к тегам в каждом компоненте.В следующей таблице перечислены места, куда добавляются стили.

Расположение компонента элемент стили, которые нужно добавить
App.js div.app
div.app-header
div.music-tab
.skin-app
.skin-app-header
.skin-music-tab
recommend/Recommend.js div.album-wrapper
h1.title
.skin-album-wrapper
album/Album.js div.album-wrapper .skin-detail-wrapper
ranking/Ranking.js div.ranking-wrapper .skin-ranking-wrapper
ranking/RankingInfo.js div.ranking-wrapper .skin-detail-wrapper
singer/SingerList.js div.music-singers .skin-music-singers
singer/Singer.js div.singer-wrapper .skin-detail-wrapper
search/Search.js div.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.js div.mini-player

После применения вышеуказанных стилей, если нет проблем, общий вид будет почти таким же, как и раньше.

пользовательский скин

В дополнение к указанным выше обложкам по умолчанию определите несколько стилей. Сначала определите цвет манго в качестве цвета кожи, а затем используйте основной цвет трех основных музыкальных плееров, Kugou, Netease и QQ Music в качестве цвета кожи, затем разверните объект кожи и добавьте следующий код в skin.js.

  1. желтый манго
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"
};
  1. крутая собака синего цвета
skin.kuGouBlue = Object.assign({}, skin.mangoYellow, {
  appHeaderBgColor: "#2CA2F9",
  activeColor: "#2CA2F9",
  searchTitleColor: "#2CA2F9",
  miniRightColor: "#2CA2F9"
});
  1. Нетиз Красный
skin.netBaseRed = Object.assign({}, skin.mangoYellow, {
  appHeaderBgColor: "#D43C33",
  activeColor: "#D43C33",
  searchTitleColor: "#D43C33",
  miniRightColor: "#D43C33"
});
  1. QQ зеленый
skin.qqGreen = Object.assign({}, skin.mangoYellow, {
  appHeaderBgColor: "#31C27C",
  activeColor: "#31C27C",
  searchTitleColor: "#31C27C",
  miniRightColor: "#31C27C"
});

Среди вышеперечисленных объектов последние три наследуются от объекта mangoYellow, а затем перезаписывают другие значения атрибутов, что может уменьшить избыточность одного и того же стиля.

реализация переключения скинов

Чтобы добиться переключения скинов, необходимо написать компонент центра скинов, и центр скинов вводится из списка меню в компоненте приложения.

Добавьте конструктор в App.js и инициализируйте свойство состояния, которое управляет меню отображения.menuShow

constructor(props) {
    super(props);

    this.state = {
        menuShow: false
    };
  }

Добавьте значок в элемент заголовка заголовка.i.icon-et-moreИ добавьте обработку события клика, нажмите, чтобы установить для menshow значение 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-header
    height: 55px
    line-height: 55px
    /*color: #FFD700*/
    text-align: center
    position: relative
    .app-more
      position: absolute
      top: 15px
      left: 15px
      font-size: 20px

Создайте новый каталог настроек в каталоге компонентов, а затем создайте новыйMenu.jsа такжеmenu.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

import MusicMenu from "./setting/Menu"

размещены в следующих местах и ​​пройденыshow,closeMenuДва реквизита, из которых show используется для управления отображением и скрытия анимации компонента Menu, а closeMenu передается в Menu, которое закрывается при нажатии кнопки отмены или фоновой маски.

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

Мы передаем значение ключа текущего скина в Redux, перечисляем все скины в компоненте Skin, отмечаем скин, соответствующий ключу, сохраненному в Redux, галочкой и щелкаем один скин, чтобы установить текущий скин. Добавьте в скин свойства 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,
    ...
});

Создайте новые 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, проверьте исходный код

Код обращения определяет в конструкторе 5 объектов скина и использует атрибут key для идентификации определенного скина.Это значение ключа соответствует скину в Redux, а имя и цвет соответствуют названию скина и основному цвету скина. Создайте новый Skin.js в каталоге контейнеров, чтобы обернуть скин в компонент-контейнер.

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)

Импортируйте компонент контейнера Skin в компонент Menu, затем добавьте свойство состоянияskinShowУправляйте скином, чтобы показать или скрыть, и напишите метод для изменения скина.showSetting

Импорт скина

import Skin from "../../containers/Skin"

конструктор инициализирует 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, добавьте к скину событие щелчка, передайте ключ текущего скина после нажатия, вызовите метод setSkinStyle, экспортированный из skin.js в util, чтобы установить скин, а затем установите скин в состояние свойство Redux, чтобы сохранить его, а затем вызвать метод 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>

стойкость кожи

После завершения функции переключения скинов последний набор скинов будет переключаться на черный цвет по умолчанию каждый раз, когда вы обновляете или повторно заходите на страницу.Мы хотим, чтобы последний набор скинов был таким же, когда вы обновляете или повторно входите на страницу. В это время нужно сменить скин.Значение ключа сохраняется в локальный

Добавьте два метода в storage.js в каталоге util.

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

В reducers.js значение скина по умолчанию получается из localStorage, а ключ скина сохраняется в 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;
    }
}

Затем в skin.js под util замените skin.coolBlack в вызове setSkinStyle(skin.coolBlack) на полученный из localStorage.

import localStorage from "./storage"

...

setSkinStyle(skin[localStorage.getSkin()]);

Эффект

Суммировать

Основное содержание этого раздела - функция переключения скина.Принцип реализации в целом заключается в извлечении стиля, замене стиля при переключении, а затем вставке его в HTML DOM.Существует много способов добиться этого.Здесь, мы в основном выбираем более подходящий способ сделать это.

На этом все главы этой серии заканчиваются.

Код для этой главы находится вchapter9ветвь

Полный адрес проекта:GitHub.com/case/mango-no…

Адрес опыта:dxx.github.io/mango-music