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,欢迎大家交流学习

相关推荐
_.Switch1 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光1 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   1 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   1 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web1 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇2 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr2 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho3 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常4 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js