利用 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;} ) ) );

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

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

相关文章

图片转Base64

在Python中, 可以使用内置的base64模块以及图像处理库(如PIL, 也称为Pillow)来将图片转换为Base64编码的字符串. 以下是一个简单的示例, 说明如何实现这一过程:首先, 需要安装Pillow库(如果尚未安装), 可以使用pip来安装: pip install pillow然后, 可以使用以下Python代码将图片…

RabbitMQ实践——交换器(Exchange)和绑定(Banding)

大纲 direct型交换器默认交换器命名交换器 fanout型交换器topic型交换器headers型交换器 RabbitMQ在概念上由三部分组成&#xff1a; 交换器&#xff08;Exchange&#xff09;&#xff1a;负责接收消息发布者发布消息的结构&#xff0c;同时它会根据“绑定关系”&#xff08;Ba…

基于SpringBoot+VueBBS论坛系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…

sku与spu的区别!!!

一个 SPU 可以有多个 SKU。

攻防演练之-成功的钓鱼邮件溯源

书接上文&#xff0c;《网络安全攻防演练风云》专栏之攻防演练之-网络安全产品大巡礼二&#xff0c;这里。 演练第一天并没有太大的波澜&#xff0c;白天的时间过得很快。夜色降临&#xff0c;攻防演练中心内的灯光依旧明亮。对于网络安全团队来说&#xff0c;夜晚和白天并没有…

教育培训知识付费在线课程小程序开发

教育培训知识付费在线课程小程序功能概述 核心功能 课程报名与缴费&#xff1a;支持线上报名、缴费&#xff0c;自定义课程时间、人数等。 砍价功能&#xff1a;用户通过分享邀请好友参与砍价&#xff0c;享受低价购买课程的优惠。 视频课程&#xff1a;支持倍速播放&#x…

Linux 基本指令3

date指令 date[选项][格式] %Y--年 %m--月 %d--日 %H--小时 %M--分 %S--秒 中间可用其他符号分割&#xff0c;不能使用空格。 -s 设置时间&#xff0c;会返回设置时间的信息并不是改变当前时间 设置全部时间年可用-或者&#xff1a;分割日期和时间用空格分隔&#xff…

Qt创建静态库及静态库使用

Qt创建静态库及静态库使用 1. 创建一个库文件 选择静态库 将需要打包的.h 和.cpp文件添加到程序中&#xff0c; 在编译器版本下的debug和release模式下分别编译&#xff08;右键项目&#xff0c;点击“qmake”,再点击“构建”&#xff09;后&#xff0c;在对应的的build目录下…

“探索机器学习的多面世界:从理论到应用与未来展望“

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;机器学习 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 一、机器学习基础理论 1.机器学习的定义与分类 监督学习 无监督学…

Python数据分析与机器学习在医疗诊断中的应用

文章目录 &#x1f4d1;引言一、数据收集与预处理1.1 数据收集1.2 数据预处理 二、特征选择与构建2.1 特征选择2.2 特征构建 三、模型选择与训练3.1 逻辑回归3.2 随机森林3.3 深度学习 四、模型评估与调优4.1 交叉验证4.2 超参数调优 五、模型部署与应用5.1 模型保存与加载5.2 …

Python:从头创建 Asyncio (2)

引言 现在&#xff0c;asyncio 已成为 Python 社区中的热门话题&#xff0c;并且名副其实——它提供了一种非常出色的处理 I/O 密集型程序的方法&#xff01;在我探索 asyncio 的过程中&#xff0c;我起初并不太明白它的工作原理。但随着深入学习&#xff0c;我意识到 asyncio …

Java 反射机制 -- Java 语言反射的概述、核心类与高级应用

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 010 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自…

GitLab教程(二):快速上手Git

文章目录 1.将远端代码克隆到本地2.修改本地代码并提交到远程仓库3.Git命令总结git clonegit statusgit addgit commitgit pushgit log 首先&#xff0c;我在Gitlab上创建了一个远程仓库&#xff0c;用于演示使用Gitlab进行版本管理的完整流程&#xff1a; 1.将远端代码克隆到本…

导出 Whisper 模型到 ONNX

前言 在语音识别领域&#xff0c;Whisper 模型因其出色的性能和灵活性备受关注。为了在更多平台和环境中部署 Whisper 模型&#xff0c;导出为 ONNX 格式是一个有效的途径。ONNX&#xff08;Open Neural Network Exchange&#xff09;是一个开放格式&#xff0c;支持不同的深度…

吴恩达2022机器学习专项课程C2W3:2.25 理解方差和偏差(诊断方差偏差正则化偏差方案搭建性能学习曲线)

目录 引言名词替代影响模型偏差和方差的因素1.多项式阶数2.正则化参数 判断是否有高偏差或高方差1.方法一&#xff1a;建立性能基准水平2.方法二&#xff1a;建立学习曲线 总结 引言 机器学习系统开发的典型流程是从一个想法开始&#xff0c;然后训练模型。初次训练的结果通常…

今日分享丨inBuilder低代码平台打印格式设计器

打印在企业日常办公中占据核心地位&#xff0c;是处理各种关键文件不可或缺的一环。无论是签署合同、报销费用、记录凭证与账表、处理回单与库存单据、开出库单据&#xff0c;还是开具发票、制作条码与标签&#xff0c;打印都发挥着至关重要的作用&#xff0c;确保企业运营的高…

rv1126-rv1109-串口显示路径不变化

串口只有#, 后来看了教程改成如下 但是没有变化,那个路径都只显示rootLonbon# 于是最后改成了这样 因为:

linux 安装sftp及使用sftp上传和下载

一、centos7 安装sftp 1.安装 OpenSSH 服务&#xff1a; sudo yum install openssh-server2.启动 SSH 服务&#xff0c;并设置为开机启动&#xff1a; sudo systemctl start sshd sudo systemctl enable sshd3.创建一个新用户&#xff0c;用于SFTP连接&#xff08;替换your_…

【C++高阶】C++继承学习手册:全面解析继承的各个方面

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;模板进阶 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 继承 &#x1f4d6;1. 继承的概念及定义…