diff --git a/src/configs/theme.ts b/src/configs/theme.ts index 08bc717c..a2938ed8 100644 --- a/src/configs/theme.ts +++ b/src/configs/theme.ts @@ -1,30 +1,93 @@ -export const PRESET_THEMES = [ - { color: '#d14424', background: '#ffffff', text: '#333' }, - { color: '#42464b', background: '#ffffff', text: '#333' }, - { color: '#5d82ba', background: '#ffffff', text: '#333' }, - { color: '#005a6f', background: '#ffffff', text: '#333' }, - { color: '#d0614c', background: '#dfb044', text: '#333' }, - { color: '#86a1ad', background: '#dfdbd4', text: '#333' }, - { color: '#697586', background: '#d5c4a4', text: '#333' }, - { color: '#333333', background: '#7acfa6', text: '#333' }, - { color: '#42464b', background: '#415065', text: '#fff' }, - { color: '#0c5999', background: '#35a2cd', text: '#fff' }, - { color: '#c49a41', background: '#8c4357', text: '#fff' }, - { color: '#dfaa00', background: '#2e4e7d', text: '#fff' }, - { color: '#d1ad88', background: '#f99070', text: '#fff' }, - { color: '#464d52', background: '#657984', text: '#fff' }, - { color: '#ffcfb6', background: '#1e4c6f', text: '#fff' }, - { color: '#c3a043', background: '#43292a', text: '#fff' }, - { color: '#464d52', background: '#60546f', text: '#fff' }, - { color: '#df9636', background: '#5b89a0', text: '#fff' }, - { color: '#b898a4', background: '#93716b', text: '#fff' }, - { color: '#c47a11', background: '#187db1', text: '#fff' }, - { color: '#333333', background: '#759564', text: '#fff' }, - { color: '#355b5e', background: '#424b50', text: '#fff' }, - { color: '#d29090', background: '#942a32', text: '#fff' }, - { color: '#00cfdf', background: '#3b434d', text: '#fff' }, - { color: '#424246', background: '#c70042', text: '#fff' }, - { color: '#2e4155', background: '#b35d44', text: '#fff' }, - { color: '#11bfce', background: '#8f98aa', text: '#fff' }, - { color: '#333333', background: '#549688', text: '#fff' }, +export interface PresetTheme { + background: string + fontColor: string + fontname: string + colors: string[] +} + +export const PRESET_THEMES: PresetTheme[] = [ + { + background: '#ffffff', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#5b9bd5', '#ed7d31', '#a5a5a5', '#ffc000', '#4472c4', '#70ad47'], + }, + { + background: '#ffffff', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#83992a', '#3c9670', '#44709d', '#a23b32', '#d87728', '#deb340'], + }, + { + background: '#ffffff', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#e48312', '#bd582c', '#865640', '#9b8357', '#c2bc80', '#94a088'], + }, + { + background: '#ffffff', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#bdc8df', '#003fa9', '#f5ba00', '#ff7567', '#7676d9', '#923ffc'], + }, + { + background: '#ffffff', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#90c225', '#54a121', '#e6b91e', '#e86618', '#c42f19', '#918756'], + }, + { + background: '#ffffff', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#1cade4', '#2683c6', '#27ced7', '#42ba97', '#3e8853', '#62a39f'], + }, + { + background: '#e9efd6', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#a5300f', '#de7e18', '#9f8351', '#728653', '#92aa4c', '#6aac91'], + }, + { + background: '#17444e', + fontColor: '#ffffff', + fontname: 'Microsoft Yahei', + colors: ['#b01513', '#ea6312', '#e6b729', '#6bab90', '#55839a', '#9e5d9d'], + }, + { + background: '#36234d', + fontColor: '#ffffff', + fontname: 'Microsoft Yahei', + colors: ['#b31166', '#e33d6f', '#e45f3c', '#e9943a', '#9b6bf2', '#d63cd0'], + }, + { + background: '#247fad', + fontColor: '#ffffff', + fontname: 'Microsoft Yahei', + colors: ['#052f61', '#a50e82', '#14967c', '#6a9e1f', '#e87d37', '#c62324'], + }, + { + background: '#103f55', + fontColor: '#ffffff', + fontname: 'Microsoft Yahei', + colors: ['#40aebd', '#97e8d5', '#a1cf49', '#628f3e', '#f2df3a', '#fcb01c'], + }, + { + background: '#242367', + fontColor: '#ffffff', + fontname: 'Microsoft Yahei', + colors: ['#ac3ec1', '#477bd1', '#46b298', '#90ba4c', '#dd9d31', '#e25345'], + }, + { + background: '#e4b75e', + fontColor: '#333333', + fontname: 'Microsoft Yahei', + colors: ['#f0a22e', '#a5644e', '#b58b80', '#c3986d', '#a19574', '#c17529'], + }, + { + background: '#333333', + fontColor: '#ffffff', + fontname: 'Microsoft Yahei', + colors: ['#bdc8df', '#003fa9', '#f5ba00', '#ff7567', '#7676d9', '#923ffc'], + }, ] \ No newline at end of file diff --git a/src/hooks/useSlideTheme.ts b/src/hooks/useSlideTheme.ts new file mode 100644 index 00000000..706525bd --- /dev/null +++ b/src/hooks/useSlideTheme.ts @@ -0,0 +1,180 @@ +import tinycolor from 'tinycolor2' +import { storeToRefs } from 'pinia' +import { useSlidesStore } from '@/store' +import { Slide } from '@/types/slides' +import { PresetTheme } from '@/configs/theme' +import useHistorySnapshot from '@/hooks/useHistorySnapshot' + +export default () => { + const slidesStore = useSlidesStore() + const { slides, currentSlide, theme } = storeToRefs(slidesStore) + + const { addHistorySnapshot } = useHistorySnapshot() + + // 获取指定幻灯片内所有颜色(主要的) + const getSlideAllColors = (slide: Slide) => { + const colors: string[] = [] + for (const el of slide.elements) { + if (el.type === 'shape' && tinycolor(el.fill).getAlpha() !== 0) { + const color = tinycolor(el.fill).toRgbString() + if (!colors.includes(color)) colors.push(color) + } + if (el.type === 'text' && el.fill && tinycolor(el.fill).getAlpha() !== 0) { + const color = tinycolor(el.fill).toRgbString() + if (!colors.includes(color)) colors.push(color) + } + if (el.type === 'table' && el.theme && tinycolor(el.theme.color).getAlpha() !== 0) { + const color = tinycolor(el.theme.color).toRgbString() + if (!colors.includes(color)) colors.push(color) + } + if (el.type === 'chart' && el.fill && tinycolor(el.fill).getAlpha() !== 0) { + const color = tinycolor(el.fill).toRgbString() + if (!colors.includes(color)) colors.push(color) + } + if (el.type === 'line' && tinycolor(el.color).getAlpha() !== 0) { + const color = tinycolor(el.color).toRgbString() + if (!colors.includes(color)) colors.push(color) + } + if (el.type === 'audio' && tinycolor(el.color).getAlpha() !== 0) { + const color = tinycolor(el.color).toRgbString() + if (!colors.includes(color)) colors.push(color) + } + } + return colors + } + + // 创建原颜色与新颜色的对应关系表 + const createSlideThemeColorMap = (slide: Slide, newColors: string[]): { [key: string]: string } => { + const oldColors = getSlideAllColors(slide) + const themeColorMap = {} + + if (oldColors.length > newColors.length) { + const analogous = tinycolor(newColors[0]).analogous(oldColors.length - newColors.length + 10) + const otherColors = analogous.map(item => item.toHexString()).slice(1) + newColors.push(...otherColors) + } + for (let i = 0; i < oldColors.length; i++) { + themeColorMap[oldColors[i]] = newColors[i] + } + + return themeColorMap + } + + // 设置幻灯片主题 + const setSlideTheme = (slide: Slide, theme: PresetTheme) => { + const colorMap = createSlideThemeColorMap(slide, theme.colors) + + if (!slide.background || slide.background.type !== 'image') { + slide.background = { + type: 'solid', + color: theme.background, + } + } + for (const el of slide.elements) { + if (el.type === 'shape') { + el.fill = colorMap[tinycolor(el.fill).toRgbString()] || el.fill + if (el.gradient) delete el.gradient + } + if (el.type === 'text') { + if (el.fill) el.fill = colorMap[tinycolor(el.fill).toRgbString()] || el.fill + el.defaultColor = theme.fontColor + el.defaultFontName = theme.fontname + } + if (el.type === 'table') { + if (el.theme) el.theme.color = colorMap[tinycolor(el.theme.color).toRgbString()] || el.theme.color + for (const rowCells of el.data) { + for (const cell of rowCells) { + if (cell.style) { + cell.style.color = theme.fontColor + cell.style.fontname = theme.fontname + } + } + } + } + if (el.type === 'chart') { + el.themeColor = [colorMap[tinycolor(el.themeColor[0]).toRgbString()]] || el.themeColor + el.gridColor = theme.fontColor + } + if (el.type === 'line') el.color = colorMap[tinycolor(el.color).toRgbString()] || el.color + if (el.type === 'audio') el.color = colorMap[tinycolor(el.color).toRgbString()] || el.color + if (el.type === 'latex') el.color = theme.fontColor + } + } + + // 应用预置主题(单页) + const applyPresetThemeToSingleSlide = (theme: PresetTheme) => { + const newSlide: Slide = JSON.parse(JSON.stringify(currentSlide.value)) + setSlideTheme(newSlide, theme) + slidesStore.updateSlide({ + background: newSlide.background, + elements: newSlide.elements, + }) + addHistorySnapshot() + } + + // 应用预置主题(全部) + const applyPresetThemeToAllSlides = (theme: PresetTheme) => { + const newSlides: Slide[] = JSON.parse(JSON.stringify(slides.value)) + for (const slide of newSlides) { + setSlideTheme(slide, theme) + } + slidesStore.setTheme({ + backgroundColor: theme.background, + themeColor: theme.colors[0], + fontColor: theme.fontColor, + fontName: theme.fontname, + }) + slidesStore.setSlides(newSlides) + addHistorySnapshot() + } + + // 将当前主题配置应用到全部页面 + const applyThemeToAllSlides = () => { + const newSlides: Slide[] = JSON.parse(JSON.stringify(slides.value)) + const { themeColor, backgroundColor, fontColor, fontName } = theme.value + + for (const slide of newSlides) { + if (!slide.background || slide.background.type !== 'image') { + slide.background = { + type: 'solid', + color: backgroundColor + } + } + + for (const el of slide.elements) { + if (el.type === 'shape') el.fill = themeColor + else if (el.type === 'line') el.color = themeColor + else if (el.type === 'text') { + el.defaultColor = fontColor + el.defaultFontName = fontName + if (el.fill) el.fill = themeColor + } + else if (el.type === 'table') { + if (el.theme) el.theme.color = themeColor + for (const rowCells of el.data) { + for (const cell of rowCells) { + if (cell.style) { + cell.style.color = fontColor + cell.style.fontname = fontName + } + } + } + } + else if (el.type === 'chart') { + el.themeColor = [themeColor] + el.gridColor = fontColor + } + else if (el.type === 'latex') el.color = fontColor + else if (el.type === 'audio') el.color = themeColor + } + } + slidesStore.setSlides(newSlides) + addHistorySnapshot() + } + + return { + applyPresetThemeToSingleSlide, + applyPresetThemeToAllSlides, + applyThemeToAllSlides, + } +} \ No newline at end of file diff --git a/src/views/Editor/Toolbar/SlideDesignPanel.vue b/src/views/Editor/Toolbar/SlideDesignPanel.vue index 43da7c2d..f52f7cef 100644 --- a/src/views/Editor/Toolbar/SlideDesignPanel.vue +++ b/src/views/Editor/Toolbar/SlideDesignPanel.vue @@ -165,29 +165,34 @@ -