整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JQ利用canvas,实现移动端手写板 「笔记」

JQ利用canvas,实现移动端手写板 「笔记」

近公司项目需求,客户可以直接在textarea可以输入签名,经过研究,采用Canvas解决,废话少说,直接上代码,样式可根据设计图自行修改。

仅此个人笔记,分享学习 。有不足之处,望大佬指点学习,共勉!!!


<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
	<title></title>
</head>
<style type="text/css">
	*{
		margin: 0;
		padding: 0;
	}
	.from_list{
		width: 96%;
		margin: 0 2%;
		color: #353535;
		line-height: 2rem;
		background: #FFFFFF;
		border-top: 1px solid #EEEEEE;
		border-bottom: 1px solid #EEEEEE;
		display: -webkit-box;
		display: -ms-flexbox;
		display: flex;
		display: -webkit-flex;
		align-items: center;
		-webkit-align-items: center;
		justify-content: space-between;
		-webkit-justify-content: space-between;
	}
	#reset_btn {
	    width: 4rem;
	    color: #ffffff;
	    font-size: 0.7rem;
	    text-align: center;
	    line-height: 1.5rem;
	    background: #2F7DCD;
	    border-radius: 0.15rem;
	    -webkit-border-radius: 0.15rem;
	}
	.sign_box {
		margin-top: 1rem;
	    padding: 0 0.75rem 0.5rem 1rem;
	    background: #ffffff;
	}
	.sign_content {
	    border: 1px solid #CCCCCC;
	}
	#canvas_box {
	    width: 100%;
	    height: 10rem;
	}
	canvas {
   		 height: 10rem !important;
	}
	.submit_box {
		width: 92%;
		margin: 0 4%;
	    height: 2.4rem;
	    line-height: 2.4rem;
	    background: #2F7DCD;
	   	border-radius: 0.2rem;
	    color: white;
	    text-align: center;
</style>
<body>
	<div class="from_list _bor_bottom0"><div>商户手动签字 <var>*</var></div><div id="reset_btn">重写</div></div>
    <div class="sign_box">
        <div class="sign_content">
            <div id="canvas_box"></div>
        </div>
    </div>
     <div class="submit_box" id="submit_btn">提交</div>
</body>
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript" src="./js/jSignature.min.js"></script>
<script type="text/javascript" src="./js/flashcanvas.js"></script>
<script type="text/javascript">
	 // *********电子签名功能*********
    let canvas_box=$("#canvas_box");    // 获取canvas标签
    canvas_box.jSignature(); // 初始化签名画板,初始化之后就可以进行操作
    let signImgSrc="";   // 提交后台的图片路径
    // 监听手动签名滑动开始、移动、释放时,执行输入框失去焦点的功能,解决签名时触发软键盘的问题
    $("#canvas_box").on("touchstart mousemove touchend",function () {
        // console.log("走签名-mousemove事件");
        $("input,textarea").blur();
    });
     // 重置按钮,生成图片之后,可重置画板,并清空图片
    $("#reset_btn").on("click",function (e) {
        canvas_box.jSignature("reset");
        signImgSrc="";
        e.preventDefault();
    });
    //提交签名
    $("#submit_btn").on("click",function (e) {
    	// 获取签名长度
    	let signLen=canvas_box.jSignature("getData","native").length; 
  		//签名判断,长度为0,提示客户签名
    	if(signLen==0){	
    		alert("请输入签名");
    	}else{
    		//当有商户签字时,赋值签字的base64码
    		 let datapair=canvas_box.jSignature("getData","image");    // 获取签名的“base64”数据
              console.log("datapair",datapair);
            // 拼接完整的base64转码,根绝接口说明,传递。
            signImgSrc='data:' + datapair[0] + "," + datapair[1];
             console.log('确认后的base64路径=',signImgSrc);
    	}
    })
</script>
</html>
yy

源码地址:https://github.com/Skingsking/signature

在中国互联网行业崛起的大背景下,大家普遍对互联网行业发展持乐观态度。据今年第二季度招聘信息显示,目前web前端工程师日均岗位缺口已经超过50000,随着互联网+的深入发展,html5作为前端展示技术,市场人才需求量将呈直线上涨。一个好的Web前端工程师在知识体系上既要有广度,又要有深度,所以很多大公司即使出高薪也很难招聘到理想的前端开发工程师。那么如何系统的学习企业实用的web前端技术呢

第一阶段 :html+div+css+ps切图+ftp网站上传(网站上线)阿里图标+html5标签+css3动画+手机网站开发+swiper.js+iscroll.js

前端开发:制作网页 ,HTML是内容,CSS是格式,JavaScript是动作

HTML即超文本标记语言,是 WWW 的描述语言,由 Tim Berners-lee提出。设计 HTML 语言的目的是为了能把存放在一台电脑中的文本或图形与另一台电脑中的文本或图形方便地联系在一起,形成有机的整体。

CSS层叠样式表是一种用来表现HTML或XML等文件样式的计算机语言。CSS不仅可以静态地修饰网页,还可以配合各种脚本语言动态地对网页各元素进行格式化。CSS 能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。

第二个阶段:JavaScript+jQuery+Ajax+正则表达式+面向对象+js插件+代码性能优化+github+sea.js+require.js+gulp

JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML网页上使用,用来给HTML网页增加动态功能。

jQuery是一个快速、简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。

AJAX 是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

第三个阶段:2D+3D应用+移动触屏事件touch+Canvas+Svg+多媒体技术+地理信息+本地存储+H5拖拽api+跨域操作+多线程+highcharts图表+bootstrap

移动端触屏事件touch滑动的效果其实就是图片轮播,在PC的页面上很好实现,绑定click和mouseover等事件来完成。但是在移动设备上,要实现这种轮播的效果,就需要用到核心的touch事件。处理touch事件能跟踪到屏幕滑动的每根手指。

Canvas是HTML5新增的组件,它就像一块幕布,可以用JavaScript在上面绘制各种图表、动画等。没有Canvas的年代,绘图只能借助Flash插件实现,页面不得不用JavaScript和Flash进行交互。有了Canvas,我们就再也不需要Flash了,直接使用JavaScript完成绘制。

以上这些是全部的知识体系。如果你想成为一名合格的程序猿,你除了知道这些知识之外,我觉得还需要以下几点:

  • 要了解敏捷软件开发流程和项目管理知识这也属于一种知识吧。
  • 要学会在网上和别人交流,交流能让自己看到自己的不足。
  • 要学会自我反省和自我学习,随时反省随时进步。

动验证码的识别介绍

本节目标:

用程序识别极验滑动验证码的验证,包括分析识别思路、识别缺口位置、生成滑块拖动路径、模拟实现滑块拼合通过验证等步骤。

准备工作:

本次案例我们使用Python库是Selenium,浏览器为Chrome。请确保已安装Selenium库和ChromeDriver浏览器驱动。

了解极验滑动验证码:

极验滑动验证码官网为:http://www.geetest.com/

验证方式为拖动滑块拼合图像,若图像完全拼合,则验证成功,否则需要重新验证,如图所示:

接下来我们链接地址:https://account.geetest.com/login,打开极验的管理后台登录页面,完成自动化登录操作。

实现步骤:

① 初始化

初始化链接地址、创建模拟浏览器对象、设置登录账户和密码等信息。

EMAIL='登录账户'
PASSWORD='登录密码'
?
class CrackGeetest():
 def __init__(self):
 self.url='https://account.geetest.com/login'
 self.browser=webdriver.Chrome()
 #设置显示等待时间
 self.wait=WebDriverWait(self.browser, 20)
 self.email=EMAIL
 self.password=PASSWORD
?
 def crack():
 pass
?
# 程序主入口
if __name__=='__main__':
 crack=CrackGeetest()
 crack.crack()

② 模拟登录填写,点开滑块验证

  • 在实例化CrackGeetest对象后调用crack()方法开始模拟登录验证...
  • 调用open()方法,打开登录界面,获取账户和密码输入框节点,完成账户和密码的输入。
  • 调用get_geetest_button()方法获取滑动验证按钮,并点击。
class CrackGeetest():
 #...
?
 def get_geetest_button(self):
 ''' 获取初始验证按钮,return:按钮对象 '''
 button=self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))
 return button
?
 def open(self):
 ''' 打开网页输入用户名密码, return: None '''
 self.browser.get(self.url)
 email=self.wait.until(EC.presence_of_element_located((By.ID, 'email')))
 password=self.wait.until(EC.presence_of_element_located((By.ID, 'password')))
 email.send_keys(self.email)
 password.send_keys(self.password)
?
 def crack(self):
 # 输入用户名密码
 self.open()
 # 点击验证按钮
 button=self.get_geetest_button()
 button.click()
 #...
 #...

③ 获取并储存有无缺口的两张图片

  • 首先获取无缺口的验证图片,并保存到本地
  • 获取滑块对象,并执行点击,让浏览器中显示有缺口图片
  • 获取有缺口的验证图片,并保存到本地
 def get_position(self):
 ''' 获取验证码位置, return: 验证码位置(元组) '''
 img=self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
 time.sleep(2)
 location=img.location
 size=img.size
 top,bottom,left,right=location['y'],location['y']+size['height'],location['x'],location['x']+size['width']
 return (top, bottom, left, right)
?
 def get_screenshot(self):
 ''' 获取网页截图, return: 截图对象 '''
 #浏览器截屏
 screenshot=self.browser.get_screenshot_as_png()
 screenshot=Image.open(BytesIO(screenshot))
 return screenshot
?
 def get_geetest_image(self, name='captcha.png'):
 ''' 获取验证码图片, return: 图片对象 '''
 top, bottom, left, right=self.get_position()
 print('验证码位置', top, bottom, left, right)
 screenshot=self.get_screenshot()
 #从网页截屏图片中裁剪处理验证图片
 captcha=screenshot.crop((left, top, right, bottom))
 captcha.save(name)
 return captcha
?
 def get_slider(self):
 ''' 获取滑块, return: 滑块对象 '''
 slider=self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
 return slider
?
 def crack(self):
 #...
?
 # 获取验证码图片
 image1=self.get_geetest_image('captcha1.png')
 # 点按呼出缺口
 slider=self.get_slider()
 slider.click()
 # 获取带缺口的验证码图片
 image2=self.get_geetest_image('captcha2.png')
?
 #...

④ 获取缺口位置

  • 对比两张图片的所有RBG像素点,得到不一样像素点的x值,即要移动的距离
BORDER=6
INIT_LEFT=60
?
class CrackGeetest(): 
 def get_gap(self, image1, image2):
 ''' 获取缺口偏移量, 参数:image1不带缺口图片、image2带缺口图片。返回偏移量 '''
 left=65
 for i in range(left, image1.size[0]):
 for j in range(image1.size[1]):
 if not self.is_pixel_equal(image1, image2, i, j):
 left=i
 return left
 return left
?
 def is_pixel_equal(self, image1, image2, x, y):
 '''
 判断两个像素是否相同
 :param image1: 图片1
 :param image2: 图片2
 :param x: 位置x
 :param y: 位置y
 :return: 像素是否相同
 '''
 # 取两个图片的像素点(R、G、B)
 pixel1=image1.load()[x, y]
 pixel2=image2.load()[x, y]
 threshold=60
 if abs(pixel1[0]-pixel2[0])<threshold and abs(pixel1[1]-pixel2[1])<threshold and abs(pixel1[2]-pixel2[2])<threshold:
 return True
 else:
 return False
?
 def crack(self):
 #...
?
 # 获取缺口位置
 gap=self.get_gap(image1, image2)
 print('缺口位置', gap)
 # 减去缺口位移
 gap -=BORDER

⑤ 获取移动轨迹

模拟人的行为习惯(先匀加速拖动后匀减速拖动),把需要拖动的总距离分成一段一段小的轨迹

 def get_track(self, distance):
 '''
 根据偏移量获取移动轨迹
 :param distance: 偏移量
 :return: 移动轨迹
 '''
 # 移动轨迹
 track=[]
 # 当前位移
 current=0
 # 减速阈值
 mid=distance * 4 / 5
 # 计算间隔
 t=0.2
 # 初速度
 v=0
?
 while current < distance:
 if current < mid:
 # 加速度为正2
 a=2
 else:
 # 加速度为负3
 a=-3
 # 初速度v0
 v0=v
 # 当前速度v=v0 + at
 v=v0 + a * t
 # 移动距离x=v0t + 1/2 * a * t^2
 move=v0 * t + 1 / 2 * a * t * t
 # 当前位移
 current +=move
 # 加入轨迹
 track.append(round(move))
 return track
?
 def crack(self):
 #...
?
 # 获取移动轨迹
 track=self.get_track(gap)
 print('滑动轨迹', track)

⑥ 按照轨迹拖动,完全验证

 def move_to_gap(self, slider, track):
 '''
 拖动滑块到缺口处
 :param slider: 滑块
 :param track: 轨迹
 :return:
 '''
 ActionChains(self.browser).click_and_hold(slider).perform()
 for x in track:
 ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
 time.sleep(0.5)
 ActionChains(self.browser).release().perform()
?
 def crack(self):
 #...
?
 # 拖动滑块
 self.move_to_gap(slider, track)
?
 success=self.wait.until(
 EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '验证成功'))
 print(success)

⑦ 完成登录