Canvas简历编辑器-选中绘制与拖拽多选交互方案

Canvas简历编辑器-选中绘制与拖拽多选交互方案

在之前我们聊了聊如何基于Canvas与基本事件组合实现了轻量级DOM,并且在此基础上实现了如何进行管理事件以及多层级渲染的能力设计。那么此时我们就依然在轻量级DOM的基础上,关注于实现选中绘制与拖拽多选交互设计。

关于Canvas简历编辑器项目的相关文章:

选中绘制

我们先来聊一聊最基本的节点点击选中以及拖拽的交互,而在聊具体的代码实现之前,我们先来看一下对于图形的绘制问题。在Canvas中我们绘制路径的话,我们可以通过fill来填充路径,也可以通过stroke来描边路径,而在我们描边的时候,如果不注意的话可能会陷入一些绘制的问题。假如此时我们要绘制一条线,我们可以分别来看下使用strokefill的绘制方法实现,此时如果在高清ctx.scale(devicePixel, devicePixel)情况下,则能明显地看出来绘制位置差0.5px,而如果基准为1px的话则会出现1px的差值以及色值偏差。

ctx.beginPath();
ctx.strokeStyle = "blue";
ctx.lineWidth = 1;
ctx.moveTo(5, 5);
ctx.lineTo(100, 5);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(100, 5);
ctx.lineTo(200, 5);
ctx.lineTo(200, 6);
ctx.lineTo(100, 6);
ctx.closePath();
ctx.fill();

在先前的选中图形frame中,我们都是用stroke来实现的,然后最近我想将其真正作为外边框来绘制,然后就发现想绘制inside stroke确实不是一件容易的事。从MDN上阅读stroke的文档可以得到其是以路径的中心线为基准的,也就是说stroke是由基准分别向内外扩展的,那么问题就来了,假如我们绘制了一条线,而这条线本身是存在1px宽度的,那么初步理解按照文档所说其本身结构应该是以这1px本身的中心点也就是0.5px的位置为中心点向外发散,然而其实际效果是以1px的外边缘为基准发散,那么就会导致1px的线在stroke之后会多出0.5px的宽度,这个效果可以通过lineTo(0, 100)外加lineWith=1来测试,可以发现其可见宽度只有0.5px,这点可以通过再画一个1pxPath来对比。

ctx.beginPath();
ctx.lineWidth = 6;
ctx.strokeStyle = "blue";
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = "red";
ctx.moveTo(100, 3);
ctx.lineTo(200, 3);
ctx.closePath();
ctx.stroke();

那么这里的Strokes are aligned to the center of a path可能与我理解的center of a path并不相同,或许其只是想表达stroke是分别向两侧绘制描边的,而并不是解释其基准位置。关于这个问题我咨询了一下,这里主要是理解有偏差,在我们使用API绘制路径时,本身并没有设置宽度的信息,而坐标信息定义的是路径的轮廓或边界,因此我们在最开始定义的路径结构1px是不成立的。在图形学的上下文中,路径path通常是指一个几何形状的轮廓或线条,路径本身是数学上的抽象概念,没有宽度,只是一个由点和线段构成的轨迹,因此当我们提到描边stroke时,指的是一个可视化过程,即在路径的周围绘制有宽度的线条。

实际上这里如果仅仅是处理frame的问题的话,可能并没有太大的问题,然而在处理节点的时候,发现由于是使用stroke绘制的操作节点,那么实际上其总是会超出原始宽度的,也就是上边说的描边问题,而因为超出的这0.5px的边缘节点,使得我一直认为绘制节点的边缘与填充是没问题的,然而今天才发现这里的顺序反了,描边的内部会被填充覆盖掉,也就是说实现的border宽度总是会被除以2的,因此要先填充再描边才是正确的绘制方式。此外,无论是frame节点的绘制还是类似border的绘制,在Firefoxinside stroke总是会出现兼容性问题,仅有组合fill以及使用fill配合Path2D + clip才能绘制正常的inside stroke

ctx.save();
ctx.beginPath();
ctx.arc(70, 75, 50, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillStyle = "white";
ctx.fill();
ctx.closePath();
ctx.restore();

ctx.save();
ctx.beginPath();
ctx.arc(200, 75, 50, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();

那么我们就可以利用三种方式绘制inside stroke,当然还有借助lineTo/fillRect分别绘制4条边的方式我们没有列举,因为这种方式自然不会出现什么问题,其本身就是使用fill的方式绘制的,而我们这里主要是讨论stroke的绘制问题,只是借助Path2D同样也是fill的方式绘制的,但是这里需要讨论一下clipfillRule-nonzero/evenodd的问题。那么借助stroke的特性,方式1是我们绘制两倍的lineWidth,然后裁剪掉外部的描边部分,这样就能够正确保留内部的描边了,方式2则是我们主动校准了描边的位置,将其向内缩小0.5px的位置,由此来绘制完整的描边,方式3是借助evenodd的填充规则,通过clip来生成规则保留内部的描边,再来实际填充即可实现。

<canvas id="canvas" width="800" height="800"></canvas>
<script>
  // https://stackoverflow.com/questions/36615592/canvas-inner-stroke
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  const devicePixelRatio = Math.ceil(window.devicePixelRatio || 1);
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  canvas.width = width * devicePixelRatio;
  canvas.height = height * devicePixelRatio;
  canvas.style.width = width + "px";
  canvas.style.height = height + "px";
  ctx.scale(devicePixelRatio, devicePixelRatio);

  ctx.save();
  ctx.beginPath();
  ctx.rect(10, 10, 150, 100);
  ctx.clip();
  ctx.closePath();
  ctx.lineWidth = 2;
  ctx.strokeStyle 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值