Canvas基本使用和简易事件基本介绍(一)

什么是Canvas

按照MDN官方文档中的定义:

实际上就是一套比使用DOM更加灵活的API,让开发者可以画出更加多样的图形和路径,在一些画板软件、阅读器软件或者数学函数展示平台上运用较多,使用canvas写的图形操作系统在同等数量级的情况下会比浏览器DOM有更好的性能

还是贴上MDN文档中的简单Canvas实例:

js 复制代码
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

ctx.fillStyle = "green";
ctx.fillRect(10, 10, 150, 100);

canvas基本使用

canvas本身提供了一套基于状态机的上下文绘制方式,使用以下方式可以获取到这个上下文

js 复制代码
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

在获取到上下文之后就可以对这个canvas的ctx设置各种属性,然后canvas就会使用这个上下文的各种属性来绘制传入的图形:

ctx中最基本的一些风格属性有: fillStyle strokeStyle lineWidth等,设置方式如下:

js 复制代码
ctx.lineWidth = 10;

接下来可以按照MDN文档中的各种标准API去使用绘制想要的图形:

js 复制代码
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();

Canvas处理事件的难点

由于canvas本身是调用显卡的API在屏幕上展示出一系列图形,这些图形本身没有经过浏览器的代理,所以用canvas画出来的东西就是一幅静态的画,没有任何事件处理机制,而很多软件在开发的时候是需要让屏幕上的元素对鼠标的动作有一些响应,所以我们需要自己对canvas实现一套事件的监听机制

最基本的就是浏览器本身提供了mousemove mouseup mousedown等事件的监听机制,我们可以使用mousemove来获取当前鼠标在画布上的坐标,然后通过维护恰当的数据结构,通过遍历找到鼠标悬停的元素,并对这个元素执行一系列处理操作

并且当画布上的元素发生变化的时候,我们还需要擦除原先的图像,然后重新绘制

下面是绑定事件的代码(省略了其他无关代码):

ts 复制代码
private bindEventHandler() {
    this.targetDom.addEventListener("mousedown", (event) => {
      mouseDownHandler(event, this);
    });

    this.targetDom.addEventListener("mousemove", (event) => {
      mouseMoveHandler(event, this);
    });

    this.targetDom.addEventListener("mouseup", (event) => {
      mouseUpHandler(event, this);
    });
  }

本章节先大概介绍基本的代码设计,不会涉及细节的讲解。。。因为细节确实有点多,很难在这里展开讲,会在后面分成多章讲

元素的基类属性

ts 复制代码
export interface ISimpleEvent {
  on(name: string, handler: Function): void;
  emit(name: string, args: SimpleEventData): void;
  delete(name: string): void;
}

/**
 * 部件抽象基类
 */
export abstract class VerbalWidget implements ISimpleEvent {
  // 包围盒位置
  x: number = 0;
  y: number = 0;
  width: number = 0;
  height: number = 0;
  scaleWidth: number = 0;
  scaleHeight: number = 0;
  basePoint: Point = { x: 0, y: 0 };

  // 变换信息
  degree: number = 0;
  scaleX: number = 1;
  scaleY: number = 1;

  // 点数组
  boundingBoxPoints: Point[] = [];
  pathPoints: Point[] = [];
  cornerPoints: Point[][] = [];

  // 是否激活事件检测
  isEventActive: boolean = true;

  // 风格
  style: any = {};

  // 携带的变换器
  transformer: VerbalWidget | null = null;

  // 类型
  shapeType: string = "unknown";

  // 事件
  eventObject: any = {};

  // 所属的组部件
  groupWidget: VerbalWidget | null = null;
}

Canvas位置的检测

那么像下面这种情况,有两个图形,然后当鼠标放在紫色的矩形上面时,要显示出一个悬停的状态

首先我们要维护canvas画布上的东西到一个可遍历的数据结构,这里可以考虑使用链表或者数组(数组的话,在有大量元素删除时可能不太方便),我自己写的时候用的是链表 + Map的结构去存储。那么按照上文中我们已经绑定了浏览器鼠标移动的监听函数,我们可以在监听函数中按照和添加元素顺序相反的顺序遍历元素,第一个检测到的就是我们要的元素:

ts 复制代码
lookupPointOnWidget(x: number, y: number): VerbalWidget | null {
    if (this.widgetToNode.size === 0) return null;
    const head = this.renderList.getHead();
    let cursor = this.renderList.getTail();
    cursor = cursor.prev!;
    while (cursor !== head) {
      if (cursor.isRender) {
        const widget = cursor.value!;
        if (widget.get("isEventActive"))
          if (widget.isPointOnWidget(x, y)) return widget;
      }
      cursor = cursor.prev!;
    }
    return null;
  }

在拿到这个元素的结构之后,我们就可以获取悬停框应该在的位置信息,然后就将这个悬停框绘制在指定位置即可

Canvas选中

当鼠标按下时还需要实现一个这样的选中样式,这里可以采用一个对象记录鼠标操作时的各种状态,当有悬停元素并且按下鼠标时变成选中(这里的源码实现细节会在后续章节陆续发布):

ts 复制代码
function mouseDownCommon(event: MouseEvent, ec: EventCenter) {
  const hovering = ec.getHovering();
  const { offsetX, offsetY } = event;
  if (hovering) {
    ec.setHitting(hovering);
    removeHoveringFlag(ec);
    placeHittingFlag(hovering, ec);
    ec.getActionRemark().mouseDownOffset = {
      x: offsetX - hovering.get("x"),
      y: offsetY - hovering.get("y"),
    };
    ec.transferToEventCanvas(hovering);
    ec.setState(StateEnum.CATCHING);
  } else {
    ec.getActionRemark().mouseDownPoint = { x: offsetX, y: offsetY };
    ec.setState(StateEnum.BOXSELECT);
  }
}

Canvas基本变换

这里运用了Canvas的几大变换API:rotate translate sclae:

  • CanvasRenderingContext2D.rotate() 是 Canvas 2D API 在变换矩阵中增加旋转的方法。角度变量表示一个顺时针旋转角度并且用弧度表示
  • Canvas 2D API 的 CanvasRenderingContext2D.translate() 方法对当前网格添加平移变换的方法
  • CanvasRenderingContext2D.scale() 是 Canvas 2D API 根据 x 水平方向和 y 垂直方向,为 canvas 单位添加缩放变换的方法

这个简易手绘风的库放在了github上:github.com/Prince-Herv...)

还有些bug在开发中hhh,欢迎大家交流学习

相关推荐
菜根Sec28 分钟前
XSS跨站脚本攻击漏洞练习
前端·xss
m0_7482571835 分钟前
Spring Boot FileUpLoad and Interceptor(文件上传和拦截器,Web入门知识)
前端·spring boot·后端
桃园码工1 小时前
15_HTML5 表单属性 --[HTML5 API 学习之旅]
前端·html5·表单属性
百万蹄蹄向前冲2 小时前
2024不一样的VUE3期末考查
前端·javascript·程序员
轻口味2 小时前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami2 小时前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
吃杠碰小鸡3 小时前
lodash常用函数
前端·javascript
emoji1111113 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼3 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
m0_748250033 小时前
Web 第一次作业 初探html 使用VSCode工具开发
前端·html