前端canvas项目实战——简历制作网站(二)——右侧属性栏(颜色)

news2025/1/11 8:44:27

目录

  • 前言
  • 一、效果展示
  • 二、实现步骤
    • 1. 实现一个自定义的选色板
    • 2. 创建属性工厂,为每个对象定制属性
    • 3. 为canvas对象注册监听器,点击不同对象时更新属性列表
  • 三、Show u the code
  • 后记

前言

上一篇博文中,我们实现了左侧工具栏,通过点击工具栏,我们可以自由得在画布中添加想要的对象。

这篇博文是《前端canvas项目实战——简历制作网站》付费专栏系列博文的第二篇——右侧属性栏(颜色),主要的内容有:

  1. 初步实现右侧属性栏,使用户可以修改画布中选中的对象的边框填充颜色。

一、效果展示

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

  • 动态效果演示

  • 本节之后,我们的简历能做成什么样子
    我们可以修改画布背景色、图形、线条和文字的颜色了。

二、实现步骤

知道了这节我们要实现的功能,接下来就开始实现。为了便于理解,我将代码拆分为4个部分进行讲解。

1. 实现一个自定义的选色板

从上述的动态图中可以看到,我们通过一个选色板来选择为画布中的对象索要设置的颜色。这里我们讲相关的代码写到一个palette.js文件中。

import paletteIcon from "../images/palette.svg";
import "./index.css";
import { ChromePicker } from "react-color";
import React, { useState } from "react";
import { Dropdown, Space } from "antd";
import transparentIcon from "../images/transparent.svg";

const _parseHex2Rgb = (hex) => {
  return {
    r: parseInt(hex.substring(1, 3), 16),
    g: parseInt(hex.substring(3, 5), 16),
    b: parseInt(hex.substring(5, 7), 16),
    a: 1,
  };
};

let ColorPicker = (props) => {
  let { handleChange, initColor } = props;

  let [color, setColor] = useState({
    rgb: { ...initColor.rgb, a: 1 },
    hex: initColor.hex,
  });

  function _handleChange(color, isCommonColor) {
    if (color.hex === "") {
      color.hex = "#FFFFFF";
      color.rgb = { r: 255, g: 255, b: 255, a: 0 };
    }

    if (!color.hasOwnProperty("rgb") && color.hex) {
      color.rgb = _parseHex2Rgb(color.hex);
    }

    setColor(color);
    handleChange && handleChange(color.hex);
  }

  return (
    <ChromePicker width="100%" color={color.hex} onChange={(color) => _handleChange(color)}/>
  );
};

const CustomePalette = (props) => {
  let { title, color, handleChange } = props;

  if ("" === color || "transparent" === color) {
    color = "#FFFFFF";
  }

  const overlay = (
    <ColorPicker initColor={{ hex: color, rgb: _parseHex2Rgb(color) }}
      handleChange={(value) => handleChange(value)} />
  );
  const overlayShadow = "0 3px 6px -4px rgb(0 0 0 / 12%), 0 6px 16px 0 rgb(0 0 0 / 8%), 0 9px 28px 8px rgb(0 0 0 / 5%)";

  return (
    <div className="palette">
      <span className="property-title">{title}</span>
      <Dropdown dropdownRender={() => overlay} overlayClassName="overlay"
        overlayStyle={{ boxShadow: overlayShadow }} trigger="click" placement="bottom">
        <div className="property-container">
          <div className="property-color-bar" style={{ backgroundColor: color }} />
          <img alt="选色盘" src={paletteIcon} className="palette-icon" />
        </div>
      </Dropdown>
    </div>
  );
};

export { Palette };

代码很清晰,分为3个部分:

  • 定义一个16进制色彩值到rgb色彩值的转换方法
  • 引入react-color库中的ChromePicker选色盘进行自定义封装。安装依赖需要在命令行执行npm install react-color
    • 设定了宽度width
    • 设置了一个ReactHook(useState),监听选色盘上当前选中的颜色值变化
    • 监听了onChange事件,当用户改变了选中的颜色值,执行传入的handleChange方法
  • 使用上述自定义封装的选色盘CustomePickerantd库的DropDown下拉菜单组件封装一个选色模块。即初始时显示下图中的颜色条和选色盘icon,点击后出现下拉菜单,其中显示选色盘。

2. 创建属性工厂,为每个对象定制属性

显而易见,画布中不同的对象有不同的属性。如矩形和圆有描边填充两种颜色属性,而线条和文字只有填充这一种。

这里,我们创建一个object-props.js

import { Palette } from "../../components/palette";
import { Actions as actions } from "../../modules/actions";
import { connect } from "react-redux";
import ObjectAPI from "../../apis/objectAPI";

const ObjectProps = (props) => {
  let { canvas, activeObject, activeObjectProperties } = props;
  let { stroke, fill } = activeObjectProperties;

  function handleChange(key, newValue) {
    ObjectAPI.updateProperty(activeObject, key, newValue);
  }

  // 1. 封装自定义的选色盘,实现描边和填充两种选色器
  const StrokeWrapper = (props) => {
    return (
      <div className="property-row" key={props.key}>
        <Palette title="描边" color={stroke} handleChange={(value) => handleChange("stroke", value)} />
      </div>
    );
  };

  const FillWrapper = (props) => {
    return (
      <div className="property-row" key={props.key}>
        <Palette title="填充" color={fill} handleChange={(value) => handleChange("fill", value)} />
      </div>
    );
  };

  const wrapperNameEntityMap = {
    StrokeWrapper: StrokeWrapper,
    FillWrapper: FillWrapper
  };

  // 2. 分别为不同的对象类型定制属性组
  const propertyWrapperMap = {
    rect: ["StrokeWrapper", "FillWrapper"],
    circle: ["StrokeWrapper", "FillWrapper"],
    textbox: ["FillWrapper"],
    line: ["StrokeWrapper"]
  };

  function handleClick() {
    canvas.discardActiveObject();
    canvas.renderAll();
    handleClearActiveObject();
  }
  
  // 3. 绘制对象的属性组
  return (
    <div className="property-list">
      <div className="property-list-header">
        <div className="property-list-header-tab" active="true" style={{ paddingLeft: "1.5rem" }}>
          <span>对象属性</span>
        </div>
        <div className="property-list-header-tab" onClick={handleClick}>
          <span>画布</span>
        </div>
      </div>
      {propertyWrapperMap[type].map((item, index) => {
        let wrapperEntity = wrapperNameEntityMap[item];
        return wrapperEntity({ key: type + "-" + index });
      })}
    </div>
  );
};
const mapStateToProps = (state) => {
  return {
  	canvas: state.canvas,
    activeObject: state.activeObject,
    activeObjectProperties: state.activeObjectProperties
  };
};

export default connect(mapStateToProps, null)(ObjectProps);

由上述代码可见,属性工厂分为3个部分:

  • 使用上文中实现的自定义选色器实现描边填充两种属性的操作模块
  • 分别为不同的对象类型定制属性列表
  • 将属性列表依次绘制出来

如下图,选中矩形对象,它的颜色属性有两个;选中文字对象它的颜色属性就只有一个

选中矩形
选中文字

注意: 其中使用了react-redux作为中央数据仓库,篇幅所限,不对其做深入的讲解,你可以自行搜索,也可以点击文章末尾CodeSandbox链接,直接看代码学习。如确实需要讲解,请在评论中留言,需要的人数较多的话,我会在react基础博文中新增一篇来介绍。

3. 为canvas对象注册监听器,点击不同对象时更新属性列表

由上述动图可见,我们点击画布中不同的对象时,右侧的属性栏会立即更新为当前选中对象的属性。要实现这里我们需要给画布对象canvas注册一个监听器。

  /**
   * 为画布添加监听器
   * @param canvas 画布
   */
  static addListeners(canvas) {
    // 初次选中对象时的监听器
    canvas.on("selection:created", () =>
    	// 向中央数据仓库更新画布中选中的对象
    	store.dispatch(actions.updateActiveObject(canvas.getActiveObject()));
    );

    // 更新选中对象时的监听器
    canvas.on("selection:updated", () =>
    	// 向中央数据仓库更新画布中选中的对象
    	store.dispatch(actions.updateActiveObject(canvas.getActiveObject()));
    );

    // 取消选中对象之前的监听器
    canvas.on("before:selection:cleared", (e) => {
      // 从中央数据仓库中清除画布中选中的对象
      store.dispatch(actions.clearActiveObject);
    });
  }

核心的代码很简洁,这里我们为canvas添加了3个监听器,下面分别介绍:

  • selection:created:选中被创建时,这里我们向中央数据仓库更新画布中选中的对象
  • selection:updated:选中更新时,这里的操作同上
  • before:selection:cleared:选中被清除之前,这里我们从中央数据仓库中清除画布中选中的对象。

注意: 这个方法可以写在任何地方,在创建canvas之后调用addListener(canvas)即可。这里我们对中央数据仓库中activeObject的任何更新,都会立即被属性工厂中监听到,同时立即渲染当前所需的属性列表。


三、Show u the code

按照惯例,本节的完整代码我也托管在了CodeSandbox中,点击前往,查看完整代码


后记

本文中,我们注册了canvas的3种监听事件。除此之外,它还有很多监听事件,比如:

  • after:render:画布重绘后
  • object:moving:对象移动时
  • object:rotating:对象被旋转时
  • object:added:对象被添加到画布中后
  • object:removed:对象被从画布中移除后

更多的监听事件,我们在后续博文中如有用到,还会详细讲解。

本节中,我们让用户可以在页面中随意修改各种对象的颜色。下一节中,我们将会对线条Line的样式编辑能力做更多的扩展。

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

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

相关文章

数字安全网:深入解析服务容错的三大绝招“

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 数字安全网&#xff1a;深入解析服务容错的三大绝招" 前言什么是服务雪崩降级&#xff1a;优雅的后退熔断&#xff1a;保卫系统的守护者流量整形&#xff1a;平衡与优化 前言 想象一下&#xff…

【java】常见的面试问题

目录 一、异常 1、 throw 和 throws 的区别&#xff1f; 2、 final、finally、finalize 有什么区别&#xff1f; 3、try-catch-finally 中哪个部分可以省略&#xff1f; 4、try-catch-finally 中&#xff0c;如果 catch 中 return 了&#xff0c;finally 还会执行吗&#…

Tosei 自助网络店铺管理系统network_test.php_RCE漏洞复现

简介 Tosei 自助洗衣机是日本一家公司的产品,在 network_test.php 文件存在命令执行 漏洞复现 FOFA语法: body="tosei_login_check.php" 主要是日本 访问界面如下所示: 验证POC: /cgi-bin/network_test.php 拼接访问url: https://ip:port/cgi-bin/network_tes…

(四)ros中ros::init(argc,argv,”节点名称”)。中的节点名称和launch文件中的节点名称关系。

1、使用rosrun 命令执行ros程序: Rosrun <功能包名称> <节点名称>。其中”节点名称”为ros::init中的ros节点名称。 2、使用launch 文件 name 参数为ros节点名称。 如果采样launch启动ros程序&#xff0c;launch文件中的ros节点名称会替换ros::init中的节点名称…

深入理解sysbench工具

文章目录 一、概述二、安装2.1、源码编译安装2.2、命令行安装2.3、安装确认 三、重要参数详解3.1、查询支持的参数3.2、重要参数说明 四、实例4.1、CPU性能测试4.2、内存性能测试4.3、IO性能测试4.4、POSIX线程性能测试4.5、多线程调度测试 团队博客: 汽车电子社区 一、概述 sy…

华为路由器IPv6基础配置

1. R2的两个接口均采用静态IPv6地址配置方法 2. R1的GigabitEthernet0/0/3接口采用无状态 地址配置 3. R3的GigabitEthernet0/0/3接口采用DHCPv6 的方式配置IPv6地址 R1配置 ipv6 #全局使能IPv6 interface GigabitEthernet0/0/0ipv6 enable ipv6 address auto link-local #为…

学习JavaEE的日子 Day17 面向对象版学生管理系统

Day17 面向对象版学生管理系统 代码已放在资源里&#xff0c;有需要可自取&#xff01;&#xff01;&#xff01; 1.需求分析 管理的是一个一个的学生对象 学生类&#xff1a; public class Student{String name&#xff1b;char sex;int age;String classId;//班级号String …

1.26 day3 C++

设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostream>using namespace std; cla…

大文件传输之以太网UDP传输延迟解决方案

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业最宝贵的资产之一。随着企业规模的扩大和业务的全球化&#xff0c;大文件传输的需求日益增长&#xff0c;它不仅关系到企业内部数据的高效管理&#xff0c;也是与外部合作伙伴进行有效沟通的关键。然而&#xff0c;大文件…

aardio - 调用C编写的dll时的不同参数类型处理方法

import console; //生成 DLL import tcc; var c tcc(); c.code /** #include <stdio.h> #include <stdlib.h> #include <stdbool.h>#ifdef __cplusplus #define EXTERN_C extern "C" __declspec(dllexport) #else #define EXTERN_C __declsp…

23111 C++ day3

思维导图 设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostream>using namespac…

零基础学编程工具简介,中文编程开发工具

零基础学编程工具简介&#xff0c;中文编程开发工具 一、前言 零基础自学编程&#xff0c;中文编程工具下载&#xff0c;中文编程工具构件之扩展系统菜单构件教程 编程系统化教程链接https://jywxz.blog.csdn.net/article/details/134073098?spm1001.2014.3001.5502 给大家…

Ansys APDL如何查看已经施加的约束和载荷

目录 查看当前已经施加的载荷和约束 查看具体的值 查看已经定义的参数 查看当前已经施加的载荷和约束 在菜单栏选择&#xff1a; 通常在有限元单元的视图下&#xff1a; 选择SOLID MODEL LOADS&#xff08;如下&#xff09;可查看当前已经施加的载荷和约束。 也可以看所有…

Redis核心技术与实战【学习笔记】 - 1.Redis为什么高性能

作为键值数据库&#xff0c;Redis 的应用非常广泛&#xff0c;如果你是后端工程师&#xff0c;我猜你出去面试&#xff0c;八成都会被问到与它相关的性能问题。比如说&#xff0c;为了保证数据的可靠性&#xff0c;Redis 需要在磁盘上读写 AOF 和 RDB&#xff0c;但在高并发场景…

使用Halcon匹配助手进行模板匹配

使用Halcon匹配助手进行模板匹配 文章目录 使用Halcon匹配助手进行模板匹配1. 选择匹配方法2. 创建模板3. 检测模板4. 优化匹配速度 使用Halcon匹配助手&#xff0c;可以很方便地选择模板图像&#xff0c;设置匹配参数&#xff0c;并测试匹配结果.Halcon匹配助手支持下面几种匹…

#常见问题总结#在docker中跑前端vue项目

目录 前言一、no such file or directory, open...总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 记录在docker中跑前端项目过程中&#xff0c;我遇到的问题以及解决方法 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一…

2022年至2023年广东省职业院校技能大赛高职组“信息安全管理与评估”赛项样题

2022 年至 2023 年广东省职业院校技能大赛高职组“信息安全管理与评估”赛项样题 一、 第一阶段竞赛项目试题 本文件为信息安全管理与评估项目竞赛第一阶段试题&#xff0c;第一阶段内容包 括&#xff1a;网络平台搭建、网络安全设备配置与防护。 本阶段比赛时间为 180 分钟…

HBuilderX插件

HBuilderX>工具插件安装 安装新插件 前往插件市场安装 1.DCloud插件市场 https://ext.dcloud.net.cn/ 2.GitHub官网 插件项目(下载zip) 本地离线包 离线安装插件 https://hx.dcloud.net.cn/Tutorial/OfflineInstall open /Applications/HBuilderX.app/Contents/HBuilderX/p…

Nginx安装以及具体应用

文章目录 Centos7安装NginxNginx命令Nginx具体应用反向代理 location指令说明负载均衡动静分离 Nginx.conf配置详解 Centos7安装Nginx 下载地址&#xff1a;nginx: download 中间这个就是tar.gz包 Centos7安装Nginx 下载nginx-1.16.1.tar.gz上传到Centos7中的/user/local目…

论述Python中列表、元组、字典和集合的概念

Python列表是用于存储任意数目、任意类型的数据集合&#xff0c;包含多个元素的有序连续的内存空间&#xff0c;是内置可变序列&#xff0c;或者说可以任意修改。在Python中&#xff0c;列表以方括号&#xff08;[ ]&#xff09;形式编写。 Python元组与Python列表类似&#x…