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

目录

  • 前言
  • 一、效果展示
  • 二、实现步骤
    • 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的样式编辑能力做更多的扩展。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

操作系统(3)---操作系统引导

在安装操作系统后&#xff0c;磁盘的分布如下&#xff1a; C盘是这个磁盘的活动分区&#xff08;又称主分区&#xff09;&#xff0c;安装了操作系统 开机过程如下&#xff1a; 1.计算机的主存由RAM和ROM组成&#xff0c;RAM关机数据消失&#xff0c;而ROM&#xff08;Basic In…

swagger2 和 knife4j 整合

swagger整合knife4j 导入依赖 <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</version></dependency>引入配置 我们自己写一个配置类也好,我这里写…

iframe框架使用

一、什么是iframe框架 通俗讲就是将一个页面嵌入另一个页面&#xff0c;可以是本地的html,jsp也可以是网址如baidu.com 二、怎么用&#xff1f; <iframe name"uploadPage" src"/uploadImg.html" width"100%" height"50" marginh…

Rollup:打包 TypeScript - React 组件库

调用浏览器摄像头拍照组件 1、前提1、安装依赖2、添加 rollup.config.js 配置3、修改 package.json3.1 添加打包命令3.2 添加组件入口3.3 添加组件声明入口3.4 浏览器支持 1、前提 1.1 通过 create-react-app take-photo --template 创建前端应用 1.2 添加组件 TakePhoto (拍照…

磺化-Cy5-谷氨酰胺,Sulfo Cyanine5 Glutamine,被广泛应用于生物医学研究中

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;磺化-Cy5-谷氨酰胺&#xff0c;Sulfo Cyanine5 Glutamine&#xff0c;Sulfo Cy5 Glutamine 一、基本信息 产品简介&#xff1a;Sulfo Cyanine5 Glutamine作为一种荧光标记物&#xff0c;被广泛应用于生物医学研究中…

Android开发修炼之路——(一)Android App开发基础-1

本文介绍基于Android系统的App开发常识&#xff0c;包括以下几个方面&#xff1a;App开发与其他软件开发有什么不一样&#xff0c;App工程是怎样的组织结构又是怎样配置的&#xff0c;App开发的前后端分离设计是如何运作实现的&#xff0c;App的活动页面是如何创建又是如何跳转…

【C++修炼秘籍】List深度剖析

【C修炼秘籍】STL-List ☀️心有所向&#xff0c;日复一日&#xff0c;必有精进 ☀️专栏《C修炼秘籍》 ☀️作者&#xff1a;早凉 ☀️如果有错误&#xff0c;烦请指正&#xff0c;如有疑问可私信联系&#xff1b; 目录 【C修炼秘籍】STL-List 文章目录 前言 一、list介绍…

three.js中Meshline库的使用

three.js中Meshline的使用 库的地址为什么要使用MeshLine,three.js内置的线不好用吗?MeshLine入门MeshLine的深入思考样条曲线一个问题 库的地址 https://github.com/spite/THREE.MeshLine?tabreadme-ov-file 为什么要使用MeshLine,three.js内置的线不好用吗? 确实不好用,…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-Tag标签管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

2023年CSDN年底总结-独立开源创作者第一年

2023年最大的变化&#xff0c;就是出来创业&#xff0c;当独立开源创作者&#xff0c;这一年发起SolidUI开源项目&#xff0c;把知乎重新开始运营起来。CSDN粉丝破万&#xff0c;CSDN博客专家和AI领域创作者。 2023年年度关键词&#xff1a;创业 https://github.com/CloudOrc…

HCIP:三层架构

配置r1 配置r2 给sw1在vlanif1上配地址&#xff0c;并且ping 23.1.1.1 可通 给sw2配地址 在sw1上做汇总 在sw2上做汇总 在sw1上启动 在sw2上启动 在sw1上启动vlan 在sw2上启动vlan 在sw1上给接口配置vlan 在sw2上给接口配置vlan 在sw3上划分vlan 在sw4上划分vlan 给sw5划分vlan…

小红书如何做混部?

作者&#xff1a;宋泽辉&#xff08;小红书&#xff09;、张佐玮&#xff08;阿里云&#xff09; 编者按&#xff1a; Koordinator 是一个开源项目&#xff0c;是基于阿里巴巴内部多年容器调度、混部实践经验孵化诞生&#xff0c;是行业首个生产可用、面向大规模场景的开源混…

【RabbitMQ】延迟队列之死信交换机

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《RabbitMQ实战》。&#x1f3af;&#x1f3af; &am…

10.多柱状图(MuliBarChart)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET 7、MAUI 话接上回&#xff08;9.单柱状图&#xff08;SingleBarChart&#xff09;&#xff09;&#xff0c;从单柱拓展到多柱状图。 1.数据设置 private void InitValue(List<BasicSerieDto> dtos){Serie…

[ACM学习] 进制转换

进制的本质 本质是每一位的数位上的数字乘上这一位的权重 将任意进制转换为十进制 原来还很疑惑为什么从高位开始&#xff0c;原来从高位开始的&#xff0c;可以被滚动地乘很多遍。 将十进制转换为任意进制

泽众云真机-机型集中化运维方案升级全面完成

2024年元月份&#xff0c;泽众云真机运维团队&#xff0c;经过几个月软硬件多轮安装调试&#xff0c;机型集中化运维方案升级全面完成。解决了云真机的机型集中化运维难题&#xff0c;方便了运营人员手机管理。 具体如下&#xff1a; 1、集中化运维&#xff0c;如服务器、PC、…

数学建模学习笔记||一文了解美赛论文如何写作

目录 ​编辑 Title/标题 要求 形式 Summary Sheet/摘要 要求 三要素 书写特点 内容 开头段 中间段 格式 内容 结尾段 关键词 Contents/目录 Introduction/引言 Problem Background/问题背景 Restatement of the Problem/问题重述 Literature Review/文献综…

RabbitMQ 笔记一

概览&#xff1a; MQ基本概念 RabbitMQ入门 基本工作模 1.MQ是什么&#xff1f; MQ:Message Queue, 存储消息的中间件&#xff0c;是消息发送过程中的暂存容器&#xff0c;主要用于解决分布式系统进程间的通信。 分布式系统通信的两种方式&#xff1a;直接远程调用、借助第三…

vue---打印本地当前时间Demo

<template><view class"content" tap"getCurrentTime()">打印时间</view> </template><script>export default {data() {return {title: Hello}},onLoad() {},methods: {getCurrentTime() {//获取当前时间并打印var _this …

E7数据库备份和恢复

E7数据库备份和恢复 一、实验目的 在Mysql上&#xff0c;学习如何备份数据库和恢复的各种方法。 二、实验要求: 1、基本硬件配置:英特尔Pentium III 以上,大于4G内存&#xff1b; 2、软件要求:Mysql&#xff1b; 3、时间:1小时&#xff1b; 4、撰写实验报告并按时提交。 三、…