前端canvas项目实战——简历制作网站(一)——左侧工具栏

目录

  • 前言
  • 一、效果展示
  • 二、实现步骤
    • [1. 拆分旧代码,优化项目结构](#1. 拆分旧代码,优化项目结构)
    • [2. 左侧工具栏](#2. 左侧工具栏)
    • [3. 组合代码](#3. 组合代码)
  • [三、Show u the code](#三、Show u the code)
  • 后记

前言

fabric基础系列博文中,我们通过代码向画布canvas中添加矩形、圆形等对象。对于用户,我们不能指望他们可以理解代码,甚至编写代码去制作他的简历。你也许使用过PhotoShop或其他的绘图软件,这些软件中都是让用户点击各种图标来向画布中绘制对应的对象的。没有使用过也没关系,下文中有效果的预览图,也会教你一步一步实现它。

这篇博文是《前端canvas项目实战------简历制作网站》付费专栏系列博文的第一篇------左侧工具栏,主要的内容有:

  1. 实现工具栏,使用户可以通过点击鼠标在画布中添加想要的对象。

一、效果展示

  • 动手体验

    CodeSandbox会自动对代码的进行编译,并提供地址以供体验代码效果
    由于CSDN的链接跳转有问题,会导致页面无法工作,请复制以下链接在浏览器打开:
    https://wvclr3.csb.app/

  • 动态效果演示

  • 本节之后,我们的简历能做成什么样子


二、实现步骤

在前面的博文HTML5画布框架fabricjs学习笔记(三)------自定义选择控制框样式中,我们通过fabric.Object.prototype.xxx = ...;的方式按照自己的喜好,对选择框的控制点样式做了一些定制化的修改。这篇博文的内容开始前,我们先对代码结构进行一点优化,避免canvas-page.js文件里写太多代码,不便于维护。

1. 拆分旧代码,优化项目结构

首先,我们把上述的代码拆分到一个wrap-object.js文件中,其含义即对fabric.Object类进行封装。

javascript 复制代码
import { fabric } from "fabric";
import shortUUID from "short-uuid";
import rotateControlIcon from "../images/rotate.svg";

fabric.Object.prototype.noScaleCache = false;

// 修改控制点的样式
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方法,为对象添加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();
  };
};

/**
 * 修改旋转控制按钮的样式
 * @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
});

显而易见,这里一共三部分:

  • 修改选择框线、控制点等的样式
  • 覆盖fabric.Objectinitialize方法,为对象添加id属性
  • 覆盖mtr中间顶部旋转控制点的样式

2. 左侧工具栏

左侧工具栏用于用户点击后,在画布中创建出其想要的基础图形。初步,我们先实现最基础的四种对象:矩形圆形直线文字输入框

这里创建一个left-side-tools.js,代码如下:

javascript 复制代码
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部分的实现比较简单,就是在页面左侧绘制四个icon,点击后调用handleClickTool方法去分别向画布中创建对应的对象。

为了提高UI和JS逻辑的维护性,handleClickTool放在同目录下的另一个文件logics.js中,其代码如下:

javascript 复制代码
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个部分:

1) 根据用户点击的不同icon传递过来的func创建不同对象的实例

2) 将上述实例通过canvas.add方法绘制到画布中

3) 避免click事件向父标签传播

3. 组合代码

在新的canvas-page.js中,我们将上述的代码"组装",实现本节所要达到的功能,其代码如下:

javascript 复制代码
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;

这里有几点需要解释说明:

1) 我们通过#content-container将刚刚实现的左侧工具栏LeftSideTools和画布canvas横向并排置于屏幕中

2) 画布的大小设为width="794px" height="1123px",这是1倍屏幕分辨率A4纸大小210mm x 297mm计算得到的像素值大小,具体的计算公式我们在晚些的博文中再谈

3) 我们在本节开头提到的wrap-object.js通过import ../wrap-object引入了进来,这样的代码拆分可以使UI和JS逻辑更便于阅读和维护


三、Show u the code

由于篇幅所限,尽数列出所有代码改动会导致博文冗长难读。本节完整的代码托管在CodeSandbox中,点击前往,查看完整代码


后记

本节之后,我们可以用现有的功能将简历做成什么样子,我将图片贴到了博文开头,为了读者可以快速理解通过这节的代码我们可以实现什么效果。

点击左侧工具栏,不同的对象会被绘制在画布左上角,我们通过拖拽、按下并拖拽9个控制点来改变该对象的位置旋转角度这几个属性,以此实现上述"简历"的样式。

目前我们在页面上还不能改变对象的其他样式,如边框颜色填充颜色等。下一节,我们通过右侧属性栏来实现这些需求。

相关推荐
BBB努力学习程序设计15 小时前
CSS Sprite技术:用“雪碧图”提升网站性能的魔法
前端·html
BBB努力学习程序设计16 小时前
CSS3渐变:用代码描绘色彩的流动之美
前端·html
冰暮流星16 小时前
css之动画
前端·css
jump68016 小时前
axios
前端
spionbo16 小时前
前端解构赋值避坑指南基础到高阶深度解析技巧
前端
用户40993225021216 小时前
Vue响应式声明的API差异、底层原理与常见陷阱你都搞懂了吗
前端·ai编程·trae
开发者小天16 小时前
React中的componentWillUnmount 使用
前端·javascript·vue.js·react.js
永远的个初学者17 小时前
图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
前端·vue.js·react.js
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ17 小时前
npm i / npm install 卡死不动解决方法
前端·npm·node.js
Kratzdisteln17 小时前
【Cursor _RubicsCube Diary 1】Node.js;npm;Vite
前端·npm·node.js