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

news2025/2/6 18:06:44

目录

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


前言

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

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

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

一、效果展示

  • 动手体验
    CodeSandbox会自动对代码的进行编译,并提供地址以供体验代码效果,点击前往,自己动手体验

  • 动态效果演示
    在这里插入图片描述

  • 本节之后,我们的简历能做成什么样子
    请添加图片描述


二、实现步骤

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

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

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

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,代码如下:

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中,其代码如下:

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中,我们将上述的代码“组装”,实现本节所要达到的功能,其代码如下:

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个控制点来改变该对象的位置旋转角度这几个属性,以此实现上述“简历”的样式。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1336649.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

改变命运第一法

不与事争&#xff0c;你争不过因果&#xff1b;不与人争&#xff0c;会伤了感情&#xff1b;不与己争&#xff0c;会让人心累。平静淡泊、守望平和&#xff0c;不指责&#xff0c;不抱怨&#xff0c;不计较&#xff0c;永远做个善良清澈的自己&#xff0c;以善为本&#xff0c;…

Linux OpenEuler(欧拉系统)无公网ip实现SSH远程连接

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

【MySQL】一文看懂MySQL所有常见问题

MySQL作为一款开源关系型数据库&#xff0c;如今绝对是占据关系型数据库的主导地位&#xff0c;不仅是面试中的常客&#xff0c;也是日常工作中最主要接触的数据库。因此&#xff0c;无论是背面试八股&#xff0c;还是工作使用&#xff0c;都是一定要深度掌握的一个知识点。今天…

云上安全责任共担模型

对于传统自建物理服务器模式&#xff0c;用户需要承担所有的安全责任&#xff0c;负责从物理基础设施到上层应用的所有层面的安全体系构建。 云服务器的安全责任确实与物理服务器不同&#xff0c;云上的安全性是一种责任共担模式&#xff0c;其中云服务器ECS的安全责任需要你&…

算法通关村第十关—归并排序(黄金)

归并排序 一、归并排序原理 归并排序(MERGE-SORT)简单来说就是将大的序列先视为若干个比较小的数组&#xff0c;分成几个比较小的结构&#xff0c;然后是利用归并的思想实现的排序方法&#xff0c;该算法采用经典的分治策略&#xff08;分就是将问题分(divide)成一些小的问题分…

阶段性复习(二)

阶段性复习第二弹&#xff01; 一.操作符 继上期复习了一部分&#xff0c;我们继续复习操作符&#xff0c;今天着重复习双目操作符 有一类题&#xff0c;通过结果分析反推求过程中的表达式 从这道题可以看出&#xff0c;t是4的时候&#xff0c;s要是2&#xff0c;所以a&…

飞天使-k8s知识点7-kubernetes升级

文章目录 验证新版本有没有问题需要安装的版本微微 1.20.6.0kubeadm upgrade plan 验证新版本有没有问题 查看可用版本的包 现有的状态 查看版本 yum list kubeadm --showduplicates |grep 1.20 yum list kubelet --showduplicates |grep 1.20 yum list kubectl --showduplic…

云渲染UE4像素流送搭建(winows、ubuntu单实例与多实例像素流送)

windows/ubuntu20.4下UE4.27.2像素流送 像素流送技术可以将服务器端打包的虚幻引擎应用程序在客户端的浏览器上运行&#xff0c;用户可以通过浏览器操作虚幻引擎应用程序&#xff0c;客户端无需下载虚幻引擎&#xff0c;本文实现两台机器通过物理介质网线实现虚幻引擎应用程序…

推荐3款高效又免费的MP4转MP3格式转换工具

在日常生活和工作中&#xff0c;我们经常会遇到需要将MP4视频文件转换成MP3音频文件的情况&#xff0c;以便在其他设备上播放或享受音频内容。如果你正在寻找一款高效又免费的MP4转MP3格式转换工具&#xff0c;那么你来对地方了&#xff01;本文将为你推荐3款最佳工具&#xff…

STM32使用printf重定向到USART

配置你想要重映射的串口&#xff0c;这里用串口1举例子&#xff0c;你完全可以定义成其他串口。 波特率和位数格式完全按照需要定义&#xff0c;这里不要开启dma和中断。 这里建议使用单独生成.c和.h&#xff0c;当然你不生成也完全可以。 /* USER CODE BEGIN 0 */ int __io_p…

重装系统后桌面图标没有了,“此电脑”怎么添加?

重装电脑系统对于使用电脑的用户来说是一种很常见的操作。但是有时在重装系统之后&#xff0c;我们会发现电脑桌面图标不见了&#xff0c;那么这时该怎么办呢&#xff1f;“此电脑”该怎么添加呢&#xff1f; 解决方法&#xff1a; 1.在桌面空白处单击鼠标右键&#xff0c;选择…

九州金榜|家庭教育中注意孩子早起抑郁症征兆

在现代社会&#xff0c;心理健康问题越来越受到关注和重视。尤其是青少年&#xff0c;由于学业、人际关系等因素&#xff0c;可能会面临抑郁症的困扰。然而&#xff0c;很多家长对孩子的心理状况缺乏了解&#xff0c;未能及时发现他们心中的烦恼。以下是一些关于抑郁症早期征兆…

anaconda prompt进入虚拟环境 打开spyder

目录 1.查看有多少虚拟环境 2.conda create 指令创建新的虚拟环境 3.进入虚拟环境 4.spyder进入虚拟环境 5.退出虚拟环境 6.删除虚拟环境 1.查看有多少虚拟环境 打开anaconda prompt&#xff0c;输入 conda env list 2.conda create 指令创建新的虚拟环境 conda cre…

免费高清无水印视频素材在哪里下?

剪辑是一门创意工作&#xff0c;但很多初学者常常感到困惑&#xff0c;原因并不是因为他们不懂剪辑&#xff0c;而是因为他们不知道从哪里找到合适的素材。今天&#xff0c;我们将为大家盘点一些超全的剪辑素材资源&#xff0c;包括视频素材、音乐素材和图片素材等&#xff0c;…

Vue axios 拦截器

正常情况下打开浏览器前端页面向后端发起请求使用的是axios&#xff0c;无论是原生的axios还是自己封装的axios都看成是axios。发起请求之后后端去数据库里面拿数据&#xff0c;然后返回给前端。 发起请求的地方是axios&#xff0c;并且你能够封装这个axios&#xff0c;那么你…

R语言的极值统计学及其在相关领域中的实践技术应用

受到气候变化、温室效应以及人类活动等因素的影响&#xff0c;自然界中极端高温、极端环境污染、大洪水和大暴雨等现象的发生日益频繁&#xff1b;在人类社会中&#xff0c;股市崩溃、金融危机等极端情况也时有发生&#xff1b;今年的新冠疫情就是非常典型的极端现象。研究此类…

TensorFlow 2.0 深度学习实战 —— 浅谈卷积神经网络 CNN

卷积神经网络 CNN&#xff08;Convolutional Neural Networks&#xff0c;ConvNet&#xff09;是一种特殊的深度学习神经网络&#xff0c;近年来在物体识别、图像重绘、视频分析等多个层面得到了广泛的应用。 本文将以VGG16预训练模型为例子&#xff0c;从人脸识别、预训练模型…

【算法与数据结构】406、LeetCode根据身高重建队列

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题难点在于如何对序列进行排序&#xff0c;以及保证序列的顺序&#xff08;符合题目要求的顺序&…

中间件系列 - Redis入门到实战(高级篇-分布式缓存)

前言 学习视频&#xff1a; 黑马程序员Redis入门到实战教程&#xff0c;深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目 本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删除 学习目标 Redis持久化Redis主从Redis哨兵Redis分片集群 一 分…

如何进行实例监控

目录 行实例监控 云监控 云监控核心功能 云监控ECS实例 安装插件 查看监控图表 云监控控制台 云服务器ECS控制台 设置报警规则 行实例监控 方式一&#xff1a;在服务器上自行编写并定时运行&#xff08;计划任务&#xff09;监控脚本&#xff08;shell、python&#x…