前端canvas项目实战——在线图文编辑器(九):逻辑画布

news2024/10/7 8:22:56

目录

  • 前言
  • 一、 效果展示
  • 二、 实现步骤
    • 1. 调整布局,最大化利用屏幕空间
    • 2. 添加逻辑画布
    • 3. 添加遮罩
    • 4. 居中显示逻辑画布
    • 5. 一个容易被忽视的bug点
  • 三、Show u the code
  • 后记

前言

上一篇博文中,我们实现了一组通用的功能按钮:复制、删除、锁定和层叠顺序

这篇博文是《前端canvas项目实战——在线图文编辑器》付费专栏系列博文的第九篇——逻辑画布,主要的内容有:

  1. 调整页面布局,将画布区域扩展至整个屏幕的剩余空间中。
  2. 区分「物理画布」和「逻辑画布」,为实现「缩放」、「辅助线」等功能打基础。

如有需要,你可以:

  • 点击这里,返回第一篇《前端canvas项目实战——在线图文编辑器(一)——左侧工具栏》
  • 点击这里,返回上一篇《前端canvas项目实战——在线图文编辑器(八):复制、删除、锁定、层叠顺序》

一、 效果展示

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

  • 效果演示


二、 实现步骤

1. 调整布局,最大化利用屏幕空间

在之前的博文中,我们的实现包含「左侧工具栏」、「画布」和「右侧属性栏」3个部分。他们是依次从左到右进行排的,因此我们可以看到屏幕的右侧和下方有空余的区域,既浪费,又不美观

要处理这个问题,我们需要修改canvas-page/index.js文件中的html部分,来充分利用屏幕空间:

	.content-container {
	  width: 100%;
	  height: 100vh;
	  display: flex;
	  flex-direction: row;
	  justify-content: space-between;
	}
	
	.left-side-tools-container {
	  width: 80px;
	  height: 100%;
	  ...
	}

	.right-side-props-container {
	  width: 16.25rem;
	  height: 100%;
	  ...
	}
	
	.scalable {
	  ...
	  position: absolute;
	  top: 0;
	  bottom: 0;
	  left: 80px;
	  right: 16.25rem;
	}
	
    <div className="content-container">
        <LeftSideTools canvas={canvas}/>
        <div className="scalable">
            <canvas id="canvas"/>
        </div>
        <RightSideProps w={canvasWidth} h={canvasHeight} u={canvasSizeUnit}/>
    </div>

可以看到,html标签的排布很简洁,下面对CSS中的样式进行说明:

  • .canvas-container: 作为父级容器
    • 首先通过width: 100%;height: 100vh;占满屏幕100%的宽度和高度,为3个子标签提供足够的空间。
    • 然后通过display: flex;flex-direction: row;justify-content: space-between;设置子标签在水平方向流式布局,并按等间距排列。
  • .left-side-tools-container: 占据80px的宽度并占满父标签100%的高度。
  • .right-side-props-container: 占据16.25rem的宽度并占满父标签100%的高度。
  • .scalable: 作为canvas的父级标签,采用绝对定位:
    • 上下两端都和父标签.canvas-container对齐。
    • 左侧从80px开始,即开始于工具栏右侧。
    • 右侧从16.25rem开始,即结束于属性栏左侧。

经过这样的调整,canvas就可以填充除工具栏和属性栏外的所有屏幕空间。

2. 添加逻辑画布

通常情况下,我们并不需要自己的画布填充满所有的空余区域,但又想让它居中显示在屏幕中央。这里,我们引入「逻辑画布」的概念来实现这样的需要,以下对几个概念做简要的说明:

  • 物理画布:canvas对象所占有的全部区域。
  • 逻辑画布: 一个相对于「物理画布canvas的概念。在canvas中,将我们关注的部分区域作为逻辑画布,在逻辑画布上,我们可以添加各种对象。
  • 背景区域: 可以理解为背景区域 = 物理画布 - 逻辑画布。即在画布中,但不在逻辑画布中的区域称之为「背景区域」。在背景区域中,只显示辅助线等少数的对象,其他对象都不会被显示出来。

可以通过下图来加深理解:

代码实现:

    useEffect(() => {
        const parentElement = document.getElementsByClassName("scalable")[0];
        let canvas = new fabric.Canvas("canvas", {
            width: parentElement.offsetWidth,
            height: parentElement.offsetHeight
        });
        ...
        addLogicCanvas(canvas, logicCanvasWidth, logicCanvasHeight);
        ...
    }, []);

	const addLogicCanvas = (canvas, width, height) => {
	    const logicCanvas = new fabric.Rect({
	        left: 0,
	        top: 0,
	        width,
	        height,
	        fill: "white",
	        stroke: "lightgray",
	        selectable: false,
	        evented: false,
	    });
	    canvas.add(logicCanvas);
	
	    // 逻辑画布永远置底
	    canvas.sendToBack(logicCanvas);
	    canvas.renderAll();
	    ...
	};

代码逻辑比较简单,以下做简要说明:

  • useEffect: 画布页面初始化的阶段,根据父级标签scalable的宽度和高度实例化「物理画布canvas对象,并向其中添加「逻辑画布logicCanvas
  • addLogicCanvas: 创建并添加逻辑画布,有以下要点:
    • 逻辑画布」实际上是一个填充色为白色的fabric.Rect矩形对象。
    • 它不可以通过鼠标点击被用户选择,不参与任何监听事件
    • 通过canvas.sendToBack方法使逻辑画布用于置于所有对象的最底层,否则会遮盖住其他对象。

3. 添加遮罩

有了逻辑画布,我们可以在其上添加和拖动各种各样的对象。但有一种情况的表现还不尽如人意,见下面的动图:

当我们把一个对象拖出逻辑画布时,预期它应该被遮盖或隐藏,但实际的表现是:它仍然显示在那里。

为了实现这个小需求点,我们可以为画布添加clipPath,即「遮罩范围」:

	const updateClipPath = (canvas, logicCanvas) => {
	    const {left, top, width, height} = logicCanvas;
	    canvas.clipPath = new fabric.Rect({
	        left,
	        top,
	        width,
	        height,
	        absolutePositioned: true,
	        selectable: false,
	        evented: false,
	    });
	};

和逻辑画布类似,遮罩是一个和逻辑画布相同位置、相同大小fabric.Rect矩形区域,同样不可以被选中,不参与任何监听事件。

设置了clipPath之后,我们来看看效果:

可以看到,对象被拖出逻辑画布的区域被隐藏了,只有选择框的控制线和控制点仍可以显示。

4. 居中显示逻辑画布

前面几个小节中,为了美观和便于说明,我直接使用了居中后的页面进行截图。实际上,我们在初始化时设置了逻辑画布的坐标为(0, 0):

    const logicCanvas = new fabric.Rect({
        left: 0,
        top: 0,
        ...
    });

要居中显示逻辑画布,需要引入canvasviewport视口」概念。

视口: 可以理解为可视窗口。当逻辑画布很小时,我们可以看到它的全貌。反之,当它很大,或者被放大超出了窗口的大小,我们就只能看到它的局部。这个我们能看到的区域就称为viewport视口。

先看下面一张图:

红色方框的区域就是上述的「视口」, 起初,视口的中心在「红色十字」的位置,我们看到的逻辑画布就在屏幕的左上方。想要看到逻辑画布处于视口正中间,就需要把视口向左上角移动一定的距离,使得视口中心和「蓝色十字」重合。

那么问题就简化为,如何计算出水平和竖直两个方向上的位移量。由图中可以很方便的计算出:

  • 水平方向的偏移量 = (canvas.width - logicCanvas.width) / 2;
  • 竖直方向的偏移量 = (canvas.height - logicCanvas.height) / 2;

因此有以下代码:

    let panX = -(canvas.width - logicCanvas.width) / 2;
    let panY = -(canvas.height - logicCanvas.height) / 2;

    ...
    canvas.absolutePan(new fabric.Point(panX, panY));
    ...

canvas.absolutePan方法的作用即对当前画布的视口做绝对的平移。 如此,我们的逻辑画布就可以居中显示在屏幕中央了。

5. 一个容易被忽视的bug点

由于「逻辑画布」也是canvas中的一个fabric.Rect对象,且在Z轴上必须永远置底,所以会造成原有的「移至底层」功能出现bug,具体的表现就是:将一个对象移至底层之后,由于被逻辑画布完全遮挡,这个对象就再也看不到,也无法选中了

具体的表现如下图:

这个问题修复的逻辑很简单:把一个对象移至底层之后,再向上移动一层,使它在Z轴上必然比逻辑画布高即可:

	if (selectedItem.key === "toBottom") {
		canvas.sendToBack(object);
		// 逻辑画布应该永远置底,所有对象都应该高于逻辑画布
		canvas.bringForward(object);
	}

问题修复后的效果,不再截图徒增篇幅。


三、Show u the code

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


后记

这篇博文中对画布进行调整的内容不算太多,但十分重要,会作为后续多篇博文的基础。后续的博文中,我们会依次实现通过工具缩放画布、通过鼠标滚轮缩放画布、鼠标拖动移动视口等功能。

如有需要,你可以:

  • 点击这里,返回第一篇《前端canvas项目实战——在线图文编辑器(一)——左侧工具栏》
  • 点击这里,返回上一篇《前端canvas项目实战——在线图文编辑器(八):复制、删除、锁定、层叠顺序》

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

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

相关文章

LeetCode-hot100题解—Day5

原题链接&#xff1a;力扣热题-HOT100 我把刷题的顺序调整了一下&#xff0c;所以可以根据题号进行参考&#xff0c;题号和力扣上时对应的&#xff0c;那么接下来就开始刷题之旅吧~ 1-8题见LeetCode-hot100题解—Day1 9-16题见LeetCode-hot100题解—Day2 17-24题见LeetCode-hot…

httpClient提交报文中文乱码

httpClient提交中文乱码&#xff0c;ContentType类型application/json 指定提交参数的编码即可 StringEntity se new StringEntity(paramBody.toJSONString(),"UTF-8");se.setContentType("application/json");context.httpPost.setHeader("Cookie&…

Go-Zero从0到1实现微服务项目开发(二)

前言 书接上回&#xff0c;继续更新GoZero微服务实战系列文章。 上一篇被GoZero作者万总点赞了&#xff0c;更文动力倍增&#xff0c;也建议大家先看巧一篇&#xff0c;欢迎粉丝股东们三连支持一波&#xff1a;Go-zero微服务快速入门和最佳实践&#xff08;一&#xff09; 本…

Windows Server 2022 OVF, updated Apr 2024 (sysin) - VMware 虚拟机模板

Windows Server 2022 OVF, updated Apr 2024 (sysin) - VMware 虚拟机模板 2024 年 4 月版本更新&#xff0c;现在自动运行 sysprep&#xff0c;支持 ESXi Host Client 部署 请访问原文链接&#xff1a;Windows Server 2022 OVF, updated Apr 2024 (sysin) - VMware 虚拟机模…

从Kernel启动到Android系统整个过程源码分析

1、 第一阶段&#xff1a; 对于ARM的处理器&#xff0c;内核第一个启动的文件是arc/arm/kernel下面的head.S文件。当然arc/arm/boot/compress下面也有这个文件&#xff0c;这个文件和上面的文件略有不同&#xff0c;当要生成压缩的内核时zImage时&#xff0c;启动的是后者&…

企业如何保证内部传输文件使用的工具是安全的?

企业内部文件的频繁交换成为了日常运营不可或缺的一环。然而&#xff0c;随着数据量的爆炸式增长和网络攻击手段的日益复杂&#xff0c;内网文件传输的安全隐患也日益凸显&#xff0c;成为企业信息安全的薄弱环节。本文将探讨内网文件传输的安全风险、企业常用的防护措施。 内网…

《Fundamentals of Power Electronics》——Buck、Boost、Buck-Boost三个电路的CCM-DCM工作特性总结

Buck、Boost、Buck-Boost这三个电路的CCM-DCM工作特性总结如下表所示&#xff1a; Buck、Boost、Buck-Boost这三个电路工作在DCM模式下电压传输比的对比图如下所示&#xff1a; 由上图可知&#xff0c;Buck-Boost电路的工作特性是一条斜率为的直线&#xff0c;Buck电路和Boost电…

小长假来临,企业借助巡检系统做好安全巡查工作

节前节后是安全隐患事故多发期&#xff0c;小长假来了&#xff0c;企业面临着员工离岗、生产活动减少等特殊情况&#xff0c;这可能导致一些安全隐患被忽视。因此&#xff0c;借助巡检系统做好全面安全巡查工作显得尤为重要。巡检系统可以帮助企业实现巡检工作的规范化、标准化…

八_实验1:创建 VLAN 和划分端口

1、实验目的 通过本实验可以掌握&#xff1a; VLAN的概念。创建VLAN的方法。把交换机端口划分到VLAN中的方法。 2、实验​​​​​​拓扑 创建 VLAN 和划分端口的实验拓扑如下图所示。 图8-5 创建 VLAN 和划分端口的实验拓扑 3、实验步骤 &#xff08;1&#xff09;实验准…

C++:map和set的封装

关于红黑树的模拟实现&#xff0c;大家不清楚的先去看看博主的博客再来看这篇文章&#xff0c;因为set和map的封装底层都是利用用的红黑树。所以这里不会过多介绍红黑树的相关内容&#xff0c;而更多的是去为了契合STL中的红黑树去进行改造&#xff0c;让封装的set和map能够去复…

第二篇:Python环境搭建:从初学者到专家

Python环境搭建&#xff1a;从初学者到专家 在编程的世界里&#xff0c;准备好一个高效而舒适的开发环境是走向成功的第一步。在这篇博客文章中&#xff0c;我们将一起探索如何为Python编程搭建一个理想的环境。无论你是完全的新手还是希望提升现有的技能&#xff0c;本文都会…

常用图像加密技术-流密码异或加密

异或加密是最常用的一种加密方式&#xff0c;广泛的适用于图像处理领域。这种加密方式依据加密密钥生成伪随机序列与图像的像素值进行异或操作&#xff0c;使得原像素值发生变化&#xff0c;进而使得图像内容发生变化&#xff0c;达到保护图像内容的目的。 该加密方法是以图像…

C语言程序设计(一)

1、指令、程序、软件 2、计算机语言&#xff1a;机器语言、汇编语言、高级语言 高级语言的发展&#xff1a;非结构化语言&#xff08;FORTRAN&#xff09;、结构化语言&#xff08;C语言&#xff09;、面向对象的语言&#xff08;C、面向对象&#xff09; 3、源程序、二进制…

在ubuntu 24.04 上安装vmware workstation 17.5.1

ubuntu安装在新组装的i9 14900机器上&#xff0c;用来学习笨叔的ARM64体系结构编程&#xff0c;也熟悉Linux的用法。但有时候写文档总是不方便&#xff0c;还是需要window来用。因此想在ubuntu 24.04上安装Linux版本的vmware worksation 17.5.1以虚拟机的方式安装windows 11。其…

Kubernetes学习笔记03

第八章、Kubernetes控制器Controller详解 Statefulset Statefulset主要是用来部署有状态应用 对于StatefulSet中的Pod&#xff0c;每个Pod挂载自己独立的存储&#xff0c;如果一个Pod出现故障&#xff0c;从其他节点启动一个同样名字的Pod&#xff0c;要挂载上原来Pod的存储…

Vitis HLS 学习笔记--AXI4 主接口

目录 1. 简介 2. 认识MAXI 3. MAXI突发操作 3.1 全局/本地存储器 3.2 MAXI优势与特点 3.3 查看MAXI报告 3.3.1 HW Interfaces 3.3.2 M_AXI Burst Information 3.4 MAXI 资源消耗 4. 理解 Volatile 4.1 标准C/C中的 volatile 4.2 HLS 中的 volatile 5. 总结 1. 简介…

CACTER AI实验室:AI大模型在邮件安全领域的应用

随着人工智能技术的飞速发展&#xff0c;AI已经深入到生活的各个领域。AI大模型在邮件安全领域展现出巨大潜力&#xff0c;尤其是反钓鱼检测上的应用&#xff0c;正逐渐展现出其独特的价值。 4月24日&#xff0c;CACTER AI实验室高级产品经理刘佳雄在直播交流会上分享了CACTER …

飞腾FT2000/4+银河麒麟全国产VPX架构 6U尺寸标准板卡,适用于船舶、交通等领域

XM-FT2000-VPX主板 XM-FT2000-VPX主板为VPX架构 6U尺寸标准板卡&#xff0c;提供的接口有DVI、USB、网络、UART、PCIE等接口。 处理器&#xff1a; FT2000/4四核国产处理器 芯片&#xff1a; 兆芯ZX-200芯片组 内存&#xff1a; 国产内存颗粒&#xff0c;双通道DDR4&#xff0…

应用场景:四大场景,用虚拟直播技术助力破圈

应用场景&#xff1a;四大场景用虚拟直播技术助力破圈 直播场景有四大类&#xff0c;看看你适合&#xff0c;哪一类场景的搭建&#xff1a; 1.教育型直播&#xff1a;寓教于货&#xff0c;文化浓厚&#xff1b; 人设&#xff1a;老师人设&#xff0c;以内容输出&#xff0c;“…

Python从0到100(十七):面向对象编程进阶

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…