perf: 文本字体相关优化

This commit is contained in:
pipipi-pikachu 2024-12-29 17:29:22 +08:00
parent 77f150b354
commit c962d4af17
39 changed files with 69 additions and 143 deletions

View File

@ -32,7 +32,6 @@ if (import.meta.env.MODE !== 'development') {
onMounted(async () => {
await deleteDiscardedDB()
snapshotStore.initSnapshotDatabase()
mainStore.setAvailableFonts()
})
// localStorage indexedDB ID

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,6 @@
$fontList: '仓耳小丸子', '优设标题黑', '字制区喜脉体', '峰广明锐体', '得意黑', '摄图摩登小方体', '站酷快乐体', '素材集市康康体', '素材集市酷方体', '途牛类圆体', '锐字真言体';
$fonts: 'SourceHanSans', 'SourceHanSerif', 'FangZhengHeiTi', 'FangZhengKaiTi', 'FangZhengShuSong', 'FangZhengFangSong', 'AlibabaPuHuiTi', 'ZhuQueFangSong', 'LXGWWenKai', 'WenDingPLKaiTi', 'DeYiHei', 'MiSans', 'CangerXiaowanzi', 'YousheTitleBlack', 'FengguangMingrui', 'ShetuModernSquare', 'ZcoolHappy', 'ZizhiQuXiMai', 'SucaiJishiKangkang', 'SucaiJishiCoolSquare', 'TuniuRounded', 'RuiziZhenyan';
@each $font in $fontList {
@each $font in $fonts {
@font-face {
font-display: swap;
font-family: $font;

View File

@ -2,6 +2,7 @@
outline: 0;
border: 0;
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
word-break: break-word;
white-space: normal;

View File

@ -1,44 +1,25 @@
export const SYS_FONTS = [
{ label: 'Arial', value: 'Arial' },
{ label: '微软雅黑', value: 'Microsoft Yahei' },
{ label: '宋体', value: 'SimSun' },
{ label: '黑体', value: 'SimHei' },
{ label: '楷体', value: 'KaiTi' },
{ label: '新宋体', value: 'NSimSun' },
{ label: '仿宋', value: 'FangSong' },
{ label: '苹方', value: 'PingFang SC' },
{ label: '华文黑体', value: 'STHeiti' },
{ label: '华文楷体', value: 'STKaiti' },
{ label: '华文宋体', value: 'STSong' },
{ label: '华文仿宋', value: 'STFangSong' },
{ label: '华文中宋', value: 'STZhongSong' },
{ label: '华文琥珀', value: 'STHupo' },
{ label: '华文新魏', value: 'STXinwei' },
{ label: '华文隶书', value: 'STLiti' },
{ label: '华文行楷', value: 'STXingkai' },
{ label: '冬青黑体', value: 'Hiragino Sans GB' },
{ label: '兰亭黑', value: 'Lantinghei SC' },
{ label: '偏偏体', value: 'Hanzipen SC' },
{ label: '手札体', value: 'Hannotate SC' },
{ label: '宋体', value: 'Songti SC' },
{ label: '娃娃体', value: 'Wawati SC' },
{ label: '行楷', value: 'Xingkai SC' },
{ label: '圆体', value: 'Yuanti SC' },
{ label: '华文细黑', value: 'STXihei' },
{ label: '幼圆', value: 'YouYuan' },
{ label: '隶书', value: 'LiSu' },
]
export const WEB_FONTS = [
{ label: '得意黑', value: '得意黑' },
{ label: '仓耳小丸子', value: '仓耳小丸子' },
{ label: '优设标题黑', value: '优设标题黑' },
{ label: '峰广明锐体', value: '峰广明锐体' },
{ label: '摄图摩登小方体', value: '摄图摩登小方体' },
{ label: '站酷快乐体', value: '站酷快乐体' },
{ label: '字制区喜脉体', value: '字制区喜脉体' },
{ label: '素材集市康康体', value: '素材集市康康体' },
{ label: '素材集市酷方体', value: '素材集市酷方体' },
{ label: '途牛类圆体', value: '途牛类圆体' },
{ label: '锐字真言体', value: '锐字真言体' },
export const FONTS = [
{ label: '默认字体', value: '' },
{ label: '思源黑体', value: 'SourceHanSans' },
{ label: '思源宋体', value: 'SourceHanSerif' },
{ label: '方正黑体', value: 'FangZhengHeiTi' },
{ label: '方正楷体', value: 'FangZhengKaiTi' },
{ label: '方正宋体', value: 'FangZhengShuSong' },
{ label: '方正仿宋', value: 'FangZhengFangSong' },
{ label: '阿里巴巴普惠体', value: 'AlibabaPuHuiTi' },
{ label: '朱雀仿宋', value: 'ZhuqueFangSong' },
{ label: '霞鹜文楷', value: 'LXGWWenKai' },
{ label: '文鼎PL楷体', value: 'WenDingPLKaiTi' },
{ label: '得意黑', value: 'DeYiHei' },
{ label: 'MiSans', value: 'MiSans' },
{ label: '仓耳小丸子', value: 'CangerXiaowanzi' },
{ label: '优设标题黑', value: 'YousheTitleBlack' },
{ label: '峰广明锐体', value: 'FengguangMingrui' },
{ label: '摄图摩登小方体', value: 'ShetuModernSquare' },
{ label: '站酷快乐体', value: 'ZcoolHappy' },
{ label: '字制区喜脉体', value: 'ZizhiQuXiMai' },
{ label: '素材集市康康体', value: 'SucaiJishiKangkang' },
{ label: '素材集市酷方体', value: 'SucaiJishiCoolSquare' },
{ label: '途牛类圆体', value: 'TuniuRounded' },
{ label: '锐字真言体', value: 'RuiziZhenyan' },
]

View File

@ -9,85 +9,85 @@ export const PRESET_THEMES: PresetTheme[] = [
{
background: '#ffffff',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#5b9bd5', '#ed7d31', '#a5a5a5', '#ffc000', '#4472c4', '#70ad47'],
},
{
background: '#ffffff',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#83992a', '#3c9670', '#44709d', '#a23b32', '#d87728', '#deb340'],
},
{
background: '#ffffff',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#e48312', '#bd582c', '#865640', '#9b8357', '#c2bc80', '#94a088'],
},
{
background: '#ffffff',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#bdc8df', '#003fa9', '#f5ba00', '#ff7567', '#7676d9', '#923ffc'],
},
{
background: '#ffffff',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#90c225', '#54a121', '#e6b91e', '#e86618', '#c42f19', '#918756'],
},
{
background: '#ffffff',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#1cade4', '#2683c6', '#27ced7', '#42ba97', '#3e8853', '#62a39f'],
},
{
background: '#e9efd6',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#a5300f', '#de7e18', '#9f8351', '#728653', '#92aa4c', '#6aac91'],
},
{
background: '#17444e',
fontColor: '#ffffff',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#b01513', '#ea6312', '#e6b729', '#6bab90', '#55839a', '#9e5d9d'],
},
{
background: '#36234d',
fontColor: '#ffffff',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#b31166', '#e33d6f', '#e45f3c', '#e9943a', '#9b6bf2', '#d63cd0'],
},
{
background: '#247fad',
fontColor: '#ffffff',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#052f61', '#a50e82', '#14967c', '#6a9e1f', '#e87d37', '#c62324'],
},
{
background: '#103f55',
fontColor: '#ffffff',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#40aebd', '#97e8d5', '#a1cf49', '#628f3e', '#f2df3a', '#fcb01c'],
},
{
background: '#242367',
fontColor: '#ffffff',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#ac3ec1', '#477bd1', '#46b298', '#90ba4c', '#dd9d31', '#e25345'],
},
{
background: '#e4b75e',
fontColor: '#333333',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#f0a22e', '#a5644e', '#b58b80', '#c3986d', '#a19574', '#c17529'],
},
{
background: '#333333',
fontColor: '#ffffff',
fontname: 'Microsoft Yahei',
fontname: '',
colors: ['#bdc8df', '#003fa9', '#f5ba00', '#ff7567', '#7676d9', '#923ffc'],
},
]

View File

@ -42,7 +42,7 @@ export const slides: Slide[] = [
lineHeight: 1.2,
content: '<p><strong><span style=\"font-size: 112px;\">PPTist</span></strong></p>',
rotate: 0,
defaultFontName: 'Microsoft Yahei',
defaultFontName: '',
defaultColor: '#333'
},
{
@ -54,7 +54,7 @@ export const slides: Slide[] = [
height: 56,
content: '<p><span style=\"font-size: 24px;\">基于 Vue 3.x + TypeScript 的在线演示文稿应用</span></p>',
rotate: 0,
defaultFontName: 'Microsoft Yahei',
defaultFontName: '',
defaultColor: '#333'
},
{
@ -88,7 +88,7 @@ export const slides: Slide[] = [
lineHeight: 1.2,
content: '<p style=\"text-align: center;\"><strong><span style=\"font-size: 48px;\">在此处添加标题</span></strong></p>',
rotate: 0,
defaultFontName: 'Microsoft Yahei',
defaultFontName: '',
defaultColor: '#333',
},
{
@ -100,7 +100,7 @@ export const slides: Slide[] = [
height: 56,
content: '<p style=\"text-align: center;\"><span style=\"font-size: 24px;\">在此处添加副标题</span></p>',
rotate: 0,
defaultFontName: 'Microsoft Yahei',
defaultFontName: '',
defaultColor: '#333',
},
{
@ -173,7 +173,7 @@ export const slides: Slide[] = [
height: 140,
content: '<p style=\"text-align: center;\"><strong><span style=\"font-size: 80px;\"><span style=\"color: rgb(255, 255, 255);\">感谢观看</span></span></strong></p>',
rotate: 0,
defaultFontName: 'Microsoft Yahei',
defaultFontName: '',
defaultColor: '#333',
wordSpace: 5
}

View File

@ -3,7 +3,7 @@ import type { SlideTheme } from '@/types/slides'
export const theme: SlideTheme = {
themeColor: '#5b9bd5',
fontColor: '#333',
fontName: 'Microsoft Yahei',
fontName: '',
backgroundColor: '#fff',
shadow: {
h: 3,

View File

@ -4,8 +4,6 @@ import { ToolbarStates } from '@/types/toolbar'
import type { CreatingElement, ShapeFormatPainter, TextFormatPainter } from '@/types/edit'
import type { DialogForExportTypes } from '@/types/export'
import { type TextAttrs, defaultRichTextAttrs } from '@/utils/prosemirror/utils'
import { SYS_FONTS } from '@/configs/font'
import { isSupportFont } from '@/utils/font'
import { useSlidesStore } from './slides'
@ -24,7 +22,6 @@ export interface MainState {
showRuler: boolean
creatingElement: CreatingElement | null
creatingCustomShape: boolean
availableFonts: typeof SYS_FONTS
toolbarState: ToolbarStates
clipingImageElementId: string
isScaling: boolean
@ -60,7 +57,6 @@ export const useMainStore = defineStore('main', {
showRuler: false, // 显示标尺
creatingElement: null, // 正在插入的元素信息,需要通过绘制插入的元素(文字、形状、线条)
creatingCustomShape: false, // 正在绘制任意多边形
availableFonts: SYS_FONTS, // 当前环境可用字体
toolbarState: ToolbarStates.SLIDE_DESIGN, // 右侧工具栏状态
clipingImageElementId: '', // 当前正在裁剪的图片ID
richTextAttrs: defaultRichTextAttrs, // 富文本状态
@ -153,10 +149,6 @@ export const useMainStore = defineStore('main', {
this.creatingCustomShape = state
},
setAvailableFonts() {
this.availableFonts = SYS_FONTS.filter(font => isSupportFont(font.value))
},
setToolbarState(toolbarState: ToolbarStates) {
this.toolbarState = toolbarState
},

View File

@ -1,36 +0,0 @@
/**
*
* @param fontName
*/
export const isSupportFont = (fontName: string) => {
if (typeof fontName !== 'string') return false
const arial = 'Arial'
if (fontName.toLowerCase() === arial.toLowerCase()) return true
const size = 100
const width = 100
const height = 100
const str = 'a'
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d', { willReadFrequently: true })
if (!ctx) return false
canvas.width = width
canvas.height = height
ctx.textAlign = 'center'
ctx.fillStyle = 'black'
ctx.textBaseline = 'middle'
const getDotArray = (_fontFamily: string) => {
ctx.clearRect(0, 0, width, height)
ctx.font = `${size}px ${_fontFamily}, ${arial}`
ctx.fillText(str, width / 2, height / 2)
const imageData = ctx.getImageData(0, 0, width, height).data
return [].slice.call(imageData).filter(item => item !== 0)
}
return getDotArray(arial).join('') !== getDotArray(fontName).join('')
}

View File

@ -187,7 +187,7 @@ const _defaultAttrs: DefaultAttrs = {
color: '#000000',
backcolor: '',
fontsize: '16px',
fontname: '微软雅黑',
fontname: '',
align: 'left',
}
export const getTextAttrs = (view: EditorView, attrs: Partial<DefaultAttrs> = {}) => {
@ -251,7 +251,7 @@ export const defaultRichTextAttrs: TextAttrs = {
color: '#000000',
backcolor: '',
fontsize: '16px',
fontname: '微软雅黑',
fontname: '',
link: '',
align: 'left',
bulletList: false,

View File

@ -58,10 +58,7 @@
search
searchLabel="搜索字体"
@update:value="value => updateFontStyle('fontname', value as string)"
:options="[
...availableFonts,
...WEB_FONTS
]"
:options="FONTS"
>
<template #icon>
<IconFontSize />
@ -139,7 +136,7 @@ import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store'
import type { LineStyleType, PPTElement, PPTElementOutline, TableCell } from '@/types/slides'
import emitter, { EmitterEvents } from '@/utils/emitter'
import { WEB_FONTS } from '@/configs/font'
import { FONTS } from '@/configs/font'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import SVGLine from '../common/SVGLine.vue'
@ -158,7 +155,7 @@ import SelectCustom from '@/components/SelectCustom.vue'
import Popover from '@/components/Popover.vue'
const slidesStore = useSlidesStore()
const { richTextAttrs, availableFonts, activeElementList } = storeToRefs(useMainStore())
const { richTextAttrs, activeElementList } = storeToRefs(useMainStore())
const { addHistorySnapshot } = useHistorySnapshot()

View File

@ -7,10 +7,7 @@
search
searchLabel="搜索字体"
@update:value="value => updateTextAttrs({ fontname: value as string })"
:options="[
...availableFonts,
...WEB_FONTS
]"
:options="FONTS"
>
<template #icon>
<IconFontSize />
@ -178,7 +175,7 @@ import { storeToRefs } from 'pinia'
import { nanoid } from 'nanoid'
import { useMainStore, useSlidesStore } from '@/store'
import type { PPTTableElement, TableCell, TableCellStyle, TableTheme, TextAlign } from '@/types/slides'
import { WEB_FONTS } from '@/configs/font'
import { FONTS } from '@/configs/font'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import ElementOutline from '../common/ElementOutline.vue'
@ -198,7 +195,7 @@ import SelectGroup from '@/components/SelectGroup.vue'
import Popover from '@/components/Popover.vue'
const slidesStore = useSlidesStore()
const { handleElement, handleElementId, selectedTableCells: selectedCells, availableFonts } = storeToRefs(useMainStore())
const { handleElement, handleElementId, selectedTableCells: selectedCells } = storeToRefs(useMainStore())
const themeColor = computed(() => slidesStore.theme.themeColor)
const fontSizeOptions = [

View File

@ -131,10 +131,7 @@
search
searchLabel="搜索字体"
@update:value="value => updateTheme({ fontName: value as string })"
:options="[
...availableFonts,
...WEB_FONTS
]"
:options="FONTS"
/>
</div>
<div class="row">
@ -303,7 +300,7 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store'
import { useSlidesStore } from '@/store'
import type {
Gradient,
GradientType,
@ -315,7 +312,7 @@ import type {
LineStyleType,
} from '@/types/slides'
import { PRESET_THEMES } from '@/configs/theme'
import { WEB_FONTS } from '@/configs/font'
import { FONTS } from '@/configs/font'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import useSlideTheme from '@/hooks/useSlideTheme'
import { getImageDataURL } from '@/utils/image'
@ -336,7 +333,6 @@ import Modal from '@/components/Modal.vue'
import GradientBar from '@/components/GradientBar.vue'
const slidesStore = useSlidesStore()
const { availableFonts } = storeToRefs(useMainStore())
const { slides, currentSlide, viewportRatio, theme } = storeToRefs(slidesStore)
const moreThemeConfigsVisible = ref(false)

View File

@ -71,7 +71,7 @@
import { onMounted, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useSlidesStore } from '@/store'
import { SYS_FONTS, WEB_FONTS } from '@/configs/font'
import { FONTS } from '@/configs/font'
import useSlideTheme from '@/hooks/useSlideTheme'
import Tabs from '@/components/Tabs.vue'
import Button from '@/components/Button.vue'
@ -99,10 +99,7 @@ const activeTab = ref<'single' | 'all'>('single')
const fontMap = ref<{ [key: string]: string }>({})
onMounted(() => {
const map: { [key: string]: string } = {}
for (const item of SYS_FONTS) {
map[item.value] = item.label
}
for (const item of WEB_FONTS) {
for (const item of FONTS) {
map[item.value] = item.label
}
fontMap.value = map

View File

@ -8,10 +8,7 @@
search
searchLabel="搜索字体"
@update:value="value => emitRichTextCommand('fontname', value as string)"
:options="[
...availableFonts,
...WEB_FONTS
]"
:options="FONTS"
>
<template #icon>
<IconFontSize />
@ -253,7 +250,7 @@ import { ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from '@/store'
import emitter, { EmitterEvents } from '@/utils/emitter'
import { WEB_FONTS } from '@/configs/font'
import { FONTS } from '@/configs/font'
import useTextFormatPainter from '@/hooks/useTextFormatPainter'
import message from '@/utils/message'
@ -271,7 +268,7 @@ import RadioButton from '@/components/RadioButton.vue'
import RadioGroup from '@/components/RadioGroup.vue'
import PopoverMenuItem from '@/components/PopoverMenuItem.vue'
const { richTextAttrs, availableFonts, textFormatPainter } = storeToRefs(useMainStore())
const { richTextAttrs, textFormatPainter } = storeToRefs(useMainStore())
const { toggleTextFormatPainter } = useTextFormatPainter()

View File

@ -22,6 +22,7 @@ import { indentCommand, textIndentCommand } from '@/utils/prosemirror/commands/s
import { toggleList } from '@/utils/prosemirror/commands/toggleList'
import { setListStyle } from '@/utils/prosemirror/commands/setListStyle'
import type { TextFormatPainterKeys } from '@/types/edit'
import message from '@/utils/message'
import { KEYS } from '@/configs/hotkey'
const props = withDefaults(defineProps<{
@ -118,10 +119,14 @@ const execCommand = ({ target, action }: RichTextCommand) => {
const actions = ('command' in action) ? [action] : action
for (const item of actions) {
if (item.command === 'fontname' && item.value) {
if (item.command === 'fontname' && item.value !== undefined) {
const mark = editorView.state.schema.marks.fontname.create({ fontname: item.value })
autoSelectAll(editorView)
addMark(editorView, mark)
if (item.value && !document.fonts.check(`16px ${item.value}`)) {
message.warning('字体需要等待加载下载后生效,请稍等')
}
}
else if (item.command === 'fontsize' && item.value) {
const mark = editorView.state.schema.marks.fontsize.create({ fontsize: item.value })