前端canvas项目实战——简历制作网站(三)——右侧属性栏(线条宽度样式)
目录
- 前言
- 一、效果展示
- 二、实现步骤
- 1. 实现线条宽度(strokeWidth)的属性模块
- 2. 实线线条样式(strokeDashArray)的属性模块
- 3. 意料之外的“联动”
- 三、Show u the code
- 后记
前言
上一篇博文中,我们初步实现了右侧属性栏,通过属性栏,我们可以便捷得修改画布中对象的颜色相关属性。
这篇博文是《前端canvas项目实战——简历制作网站》付费专栏系列博文的第三篇——右侧属性栏(线条宽度&样式),主要的内容有:
- 针对线条对象: 扩充属性列表,使用户可以修改画布中选中的线条的宽度和样式(实线、虚线、点线等)。
一、效果展示
-
动手体验
CodeSandbox会自动对代码进行编译,并提供地址以供体验代码效果
由于CSDN的链接跳转有问题,会导致页面无法工作,请复制以下链接在浏览器打开:
https://4z795q.csb.app/ -
动态效果演示

- 本节之后,我们的简历能做成什么样子
我们可以修改线条的宽度和样式了。

二、实现步骤
本节的实现的功能在我们的简历模板上没有太多的改变(只改变了蓝色分隔线的宽度,1 --> 2),但作为一个通用的编辑器,我们实现的功能还是很有价值的。下面开始实现:
1. 实现线条宽度(strokeWidth)的属性模块
这里我们要继续修改上一篇博文中的属性工厂——object-props.js
,向其中添加一个控件StrokeWidthWrapper
const StrokeWidthWrapper = (props) => {const strokeWidthOptions = [1, 2, 3, 4, 5];// 下拉菜单选项列表const optionViews = strokeWidthOptions.map((option, index) => {return (<Option className="property-stroke-width"key={`stroke-width-${option}`} value={option} title={option}><div className="property-stroke-width-line" style={{ height: `${option}px` }} /></Option>);});return (<div className="property-row" key={props.key}><span className="property-title">宽度</span><div className="property-container"><Select value={strokeWidth} bordered={false} style={{ width: "100%" }}onChange={(value) => { handleChange("strokeWidth", value) }>{optionViews}</Select></div></div>);};
代码很简洁,分为 3 个部分:
- 定义可选的线条宽度列表
strokeWidthOptions
,这里我设置了最小为1
,最大为5
,可以根据自己的需要做出调整。 - 通过
strokeWidthOptions
构造出下拉菜单的选项列表optionViews
- 组装模块
注意:
- 构造下拉菜单选项的代码中有一行
<div className="property-stroke-width-line" style={{ height:
${option}px}} />
,这里的作用是将下拉菜单的每个选项绘制成一条宽度对应的线条,如下图所示:2. 实现一个新模块后,要记得添加到属性列表中: ![]()
const propertyWrapperMap = {...line: ["StrokeWrapper", "StrokeWidthWrapper"],...};
2. 实线线条样式(strokeDashArray)的属性模块
strokeDashArray
和strokeWidth
类似,但实现更复杂一些
const strokeDashArrayWrapper = (props) => {const strokeDashArrayOptions = [{ key: "实线", fabricValue: null, cssValue: "solid" },{ key: "虚线", fabricValue: [5, 5], cssValue: "dashed" },{ key: "点线", fabricValue: [1, 1], cssValue: "dotted" },];/*** 根据传入的value,返回对应的index*/const mapValueToIndex = (value) => {if (null !== value && Array.isArray(value)) {for (let i = 1; i < strokeDashArrayOptions.length; i++) {if (strokeDashArrayOptions[i].fabricValue[0] === value[0]) {return i;}}}return 0;};const optionViews = strokeDashArrayOptions.map((option, index) => {return (<Option className="property-stroke-width" key={`stroke-dasharray-${index}`}value={index} title={option.key}><div className="property-stroke-dasharray-line"style={{ borderTopStyle: option.cssValue }} /></Option>);});return (<div className="property-row" key={props.key}><span className="property-title">线条</span><div className="property-container"><Select value={mapValueToIndex(_recoverValue(strokeDashArray, strokeWidth))}bordered={false} style={{ width: "100%" }}onChange={(value) =>let adjustedValue = _adjustValue(strokeDashArrayOptions[value].fabricValue, strokeWidth);handleChange("strokeDashArray", adjustedValue);}>{optionViews}</Select></div></div>);};
可以看到,strokeDashArray
的实现复杂很多,这里分别讲解:
- 方法
_adjustValue
和_recoverValue
: 这两个方法在这里没有给出代码,在下一小节会讲到。 - 这个模块的 value 不直接设置,设置的是选项列表中的索引
index
(0, 1, 2, …),原因:antd
的<Select.Option>
不允许设置value=null
,且只接受string
和number
类型。fabric.Line.strokeDashArray
的默认值是null
,同时接受数组(例如[2, 2]
)作为参数。
- 和
strokeWidth
类似,这个模块的下拉菜单选项也由<div>
标签来绘制。- 我们使用的是它的
border-top-style
来表示线段样式,但其接受的值为string
,比如实线为solid
,虚线为dashed
。 - 所以这个模块的
value
拆分为fabricValue
和cssValue
两个。
- 我们使用的是它的
strokeDashArray实现的效果如下:

3. 意料之外的“联动”
本以为实现可以到此为止了,但是在测试中发现了一个问题。当我设置线条的样式为虚线(strokeDashArray=[5, 5]
) 时,如果将线条宽度strokeWidth
从1
逐渐增大到5
,一条细的虚线会变成粗的点线,如下图所示:


但按照常理,虚线加粗之后应该仍是虚线! 说明我们的实现还存在问题,对于上述strokeWidth
和strokeDashArray
两部分代码,我们作出以下调整:
- 将
strokeWidth
作为strokeDashArray
的系数 “联动” 起来,例如strokeWidth=1
时虚线的strokeDashArray=[5, 5]
,strokeWidth=2
时,strokeDashArray
就变为[5 * 2, 5 * 2] = [10, 10]
。 - 则有了以下代码,分别用来根据当前的线条宽度缩放
strokeDashArray
数组中的每一位:
const _adjustValue = (value, factor) => {if (null === value) {return null;}return value.map((item) => item * factor);};const _recoverValue = (value, factor) => {if (null === value) {return null;}return value.map((item) => Math.round(item / factor));};
- 用户修改了线条宽度,应该同时通过
_adjustValue
和_recoverValue
更新strokeDashArray
。修改strokeWidthWrapper
中<Select>
标签的onChange
方法为:
onChange={(value) => {let adjustedValue = _adjustValue(_recoverValue(strokeDashArray, strokeWidth), value);handleChange("strokeDashArray", adjustedValue);handleChange("strokeWidth", value);}}
经过上述的调整,一条虚线的宽度由1
放大到5
时,仍是虚线。


三、Show u the code
按照惯例,本节的完整代码我也托管在了CodeSandbox中,点击前往,查看完整代码
后记
本节中,我们为线条对象Line
实现了修改宽度和样式的属性模块。
在下一节中,我们会为线条两端加上端点,如箭头、圆点、菱形等。
相关文章:

前端canvas项目实战——简历制作网站(三)——右侧属性栏(线条宽度样式)
目录 前言一、效果展示二、实现步骤1. 实现线条宽度(strokeWidth)的属性模块2. 实线线条样式(strokeDashArray)的属性模块3. 意料之外的“联动” 三、Show u the code后记 前言 上一篇博文中,我们初步实现了右侧属性栏…...

字节跳动二面经典题目
前言 语论即为「语兴式论语」,以语录体及对话的形式,沉淀球友实际工作学习中存在的疑难杂症解答,希望能够更好的帮助到球友和粉丝。欢迎关注公众号:语数 本期投稿 本期语数精选来源于球友应对字节跳动二面时候的场景问题 数仓工程…...

微搭低代码从入门到精通01应用介绍
目录 1 学习路线图2 应用介绍3 编辑器介绍总结 低代码的概念于2014年由 Forrester 首次正式提出。其将低代码定义为:能够以“最少的手写代码”和设置快速开发应用、配置和部署业务应用程序。 不同应用厂商的解法不一样,Gartner评估了400多款低代码/无代码…...

论文阅读《thanking frequency fordeepfake detection》
项目链接:https://github.com/yyk-wew/F3Net 这篇论文从频域的角度出发,提出了频域感知模型用于deepfake检测的模型 整体架构图: 1.FAD: 频域感知分解,其实就是利用DCT变换,将空间域转换为频域ÿ…...

ArcgisForJs快速入门
文章目录 0.引言1.前端代码编辑工具2.使用ArcgisForJs创建一个简单应用3.切片地图服务图层4.动态地图服务图层5.地图事件 0.引言 ArcGIS API for JavaScript是一款由Esri公司开发的用于创建WebGIS应用的JavaScript库。它允许开发者通过调用ArcGIS Server的REST API,…...
【解决方法】git pull报错ssh: connect to host github.com port 22: Connection timed out
问题 git pull ssh: connect to host github.com port 22: Connection timed out fatal: Could not read from remote repository.解决方法 在C:\Users\username.ssh文件夹下新建config文件,填入以下文本(如有则直接在文件最后一行新增)&am…...
30天精通Nodejs--第三十天:项目实战-物联网应用
目录 引言架构设计编码创建项目数据服务模拟设备消息接收并保存设备数据后端接口项目启动及接口测试项目启动测试源码地址结语引言 在之前的一系列文章中,我们已系统性地探讨了诸多Node.js相关的技术要点与理论背景。随着知识体系的铺垫到位,我们现在步入了实战环节。接下来…...

java 社区资源管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
一、源码特点 java Web社区资源管系统是一套完善的java web信息管理系统 ,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql5.…...

网络编程套接字(Socket)
为什么需要网络编程??? -丰富的网络资源 每天你在b站上刷着喜欢的up主的视频,实质是通过网络,获取到网络上的一个视频资源 与本地打开文件类似,只是视频文件这个资源来源是网络 所谓的网络编程,其实就是从网络上获取各种数据资源 什么是网络编程?? 网络编程,指的是网络…...

C语言第十一弹---函数(下)
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 函数 1、嵌套调用和链式访问 1.1、嵌套调用 1.2、链式访问 2、函数的声明和定义 2.1、单个文件 2.2、多个文件 2.3、static 和 extern 2.3.1、static…...

Unity读书系列《Unity3D游戏开发》——拓展编辑器(一)
文章目录 前言一、扩展Project视图1、右键扩展菜单(Asset)2、监听事件3、拓展布局 二、扩展Hierarchy视图1、拓展菜单(GameObject)2、拓展布局3、重写菜单 三、扩展Inspector视图1、扩展原生组件2、扩展继承组件 四、扩展Scene视图…...

【Git】项目管理笔记
文章目录 本地电脑初始化docker报错.gitignoregit loggit resetgit statusgit ls-filesgit rm -r -f --cached拉取仓库文件更新本地的项目报错处理! [rejected] master -> master (fetch first)gitgitee.com: Permission denied (publickey).error: remote origin already e…...
中文词性标注工具pkuseg例子(运行结果,不太好)
pkuseg_demo.md pkuseg 预训练模型 预训练模型science 安装 pip3 install pkuseg cd /rot/pkuseg_home/model/wget https://github.com/lancopku/pkuseg-python/releases/download/v0.0.25/science.zip uzip science.zip -d ./science/ ls /rot/pkuseg_home/model/science/…...
获取URL参数:split方法、URLSearchParams方法示例
在JavaScript中,可以使用多种方法来获取URL参数,其中常用的方法有split()和URLSearchParams()。 使用split()方法获取URL参数: split()方法将字符串分割成数组。可以使用split()方法将URL分割成协议、主机、路径和查询字符串等部分。然后可…...

SparkSql---用户自定义函数UDFUDAF
文章目录 1.UDF2.UDAF2.1 UDF函数实现原理2.2需求:计算用户平均年龄2.2.1 使用RDD实现2.2.2 使用UDAF弱类型实现2.2.3 使用UDAF强类型实现 1.UDF 用户可以通过 spark.udf 功能添加自定义函数,实现自定义功能。 如:实现需求在用户name前加上"Name:…...

系统架构15 - 软件工程(3)
软件过程模型 瀑布模型特点缺点 原型化模型特点两个阶段不同类型注意 螺旋模型V 模型特点 增量模型特点 喷泉模型基于构件的开发模型(CBSD)形式化方法模型敏捷模型特点“适应性” (adaptive) 而非“预设性” (predictive)“面向人的” (People-oriented) 而非“面向过程的” (P…...

两个近期的计算机领域国际学术会议(软件工程、计算机安全):欢迎投稿
近期,受邀担任两个国际学术会议的Special session共同主席及程序委员会成员(TPC member),欢迎广大学界同行踊跃投稿,分享最新研究成果。期待这个夏天能够在夏威夷檀香山或者加利福尼亚圣荷西与各位学者深入交流。 SERA…...

(二十一)Flask之上下文管理第二篇(细细扣一遍源码)
每篇前言: 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者 🔥🔥本文已收录于Flask框架从入门到实战专栏:《Flask框架从入…...

Java项目:基于SSM框架实现的企业员工岗前培训管理系统(ssm+B/S架构+源码+数据库+毕业论文)
一、项目简介 本项目是一套ssm821基于ssm框架实现的企业员工岗前培训管理系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格…...

深入了解Redis:选择适用于你的场景的持久化方案
自然语言处理的发展 文章目录 自然语言处理的发展强烈推荐前言:Redis提供了几种主要的持久化方案:RDB快照持久化:工作原理: AOF日志文件持久化:混合持久化: 总结强烈推荐专栏集锦写在最后 强烈推荐 前些天…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...