设置电子签名

news2024/11/16 12:56:24

在这里插入图片描述

设置点赞签名代码

export class Signature {
  width: number = 300
  height: number = 300
  canvas!: HTMLCanvasElement
  ctx!: CanvasRenderingContext2D
  private drawing: boolean = false
  preTask: string[] = []
  nextTask: string[] = []
  private allTask: { x: number; y: number; color: string }[][] = []
  private itemTask: { x: number; y: number; color: string }[] = []
  private lineWidth: number = 1
  private color: string = '#000'
  private background: string = '#ff234234'

  constructor(
    canvas: HTMLCanvasElement,
    width: number,
    height: number,
    lineWidth?: number,
    color?: string
  ) {
    if (canvas instanceof HTMLCanvasElement != true) {
      console.warn('请传入一个Canvas元素')
      return
    }
    this.canvas = canvas
    this.canvas.width = width || this.width
    this.canvas.height = height || this.height
    this.lineWidth = lineWidth || this.lineWidth
    this.color = color || this.color

    this.ctx = canvas.getContext('2d') as CanvasRenderingContext2D
    this.eventListener()
    this.initCanvas()
    this.setColor()
    this.setLineWidth()
  }

  private initCanvas() {
    this.allTask = []
    this.preTask = []
    this.nextTask = []
    this.ctx.imageSmoothingEnabled = true
    this.ctx.lineJoin = 'round' // 圆角连接
    this.ctx.lineCap = 'round' // 圆角线帽
    // this.ctx.translate(0.5, 0.5)
    this.ctx.strokeStyle = this.color
  }
  private eventListener() {
    this.canvas.addEventListener('mousedown', this.canvasMouseDown.bind(this))
    this.canvas.addEventListener('mousemove', this.canvasMouseMove.bind(this))
    this.canvas.addEventListener('mouseup', this.canvasMouseEnd.bind(this))
    this.canvas.addEventListener('mouseout', this.canvasMouseEnd.bind(this))
  }
  removeEventListener() {
    this.canvas.removeEventListener(
      'mousedown',
      this.canvasMouseDown.bind(this)
    )
    this.canvas.removeEventListener(
      'mousemove',
      this.canvasMouseMove.bind(this)
    )
    this.canvas.removeEventListener('mouseup', this.canvasMouseEnd.bind(this))
    this.canvas.removeEventListener('mouseout', this.canvasMouseEnd.bind(this))
  }

  private canvasMouseDown(e: MouseEvent) {
    this.drawing = true
    this.ctx.beginPath() // 开始绘制
    this.ctx.moveTo(e.offsetX, e.offsetY) // 移动到
    this.itemTask.push({
      x: e.offsetX,
      y: e.offsetY,
      color: this.color,
    })
  }
  private canvasMouseMove(e: MouseEvent) {
    if (this.drawing) {
      this.ctx.lineTo(e.offsetX, e.offsetY) // 绘制到
      this.ctx.stroke() // 绘制
      this.itemTask.push({
        x: e.offsetX,
        y: e.offsetY,
        color: this.color,
      })
    }
  }
  private canvasMouseEnd(e: MouseEvent) {
    this.drawing = false
    this.ctx.closePath()
    const index = this.preTask.findIndex((item) => {
      return item === this.toDataURL()
    })
    if (index === -1) {
      this.preTask.push(this.toDataURL())
    }
    this.allTask.push(this.itemTask)
    this.itemTask = []
  }
  /**
   * @description: 设置画笔颜色
   * @param {string} color
   * @return {*}
   */
  setColor(color?: string) {
    this.color = color || this.color
    this.ctx.strokeStyle = this.color
  }
  /**
   * @description: 设置画布背景
   * @param {string} background
   * @return {*}
   */
  setBgColor(background?: string) {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
    this.background = background || this.background
    this.ctx.fillStyle = this.background
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
    this.allTask.length ? this.setPathLine() : ''
  }
  /**
   * @description: 设置折线
   * @return {*}
   */
  private setPathLine() {
    const taskFilter = this.allTask.filter((item) => {
      return item.length
    })
    taskFilter.forEach((item) => {
      this.ctx.beginPath()

      this.ctx.moveTo(item[0].x, item[0].y)

      item.forEach((list, index) => {
        if (index) {
          this.ctx.lineTo(list.x, list.y)
          this.ctx.strokeStyle = list.color
          this.ctx.stroke()
        }
      })
      this.ctx.closePath()
    })
  }

  /**
   * @description: 设置线条宽度
   * @param {number} width
   * @return {*}
   */
  setLineWidth(width?: number) {
    this.lineWidth = width || this.lineWidth
    this.ctx.lineWidth = this.lineWidth
  }
  /**
   * @description: 清空画布
   * @return {*}
   */
  clear() {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
    this.preTask = []
    this.nextTask = []
    this.allTask = []
  }

  /**
   * @description: 上一个画布
   * @return {*}
   */
  setPreImage() {
    if (this.preTask.length > 0) {
      const currentData = this.preTask.pop()!
      this.allTask.pop()!
      this.nextTask.push(currentData)
      const previousData = this.preTask[this.preTask.length - 1]
      if (previousData) {
        this.drawImage(previousData).then(() => console.log('撤销成功'))
      } else {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
        console.log('画布已清空')
      }
      console.log(
        `撤销: preTask = ${this.preTask.length}, nextTask = ${this.nextTask.length}`
      )
    }
  }
  /**
   * @description: 下一个画布
   * @return {*}
   */
  setNextImage() {
    if (this.nextTask.length > 0) {
      const nextData = this.nextTask.pop()!
      this.preTask.push(nextData)
      this.drawImage(nextData).then(() => console.log('恢复成功'))
      console.log(
        `恢复: preTask = ${this.preTask.length}, nextTask = ${this.nextTask.length}`
      )
    }
  }
  /**
   * @description: 导入图片
   * @param {string} src
   * @return {*}
   */
  drawImage(
    src: string,
    x = 0,
    y = 0,
    width = this.canvas.width,
    height = this.canvas.height
  ) {
    return new Promise((resolve, reject) => {
      const image = new Image()
      image.onload = (e) => {
        this.ctx.clearRect(x, y, width, height)
        this.ctx.drawImage(image, x, y, width, height)
        resolve(e)
      }
      image.onerror = (e) => {
        reject(e)
      }
      image.src = src
    })
  }
  /**
   * @description: 转换成base64
   * @return {*}
   */
  toDataURL(
    format: 'image/png' | 'image/jpeg' = 'image/png',
    quality: number = 1.0
  ): string {
    return this.canvas.toDataURL(format, quality)
  }
  /**
   * @description: 导出图片
   * @return {*}
   */

  exportToImage(fileName: string) {
    const url = this.canvas.toDataURL('image/png')
    var link = document.createElement('a')
    link.download = fileName
    link.href = url
    link.click()
  }
}

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

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

相关文章

阿里巴巴API在电商中的新应用:商品详情实时获取与解析

阿里巴巴API在电商中的新应用主要体现在商品详情实时获取与解析上,这对于提升电商平台的数据处理能力、用户体验以及运营效率具有重要意义。以下将详细介绍这一应用,并包含具体的代码示例。 一、阿里巴巴商品详情API概述 阿里巴巴商品详情API是阿里巴巴…

监控易赋能行动:打造专业运维监控团队,助力代理商成长

在当今数字化转型的浪潮中,运维监控作为确保业务连续性和稳定性的关键一环,其重要性日益凸显。作为行业领先的运维监控解决方案提供商,监控易深知合作伙伴在市场拓展和服务交付中的核心作用。为此,我们隆重推出“监控易赋能行动”…

山东大学OLED透明展示柜案例:科技赋能,创新展示新体验

随着科技的飞速发展,显示技术也在不断突破传统界限,为各行各业带来了全新的展示体验。山东大学集成攻关大平台展厅近期引入了OLED透明展示柜,这一创新举措不仅提升了展厅的展示效果,还为参观者带来了前所未有的互动体验。 背景介绍…

基于Java+SpringBoot+Vue+MySQL的驾校预约管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于SpringBootVue的驾校预约管理系统【附源码文档】、前后…

tenda路由器登录后台后添加端口映射

实现本地地址通过公网地址访问应用。 前提条件:本地网络为专线并且有公网IP 一、登录路由器后台 二、添加映射的内网地址和外网地址以及端口 三、查看公网IP 四、访问本地应用

VisionMaster二次开发-获取全局变量

GlobalVariableModuleCs.GlobalVariableModuleTool类 ,继承自 VmModule ,用于操作和管理全局变量 using GlobalVariableModuleCs;// 创建实例 GlobalVariableModuleTool global new GlobalVariableModuleTool(); // 直接调用 GetGlobalVar 方法 int res…

截取pdf中的部分内容,又保证内容不失真,实现方式

文章目录 0.为什么需要截取pdf1.访问网站2.上传你需要截取的pdf文件3.选取区域下载 0.为什么需要截取pdf 如果直接截图工具截图的话,里面的文字和图片内容会出现失真的情况,但是我们希望pdf空白部分的内容不要,这个时候就需要截取pdf工具 1…

From Man vs Machine to Man + Machine

From Man vs. Machine to Man Machine: The Art and AI of Stock Analyses 论文阅读 文章目录 From Man vs. Machine to Man Machine: The Art and AI of Stock Analyses 论文阅读 AbstractConstruction and Performance of the AI AnalystMethodologyThe Performance of Ana…

【数据结构与算法 | 搜索二叉树篇 力扣篇】力扣530

1. 力扣530:二叉搜索树的最小绝对差 1.1 题目: 给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数,其数值等于两值之差的绝对值。 示例 1: 输入:root [4,…

TCP协议必懂内容

网络分层结构 计算机的网络体系分为三种,OSI网络分层结构,TCP/IP 五层模型,TCP/IP四层模型。 TCP/IP五层模型: 应用层:为应用程序提供交互服务,在应用层中有较多的协议,较为出名的是&#xff…

Java八股文总结三

JVM部分 一、JVM是由哪几部分组成的 类加载器运行时数据区执行引擎本地库接口 说一下运行时数据区的组成: 本地方法栈、虚拟机栈、堆区、程序计数器、方法区。 虚拟机栈帧的组成: 每个栈帧包含五部分,分别包括局部变量表、操作数栈、动…

【鸿蒙开发从0到1 day06】

一.视口 视口:用来约束html,html和设备的大小进行适配 (注释视口) 添加视口 二倍图 概念:设计稿里面每个元素的尺寸的倍数 作用:防止图片在高分辨率屏幕下模糊失真 可以使用pxcook 如果我们的原图大小是设计图的两倍,可以使用pxCook去原图抓取到设…

墨刀基础篇(一):全局事件和动效

一:全局事件 全局事件是针对于整个页面设置的,而不是针对页面中的某个组件设置的。 事件 手势事件: 单击左滑、又滑、上滑、下滑、长按、双击摇一摇 鼠标事件: 单击、双击长按鼠标移入、鼠标移出右键 定时事件 定时器 行为 …

C++11第一弹:简介 | 统一的列表初始化 | 声明

🌈个人主页: 南桥几晴秋 🌈C专栏: 南桥谈C 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据…

盘点2024年最常用的透明加密软件!TOP10排行榜

随着数字化生活的深入,数据安全成为每个人和企业都不可忽视的重要议题。透明加密软件因其在保障数据安全的同时,不影响用户日常操作的特性,越来越受到人们的青睐。以下是2024年最常用的透明加密软件TOP10排行榜,它们以卓越的性能和…

Vue3 使用 富文本编辑器 wangeditor/editor-for-vue 配置详解

Vue3 使用 富文本编辑器 wangeditor/editor-for-vue 配置详解 先上官网地址 wangEditor 5 点这里 wangeditor 主要API 配置功能栏 let toolbarConfig {toolbarKeys: [ "bold", // 字体加粗 "underline", // 字体下划线 "italic", // 字体斜体…

一款用于分析java socket连接问题的工具

network-tools 介绍 network-tools基于sun jdk、Oracle jdk开发,拦截基于java socket请求,它包括 ​ http 客户端 ​ jdbc 客户端 ​ mq 客户端 ​ redis 客户端 目前提供如下功能: ​ 最近端点连接情况 ​ 最近与远程端点连接情况&am…

公寓项目(尚庭公寓笔记)

公寓项目 课程介绍项目概述移动端业务功能后台管理系统业务功能-公寓管理后台管理系统业务功能-租赁功能后台管理系统业务功能-系统管理&用户管理核心业务功能技术概述 项目开发流程项目原型数据库设计理论ER模型数据库设计流程 数据库设计实操概念模型逻辑模型公寓信息房间…

sse fetch-event-source插件的使用

sse简单介绍https://blog.csdn.net/weixin_42400404/article/details/141895877?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22141895877%22%2C%22source%22%3A%22weixin_42400404%22%7D fetch-event-source gitHub地址 通信…

快速构建 AI 应用的利器:Python 库 Mesop

在当今这个 AI 技术飞速发展的时代,开发者们总是希望能够更快、更便捷地构建 AI 应用程序。今天,我要给大家介绍一个由 Google 推出的 Python 库——Mesop。它的出现,让我们能够轻松地搭建高效的 AI 应用。 Mesop 是什么? Mesop …