当前位置: 首页 > news >正文

利用 HTML5 Canvas 实现在线签字功能

目录

前言

一、HTML5 Canvas 简介

二、签字功能的实现

效果演示

完整代码


前言

        在现代互联网应用中,有时我们需要让用户在网页上进行签字操作,比如确认文件、填写电子表格或者签署合同。利用 HTML5 的 canvas 画布,我们可以轻松地实现这一功能,为用户提供方便快捷的在线签字体验。

一、HTML5 Canvas 简介

HTML5 的 canvas 元素是一种强大的图形渲染工具,它允许开发者使用 JavaScript 在网页上绘制各种图形、动画和交互式内容。通过 canvas,开发者可以创建丰富多彩的视觉效果,并实现复杂的用户交互体验。

HTML5 Canvas的关键特性:

  1. 图形绘制能力:Canvas 元素提供了绘制路径、矩形、圆形、直线、文本等基本图形的功能,同时还支持图像的绘制和变换操作,使得开发者能够轻松地创建各种视觉效果。

  2. 动画和交互:借助 JavaScript,开发者可以在 Canvas 上创建复杂的动画效果,并添加交互式的操作。这使得 Canvas 成为开发游戏、数据可视化和其他需要动态效果的应用的理想选择。

  3. 性能优势:由于 Canvas 是基于 GPU 加速的,因此它具有良好的性能表现,能够处理大量的图形元素和动画效果,而不会对页面的整体性能产生太大影响。

  4. 灵活性:Canvas 元素可以轻松地与其他 HTML 元素结合使用,使得开发者可以在页面上创建复杂的混合媒体效果,同时还可以响应用户的交互操作。

二、签字功能的实现

效果演示

完整代码

HTML代码

<!DOCTYPE html>
<html class="no-js">
<head><meta name="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no,viewport-fit=cover"><meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /><meta http-equiv="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta http-equiv="Pragma" content="no-cache"><meta http-equiv="Cache" content="no-cache"><meta http-equiv="Expires" content="0"><meta charset="utf-8"><title>画图</title><link rel="stylesheet" href="css/bootstrap.css"><style>* {margin: 0;padding: 0;}html,body {width: 100%;height: 100%;text-align: center;}canvas {max-width: 100%;border: 2px dotted #ccc;}</style>
</head><body><script src="./index.js"></script><script>//初始化var sign = new Draw( {// canvas:document.getElementById('canvas'),lineWidth: 10, // 线条宽度width: 400, // canvas 宽height: 400, //canvas 高strokeStyle: '#333333' // 线条颜色} );window.onload = function () {// 点击输出图片document.querySelector( '.ouput' ).onclick = function () {var img = new Image();img.style.width = '200px';img.src = sign.ouput();img.onload = function () {document.body.appendChild( img );}document.querySelector( 'img' ) && document.querySelector( 'img' ).remove();}// 点击清除document.querySelector( '.clear' ).onclick = function () {sign.clear();}// 点击撤销document.querySelector( '.undo' ).onclick = function () {if ( sign.state.undopath.length > 0 ) {sign.undo();} else {console.log( '还没有签名' );}}}</script><div class="buttons"><button type="button" class="btn btn-primary ouput">生成图片</button><button type="button" class="btn btn-light undo">撤销</button><button type="button" class="btn btn-light clear">清除画布</button></div>
</body></html>

js代码

( function ( global, factory ) {typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :typeof define === 'function' && define.amd ? define( factory ) :( global = global || self, global.Draw = factory() );
}( this, ( function () {'use strict';var classCallCheck = function ( instance, Constructor ) {if ( !( instance instanceof Constructor ) ) {throw new TypeError( "Cannot call a class as a function" );}};var createClass = function () {function defineProperties ( target, props ) {for ( var i = 0; i < props.length; i++ ) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ( "value" in descriptor ) descriptor.writable = true;Object.defineProperty( target, descriptor.key, descriptor );}}return function ( Constructor, protoProps, staticProps ) {if ( protoProps ) defineProperties( Constructor.prototype, protoProps );if ( staticProps ) defineProperties( Constructor, staticProps );return Constructor;};}();/*** * @description  手写签字版*/var Draw = function () {function Draw () {var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};classCallCheck( this, Draw );this.el = params.el || document.createElement( 'canvas' );this.state = {undopath: [],index: -1,old: void 0,isStart: false,width: params.width || 400,height: params.height || 400,lineWidth: params.lineWidth || 1,isTouch: 'ontouchstart' in window,strokeStyle: params.strokeStyle || '#333333'};var _state = this.state,width = _state.width,height = _state.height,lineWidth = _state.lineWidth;this.el.width = width * 2;this.el.height = height * 2;document.body.appendChild( this.el );this.ctx = this.el.getContext( '2d' );this.ctx.scale( 2, 2 );this.ctx.lineWidth = lineWidth;this.ctx.lineJoin = 'round';this.ctx.lineCap = 'round';this.init();}createClass( Draw, [{key: 'onStart',value: function onStart () {++this.state.index;this.state.isStart = true;}}, {key: 'onMove',value: function onMove ( e ) {e.preventDefault();if ( !this.state.isStart ) return;var pos = this.pos( e );var index = this.state.index;this.ctx.strokeStyle = this.state.strokeStyle;if ( this.state.old ) {this.ctx.beginPath();this.ctx.moveTo( this.state.old.x, this.state.old.y );this.ctx.lineTo( pos.x, pos.y );this.ctx.stroke();}this.state.old = pos;if ( this.state.undopath[index] ) {this.state.undopath[index].push( { x: this.state.old.x, y: this.state.old.y } );} else {this.state.undopath[index] = [{x: this.state.old.x,y: this.state.old.y,strokeStyle: this.ctx.strokeStyle,lineWidth: this.ctx.lineWidth}];}}}, {key: 'onEnd',value: function onEnd () {this.state.old = void 0;this.state.isStart = false;}}, {key: 'pos',value: function pos ( e ) {var x = 0,y = 0;if ( e.touches ) {x = e.touches[0].pageX;y = e.touches[0].pageY;} else {x = e.offsetX / 2;y = e.offsetY / 2;}return { x: x, y: y };}}, {key: 'ouput',value: function ouput () {// 输出图片return this.el.toDataURL();}}, {key: 'init',value: function init () {// 绑定事件var isTouch = this.state.isTouch;this.el.addEventListener( isTouch ? 'touchstart' : 'mousedown', this.onStart.bind( this ), false );this.el.addEventListener( isTouch ? 'touchmove' : 'mousemove', this.onMove.bind( this ), false );this.el.addEventListener( isTouch ? 'touchend' : 'mouseup', this.onEnd.bind( this ), false );this.el.addEventListener( isTouch ? 'touchcancel' : 'mouseout', this.onEnd.bind( this ), false );}}, {key: 'destroyed',value: function destroyed () {if ( this.el ) {var isTouch = this.state.isTouch;this.el.removeEventListener( isTouch ? 'touchstart' : 'mousedown', this.onStart.bind( this ) );this.el.removeEventListener( isTouch ? 'touchmove' : 'mousemove', this.onMove.bind( this ) );this.el.removeEventListener( isTouch ? 'touchend' : 'mouseup', this.onEnd.bind( this ) );this.el.removeEventListener( isTouch ? 'touchcancel' : 'mouseout', this.onEnd.bind( this ) );}}}, {key: 'clear',value: function clear () {// 清除画布this.state.index = -1;this.state.undopath = [];this.ctx.clearRect( 0, 0, this.el.width, this.el.height );}}, {key: 'undo',value: function undo () {// 撤销this.state.index >= 0 && --this.state.index;var undopath = this.state.undopath;this.state.undopath.pop();this.ctx.clearRect( 0, 0, this.el.width, this.el.height );if ( undopath ) {this.ctx.beginPath();for ( var z = 0; z < undopath.length; ++z ) {this.ctx.moveTo( undopath[z][0].x, undopath[z][0].y );this.ctx.lineWidth = undopath[z][0].lineWidth;this.ctx.strokeStyle = undopath[z][0].strokeStyle;for ( var i = 0; i < undopath[z].length; ++i ) {this.ctx.lineTo( undopath[z][i].x, undopath[z][i].y );}}this.ctx.stroke();this.ctx.closePath();} else {this.state.undopath = [];}}}] );return Draw;}();return Draw;} ) ) );

相关文章:

利用 HTML5 Canvas 实现在线签字功能

目录 前言 一、HTML5 Canvas 简介 二、签字功能的实现 效果演示 完整代码 前言 在现代互联网应用中&#xff0c;有时我们需要让用户在网页上进行签字操作&#xff0c;比如确认文件、填写电子表格或者签署合同。利用 HTML5 的 canvas 画布&#xff0c;我们可以轻松地实现这一…...

GaussDB技术解读——GaussDB架构介绍(二)

上篇图文&#xff0c;从GaussDB关键架构目标、GaussDB分布式架构、数据计算路由层&#xff08;Coordinator&#xff09;关键技术方案等三方面对GaussDB架构进行了介绍。本篇将从数据持久化存取层(DataNode)关键技术方案、全局事务管理层&#xff08;GTM&#xff09;关键技术方案…...

EfficientNet详解

原论文名称&#xff1a;EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks 论文下载地址&#xff1a;https://arxiv.org/abs/1905.11946 原论文提供代码&#xff1a;https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet 自己…...

UI前端交互的艺术:探索设计的四个维度、五个层面、六个要点与七大原则

UI前端交互的艺术&#xff1a;探索设计的四个维度、五个层面、六个要点与七大原则 在数字时代的浪潮中&#xff0c;UI前端交互设计如同一门深邃的艺术&#xff0c;既需要技术支撑&#xff0c;又需要设计灵感。它关乎用户体验&#xff0c;影响着产品的成败。那么&#xff0c;UI…...

java接口设计需要考虑哪些方面

1.签名 目的&#xff1a;防止数据被篡改 &#xff08;1&#xff09;接口请求方将请求参数、时间戳和密钥拼接成一个字符串。 &#xff08;2&#xff09;使用MD5等hash算法生成签名。 &#xff08;3&#xff09;在请求参数或请求头中增加sign参数传递给API接口。 &#xff08;4&…...

Opencv图像处理

Opencv图像处理 图像阈值处理 图像阈值的处理通过cv2.threshold函数来进行处理&#xff0c;该函数的具体说明如下所示 ret, dst cv2.threshold(src, thresh, maxval, type) src&#xff1a; 输入图&#xff0c;只能输入单通道图像&#xff0c;通常来说为灰度图 dst&#x…...

LeetCode | 2879.显示前三行

在 pandas 中&#xff0c;可以使用 head() 方法来读取 DataFrame 的前几行数据。如果想读取指定数量的行&#xff0c;可以在 head() 方法中传入一个参数 n&#xff0c;读取前 n 行 import pandas as pddef selectFirstRows(employees: pd.DataFrame) -> pd.DataFrame:retur…...

Qt实现简易播放器

效果如图 源码地址&#xff1a; 简易播放器: 基于Qt的简易播放器&#xff0c;底层采用VLC源码 - Gitee.com GitHub:GitHub - a-mo-xi-wei/easy-player: 基于Qt的调用VLC的API的简易播放器...

适配Android12启动页

今天我们讲个什么话题呢&#xff1f;我们今天讲的内容是&#xff0c;Android12新启动页的支持API。 启动页我想大家都不陌生吧&#xff0c;通常的写法就是先创建一个SplashActivity&#xff0c;在onCreate中 Handler(Looper.getMainLooper()).postDelayed({// 在这里跳转主界…...

人工智能在医学领域的应用及技术实现

欢迎来到 Papicatch的博客 目录 &#x1f349;引言 &#x1f349; 医学影像分析 &#x1f348;技术实现 &#x1f34d;数据准备 &#x1f34d;模型构建 &#x1f34d;模型训练 &#x1f34d;模型评估 &#x1f34d;应用部署 &#x1f348;示例代码 &#x1f349; 基因…...

MySQL—多表查询—练习(1)

一、引言 上几篇关于多表查询的基本几个部分全部学习完了。 多表查询的基本类型的查询包括以下&#xff1a; 1、内连接&#xff08;隐式内连接、显示内连接&#xff09;&#xff1a;... [INNER] JOIN ... ON 条件; &#xff09; 2、外连接&#xff08;左外连接、右外连接&…...

千益畅行:合法合规的旅游卡服务,打破误解

近期&#xff0c;千益畅行旅游卡服务引起了公众的广泛关注。然而&#xff0c;一些人对该服务存在误解&#xff0c;认为其存在某种欺诈行为。但经过深入了解和全网搜索证据&#xff0c;我们可以确认&#xff0c;千益畅行实际上是一家合法合规的旅游卡服务提供商。 千益畅行旅游…...

【Echarts系列】水平柱状图

【Echarts系列】水平柱状图 序示例数据格式代码 序 为了节省后续开发学习成本&#xff0c;这个系列将记录我工作所用到的一些echarts图表。 示例 水平柱状图如图所示&#xff1a; 数据格式 data [{name: 于洪区,value: 2736},{name: 新民市,value: 2844},{name: 皇姑区,…...

怎样把便签里的内容移到桌面?桌面便签软件使用方法

每次打开电脑&#xff0c;我总是被满屏的文件和图标弄得眼花缭乱。那些记录在各式各样便签里的重要事项&#xff0c;经常被埋没在这信息的海洋中&#xff0c;找起来真是头疼。想必很多人都有过这样的困扰&#xff1a;如何在繁杂的桌面环境中&#xff0c;一眼就看到自己需要提醒…...

量化入门:qmt获取可转债基本信息和行情数据

💻专业版获取可转债数据 今天将展示如何使用Python和QMT来获取可转债的实时数据和财务数据。 🔬 获取可转债基本信息 迅投的券商版和基础版都不支持可转债行情,投研专业版才支持,一年大概5000元。免费的券商版可参考QMT量化入门 投研专业版才有权限调用download_cb_d…...

AVL树 ---(C++)

本篇讲全面的讲解 AVL 树的插入&#xff0c;旋转以及验证 AVL 树的性能&#xff08;本篇未实现删除代码&#xff09;。至于为什么会有 AVL 树&#xff0c;这是因为简单的二叉搜索树并不能直接的保证搜索的效率&#xff0c;因为当我们在二叉搜索树中插入一段有序的序列的时候&am…...

基于spring boot+MySQL 小区物业管理系统-计算机毕设 附源码37236

spring boot 小区物业管理系统 摘 要 在网络信息的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;小区物业只能以客户为导向&#xff0c;以产品的持续创新作为小区物…...

Linux/Ubuntu/Debian常用服务管理命令

Linux/Ubuntu/Debian常用服务管理命令 在 Linux 系统中&#xff0c;服务管理是系统管理员日常维护工作的重要组成部分。通过一些常用的命令&#xff0c;我们可以查看服务状态、启动或停止服务、重启服务等。掌握这些命令&#xff0c;可以让系统管理工作更加高效和便捷。 1. s…...

Maven的三种项目打包方式——pom,jar,war的区别

1、pom&#xff1a;用在父级工程或聚合工程中&#xff0c;用来做jar包的版本控制&#xff0c;必须指明这个聚合工程的打包方式为pom。 聚合工程只是用来帮助其他模块构建的工具&#xff0c;本身并没有实质的内容。具体每个工程代码的编写还是在生成的工程中去写。 对于在父工程…...

[手游] 三色绘恋S Mobile Link

语音合成TTS: 文字转成语音的工具 WPS免登录一键修改器: 去除烦人的登录且能正常使用 故事简介&#xff1a; 深秋的雨季即将到来&#xff0c;正值那个为人所熟知的故事发生的前一年—— 地点&#xff1a;湖北省的重点高中&#xff0c;武汉师贰高校。 新学年开始&#xff0c;各…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...