TextboxStylePanel.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. <template>
  2. <div class="text-style-panel">
  3. <ElementPermission ele-type="text"/>
  4. <ElementPosition/>
  5. <el-divider style="margin: 12px 0"/>
  6. <el-row>
  7. <el-col :span="12">
  8. <el-select v-model="elementFontFamily" placement="left" @change="handleElementFontFamily">
  9. <el-option-group v-for="group in fontOptionGroups" :key="group.label" :label="group.label">
  10. <template v-if="group.type == 0">
  11. <el-option v-for="item in group.options" :key="item" :value="item.value" :label="item.label"
  12. :style="{fontFamily: item.value}"></el-option>
  13. </template>
  14. <template v-else>
  15. <el-option v-for="item in group.options" :key="item.id" :value="item.fontFamily" :label="item.fontName">
  16. <el-image style="height: 18px; width: 90px" :src="item.fontThumbUrl"></el-image>
  17. </el-option>
  18. </template>
  19. </el-option-group>
  20. </el-select>
  21. </el-col>
  22. <el-col :span="12">
  23. <el-select
  24. v-model="handleElement.fontSize"
  25. filterable
  26. allow-create
  27. default-first-option
  28. :filterMethod="handleElementInputSize"
  29. placement="left"
  30. @change="handleElementFontSize"
  31. >
  32. <el-option v-for="item in FontSizeLibs" :key="item" :label="item" :value="item"></el-option>
  33. </el-select>
  34. </el-col>
  35. </el-row>
  36. <el-row class="mt-10">
  37. <el-col :span="6">
  38. <el-tooltip placement="top" content="文字颜色" :hide-after="0">
  39. <div @click.stop class="tooltip-popover">
  40. <el-popover trigger="click" placement="bottom" :width="265" @click.stop>
  41. <template #reference>
  42. <el-button class="font-color">
  43. <TextColorButton :color="handleElement.color">
  44. <IconText/>
  45. </TextColorButton>
  46. </el-button>
  47. </template>
  48. <ColorPicker :modelValue="handleElement.color"
  49. @update:modelValue="(color: string) => updateFontColor(color)"/>
  50. </el-popover>
  51. </div>
  52. </el-tooltip>
  53. </el-col>
  54. <el-col :span="6">
  55. <el-tooltip placement="top" content="背景颜色" :hide-after="0">
  56. <div @click.stop class="tooltip-popover">
  57. <el-popover trigger="click" placement="bottom" :width="265">
  58. <template #reference>
  59. <el-button class="high-light">
  60. <TextColorButton :color="elementBackgrounColor">
  61. <IconHighLight/>
  62. </TextColorButton>
  63. </el-button>
  64. </template>
  65. <ColorPicker :modelValue="elementBackgrounColor"
  66. @update:modelValue="(color: string) => updateBackgroundColor(color)"/>
  67. </el-popover>
  68. </div>
  69. </el-tooltip>
  70. </el-col>
  71. <el-col :span="12">
  72. <el-button-group class="full-group">
  73. <el-tooltip placement="top" content="增大字号" :hide-after="0">
  74. <el-button class="font-size" @click="handleElementFontsize('+')">
  75. <IconFontSize/>
  76. +
  77. </el-button>
  78. </el-tooltip>
  79. <el-tooltip placement="top" content="减小字号" :hide-after="0">
  80. <el-button @click="handleElementFontsize('-')">
  81. <IconFontSize/>
  82. -
  83. </el-button>
  84. </el-tooltip>
  85. </el-button-group>
  86. </el-col>
  87. </el-row>
  88. <el-row class="mt-10">
  89. <div class="full-checkbox">
  90. <el-tooltip placement="top" content="加粗" :hide-after="0">
  91. <el-checkbox-button :value="hasFontWeight" @change="handleElementBlod()">
  92. <IconTextBold/>
  93. </el-checkbox-button>
  94. </el-tooltip>
  95. <el-tooltip placement="top" content="斜体" :hide-after="0">
  96. <el-checkbox-button v-model="hasFontStyle" @change="handleElementItalic()">
  97. <IconTextItalic/>
  98. </el-checkbox-button>
  99. </el-tooltip>
  100. <el-tooltip placement="top" content="下划线" :hide-after="0">
  101. <el-checkbox-button v-model="hasUnderline" @change="handleElementUnderline()">
  102. <IconTextUnderline/>
  103. </el-checkbox-button>
  104. </el-tooltip>
  105. <el-tooltip placement="top" content="删除线" :hide-after="0">
  106. <el-checkbox-button v-model="hasLinethrough" @change="handleElementLinethrough()">
  107. <IconStrikethrough/>
  108. </el-checkbox-button>
  109. </el-tooltip>
  110. </div>
  111. </el-row>
  112. <el-row class="mt-10">
  113. <el-button-group class="full-group">
  114. <el-tooltip placement="top" content="横向" :hide-after="0">
  115. <el-button @click="handleElementArrange(false)" :type="elementGrapheme ? 'primary': ''">
  116. <IconTextRotationNone/>
  117. </el-button>
  118. </el-tooltip>
  119. <el-tooltip placement="top" content="纵向" :hide-after="0">
  120. <el-button @click="handleElementArrange(true)" :type="!elementGrapheme ? 'primary': ''">
  121. <IconTextRotationDown/>
  122. </el-button>
  123. </el-tooltip>
  124. <el-tooltip placement="top" content="减小缩进" :hide-after="0">
  125. <el-button @click="handleElementCharSpacing('-')">
  126. <IconIndentLeft/>
  127. </el-button>
  128. </el-tooltip>
  129. <el-tooltip placement="top" content="增大缩进" :hide-after="0">
  130. <el-button @click="handleElementCharSpacing('+')">
  131. <IconIndentRight/>
  132. </el-button>
  133. </el-tooltip>
  134. </el-button-group>
  135. </el-row>
  136. <el-row class="mt-10">
  137. <el-col :span="24">
  138. <el-radio-group class="full-ratio" v-model="textAlign" @change="handleTextAlign">
  139. <el-tooltip placement="top" content="左对齐" :hide-after="0">
  140. <el-radio-button value="justify-left">
  141. <IconAlignTextLeft/>
  142. </el-radio-button>
  143. </el-tooltip>
  144. <el-tooltip placement="top" content="居中" :hide-after="0">
  145. <el-radio-button value="justify-center">
  146. <IconAlignTextCenter/>
  147. </el-radio-button>
  148. </el-tooltip>
  149. <el-tooltip placement="top" content="右对齐" :hide-after="0">
  150. <el-radio-button value="justify-right">
  151. <IconAlignTextRight/>
  152. </el-radio-button>
  153. </el-tooltip>
  154. <el-tooltip placement="top" content="两边对齐" :hide-after="0">
  155. <el-radio-button value="justify">
  156. <IconAlignTextBoth/>
  157. </el-radio-button>
  158. </el-tooltip>
  159. </el-radio-group>
  160. </el-col>
  161. </el-row>
  162. <el-row class="mt-10">
  163. <el-col :span="12">
  164. <el-tooltip placement="top" content="转曲" :hide-after="0">
  165. <el-button class="full-button" @click="handleElementCurveEx">
  166. <IconTextStyleOne/>
  167. </el-button>
  168. </el-tooltip>
  169. </el-col>
  170. <el-col :span="12">
  171. <el-tooltip placement="top" content="变形" :hide-after="0">
  172. <el-button class="full-button"
  173. :type="handleElement.type.toLowerCase() === ElementNames.ARCTEXT ? 'primary' : ''"
  174. @click="handleElementDeformation">
  175. <i class="handler-item iconfont icon-text-path"/>
  176. </el-button>
  177. </el-tooltip>
  178. </el-col>
  179. </el-row>
  180. <el-row class="mt-10" v-show="handleElement.type.toLowerCase() === ElementNames.ARCTEXT">
  181. <el-col :span="4" class="flex-align">
  182. <el-radio-group class="full-ratio" v-model="(handleElement as ArcText).showCurvature"
  183. @change="changeArcTextStatus">
  184. <el-tooltip placement="top" content="隐藏弧度" :hide-after="0"
  185. v-if="(handleElement as ArcText).showCurvature">
  186. <el-radio-button :value="false">
  187. <IconPreviewClose/>
  188. </el-radio-button>
  189. </el-tooltip>
  190. <el-tooltip placement="top" content="显示弧度" :hide-after="0" v-else>
  191. <el-radio-button :value="true">
  192. <IconPreviewOpen/>
  193. </el-radio-button>
  194. </el-tooltip>
  195. </el-radio-group>
  196. </el-col>
  197. <el-col :span="1"></el-col>
  198. <el-col :span="12" class="flex-align">
  199. <el-slider :min="66" :max="1000" :step="1" v-model="(handleElement as ArcText).radius"
  200. @change="changeArcTextRadius" size="small"></el-slider>
  201. </el-col>
  202. <el-col :span="1"></el-col>
  203. <el-col :span="6" class="flex-align">
  204. <el-input :min="1" :max="10" v-model="(handleElement as ArcText).radius" controls-position="right"
  205. size="default"/>
  206. </el-col>
  207. </el-row>
  208. <el-divider style="margin: 12px 0"/>
  209. <ElementFill/>
  210. <el-divider style="margin: 12px 0"/>
  211. <div class="row">
  212. <div style="flex: 2;">行距:</div>
  213. <el-select style="flex: 3" suffix-icon="IconRowHeight" v-model="handleElement.lineHeight"
  214. @change="changeLineHeight">
  215. <el-option v-for="item in LineHeightLibs" :key="item" :value="item" :label="item"></el-option>
  216. </el-select>
  217. <div style="flex: 1;"></div>
  218. <div style="flex: 2;">字距:</div>
  219. <el-select style="flex: 3" suffix-icon="IconFullwidth" v-model="handleElement.charSpacing"
  220. @change="changeCharSpacing">
  221. <el-option v-for="item in CharSpaceLibs" :key="item" :value="item" :label="item"></el-option>
  222. </el-select>
  223. </div>
  224. <el-divider style="margin: 12px 0"/>
  225. <ElementEffects/>
  226. <el-divider style="margin: 12px 0"/>
  227. <ElementStroke :hasStroke="hasStroke"/>
  228. <el-divider style="margin: 12px 0"/>
  229. <ElementShadow :hasShadow="hasShadow"/>
  230. <el-divider style="margin: 12px 0"/>
  231. <ElementPatterns :hasPatterns="hasPatterns"/>
  232. <el-divider style="margin: 12px 0"/>
  233. <ElementOpacity/>
  234. </div>
  235. </template>
  236. <script lang="ts" setup>
  237. import {computed, ref, onMounted} from 'vue'
  238. import {useMainStore, useTemplatesStore} from '@/store'
  239. import {storeToRefs} from 'pinia'
  240. import {ElMessage} from 'element-plus'
  241. import {FabricObject, IText, Textbox} from 'fabric'
  242. import {FontSizeLibs, LineHeightLibs, CharSpaceLibs} from '@/configs/texts'
  243. import {WEB_FONTS} from '@/configs/fonts'
  244. import {propertiesToInclude} from '@/configs/canvas'
  245. import {TextboxElement} from '@/types/canvas'
  246. import {ElementNames, FontGroupOption} from '@/types/elements'
  247. import {loadFont} from '@/utils/fonts'
  248. import {nanoid} from 'nanoid'
  249. import {ArcText} from '@/extension/object/ArcText'
  250. import {CurvedText} from '@/extension/object/CurvedText'
  251. import {VerticalText} from '@/extension/object/VerticalText'
  252. import opentype from "opentype.js"
  253. import ElementPosition from '../Components/ElementPosition.vue'
  254. import ElementStroke from '../Components/ElementStroke.vue'
  255. import ElementShadow from '../Components/ElementShadow.vue'
  256. import ElementOpacity from '../Components/ElementOpacity.vue'
  257. import ElementPatterns from '../Components/ElementPatterns.vue'
  258. import ElementEffects from '../Components/ElementEffects.vue'
  259. import ElementFill from '../Backgrounds/ElementFill.vue'
  260. import useHandleCreate from "@/hooks/useHandleCreate"
  261. import useCanvas from '@/views/Canvas/useCanvas'
  262. import ElementPermission from "@/views/Editor/CanvasRight/Components/ElementPermission.vue";
  263. import useHandleElement from "@/hooks/useHandleElement";
  264. const {
  265. selectElement,
  266. visibleElement,
  267. lockElement,
  268. deleteElement,
  269. showElement,
  270. mouseoverElement,
  271. mouseleaveElement,
  272. checkElement,
  273. } = useHandleElement()
  274. const mainStore = useMainStore()
  275. const templatesStore = useTemplatesStore()
  276. const {canvasObject, systemFonts, onlineFonts} = storeToRefs(mainStore)
  277. const {createPathElement} = useHandleCreate()
  278. const [canvas] = useCanvas()
  279. const handleElement = computed(() => canvasObject.value as Textbox | ArcText)
  280. const elementGrapheme = computed(() => handleElement.value.type.toLowerCase() !== ElementNames.VERTICALTEXT)
  281. const elementBackgrounColor = computed(() => {
  282. if (handleElement.value.type.toLowerCase() === ElementNames.ARCTEXT) {
  283. return handleElement.value.textBackgroundColor
  284. }
  285. return handleElement.value.backgroundColor
  286. })
  287. const hasFontFamily = computed(() => handleElement.value.fontFamily)
  288. const hasFontWeight = computed(() => handleElement.value.fontWeight !== 'normal')
  289. const hasFontStyle = computed(() => handleElement.value.fontStyle !== 'normal')
  290. const hasUnderline = computed(() => handleElement.value.underline)
  291. const hasLinethrough = computed(() => handleElement.value.linethrough)
  292. const textAlign = computed(() => handleElement.value.textAlign)
  293. const hasStroke = computed(() => handleElement.value.stroke ? true : false)
  294. const hasShadow = computed(() => handleElement.value.shadow ? true : false)
  295. const hasPatterns = computed(() => (handleElement.value as TextboxElement).fillType === 1 ? true : false)
  296. const elementFontFamily = ref<string>(hasFontFamily.value)
  297. const fontOptionGroups = ref<FontGroupOption[]>([
  298. {
  299. type: 0,
  300. label: '系统字体',
  301. options: systemFonts.value
  302. },
  303. {
  304. type: 1,
  305. label: '在线字体',
  306. options: onlineFonts.value
  307. }
  308. ])
  309. // 修改字体族
  310. const handleElementFontFamily = (fontFamily: string) => {
  311. if (handleElement.value.isEditing) {
  312. handleElement.value.setSelectionStyles({fontFamily})
  313. } else {
  314. templatesStore.modifedElement(handleElement.value, {fontFamily})
  315. }
  316. canvas.renderAll()
  317. }
  318. // 修改输入字体大小
  319. const handleElementInputSize = (val: string) => {
  320. val = val.replace(/[^\d]/g, '')
  321. if (val) {
  322. templatesStore.modifedElement(handleElement.value, {fontSize: val})
  323. }
  324. }
  325. // 修改字体大小
  326. const handleElementFontSize = (fontSize: string) => {
  327. fontSize = fontSize.replace(/[^\d]/g, '')
  328. if (!fontSize) return
  329. if (handleElement.value.isEditing) {
  330. handleElement.value.setSelectionStyles({fontSize})
  331. } else {
  332. templatesStore.modifedElement(handleElement.value, {fontSize})
  333. }
  334. canvas.renderAll()
  335. }
  336. // 修改字体颜色
  337. const updateFontColor = (fill: string) => {
  338. if (handleElement.value.isEditing) {
  339. handleElement.value.setSelectionStyles({fill})
  340. } else {
  341. templatesStore.modifedElement(handleElement.value, {fill, color: fill})
  342. }
  343. }
  344. // 修改背景颜色
  345. const updateBackgroundColor = (backgroundColor: string) => {
  346. let changeData: Record<string, any> = {backgroundColor}
  347. if (handleElement.value.type.toLowerCase() === ElementNames.ARCTEXT) {
  348. changeData = {'textBackgroundColor': backgroundColor}
  349. }
  350. if (handleElement.value.isEditing) {
  351. handleElement.value.setSelectionStyles(changeData)
  352. } else {
  353. templatesStore.modifedElement(handleElement.value, changeData)
  354. }
  355. }
  356. // 修改字体大小
  357. const handleElementFontsize = (mode: string) => {
  358. if (handleElement.value.fontSize <= 6) return
  359. const fontSize = mode === '+' ? Number(handleElement.value.fontSize) + 1 : Number(handleElement.value.fontSize) - 1
  360. if (handleElement.value.isEditing) {
  361. handleElement.value.setSelectionStyles({fontSize})
  362. } else {
  363. templatesStore.modifedElement(handleElement.value, {fontSize})
  364. }
  365. canvas.renderAll()
  366. }
  367. // 修改字体加粗
  368. const handleElementBlod = () => {
  369. const fontBold = 'bold', fontNormal = 'normal'
  370. if (handleElement.value.isEditing) {
  371. const blodState = handleElement.value.getSelectionStyles().find(item => item.fontWeight !== fontBold)
  372. if (!blodState || (JSON.stringify(blodState) === '{}' && handleElement.value.fontWeight === fontBold)) {
  373. handleElement.value.setSelectionStyles({'fontWeight': fontNormal})
  374. } else {
  375. handleElement.value.setSelectionStyles({'fontWeight': fontBold})
  376. }
  377. } else {
  378. const elementStyle = handleElement.value.styles
  379. if (handleElement.value.fontWeight === fontBold) {
  380. templatesStore.modifedElement(handleElement.value, {fontWeight: fontNormal})
  381. for (let i in elementStyle) {
  382. for (let j in elementStyle[i]) {
  383. (elementStyle[i][j] as TextboxElement).set({fontWeight: fontNormal})
  384. }
  385. }
  386. } else {
  387. templatesStore.modifedElement(handleElement.value, {fontWeight: fontBold})
  388. for (let i in elementStyle) {
  389. for (let j in elementStyle[i]) {
  390. (elementStyle[i][j] as TextboxElement).set({fontWeight: fontBold})
  391. // elementStyle[i][j].fontWeight = fontBold
  392. }
  393. }
  394. }
  395. }
  396. }
  397. // 修改斜体
  398. const handleElementItalic = () => {
  399. const fontStyle = handleElement.value.fontStyle === 'italic' ? 'normal' : 'italic'
  400. if (handleElement.value.isEditing) {
  401. handleElement.value.setSelectionStyles({fontStyle})
  402. } else {
  403. templatesStore.modifedElement(handleElement.value, {fontStyle})
  404. }
  405. }
  406. // 修改删除线
  407. const handleElementLinethrough = () => {
  408. if (handleElement.value.isEditing) {
  409. handleElement.value.setSelectionStyles({linethrough: !handleElement.value.linethrough})
  410. } else {
  411. templatesStore.modifedElement(handleElement.value, {linethrough: !handleElement.value.linethrough})
  412. }
  413. }
  414. // 修改中划线
  415. const handleElementUnderline = () => {
  416. if (handleElement.value.isEditing) {
  417. handleElement.value.setSelectionStyles({underline: !handleElement.value.underline})
  418. } else {
  419. templatesStore.modifedElement(handleElement.value, {underline: !handleElement.value.underline})
  420. }
  421. }
  422. // 修改字体居中
  423. const handleTextAlign = (textAlign: string) => {
  424. if (handleElement.value.isEditing) {
  425. handleElement.value.setSelectionStyles({textAlign})
  426. } else {
  427. templatesStore.modifedElement(handleElement.value, {textAlign})
  428. }
  429. }
  430. // 修改缩进
  431. const handleElementCharSpacing = (mode: '+' | '-') => {
  432. const handleCharSpacing = handleElement.value.charSpacing
  433. const charSpacing = mode === '+' ? handleCharSpacing + 10 : handleCharSpacing - 10
  434. templatesStore.modifedElement(handleElement.value, {charSpacing})
  435. }
  436. const changeLineHeight = (lineHeight: number) => {
  437. if (handleElement.value.isEditing) {
  438. handleElement.value.setSelectionStyles({lineHeight: lineHeight})
  439. } else {
  440. templatesStore.modifedElement(handleElement.value, {lineHeight: lineHeight})
  441. }
  442. }
  443. const changeCharSpacing = (charSpacing: number) => {
  444. if (handleElement.value.isEditing) {
  445. handleElement.value.setSelectionStyles({charSpacing: charSpacing})
  446. } else {
  447. templatesStore.modifedElement(handleElement.value, {charSpacing: charSpacing})
  448. }
  449. canvas.renderAll()
  450. }
  451. const handleElementArrange = (status: boolean) => {
  452. const options = (handleElement.value as any).toObject(propertiesToInclude as any[])
  453. options.lineHeight = 12
  454. delete options.type
  455. options.id = nanoid(10)
  456. let textElement: FabricObject = new Textbox(handleElement.value.text, options)
  457. if (status) {
  458. textElement = new VerticalText(handleElement.value.text, options)
  459. }
  460. const activeObject = canvas.getActiveObject()
  461. if (activeObject) canvas.remove(activeObject)
  462. canvas.discardActiveObject()
  463. canvas.add(textElement)
  464. templatesStore.addElement(textElement)
  465. canvas.setActiveObject(textElement)
  466. mainStore.setCanvasObject(textElement)
  467. canvas.renderAll()
  468. }
  469. const handleElementCurve = async () => {
  470. // ElMessage
  471. let fontElement: opentype.Font | undefined
  472. if (WEB_FONTS.filter(item => item.value === hasFontFamily.value)[0]) {
  473. const fontURL = import.meta.env.MODE === 'production' ? `/assets/${hasFontFamily.value}.ttf` : `/src/assets/fonts/${hasFontFamily.value}.ttf`
  474. fontElement = await opentype.load(fontURL)
  475. } else {
  476. const fontData = await loadFont(hasFontFamily.value)
  477. if (!fontData) return
  478. const fontBlob = await fontData.blob()
  479. const fontBuffer = await fontBlob.arrayBuffer()
  480. fontElement = opentype.parse(fontBuffer)
  481. }
  482. if (!fontElement) return
  483. const path = fontElement.getPath(handleElement.value.text, 0, 0, handleElement.value.fontSize);
  484. createPathElement(path.toPathData(2), handleElement.value.left, handleElement.value.top)
  485. canvas.remove(handleElement.value)
  486. canvas.renderAll()
  487. }
  488. const handleElementCurveEx = async () => {
  489. // ElMessage
  490. let fontElement: opentype.Font | undefined
  491. const currentFont = onlineFonts.value.filter(item => item.fontFamily === hasFontFamily.value)[0]
  492. if (currentFont) {
  493. const fontURL = currentFont.fontFilePath
  494. fontElement = await opentype.load(fontURL)
  495. } else {
  496. const fontData = await loadFont(hasFontFamily.value)
  497. if (!fontData) return
  498. const fontBlob = await fontData.blob()
  499. const fontBuffer = await fontBlob.arrayBuffer()
  500. fontElement = opentype.parse(fontBuffer)
  501. }
  502. if (!fontElement) return
  503. const path = fontElement.getPath(handleElement.value.text, 0, 0, handleElement.value.fontSize);
  504. createPathElement(path.toPathData(2), handleElement.value.left, handleElement.value.top)
  505. canvas.remove(handleElement.value)
  506. canvas.renderAll()
  507. }
  508. const handleElementDeformation = () => {
  509. const options = (handleElement.value as any).toObject(propertiesToInclude as any[]) as any
  510. options.originType = options.type
  511. delete options.type
  512. options.id = nanoid(10)
  513. let text
  514. if (handleElement.value.type.toLowerCase() === ElementNames.ARCTEXT) {
  515. text = new Textbox(options.text, options)
  516. } else {
  517. text = new ArcText(options.text, options)
  518. }
  519. // handleElement.value.set({visible: false})
  520. // templatesStore.deleteElement(handleElement.value)
  521. deleteElement(handleElement.value.id)
  522. canvas.add(text)
  523. templatesStore.addElement(text)
  524. canvas.setActiveObject(text)
  525. canvas.renderAll()
  526. }
  527. const changeArcTextRadius = (val: number) => {
  528. (handleElement.value as ArcText).setRadius(val)
  529. templatesStore.modifedElement(handleElement.value, {radius: val})
  530. }
  531. const changeArcTextStatus = (showCurvature: boolean) => {
  532. (handleElement.value as ArcText).set({showCurvature})
  533. templatesStore.modifedElement(handleElement.value, {showCurvature})
  534. }
  535. </script>
  536. <style lang="scss" scoped>
  537. .text-style-panel {
  538. user-select: none;
  539. }
  540. .row {
  541. width: 100%;
  542. display: flex;
  543. align-items: center;
  544. margin-bottom: 10px;
  545. }
  546. .preset-style {
  547. display: flex;
  548. flex-wrap: wrap;
  549. margin-bottom: 10px;
  550. }
  551. .preset-style-item {
  552. width: 50%;
  553. height: 50px;
  554. border: solid 1px #d6d6d6;
  555. box-sizing: border-box;
  556. display: flex;
  557. justify-content: center;
  558. align-items: center;
  559. position: relative;
  560. cursor: pointer;
  561. transition: all $transitionDelay;
  562. &:hover {
  563. border-color: $themeColor;
  564. color: $themeColor;
  565. z-index: 1;
  566. }
  567. &:nth-child(2n) {
  568. margin-left: -1px;
  569. }
  570. &:nth-child(n+3) {
  571. margin-top: -1px;
  572. }
  573. }
  574. .font-size-btn {
  575. padding: 0;
  576. }
  577. .link-popover {
  578. width: 240px;
  579. .btns {
  580. margin-top: 10px;
  581. text-align: right;
  582. }
  583. }
  584. .mt-10 {
  585. margin-top: 10px;
  586. }
  587. .full-group {
  588. display: flex;
  589. flex: 1;
  590. .el-button {
  591. width: 50%;
  592. }
  593. }
  594. .tooltip-popover {
  595. .el-button {
  596. width: 100%;
  597. border-radius: 0;
  598. }
  599. .font-color {
  600. border-top-left-radius: 4px;
  601. border-bottom-left-radius: 4px;
  602. border-right: 0;
  603. }
  604. .high-light {
  605. border-right: 0;
  606. }
  607. }
  608. .font-size {
  609. border-top-left-radius: 0;
  610. border-bottom-left-radius: 0;
  611. }
  612. .full-ratio {
  613. display: flex;
  614. flex: 1;
  615. .el-radio-button {
  616. position: relative;
  617. display: inline-flex;
  618. outline: 0;
  619. flex: 1;
  620. }
  621. .el-radio-button__inner {
  622. width: 100%
  623. }
  624. }
  625. .flex-align {
  626. display: flex;
  627. align-items: center;
  628. }
  629. .full-checkbox {
  630. display: flex;
  631. flex: 1;
  632. }
  633. .full-button {
  634. width: 100%;
  635. .iconfont {
  636. font-size: 32px;
  637. }
  638. }
  639. </style>
  640. <style scoped>
  641. :deep(.full-ratio .el-radio-button__inner) {
  642. width: 100%;
  643. }
  644. :deep(.full-ratio .el-radio-button) {
  645. position: relative;
  646. display: inline-flex;
  647. outline: 0;
  648. flex: 1;
  649. }
  650. :deep(.full-checkbox .el-checkbox-button) {
  651. position: relative;
  652. display: inline-flex;
  653. outline: 0;
  654. flex: 1;
  655. }
  656. :deep(.full-checkbox .el-checkbox-button__inner) {
  657. width: 100%;
  658. }
  659. </style>