H5拖拽 构造拖拽及缩放 pdf展示

时间:2022-02-07 08:09:13

前言:

协助项目需要实现一个签名的功能。

功能说明:1.有文本签名和头像签名。2.头像签名需要实现可拖拽功能。3.需要展示的是pdf的文件并需要获取签名位于pdf文件的相对位置。

功能一:实现拖拽

思路:H5拖拽及构造函数实现拖拽及缩放

要点:1.需要设置拖拽元素属性

    draggable="true"  

   2.可拖拽的元素设置ondragstart获取数据  

     3.对可放置拖拽元素的设置ondragover函数

    默认地,无法将数据/元素放置到其他元素中。如果需要设置允许放置,我们必须阻止对元素的默认处理方式。

     4.ondrop函数

        设置放置后需要执行的方法。

  /*H5拖拽并复制*/
var moveDemo=null;
var _type;
var divs=document.querySelectorAll(".left_list li");
var showBox=document.querySelector(".section_main_pdf");
for(var i=0;i<divs.length;i++){
divs[i].ondragstart=function(e){
moveDemo=this.querySelector("img");
_type=this.querySelector('.list_name').innerText;
}
};
showBox.ondragover =function(e){
e.preventDefault();
//console.log('x:'+ e.pageX+';y:'+ e.pageY);
};
showBox.ondrop=function(e){
var _clone=moveDemo.cloneNode();
this.append(_clone);
console.log(_clone);
_clone.className='dragIcon';
$(_clone).css({
'width':'100%',
'height':'100%'
}).attr('draggable',false);
$(_clone).wrap('<span class="img_outer"></span>');
if(_type=='Signature'){
$(_clone).parent().css({
'position':'absolute',
'top': e.pageY,
'left': e.pageX,
'cursor':'move',
'display':'inline-block',
'border':'2px solid'
}).append('<span class="iconResize" style="position:absolute;display: inline-block;width: 10px;height: 10px;' +
'cursor:se-resize;right:-5px;bottom:-5px;background-color:#fff;' +
'border:2px solid;border-radius:50%"></span>').attr({
'data-icon':'signature',
'data-page':config.defaultPage
});
}
else if(_type=='PIN'){
$(_clone).parent().css({
'position':'absolute',
'top': e.pageY,
'left': e.pageX,
'cursor':'move',
'display':'inline-block',
'border':'2px solid'
}).attr({
'data-icon':'pin',
'data-page':config.defaultPage
});
}
};

功能二:构造拖拽及缩放函数

思路:通过获取鼠标事件及元素位置

 /*构造拖拽及缩放函数*/
$(document).mousemove(function(e) {
if (!!this.move) {
var posix = !document.move_target ? {'x': 0, 'y': 0} : document.move_target.posix,
callback = document.call_down || function() {
$(this.move_target).css({
'top': e.pageY - posix.y,
'left': e.pageX - posix.x
});
};
callback.call(this, e, posix);
}
}).mouseup(function(e) {
if (!!this.move) {
var callback = document.call_up || function(){};
callback.call(this, e);
$.extend(this, {
'move': false,
'move_target': null,
'call_down': false,
'call_up': false
});
}
});

备注于2017/7/26

而在拖拽过程中,由于说拖拽中也要限制不能拖拽出canvas画布外,于是又在构造函数这里添加了判断实现不可拖拽出canvas

附带如下代码:

 $(document).mousemove(function(e) {
if (!!this.move) {
var posix = !document.move_target ? {'x': 0, 'y': 0} : document.move_target.posix,
callback = document.call_down || function() {
var _toTop=e.pageY-posix.y,
_toLeft=e.pageX-posix.x; /*获取canvas左下角位置*/
var canvasHigh=$("#canvas").height();//pdf高
var canvasWid=$("#canvas").width();//pdf宽
var canvasPosTop=$("#canvas").offset().top;//pdf距离顶部
var canvasPosBottom=$("#canvas").offset().top+canvasHigh;//pdf底部距离顶部
var canvasPosLeft=$("#canvas").offset().left;//pdf距离左边
var canvasPosRight=canvasPosLeft+canvasWid;//pdf右边距离左边 if(_toTop < canvasPosTop){
_toTop = canvasPosTop
}
if(_toTop > (canvasPosBottom - $(this.move_target).height()) ){
_toTop = (canvasPosBottom - $(this.move_target).height())
}
if(_toLeft < canvasPosLeft){
_toLeft = canvasPosLeft
}
if(_toLeft > (canvasPosRight - $(this.move_target).width()) ){
_toLeft =(canvasPosRight - $(this.move_target).width())
} $(this.move_target).css({
'top': _toTop ,//e.pageY - posix.y,
'left': _toLeft//e.pageX - posix.x
});
};
callback.call(this, e, posix);
}
})

功能三:pdf展示

要点:引入pdf.js和pdf.worker.js进行API解析和核心解析

说明:1.pdf.js不能打开本地文件,需要服务器启动

      2.需要Firefox下运行报错才有提示(firefox的pdf解析)

 var loadingTask=PDFJS.getDocument(config.url);
loadingTask.promise.then(
function getPdf(pdf){
pdf.getPage(config.defaultPage).then(
function getPage(page){
var scale=1;
var viewport=page.getViewport(scale);
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
canvas.height=config.outerHigh;
canvas.width=config.outerWid;
var renderContext={
canvasContext:context,
viewport:viewport
};
page.render(renderContext);
},
function pageError(msg){
console.log(msg);
}
)
},
function pdfError(msg){
alert(msg);
}
);

备注于2017/7/26

由于pdf文件的大小各异,于是进行了如下调整,是canvas展示的pdf进行缩放显示

调整上段代码后的结果:

 var loadingTask=PDFJS.getDocument(config.url);
loadingTask.promise.then(
function getPdf(pdf){
pdf.getPage(config.defaultPage).then(
function getPage(page){
var scale=1;
var viewport=page.getViewport(scale);
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
var ch2vh=(config.outerHigh/viewport.height).toFixed(2),
cw2vw=(config.outerWid/viewport.width).toFixed(2);
if(ch2vh < cw2vw){
viewport=page.getViewport(ch2vh);
}else{
viewport=page.getViewport(cw2vw);
}
canvas.height=config.outerHigh;
canvas.width=config.outerWid;
var renderContext={
canvasContext:context,
viewport:viewport
};
page.render(renderContext);
},
function pageError(msg){
console.log(msg);
}
)
},
function pdfError(msg){
alert(msg);
}
);

汇总:

1.签名js文件

备注:在交付的时候那边发现了在ie下拖拽时获取的图片有问题,然后又来找到了我,经过一轮的调试(ps:ie调试真不爽),

发现获取字符串时候,ie好像默认添加了空格符,所以进不了if语句,现见if判断调整为:

if(

(_type).toLowerCase().trim()=='signature'

)

elseif同理。

再则ie报错不支持append(),所以将this.append()调整为this.appendChild()。

(备注:调整于2017/07/05)

 $(function(){

     /*H5拖拽并复制*/
var moveDemo=null;
var _type;
var divs=document.querySelectorAll(".left_list li");
var showBox=document.querySelector(".section_main_pdf");
for(var i=0;i<divs.length;i++){
divs[i].ondragstart=function(e){
moveDemo=this.querySelector("img");
_type=this.querySelector('.list_name').innerText;
}
};
showBox.ondragover =function(e){
e.preventDefault();
//console.log('x:'+ e.pageX+';y:'+ e.pageY);
};
showBox.ondrop=function(e){
var _clone=moveDemo.cloneNode();
this.append(_clone);
console.log(_clone);
_clone.className='dragIcon';
$(_clone).css({
'width':'100%',
'height':'100%'
}).attr('draggable',false);
$(_clone).wrap('<span class="img_outer"></span>');
if(_type=='Signature'){
$(_clone).parent().css({
'position':'absolute',
'top': e.pageY,
'left': e.pageX,
'cursor':'move',
'display':'inline-block',
'border':'2px solid'
}).append('<span class="iconResize" style="position:absolute;display: inline-block;width: 10px;height: 10px;' +
'cursor:se-resize;right:-5px;bottom:-5px;background-color:#fff;' +
'border:2px solid;border-radius:50%"></span>').attr({
'data-icon':'signature',
'data-page':config.defaultPage
});
}
else if(_type=='PIN'){
$(_clone).parent().css({
'position':'absolute',
'top': e.pageY,
'left': e.pageX,
'cursor':'move',
'display':'inline-block',
'border':'2px solid'
}).attr({
'data-icon':'pin',
'data-page':config.defaultPage
});
}
}; /*构造拖拽及缩放函数*/
$(document).mousemove(function(e) {
if (!!this.move) {
var posix = !document.move_target ? {'x': 0, 'y': 0} : document.move_target.posix,
callback = document.call_down || function() {
$(this.move_target).css({
'top': e.pageY - posix.y,
'left': e.pageX - posix.x
});
};
callback.call(this, e, posix);
}
}).mouseup(function(e) {
if (!!this.move) {
var callback = document.call_up || function(){};
callback.call(this, e);
$.extend(this, {
'move': false,
'move_target': null,
'call_down': false,
'call_up': false
});
}
}); /*实例化*/
$(document).on('mousedown','.img_outer',function(e){
var offset=$(this).offset();
this.posix={
'x': e.pageX-offset.left,
'y': e.pageY-offset.top
};
$.extend(document,{
'move':true,
'move_target':this
});
});
$(document).on('mousedown','.iconResize',function(e){
var that=this;
var posix = {
'w': $(this).parent().width(),
'h': $(this).parent().height(),
'x': e.pageX,
'y': e.pageY
};
$.extend(document, {'move': true, 'call_down': function(e) {
$(that).parent().css({
'width': Math.max(30, e.pageX - posix.x + posix.w),
'height': Math.max(30, e.pageY - posix.y + posix.h)
});
}});
return false;
}); //获取选中元素
$(document).on('click','.img_outer',function(e){
$(".img_outer").removeClass("active");
var _active=$(this).hasClass("active");
if(_active){
$(this).removeClass("active");
}else{
$(this).addClass("active");
}
}); /*渲染PDF*/
/* var _container=$(".section_main_pdf");
PDFJS.workerSrc='js/pdf.worker.js'; var config={
//url:'txt/userAgreeDoc.pdf',//pdf路径
url:'txt/webpack.pdf',
outerHigh:_container.height(),//容器高
outerWid:_container.width(),//容器宽
defaultPage:1,//默认第一页
totalPage:6//总页数
};*/ renderPdf();
/*上一页*/
$("input.page_pre").on('click',function(){
if(config.defaultPage>1){
--config.defaultPage;
$(".page_num_cur").text(config.defaultPage);
renderPdf();
}
});
/*下一页*/
$("input.page_next").on('click',function(){
if(config.defaultPage<config.totalPage){
++config.defaultPage;
$(".page_num_cur").text(config.defaultPage);
renderPdf();
}
});
/*GO*/
$("input.page_go_val").on('click',function(){
var _val=$("input.page_val").val();
if(_val>0 && _val<=config.totalPage){
config.defaultPage=parseInt(_val);//pdf不会自动转换字符串为数字
console.log(config.defaultPage);
$(".page_num_cur").text(_val);
renderPdf();
}
}); function renderPdf(){
var loadingTask=PDFJS.getDocument(config.url);
loadingTask.promise.then(
function getPdf(pdf){
pdf.getPage(config.defaultPage).then(
function getPage(page){
var scale=1;
var viewport=page.getViewport(scale);
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
canvas.height=config.outerHigh;
canvas.width=config.outerWid;
var renderContext={
canvasContext:context,
viewport:viewport
};
page.render(renderContext);
},
function pageError(msg){
console.log(msg);
}
)
},
function pdfError(msg){
alert(msg);
}
); //对不在当前页面的签名进行隐藏或显示
var lists=$(".section_main_pdf .img_outer");
for(var i=0;i<lists.length;i++){
if(lists.eq(i).data('page')==config.defaultPage){
lists.eq(i).css({
'visibility':'visible'
});
}else{
lists.eq(i).css({
'visibility':'hidden'
});
}
}
}
}); //选择头像
$(document).on('change','#selectUser',function(e){
//通过字符串拼接选中值来实现切换图片
var _val=$(this).find('option:selected').val();
var _imgVal=$(".img_outer.active");
if(_val=='spouse'){
_imgVal.find('img').attr({
'src':'images/pin.png'
});
_imgVal.attr('data-id',_val);
}
}); //保存信息
$(document).on('click','#saveMsg',function(e){
/*获取canvas左下角位置*/
var canvasHigh=$("#canvas").height();//pdf高
var canvasWid=$("#canvas").width();//pdf宽
//console.log('canvasHigh:'+canvasHigh);
var canvasPosTop=$("#canvas").offset().top;//pdf距离顶部
var canvasPosBottom=$("#canvas").offset().top+canvasHigh;//pdf底部距离顶部
var canvasPosLeft=$("#canvas").offset().left;//pdf距离左边
var canvasPosRight=canvasPosLeft+canvasWid;//pdf右边距离左边
//console.log('canvasPosTop:'+canvasPosTop+';canvasPosLeft:'+canvasPosLeft); /*遍历获取每个签名的位置*/
var obj=new Array();
var lists=$(".section_main_pdf .img_outer");
for(var i=0;i<lists.length;i++){
var _left=parseInt(lists.eq(i).css('left'));
if(_left<canvasPosLeft || _left> canvasPosRight){
alert('请在文件中签名');
return;
}
var _top=parseInt(lists.eq(i).css('top'));
if(_top<canvasPosTop || _top>canvasPosBottom){
alert('请在文件中签名');
return;
}
var arr={
type:lists.eq(i).data('icon'),//签名类型
wid:lists.eq(i).width(),//签名的宽
high:lists.eq(i).height(),//签名的高
id:lists.eq(i).data('id'),//用户id
x:_left-canvasPosLeft,//距离pdf左下角的x,
y:Math.abs(_top+lists.eq(i).height()-canvasPosBottom),//距离pdf左下角的y,
page:lists.eq(i).data('page'),//签名所在页
};
obj.push(arr);
}
console.log('obj:'+JSON.stringify(obj));
//下一步:异步提交
});

js

2.签名html文件

 <!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>签名</title>
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/signature.css">
</head>
<body>
<div class="container">
<div class="header">
<div class="header_inner clearfix">
<div class="header_left">
<span class="header_left_name">webpack中文指南.pdf</span>
</div>
<div class="header_right">
<input id="saveMsg" type="button" value="Save" class="header_right_save">
</div>
</div>
</div>
<div class="section clearfix">
<div class="section_left">
<h3 class="left_title">Select and Drag Icon</h3>
<ul class="left_list">
<li draggable="true">
<span class="img_outer">
<img src="data:images/signature.png">
</span>
<span class="list_name">
Signature
</span>
</li>
<li draggable="true">
<span class="img_outer">
<img src="data:images/pin.png">
</span>
<span class="list_name">
PIN
</span>
</li>
</ul>
</div>
<div class="section_main">
<div class="section_main_pdf">
<canvas id="canvas"></canvas>
</div>
<div class="section_main_page">
<div class="page clearfix">
<div class="page_detail">
<input type="button" value="<" class="page_pre">
<span class="page_num">
<span class="page_num_cur">1</span>
/
<span class="total_page">29</span>
</span>
<input type="button" value=">" class="page_next active">
</div>
<div class="page_go">
<span class="page_go_page">Page</span>
<input type="text" class="page_val">
<input type="button" value="GO" class="page_go_val"/>
</div>
</div>
</div>
</div>
<div class="section_right">
<h3 class="right_title">Select the Signature</h3>
<div class="right_select">
<select id="selectUser">
<option>Please Select</option>
<option value="spouse">Spouse</option>
</select>
</div>
</div>
</div>
</div> <script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.js"></script>
<!--API解析-->
<script src="js/pdf.js"></script>
<!--核心解析-->
<!--<script src="js/pdf.worker.js"></script>-->
<script src="js/signature.js"></script>
<script>
/*渲染PDF*/
var _container=$(".section_main_pdf");
PDFJS.workerSrc='js/pdf.worker.js'; var config={
//url:'txt/userAgreeDoc.pdf',//pdf路径
url:'txt/webpack.pdf',
outerHigh:_container.height(),//容器高
outerWid:_container.width(),//容器宽
defaultPage:1,//默认第一页
totalPage:29//总页数
};
</script>
<script src="js/responseData.js"></script>
<script>
</script>
</body>
</html>

html

3.签名已存在时的反显

备注:之前上传的版本后来发现了位置反显有误,调整了.img_outer的top位置:

top:'+(this.canvasBottom-resData[i].y-resData[i].high)+'px;
(修改时间:17/06/30)
 /**
* Created by aaron on 2017/6/16.
*/ //var data=[{"type":"pin","wid":44,"high":44,"x":332,"y":379,"page":1},{"type":"signature","wid":44,"high":44,"x":187,"y":608,"page":1}]; (function($,window,undefined){
var resData={
canvasLeft:$(".section_main_pdf").offset().left,
canvasBottom:$(".section_main_pdf").height()+$(".section_main_pdf").offset().top,
init:function(){
this.getData();
},
getData:function(){
var that=this;
$.ajax({
url:'./data/signature.json',
type:'post',
dataType:'json',
async:false,
success:function(data){
console.log(data);
if(data.isSuccess){
that.resolveData(data.dataList);
}
}
})
},
resolveData:function(data){
console.log(this);
console.log(JSON.stringify(data));
var html='';
var resData=data;
console.log('resData:'+resData.length);
for(var i=0;i<resData.length;i++){
html+='<span class="img_outer" data-icon="'+resData[i].type+'" data-page="'+resData[i].page+'" ' +
'style="position:absolute;cursor:move;display:inline-block;border:2px solid;top:'+(resData[i].y+this.canvasBottom-resData[i].high)+'px;' +
'left:'+(resData[i].x+this.canvasLeft)+'px;width:'+(resData[i].wid+4)+'px;height:'+(resData[i].high+4)+'px;">' ;
if(resData[i].type=='signature'){
html+='<img src="" class="dragIcon" style="width: 100%;height: 100%"/>'+
'<span class="iconResize" style="display: inline-block;width:10px;position:absolute;' +
'height:10px;cursor:se-resize;right:-5px;bottom:-5px;background-color:#fff;border:2px solid ;border-radius:50%"></span>';
}else{
html+='<img src="" class="dragIcon" style="width: 100%;height: 100%"/>';
}
html+='</span>';
}
$(".section_main_pdf").append(html);
}
};
resData.init();
})(jQuery,window,undefined);

setData

4.样式css

 /*CSS RESET*/
@charset "utf-8";
html, body, div, span,
h1, h2, h3, h4, h5, h6, p,
img, small, strong,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, menu, nav, section, summary,
time, mark, audio, video {
margin:;
padding:;
border:;
outline:;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
/*重置界面字体字号色号*/
body {
font-family:'Microsoft YaHei',"Trebuchet MS",Verdana,Helvetica,Arial,sans-serif;
font-size:16px;
color:#000000;
min-width:1200px;
}
dl li,ul li{
list-style-type: none;
}
a {
margin:;
padding:;
font-size:100%;
text-decoration: none;
vertical-align:baseline;
background:transparent;
}
table {
border-collapse:collapse;
border-spacing:;
}
i,b{
font-style: normal;
font-weight: normal;
}
img{
border:;
vertical-align: middle;
}
*{
box-sizing: border-box;
}
/*清除浮动*/
.clearfix:after{
content:'';
display:block;
height:;
visibility:hidden;
clear:both;
}
.clearfix{
zoom:;
} /*重置input/select样式*/
input,select{
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
} ::-webkit-input-placeholder {
color: #d2d2d2; }
:-moz-placeholder {
color: #d2d2d2; }
::-moz-placeholder {
color: #d2d2d2; }
:-ms-input-placeholder {
color: #d2d2d2; }
input:focus::-webkit-input-placeholder{ color:#d2d2d2;}

reset

 @charset "utf-8";
body{
background-color: #e2e2ee;
}
.header{
width: 100%;
background-color:#3d62ad;
}
.header .header_inner,.section{
width: 1200px;
margin:0 auto;
}
.header .header_inner{
line-height: 60px;
}
.header .header_left{
float: left;
}
.header .header_left_name{
font-size: 18px;
color:#ffffff;
}
.header .header_right{
float: right;
}
.header input.header_right_save{
font-size: 16px;
width: 140px;
height: 34px;
line-height: 34px;
background-color: #ffffff;
color:#3d62ad;
border:;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor: pointer;
} .section .section_left{
float: left;
width: 230px;
background-color: #ffffff;
margin-right: 20px;
min-height: 840px;
}
.section .section_main{
float: left;
width: 700px;
background-color: #ffffff;
min-height: 840px;
}
.section .section_right{
float: right;
width: 230px;
background-color: #ffffff;
min-height: 840px;
} .section h3.left_title,
.section h3.right_title{
line-height: 55px;
text-align: center;
}
/*实现可拖拽*/
.section ul.left_list li{
display: inline-block;
width: 230px;
height: 45px;
line-height: 45px;
padding-left: 25px;
margin:10px 0;
color:#3d62ad;
cursor: move;
}
.section span.list_name{
margin-left:15px;
}
.section ul.left_list li:first-child{
margin-top:;
} .section_main .section_main_pdf{
width: 550px;
height: 755px;
margin: 15px auto;
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.30);
}
.section_main .page{
width: 310px;
height: 45px;
line-height: 45px;
margin: 0 auto;
}
.section_main .page_detail{
float: left;
}
.section_main .page_go{
float: right;
}
.section_main input.page_pre,
.section_main input.page_next{
width: 22px;
height: 22px;
line-height: 22px;
color:#a7a7a7;
background-color:#e9edf5;
border:;
cursor: pointer;
}
.section_main input.page_pre.active,
.section_main input.page_next.active{
color:#3d62ad;
}
.section_main span.page_num{
padding:0 10px;
}
.section_main span.page_num_cur{
color:#3d62ad;
} .section_main input.page_val{
width: 35px;
height: 20px;
line-height: 20px;
margin:0 2px 0 10px;
text-align: center;
border:1px solid #3d62ad;
}
.section_main input.page_go_val{
font-weight:;
color:#3d62ad;
background-color: transparent;
border:;
font-size: 16px;
cursor: pointer;
}
input:focus{
outline: none;
} .section_right{
text-align: center;
}
.section_right .right_select{
width: 185px;
height: 29px;
line-height: 29px;
margin: 0 auto;
position: relative;
}
.section_right .right_select:after{
position: absolute;
content: '';
right: 1px;
top: 1px;
display: inline-block;
width: 34px;
height: 27px;
background:url("../images/arrow_bottom.png") no-repeat center;
color:#ffffff;
z-index:;
}
.section_right .right_select select{
width: 185px;
height: 29px;
padding:0 34px 0 15px ;
border:1px solid #6b6e82;
outline: none;
background-color: transparent;
position: absolute;
left:;
top:;
z-index:;
} .img_outer.active{
border-color:#3d62ad !important;
}

css

5.文件结构

H5拖拽  构造拖拽及缩放  pdf展示