前言
在fabric
基礎(chǔ)系列博文中,我們通過代碼向畫布canvas
中添加矩形、圓形等對(duì)象。對(duì)于用戶,我們不能指望他們可以理解代碼,甚至編寫代碼去制作他的簡歷。你也許使用過PhotoShop或其他的繪圖軟件,這些軟件中都是讓用戶點(diǎn)擊各種圖標(biāo)來向畫布中繪制對(duì)應(yīng)的對(duì)象的。沒有使用過也沒關(guān)系,下文中有效果的預(yù)覽圖,也會(huì)教你一步一步實(shí)現(xiàn)它。
這篇博文是《前端canvas項(xiàng)目實(shí)戰(zhàn)——簡歷制作網(wǎng)站》付費(fèi)專欄系列博文的第一篇——左側(cè)工具欄,主要的內(nèi)容有:
- 實(shí)現(xiàn)工具欄,使用戶可以通過點(diǎn)擊鼠標(biāo)在畫布中添加想要的對(duì)象。
一、效果展示
-
動(dòng)手體驗(yàn)
CodeSandbox會(huì)自動(dòng)對(duì)代碼的進(jìn)行編譯,并提供地址以供體驗(yàn)代碼效果
由于CSDN的鏈接跳轉(zhuǎn)有問題,會(huì)導(dǎo)致頁面無法工作,請(qǐng)復(fù)制以下鏈接在瀏覽器打開:
https://wvclr3.csb.app/ -
動(dòng)態(tài)效果演示

- 本節(jié)之后,我們的簡歷能做成什么樣子

二、實(shí)現(xiàn)步驟
在前面的博文HTML5畫布框架fabricjs學(xué)習(xí)筆記(三)——自定義選擇控制框樣式中,我們通過fabric.Object.prototype.xxx = ...;
的方式按照自己的喜好,對(duì)選擇框的控制點(diǎn)樣式做了一些定制化的修改。這篇博文的內(nèi)容開始前,我們先對(duì)代碼結(jié)構(gòu)進(jìn)行一點(diǎn)優(yōu)化,避免canvas-page.js
文件里寫太多代碼,不便于維護(hù)。
1. 拆分舊代碼,優(yōu)化項(xiàng)目結(jié)構(gòu)
首先,我們把上述的代碼拆分到一個(gè)wrap-object.js
文件中,其含義即對(duì)fabric.Object
類進(jìn)行封裝。
import { fabric } from "fabric";
import shortUUID from "short-uuid";
import rotateControlIcon from "../images/rotate.svg";
fabric.Object.prototype.noScaleCache = false;
// 修改控制點(diǎn)的樣式
fabric.Object.prototype.transparentCorners = false;
fabric.Object.prototype.cornerColor = "white";
fabric.Object.prototype.borderColor = "dodgerblue";
fabric.Object.prototype.cornerStrokeColor = "gray";
fabric.Object.prototype.cornerSize = 8;
fabric.Object.prototype.cornerStyle = "circle";
fabric.Object.prototype.strokeUniform = true;
fabric.Object.prototype.padding = 10;
/**
* 覆蓋fabric.Object的initialize方法,為對(duì)象添加id屬性
* @type {function(...[*]): fabric.Object.initialize}
*/
fabric.Object.prototype.initialize = (function (fn) {
return function (...args) {
fn.call(this, ...args);
let { id } = args;
id ||= shortUUID().new();
this.set("id", id);
this.objectCaching = false;
return this;
};
})(fabric.Object.prototype.initialize);
const renderIcon = (image, initialAngle) => {
let imageIcon = document.createElement("img");
imageIcon.src = image;
return function (ctx, left, top, styleOverride, fabricObject) {
let size = this.cornerSize;
ctx.save();
ctx.translate(left, top);
ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle + initialAngle));
ctx.drawImage(imageIcon, -size / 2, -size / 2, size, size);
ctx.restore();
};
};
/**
* 修改旋轉(zhuǎn)控制按鈕的樣式
* @type {Control}
*/
fabric.Object.prototype.controls.mtr = new fabric.Control({
x: 0,
y: -0.5,
offsetY: -20,
cursorStyle: "pointer",
actionName: "rotate",
actionHandler: fabric.controlsUtils.rotationWithSnapping,
cursorStyleHandler: fabric.controlsUtils.rotationStyleHandler,
withConnection: false,
render: renderIcon(rotateControlIcon, 0),
cornerSize: 20
});
顯而易見,這里一共三部分:
- 修改選擇框線、控制點(diǎn)等的樣式
- 覆蓋
fabric.Object
的initialize
方法,為對(duì)象添加id
屬性 - 覆蓋
mtr
中間頂部旋轉(zhuǎn)控制點(diǎn)的樣式
2. 左側(cè)工具欄
左側(cè)工具欄用于用戶點(diǎn)擊后,在畫布中創(chuàng)建出其想要的基礎(chǔ)圖形。初步,我們先實(shí)現(xiàn)最基礎(chǔ)的四種對(duì)象:矩形
、圓形
、直線
、文字輸入框
這里創(chuàng)建一個(gè)left-side-tools.js
,代碼如下:
import "./index.css";
import squareIcon from "../../images/square.svg";
import circleIcon from "../../images/circle.svg";
import lineIcon from "../../images/line.svg";
import textIcon from "../../images/typeface.svg";
import { handleClickTool } from "./logics";
const Tool = (props) => {
let { icon, handleClick } = props;
return (
<div className="left-side-tool" onClick={handleClick}>
<img className="left-side-tool-icon" src={icon} alt="Icon of tool" />
</div>
);
};
const LeftSideTools = (props) => {
let { canvas } = props;
return (
<div className="left-side-tools-container">
<Tool icon={squareIcon} handleClick={(e) => handleClickTool(e, 0, canvas)} />
<Tool icon={circleIcon} handleClick={(e) => handleClickTool(e, 1, canvas)} />
<Tool icon={lineIcon} handleClick={(e) => handleClickTool(e, 2, canvas)} />
<Tool icon={textIcon} handleClick={(e) => handleClickTool(e, 3, canvas)} />
</div>
);
};
export default LeftSideTools;
UI部分的實(shí)現(xiàn)比較簡單,就是在頁面左側(cè)繪制四個(gè)icon
,點(diǎn)擊后調(diào)用handleClickTool
方法去分別向畫布中創(chuàng)建對(duì)應(yīng)的對(duì)象。
為了提高UI和JS邏輯的維護(hù)性,handleClickTool
放在同目錄下的另一個(gè)文件logics.js
中,其代碼如下:
import { fabric } from "fabric";
const handleClickTool = (e, func, canvas) => {
if (!canvas) {
return;
}
let newFabricObject;
switch (func) {
case 0:
newFabricObject = new fabric.Rect();
break;
case 1:
newFabricObject = new fabric.Circle();
break;
case 2:
newFabricObject = new fabric.Line();
break;
case 3:
newFabricObject = new fabric.Textbox();
break;
default:
throw RangeError(`Func ${func} not implemented!`);
}
if (newFabricObject) {
canvas.add(newFabricObject);
canvas.renderAll();
canvas.setActiveObject(newFabricObject);
canvas.renderAll();
}
e.stopPropagation();
e.preventDefault();
};
export { handleClickTool };
其代碼邏輯可大致分為3個(gè)部分:
1) 根據(jù)用戶點(diǎn)擊的不同icon
傳遞過來的func
創(chuàng)建不同對(duì)象的實(shí)例
2) 將上述實(shí)例通過canvas.add
方法繪制到畫布中
3) 避免click
事件向父標(biāo)簽傳播
3. 組合代碼
在新的canvas-page.js
中,我們將上述的代碼“組裝”,實(shí)現(xiàn)本節(jié)所要達(dá)到的功能,其代碼如下:
import "./index.css";
import "../wrap-fabric/wrap-object";
import "../wrap-fabric/wrap-rect";
import "../wrap-fabric/wrap-circle";
import "../wrap-fabric/wrap-line";
import "../wrap-fabric/wrap-text";
import { fabric } from "fabric";
import React, { useEffect, useState } from "react";
import LeftSideTools from "./left-side-tools";
const CanvasPage = (props) => {
const [canvas, setCanvas] = useState(null);
useEffect(() => {
let canvas = new fabric.Canvas("canvas");
setCanvas(canvas);
}, []);
return (
<div className="content-container">
<LeftSideTools canvas={canvas} />
<canvas id="canvas" width="794px" height="1123px" />
</div>
);
};
export default CanvasPage;
這里有幾點(diǎn)需要解釋說明:
1) 我們通過#content-container
將剛剛實(shí)現(xiàn)的左側(cè)工具欄LeftSideTools
和畫布canvas
橫向并排置于屏幕中
2) 畫布的大小設(shè)為width="794px" height="1123px"
,這是1倍屏幕分辨率
下A4
紙大小210mm x 297mm
計(jì)算得到的像素值大小,具體的計(jì)算公式我們?cè)谕硇┑牟┪闹性僬?br> 3) 我們?cè)诒竟?jié)開頭提到的wrap-object.js
通過import ../wrap-object
引入了進(jìn)來,這樣的代碼拆分可以使UI和JS邏輯更便于閱讀和維護(hù)
三、Show u the code
由于篇幅所限,盡數(shù)列出所有代碼改動(dòng)會(huì)導(dǎo)致博文冗長難讀。本節(jié)完整的代碼托管在CodeSandbox中,點(diǎn)擊前往,查看完整代碼
后記
本節(jié)之后,我們可以用現(xiàn)有的功能將簡歷做成什么樣子,我將圖片貼到了博文開頭,為了讀者可以快速理解通過這節(jié)的代碼我們可以實(shí)現(xiàn)什么效果。
點(diǎn)擊左側(cè)工具欄,不同的對(duì)象會(huì)被繪制在畫布左上角,我們通過拖拽、按下并拖拽9
個(gè)控制點(diǎn)來改變?cè)搶?duì)象的位置
、寬
、高
、旋轉(zhuǎn)角度
這幾個(gè)屬性,以此實(shí)現(xiàn)上述“簡歷”的樣式。文章來源:http://www.zghlxwxcb.cn/news/detail-804543.html
目前我們?cè)陧撁嫔线€不能改變對(duì)象的其他樣式,如邊框顏色
、填充顏色
等。下一節(jié),我們通過右側(cè)屬性欄
來實(shí)現(xiàn)這些需求。文章來源地址http://www.zghlxwxcb.cn/news/detail-804543.html
到了這里,關(guān)于前端canvas項(xiàng)目實(shí)戰(zhàn)——簡歷制作網(wǎng)站(一)——左側(cè)工具欄的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!