前端canvas项目实战——简历制作网站(六):加粗、斜体、下划线、删除线(上)

目录

  • 前言
  • 一、效果展示
  • 二、实现步骤
    • 1. 视图部分:实现用于切换字体属性的按钮
    • 2. 逻辑部分:点击按钮之后要做什么?
    • 3. 根据Textbox的属性实时更新按钮的状态
  • 三、Show u the code
  • 后记

前言

上一篇博文中,我们实现了对文字的字体、字号和行间距的编辑。

这篇博文是《前端canvas项目实战——简历制作网站》付费专栏系列博文的第六篇——加粗、斜体、下划线、删除线,主要的内容有:

  1. 针对文本框(fabric.Textbox)对象: 扩充属性列表,使用户可以为画布中选中的文本框设置加粗、斜体、下划线和删除线

如有需要,你可以:

  • 点击这里,返回第一篇《前端canvas项目实战——简历制作网站(一)——左侧工具栏》
  • 点击这里,返回上一篇《前端canvas项目实战——简历制作网站(五):右侧属性栏(字体、字号、行间距)》
  • 点击这里,前往下一篇《前端canvas项目实战——简历制作网站(七):加粗、斜体、下划线、删除线(下)》

一、效果展示

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

  • 动态效果演示

  • 本节之后,我们的简历能做成什么样子
    我们可以对部分文字设置加粗,将标题或重要文字和其他部分区别开来,使面试官可以快速定位到重点信息。

二、实现步骤

1. 视图部分:实现用于切换字体属性的按钮

在这里,我把这种按钮命名为SwitchValueButton

import "./index.css";
import React from "react";
import { Button, Tooltip } from "antd";const SwitchValueButton = (props) => {let { handleClick, tip, style, className, children } = props;let _props = {type: "text",className: className || "property-operation",onClick: (e) => handleClick(e),style: {...style,height: "40px",padding: "0.5rem 0.75rem",},};return (<Tooltip title={tip}><Button {..._props}>{children}</Button></Tooltip>);
};export default SwitchValueButton;

代码很简单,实现了一个带有图标的按钮。因为并不是每个用户都可以一看到图标就明白这个按钮的作用,所以还为按钮加上了提示。 即当鼠标的光标移动到按钮上时,按钮上方会弹出对应的Tips,表明这个按钮的作用。

有了这个基础的按钮组件,相信大家实现一组 4 个按钮也没有什么困难。

2. 逻辑部分:点击按钮之后要做什么?

这里的代码和上一节有部分交集,但仍有很多改动。为了便于说明,仍列出了部分在上节出现过的代码。

const updateFontProperty = (key, newValue) => {let { activeObject, canvas } = store.getState();if (_shouldUpdateTheWholeTextbox(activeObject, key)) {_updateFontPropertyForWholeObject(key, newValue);} else {_updateFontPropertyForSelection(key, newValue);}canvas.renderAll();
};const _shouldUpdateTheWholeTextbox = (object, key) => {let { text, isEditing, selectionStart, selectionEnd } = object;let isSelectAll = text.length === selectionEnd - selectionStart;// Bug点1:容易忘记考虑编辑状态时,选中了全部文字if (!isEditing || isSelectAll) {return true;}return key === "lineHeight";
};const _updateFontPropertyForWholeObject = (key, newValue) => {let { activeObject } = store.getState();// Bug点2:由于部分文字的样式优先级高于整个文本框的样式,先清除整个文本框的对应属性activeObject.removeStyle(key);newValue = _getNewValueForWholeObject(key, newValue);ObjectAPI.updateProperty(activeObject, key, newValue);
};const _updateFontPropertyForSelection = (key, newValue) => {let { activeObject, canvas } = store.getState();let { selectionStart, selectionEnd } = activeObject;// Bug点3:编辑态时,如果没有选中任何文字,不做任何处理if (selectionEnd === selectionStart) {return;}newValue = _getNewValueForSelection(key, newValue);let style = {};style[key] = newValue;activeObject.setSelectionStyles(style);// 优化点:删除冗余的数据activeObject.minifySelectionStyles();
};const _getNewValueForWholeObject = (key, value) => {let { activeObject } = store.getState();if (defaultValues.hasOwnProperty(key)) {value = defaultValues[key].init;if (activeObject[key] === value) {value = defaultValues[key].target;}}return value;
};const _getNewValueForSelection = (key, value) => {let { activeObject } = store.getState();if (defaultValues.hasOwnProperty(key)) {let { init, target } = defaultValues[key];let selectionStyles = activeObject.getSelectionStyles();let valueOfFirstChar = selectionStyles[0][key];if (valueOfFirstChar === undefined) {value = activeObject[key] === init ? target : init;} else {value = activeObject[key];}}return value;
};

一共6个方法,分为3个部分来讲解:

  • 入口方法及分支判断逻辑
    • updateFontProperty方法: 作为入口方法,本节实现的加粗、斜体、下划线、删除线等4个按钮的点击事件都直接指向它,例如为一个Textbox添加下划线:handleClick={() => updateFontProperty("underline", true)}
    • _shouldUpdateTheWholeTextbox方法: 分支判断方法,判断当前应该为整个Textbox设置新的属性值,还是仅为用户选中的部分文字设置局部的属性。
  • 分支1:为整个Textbox设置新的属性值
    • _updateFontPropertyForWholeObject方法: 通过下面的_getNewValueForWholeObject方法获取到新的属性值后,将新值直接设置给Textbox对象。
    • _getNewValueForWholeObject方法: 获取新值。
      • 如果属性为颜色等用户从下拉菜单中选择的值,直接返回;
      • 如果属性为加粗、斜体、下划线、删除线这类点击了按钮进行切换的值,则返回取反后的值。如下划线,当前值为true则返回false,当前值为false则返回true
  • 分支2:为用户选中的部分文字设置新的属性值
    • _updateFontPropertyForSelection方法: 通过下面的_getNewValueForSelection方法获取到新的属性值后,通过setSelectionStyles方法为选中的文字设置新的属性值。
    • _getNewValueForSelection方法: 以选中的文字的第一个字符的属性为准获取新值,取值的逻辑和上述的_getNewValueForWholeObject类似。

代码注释中的Bug点和优化点在下文中会详细介绍。

3. 根据Textbox的属性实时更新按钮的状态

前面两个小节中的代码组合起来,实现了通过点击按钮来更新Textbox的属性值。现在我们反过来,将Textbox的属性状态体现在这些按钮上。先通过下面的动图来看看我们要实现的效果:

可以看到,按钮的颜色会根据我们选中的文字属性实时变化。这样就完成了Textbox属性和按钮状态的双向链接。以下是代码:

fabric.Textbox.prototype.initialize = (function (fn) {return function (text, options, id) {fn.call(this, text, options, id);this.on("selection:changed", function () {store.dispatch(actions.updateActiveObject(this));});};
})(fabric.Textbox.prototype.initialize);const updateActiveObject = (newActiveObject) => {newProperties = {...fontWeight: newActiveObject.fontWeight),fontStyle: newActiveObject.fontStyle),underline: newActiveObject.underline),linethrough: newActiveObject.linethrough"),};return {...activeObjectProperties: newProperties};
}const getIconColor = (key) => {let { activeObjectProperties } = store.getState();let value = activeObjectProperties[key];let initValue = defaultValues[key].init;if (value === initValue || value === undefined) {return "#000000";}return "dodgerblue";
};

为了便于说明,这里将整个流程简化为3个方法进行介绍。如需查看完整代码,请留意文末的「Show u the code」部分。

  • 覆盖fabric原型代码: 为了在用户选择的文字更改时,可以立即更新按钮状态,这里覆盖了fabric.Textbox原型里的initialize方法,这个方法用于创建并初始化Textbox原型。我们的改动是:
    • 当初始化结束后,通过Textbox.on("selection:changed")为其添加一个时间监听器,监听selection:change事件。
    • 当代码中通过Textbox.fire("selection:changed")「引发」 一个selection:change事件时,监听器会立即执行立即执行我们设置的「回调方法」。
    • 在回调方法中,我们向中央数据仓库store分发一个数据更新任务,要求通过updateActiveObject方法更新画布中当前选中的对象。
  • updateActiveObject方法: 根据参数中新的被选中对象,更新activeObjectProperties字典。
    • 注意: 为了便于说明,这个方法中的代码有简化,实际获取属性的方法要比直接newActiveObject.xxx要复杂一些,具体见后文中的源代码。
  • getIconColor方法: 通过中央数据仓库中最新的activeObjectProperties字典,判断按钮中的图标应该是用什么颜色。
    • 如果是初始值(如下划线underline的初始值为false,目标值为true),则设置为黑色#000000
    • 否则设置为一种浅蓝色dodgerblue,上文的动图中有显示。

这样,一排美观、精致、且逻辑完整属性按钮就实现了


三、Show u the code

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


后记

这篇博文中,我们实现了用按钮设置文本框的加粗、斜体、下划线、删除线

起初觉得并不复杂的几个功能,在实现中也涌现出了一个又一个的问题。本来想一篇博文写完,但限于篇幅,拆分到两篇博文中。

上述在实现过程中发现的Bug及其解决方法、需要优化的点以及是如何优化的,我都放在了下一篇博文中。相信通过动手解决这些实现过程中遇到的,真实的阻碍和问题,我们会走上学习和成长的快速车道。

如有需要,你可以:

  • 点击这里,返回第一篇《前端canvas项目实战——简历制作网站(一)——左侧工具栏》
  • 点击这里,返回上一篇《前端canvas项目实战——简历制作网站(五):右侧属性栏(字体、字号、行间距)》
  • 点击这里,前往下一篇《前端canvas项目实战——简历制作网站(七):加粗、斜体、下划线、删除线(下)》

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

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

相关文章

【系统设计】面试问题:设计 Spotify

目录 初始阶段:基础版本估计:数据计算高层设计**数据存储**SQL数据库结构把它们放在一起规模化阶段:5000 万用户、2 亿首歌曲引入 CDN扩展数据库:领导者-跟随者技术推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战这是一道系统设计面…

优化选址问题 | 基于灰狼算法求解基站选址问题含Matlab源码

目录 问题代码问题 灰狼优化算法(Grey Wolf Optimizer, GWO)是一种基于自然界中灰狼群体狩猎行为的优化算法。这种算法通过模拟灰狼的社会等级和狩猎行为来寻找问题的最优解。 基站选址问题通常是一个多目标优化问题,涉及到覆盖范围、信号质量、成本等多个因素。使用灰狼算…

量化交易入门(十一)Python开发-数据结构

Python提供了几种内置的数据结构,可以用来存储和组织数据。以下是Python中常见的数据结构:列表&#xff0c;元组&#xff0c;字典&#xff0c;集合&#xff0c;字符串&#xff0c;栈&#xff0c;队列&#xff0c;树&#xff0c;图。我们将介绍这些数据结构&#xff0c;并举例说…

使用JMeter从JSON响应的URL参数中提取特定值

在使用Apache JMeter进行API测试时&#xff0c;我们经常需要从JSON格式的响应中提取特定字段的值。这可以通过使用JMeter内置的JSON提取器和正则表达式提取器来完成。以下是一个具体的例子&#xff0c;展示了如何从一个JSON响应中提取rowId的值&#xff0c;同时处理字符串终止符…

C#使用Poll/Select实现多路I/O复用

在实际的应用中&#xff0c;如果全部采用异步的操作来&#xff0c;会增加代码的复杂程度&#xff0c;某些时候使用Poll/Select来实现单线程多路的I/O复用会更合适一些 一、Poll 原型函数 public bool Poll ( int microSeconds, SelectMode mode ) 1&#xff1a;客户端 pri…

web服务架构

1 Web服务器&#xff08;如Nginx、Apache等&#xff09;和Web应用框架&#xff08;如Flask、Django等&#xff09; Web服务器&#xff08;如Nginx、Apache等&#xff09;和Web应用框架&#xff08;如Flask、Django等&#xff09;在Web应用开发和部署中扮演着不同的角色&#xf…

python turtle库简单应用题

三角形 import turtle for i in range(3):turtle.seth(i * 120)turtle.fd(100) turtle.done()矩形 import turtle angle 90 for x in range(4):turtle.fd(100)turtle.seth(angle)angle 90 turtle.done()五边形 import turtle angle 360/5 for x in range(5):turtle.fd(10…

软考中级 --网络工程师真题试卷 2023下半年

在EIGRP协议中&#xff0c;某个路由器收到了两条路径到达目标网络&#xff0c;路径1的带宽为100Mbps&#xff0c;延迟2ms&#xff0c;路径2的带宽为50Mbps&#xff0c;迟为4ms&#xff0c;如果EIGRP使用带宽和延迟的综合度量标准&#xff0c;那么该路由器选择的最佳路径是(D)。…

Codeforces Round 930 (Div. 2)(A,B,C,D)

比赛链接 C是个交互&#xff0c;D是个前缀和加二分。D还是很难写的。 A. Shuffle Party 题意&#xff1a; 您将得到一个数组 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1​,a2​,…,an​ 。最初&#xff0c;每个 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n 对应 a i i a_ii…

你用对const了吗?C++中const小结

const 修饰普通变量 表示变量的值不能被改变。下面两条语句(第2行和第3行)表示的意思一致。 int a; const int ca 42; //int const ca 42;const 修饰指针 指向常量的指针不能改变其指对象的值。第 5 行代码是错误的。 int a 42;const int * ip &a; int const * ipp…

win10 禁止谷歌浏览器自动更新(操作贼简单)

禁止谷歌浏览器自动更新 &#xff08;1&#xff09;修改 "C:\Windows\System32\drivers\etc\hosts 文件&#xff0c;在最后增加 127.0.0.1 update.googleapis.com&#xff08;2&#xff09;保存后&#xff0c;winr 快捷键&#xff0c;输入cmd &#xff0c;打开命令行 &am…

RK3588 rknpu2及rknn-toolkit2使用说明

RKNN模型推理共有四种方式&#xff1a; 第一种是借助RKNN-Toolkit2的功能在模拟NPU上运行RKNN模型并获取推理结果&#xff08;在PC端&#xff09; 第二种是借助RKNN-Toolkit2的功能, 将板子与PC连接&#xff0c;将RKNN模型分发到指定的NPU设备进行推理并获取推理结果&#xff0…

AJAX踩坑指南(知识点补充)

JWT JSON Web Token是目前最为流行的跨域认证解决方案 如何获取&#xff1a;在使用JWT身份验证中&#xff0c;当用户使用其凭据成功登录时&#xff0c;将返回JSON Web Token(令牌&#xff09; Token本质就是一个包含了信息的字符串 如何获取Token:登录成功之后&#xff0c;服务…

Springboot解决跨域问题方案总结(包括Nginx,Gateway网关等)

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 前言 解决跨域问题方案 1.Spring Boot 中解决跨域 1.1 通过注解跨域 1.2 通…

什么是RabbitMQ的死信队列

RabbitMQ的死信队列&#xff08;Dead Letter Queue&#xff0c;简称DLQ&#xff09;是一种用于处理消息失败或无法路由的消息的机制。它允许将无法被正常消费的消息重新路由到另一个队列&#xff0c;以便稍后进行进一步处理、分析或排查问题。 当消息对立里面的消息出现以下几…

C语言经典面试题目(二十七)

1、什么是头文件&#xff1f;为什么在C语言中需要使用头文件&#xff1f; 头文件是C语言中的一种文件&#xff0c;通常以.h为文件扩展名&#xff0c;用于存放函数声明、宏定义、结构体声明等。在C语言中&#xff0c;头文件的主要作用是将程序的接口与实现分离开来&#xff0c;…

大数据的实时计算和离线计算你理解吗?

不管是实时计算还是离线计算&#xff0c;都有着同样的业务目标&#xff0c;那就是根据业务要求把数据源计算处理成业务需要的直接可用的数据结果。 如果把数据源比作是水龙头里的水&#xff0c;把数据计算比作是生产纯净水的过程&#xff1b;那么实时计算就是用一根水管接在水龙…

css的text-shadow详解

CSS的text-shadow属性用于为文本添加阴影效果&#xff0c;以增强文本的立体感和印刷品质感。该属性可以接受多个值&#xff0c;每个值通过空格分隔&#xff0c;以定义阴影的各个方面。以下是text-shadow属性的详细介绍&#xff1a; 阴影颜色 (Color): 这是阴影的颜色值。它可以…

深度学习基础之《TensorFlow框架(10)—案例:实现线性回归(2)》

增加其他功能 一、增加变量显示 1、目的&#xff1a;在TensorBoard当中观察模型的参数、损失值等变量值的变化 2、收集变量 不同的变量要用不同的方式收集 &#xff08;1&#xff09;tf.summary.scalar(name, tensor) 收集对于损失函数和准确率等单值变量&#xff0c;name为…

ES6生成器(Generator)

一、function* 概念简介&#xff1a;function* - JavaScript | MDN (mozilla.org) function* 声明创建一个绑定到给定名称的新生成器函数。生成器函数可以退出&#xff0c;并在稍后重新进入&#xff0c;其上下文&#xff08;变量绑定&#xff09;会在重新进入时保存。 1.1 y…