目錄
- 前言
- 一、效果展示
- 二、實現步驟
- 1. 優化代碼,提取常量
- 2. 實現3個編輯模塊
- 3. 實現updateFontProperty方法
- 4. 一個常見的用法:僅更新當前選中文字的樣式
- 三、Show u the code
- 后記
前言
上一篇博文中,我們擴充了線條對象(fabric.Line)的屬性列表,使用戶可以為畫布中選中的線條增加或修改端點樣式(多種剪頭、圓形、菱形等)。
這篇博文是《前端canvas項目實戰——簡歷制作網站》付費專欄系列博文的第五篇——右側屬性欄(字體、字號、行間距),主要的內容有:
- 針對文本框(fabric.Textbox)對象: 擴充屬性列表,使用戶可以修改畫布中選中的文本框的字體、字號和行間距。
- 在實現了整體更新文本框屬性的基礎上,還實現了僅更新用戶選中的部分文字的樣式。
如有需要,可以:
- 點擊這里,返回第一篇《前端canvas項目實戰——簡歷制作網站(一)——左側工具欄》
- 點擊這里,返回上一篇《前端canvas項目實戰——簡歷制作網站(四)——右側屬性欄(線條端點樣式)》
一、效果展示
-
動手體驗
CodeSandbox會自動對代碼進行編譯,并提供地址以供體驗代碼效果
由于CSDN的鏈接跳轉有問題,會導致頁面無法工作,請復制以下鏈接在瀏覽器打開:
https://cy37vt.csb.app/ -
動態效果演示

- 本節之后,我們的簡歷能做成什么樣子
我們可以修改字體、字號和行間距了

二、實現步驟
作為簡歷,其中包含最多的就是文字。因此,文字的各種樣式至關重要,我們在本節初步實現字體、字號和行間距的編輯能力。
1. 優化代碼,提取常量
當我們實現越來越多的編輯模塊時,object-props.js
文件越來越大。其中包含了很多我們為下拉菜單的菜單項設置的常量,這些常量不需要經常修改,但占據了很多行。為了避免這個文件過于龐大,以及做好Modal
和View
的分離,我們將這些數據拆分到一個constants.js
常量文件中。
const fontFamilyOptions = [{ key: "黑體", value: "SimHei", platform: "windows" },{ key: "黑體", value: "STHeitiSC-Light", platform: "mac" },..."Times New Roman","Georgia",...
];const fontSizeOptions = [{ key: "初號", value: 36 },{ key: "小初", value: 31 },...9,10,...
];const lineHeightOptions = [ 0, 2,4, ...
];export { fontFamilyOptions, fontSizeOptions, lineHeightOptions };
我們將本節新增定義的fontFamilyOptions
, fontSizeOptions
和lineHeightOptions
提取到了這個文件中,它們分別是字體、字號和行間距的可取值列表。
2. 實現3個編輯模塊
這里的3個編輯模塊的代碼邏輯大同小異,且互相獨立,各不影響。所以僅列出fontFamily
字體屬性編輯模塊的實現。
import { fontFamilyOptions } from "../../apis/constants";const isWindows = navigator.userAgent.toUpperCase().indexOf("WIN") !== -1;const isMac = navigator.userAgent.toUpperCase().indexOf("MAC") !== -1;const FontFamilyWrapper = (props) => {const optionFilter = (option) => {return (!option.hasOwnProperty("key") ||(option.platform === "windows" && isWindows) ||(option.platform === "mac" && isMac));};const menuItems = useMemo(() => {return fontFamilyOptions.filter(optionFilter).map((option, index) => {let _key = option, _value = option;if (option.hasOwnProperty("key")) {_key = option.key;_value = option.value;}return (<Option key={`font-family-${_key}`} value={_value} style={{ fontFamily: _value }}>{_key}</Option>);});});return (<div className="property-row" key={props.key}><span className="property-title">字體</span><div className="property-container"><Select value={fontFamily} bordered={false} style={{ width: "100%" }}onChange={(value) => canvasAPI.updateFontProperty("fontFamily", value)}>{menuItems}</Select></div></div>);};
由上述代碼可見,共分為4個部分,下面分別講解:
- 從常量文件
constants.js
中引入定義好的字體常量- 由于同樣的字體在
Windows
和Mac
系統中的名稱不同,而我們的項目目前沒有后臺的支持,所以暫且區分不同系統來實現字體。這里在后續章節中開始實現后臺服務器時將會重構。- 組裝下拉菜單的菜單項,這里與之前的章節類似。
- 繪制字體的下拉菜單,這里用戶選擇了不同的字體后,
onChange
事件調用canvasAPI.updateFontProperty
方法去更新整個文本框的字體,讓每個字符都使用相同的字體。
3. 實現updateFontProperty方法
static updateFontProperty(key, newValue) {let { activeObject, canvas } = store.getState();ObjectAPI.updateProperty(activeObject, key, newValue);canvas.renderAll();}
這里的代碼很簡單,直接調用了updateProperty
方法,去更新整個Textbox
的fontFamily
、fontSize
和lineHeight
等屬性。
updateProperty
方法的實現在上一篇博文中有列出,這里不再贅述。如需回顧,可點擊前往
4. 一個常見的用法:僅更新當前選中文字的樣式
在目前的實現中,我們可以直接設置整個Textbox
的屬性,這樣其中的所有字符都會設置為相同的屬性。但有時,我們會選中其中的部分文字,然后單獨更新它們的屬性。 這個時候就需要更加細致的實現來區分兩種情況。
首先確定目標,我們要實現的效果如下圖所示:

我們實現以下代碼在一個CanvasAPI.js
文件中:
// 1. 判斷是否對整個Textbox更新屬性static shouldUpdateTheWholeTextbox(object, key) {return !object?.isEditing || key === "lineHeight";}// 2. 更新Textbox的屬性static updateFontProperty(key, newValue) {let { activeObject, canvas } = store.getState();if (this.shouldUpdateTheWholeTextbox(activeObject, key)) {CanvasAPI._updateFontPropertyForWholeObject(key, newValue);} else {CanvasAPI._updateFontPropertyForSelection(key, newValue);}canvas.renderAll();}// 3. 更新整個Textbox的屬性static _updateFontPropertyForWholeObject(key, newValue) {let { activeObject } = store.getState();ObjectAPI.updateProperty(activeObject, key, newValue);}// 4. 更新Textbox中當前選中的部分文字的屬性static _updateFontPropertyForSelection(key, newValue) {let { activeObject, canvas } = store.getState();let style = {};style[key] = newValue;activeObject.setSelectionStyles(style);}
代碼邏輯比較清晰,共分為4個方法,下面分別解析:
1.
shouldUpdateTheWholeTextbox
方法: 判斷當前狀態下,應該整體更新文本框的屬性,還是僅更新選中的文字的屬性。邏輯上,處于非編輯態的文本框適用前者,否則適用后者。這里有一個特殊屬性lineHeight
行間距。根據定義,這個屬性只能對整個文本框設置,不能僅對選中的文字設置。
2.updateFontProperty
方法: 前文中實現過的方法,這里根據shouldUpdateTheWholeTextbox
的返回值區分兩種狀態,調用不同的方法更新文本框屬性。
3._updateFontPropertyForWholeObject
方法: 更新整個文本框的屬性。
4._updateFontPropertyForSelection
方法: 僅更新文本框中當前選中的文字的屬性。
三、Show u the code
按照慣例,本節的完整代碼我也托管在了CodeSandbox中,點擊前往,查看完整代碼
后記
這篇博文中,我們實現了對文字的字體、字號和行間距的編輯。同時,根據我們日常的使用習慣,實現了對部分用戶選中的文字的屬性進行單獨地編輯。
作為簡歷中占據最大部分空間的文字對象,本章的實現很大程度上實現了用戶在制作簡歷時的核心需要。當然,這還遠遠不夠。在下一篇博文中,我們將實現設置文字的加粗、斜體、刪除線、下劃線等能力。
如有需要,可以:
- 點擊這里,返回第一篇《前端canvas項目實戰——簡歷制作網站(一)——左側工具欄》
- 點擊這里,返回上一篇《前端canvas項目實戰——簡歷制作網站(四)——右側屬性欄(線條端點樣式)》