【前端小点】vue3项目内根据主题读取不同文件夹下的图片资源(图片文件)
项目要求实现一键换肤的功能,不仅仅是主题颜色上的替换,还有图片素材的替换,主题颜色替换的方案大同小异,下面仅对图片素材的一件替换进行方法描述。
主要思路
使用本地仓库对当前主题进行存储,系统根据主题去加载不同文件夹下的素材文件。
实现方式
1、动态读取文件夹下的图片文件。
注意:不同的素材我们用的是不同文件夹,但是文件名字是一样的,存储位置也是相对统一的,如下图所示。例如:暗色主题下有一个图片地址为bg.jpg,则亮色主题下也需要有一个图片地址为bg.jpg
编写js/ts对文件内容进行读取并存储,我的文件位置: src/themes.js
注意: import.meta.glob
必须是静态字符串,不可设置为动态获取的.
themes.js
// 获取文件夹下所有文件
const images = import.meta.glob(`./assets/themes/light/**/**/*.*`)
const commonImages = import.meta.glob(`./assets/themes/common/**/**/*.*`)
let prefix = './assets/themes/light/'
let commonPrefix = './assets/themes/common/'
/**
*
* @param {*} imagePaths
* @returns
*/
const handleFileName = (imagePaths, pathPrefix) => {
console.log(imagePaths)
let pathObj = {}
Object.keys(imagePaths).forEach((key) => {
key = key.replace(pathPrefix, '')
let pathArr = key.split('/')
let length = pathArr.length
// 名字
let name = pathArr[length - 1]
// 文件夹
let folder = ''
if (length >= 2) {
folder = pathArr[length - 2]
}
// 父文件夹
let folderParent = ''
if (length >= 3) {
folderParent = pathArr[length - 3]
}
pathObj[key] = {
name,
folder,
folderParent
}
})
return pathObj
}
// 获取文件名和文件夹名
let files = handleFileName(images, prefix)
console.log(files)
let commonFiles = handleFileName(commonImages, commonPrefix)
const themes = {
light: {},
dark: {},
common: {}
}
// 设置不同主题下的图片 文件夹/文件
Object.keys(files).forEach((key) => {
let fileObj = files[key]
let { name, folder, folderParent } = fileObj
let lightHref = new URL(`./assets/themes/light/${key}`, import.meta.url).href
let darkHref = new URL(`./assets/themes/dark/${key}`, import.meta.url).href
if (folderParent) {
themes.light[folderParent] ? {} : themes.light[folderParent] = {}
themes.light[folderParent][folder] ? {} :themes.light[folderParent][folder]
themes.light[folderParent][folder][name] = lightHref
themes.dark[folderParent] ? {} : themes.dark[folderParent] = {}
themes.dark[folderParent][folder] ? {} : themes.dark[folderParent] = {}
themes.dark[folderParent][folder][name] = darkHref
} else if (folder) {
themes.light[folder] ? {} : themes.light[folder] = {}
themes.light[folder][name] = lightHref
themes.dark[folder] ? {} : themes.dark[folder] = {}
themes.dark[folder][name] = darkHref
} else {
themes.light[name] = lightHref
themes.dark[name] = darkHref
}
})
// 设置公共图片文件
Object.keys(commonFiles).forEach((key) => {
let fileObj = commonFiles[key]
let { name, folder, folderParent } = fileObj
let commonHref = new URL(`./assets/themes/common/${key}`, import.meta.url).href
if (folderParent) {
themes.common[folderParent] ? {} : themes.common[folderParent] = {}
themes.common[folderParent][folder] = {}
themes.common[folderParent][folder][name] = commonHref
} else if (folder) {
themes.common[folder] ? {} : themes.common[folder] = {}
themes.common[folder][name] = commonHref
} else {
themes.common[name] = commonHref
}
})
export default themes
2、对项目内的主题(暗色\亮色)进行仓库存储
这里我使用的是pinia进行本地数据存储,我的文件位置src/pinia/modules/theme.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import themesImage from '@/themes'
const useThemeStore = defineStore('themeStore', () => {
let theme = ref('light')
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
const getThemeImage = (imgPath: string) => {
return getImage(theme.value, imgPath)
}
const getCommonImage = (imgPath: string) => {
return getImage('common', imgPath)
}
const getImage = (themeName: string, imgPath: string) => {
let imagePath = imgPath.split('/')
imagePath.length
if (imagePath.length == 1) {
return themesImage[themeName][imagePath[0]]
} else if (imagePath.length == 2) {
return themesImage[themeName][imagePath[0]][imagePath[1]]
} else {
return themesImage[themeName][imagePath[0]][imagePath[1]][imagePath[2]]
}
}
return {
theme,
toggleTheme,
getThemeImage,
getCommonImage
}
})
export default useThemeStore
3、编写工具类获取图片地址
编写工具类,便于后续界面读取文件,我的文件地址为: src/utils/themes.ts
import useThemeStore from '@/pinia/modules/theme'
const themeStore = useThemeStore()
// 获取主题图片
export const getThemeImage = themeStore.getThemeImage
// 获取公共图片
export const getCommonImage = themeStore.getCommonImage
4、在vue组件中使用
做好准备工作以后,我们只需在组件中引入我们的工具类即可。
<template>
<div class="container">
<img :src="themeUtils.getThemeImage('index/logo.png')" alt="" class="img" />
<img :src="themeUtils.getCommonImage('logo.png')" alt="" class="img" />
<el-button @click="themeUtils.toggleTheme">一键换肤</el-button>
</div>
</template>
<script lang="ts" setup>
import * as themeUtils from '@/utils/themes'
</script>
<style lang="scss" scoped>
.img{
border: 1px solid #ccc;
border-radius: 10px;
margin-right: 10px;
}
.container{
display: flex;
align-items: center;
}
</style>
效果如下:
点击一键换肤以后