在现代互联网应用中,有时我们需要让用户在网页上进行签字操作,比如确认文件、填写电子表格或者签署合同。利用 HTML5 的 canvas 画布,我们可以轻松地实现这一功能,为用户提供方便快捷的在线签字体验。
HTML5 的 canvas 元素是一种强大的图形渲染工具,它允许开发者使用 JavaScript 在网页上绘制各种图形、动画和交互式内容。通过 canvas,开发者可以创建丰富多彩的视觉效果,并实现复杂的用户交互体验。
HTML5 Canvas的关键特性:
图形绘制能力:Canvas 元素提供了绘制路径、矩形、圆形、直线、文本等基本图形的功能,同时还支持图像的绘制和变换操作,使得开发者能够轻松地创建各种视觉效果。
动画和交互:借助 JavaScript,开发者可以在 Canvas 上创建复杂的动画效果,并添加交互式的操作。这使得 Canvas 成为开发游戏、数据可视化和其他需要动态效果的应用的理想选择。
性能优势:由于 Canvas 是基于 GPU 加速的,因此它具有良好的性能表现,能够处理大量的图形元素和动画效果,而不会对页面的整体性能产生太大影响。
灵活性:Canvas 元素可以轻松地与其他 HTML 元素结合使用,使得开发者可以在页面上创建复杂的混合媒体效果,同时还可以响应用户的交互操作。
效果演示
完整代码
HTML代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<!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代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
( 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; } ) ) ); |