鸿蒙扫描手机端签字问题
This commit is contained in:
@@ -22,7 +22,9 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<source>15</source>
|
<source>15</source>
|
||||||
<target>15</target>
|
<target>15</target>
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
<compilerArgs>
|
||||||
|
<arg>--enable-preview</arg>
|
||||||
|
</compilerArgs>
|
||||||
<useIncrementalCompilation>false</useIncrementalCompilation>
|
<useIncrementalCompilation>false</useIncrementalCompilation>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@@ -70,13 +72,6 @@
|
|||||||
<artifactId>nutzboot-maven-plugin</artifactId>
|
<artifactId>nutzboot-maven-plugin</artifactId>
|
||||||
<version>${nutzboot.version}</version>
|
<version>${nutzboot.version}</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<source>15</source>
|
|
||||||
<target>15</target>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|||||||
@@ -546,7 +546,10 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<source>15</source>
|
<source>15</source>
|
||||||
<target>15</target>
|
<target>15</target>
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
<!-- Java 15 预览功能必须由 JDK 15 编译,运行时也需启用预览功能。 -->
|
||||||
|
<compilerArgs>
|
||||||
|
<arg>--enable-preview</arg>
|
||||||
|
</compilerArgs>
|
||||||
<useIncrementalCompilation>false</useIncrementalCompilation>
|
<useIncrementalCompilation>false</useIncrementalCompilation>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@@ -598,14 +601,6 @@
|
|||||||
<artifactId>nutzboot-maven-plugin</artifactId>
|
<artifactId>nutzboot-maven-plugin</artifactId>
|
||||||
<version>${nutzboot.version}</version>
|
<version>${nutzboot.version}</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<source>15</source>
|
|
||||||
<target>15</target>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};!function(t,e){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self,t.Signature=e)}(void 0,function(t){function e(){o.step++,o.step<status.length&&(o.status.length=o.step),o.status.push(o.getContentBase64())}var o=this;this.obj=t&&!Array.isArray(t)?t:{},this.option={el:o.obj.el?o.obj.el:"body",lineWidth:o.obj.lineWidth&&!isNaN(parseInt(o.obj.lineWidth))?o.obj.lineWidth:5,lineColor:o.obj.lineColor&&/^#[1-9|A-F]{6}/.test(o.obj.lineColor.substr(0,7).toLocaleUpperCase())?o.obj.lineColor:"#000000",overflow:"boolean"===typeof o.obj.overflow?o.obj.overflow:true,background:o.obj.background&&/^#[1-9|A-F]{6}/.test(o.obj.background.substr(0,7).toLocaleUpperCase())?o.obj.background:"#ffffff"},this.status=[],this.step=-1,this.firstStatus="";var n=document.querySelector(this.option.el);if("CANVAS"!==n.tagName)throw new Error("Target element must be canvas");n.style.backgroundColor=this.option.background;var i=n.getContext("2d");// 根据浏览器输入能力绑定事件;不依赖 Android/iPhone 标识,兼容鸿蒙触屏。
|
||||||
|
// overflow=false 仅限制画布手势,不禁止整页滚动;未传时保持原默认行为。
|
||||||
|
this.create = (options = o.option) => {
|
||||||
|
i.lineWidth = options.lineWidth;
|
||||||
|
i.strokeStyle = options.lineColor;
|
||||||
|
if (options.overflow === true) document.body.style.overflow = 'hidden';
|
||||||
|
n.style.touchAction = 'none';
|
||||||
|
try {
|
||||||
|
o.firstStatus = o.getContentBase64();
|
||||||
|
} catch (error) {
|
||||||
|
// 空白快照失败时,清空仍可直接擦除画布,不依赖图片解码。
|
||||||
|
o.reset = () => {
|
||||||
|
i.clearRect(0, 0, n.width, n.height);
|
||||||
|
o.step = -1;
|
||||||
|
o.status = [];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let point = null;
|
||||||
|
let activeId = null;
|
||||||
|
// client 坐标按画布实际显示尺寸换算,避免边框、缩放造成笔迹偏移。
|
||||||
|
const position = (event) => {
|
||||||
|
const rect = n.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
x: (event.clientX - rect.left - n.clientLeft) * n.width / n.clientWidth,
|
||||||
|
y: (event.clientY - rect.top - n.clientTop) * n.height / n.clientHeight
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const begin = (event) => {
|
||||||
|
point = position(event);
|
||||||
|
try {
|
||||||
|
const snapshot = o.getContentBase64();
|
||||||
|
o.step++;
|
||||||
|
o.status.length = o.step;
|
||||||
|
o.status.push(snapshot);
|
||||||
|
} catch (error) {
|
||||||
|
// 历史记录仅用于撤销;快照失败不能阻断当前笔画。
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const move = (event) => {
|
||||||
|
if (!point) return;
|
||||||
|
const next = position(event);
|
||||||
|
i.beginPath();
|
||||||
|
i.moveTo(point.x, point.y);
|
||||||
|
i.lineTo(next.x, next.y);
|
||||||
|
i.stroke();
|
||||||
|
point = next;
|
||||||
|
};
|
||||||
|
let source = '';
|
||||||
|
let pointerType = '';
|
||||||
|
let captured = false;
|
||||||
|
let ignoreMouseUntil = 0;
|
||||||
|
// 仅阻止画布内可取消的手势,避免被动监听异常和浏览器滚动抢占笔画。
|
||||||
|
const prevent = (event) => {
|
||||||
|
if (event.cancelable) event.preventDefault();
|
||||||
|
};
|
||||||
|
const end = () => {
|
||||||
|
point = null;
|
||||||
|
activeId = null;
|
||||||
|
source = '';
|
||||||
|
pointerType = '';
|
||||||
|
captured = false;
|
||||||
|
};
|
||||||
|
const suppressMouse = () => { ignoreMouseUntil = Date.now() + 800; };
|
||||||
|
// changedTouches 是 TouchList,不要求内核实现迭代器;只处理当前落笔手指。
|
||||||
|
const findTouch = (list) => {
|
||||||
|
for (let index = 0; list && index < list.length; index++) {
|
||||||
|
if (list[index].identifier === activeId) return list[index];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
n.addEventListener('pointerdown', (event) => {
|
||||||
|
const type = event.pointerType || '';
|
||||||
|
if (source || (type === 'mouse' && (event.button !== 0 || Date.now() < ignoreMouseUntil))) return;
|
||||||
|
source = 'pointer';
|
||||||
|
pointerType = type;
|
||||||
|
activeId = event.pointerId;
|
||||||
|
begin(event);
|
||||||
|
if (type !== 'mouse') suppressMouse();
|
||||||
|
// 部分内核提供 PointerEvent 却不支持捕获;捕获失败仍允许在画布内绘制。
|
||||||
|
try {
|
||||||
|
if (typeof n.setPointerCapture === 'function') {
|
||||||
|
n.setPointerCapture(event.pointerId);
|
||||||
|
captured = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
captured = false;
|
||||||
|
}
|
||||||
|
prevent(event);
|
||||||
|
});
|
||||||
|
n.addEventListener('pointermove', (event) => {
|
||||||
|
if (source !== 'pointer' || event.pointerId !== activeId) return;
|
||||||
|
move(event);
|
||||||
|
prevent(event);
|
||||||
|
});
|
||||||
|
['pointerup', 'pointercancel', 'lostpointercapture'].forEach((name) => {
|
||||||
|
n.addEventListener(name, (event) => {
|
||||||
|
if (source !== 'pointer' || event.pointerId !== activeId) return;
|
||||||
|
if (pointerType !== 'mouse') suppressMouse();
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
n.addEventListener('pointerleave', () => {
|
||||||
|
if (source === 'pointer' && !captured) end();
|
||||||
|
});
|
||||||
|
// 始终绑定触摸兜底。若只收到 pointerdown 而没有 pointermove,touchstart 可接管该笔画。
|
||||||
|
n.addEventListener('touchstart', (event) => {
|
||||||
|
suppressMouse();
|
||||||
|
prevent(event);
|
||||||
|
const touch = event.changedTouches && event.changedTouches[0];
|
||||||
|
if (!touch || source === 'touch' || source === 'mouse') return;
|
||||||
|
if (source === 'pointer') {
|
||||||
|
if (pointerType === 'mouse' || pointerType === 'pen') return;
|
||||||
|
// 接管不再保存第二份历史,也不重复画线;后续指针事件由来源锁忽略。
|
||||||
|
point = position(touch);
|
||||||
|
} else {
|
||||||
|
begin(touch);
|
||||||
|
}
|
||||||
|
source = 'touch';
|
||||||
|
activeId = touch.identifier;
|
||||||
|
}, {passive: false});
|
||||||
|
n.addEventListener('touchmove', (event) => {
|
||||||
|
suppressMouse();
|
||||||
|
prevent(event);
|
||||||
|
if (source !== 'touch') return;
|
||||||
|
const touch = findTouch(event.changedTouches);
|
||||||
|
if (touch) move(touch);
|
||||||
|
}, {passive: false});
|
||||||
|
['touchend', 'touchcancel'].forEach((name) => {
|
||||||
|
n.addEventListener(name, (event) => {
|
||||||
|
suppressMouse();
|
||||||
|
prevent(event);
|
||||||
|
if (source === 'touch' && findTouch(event.changedTouches)) end();
|
||||||
|
}, {passive: false});
|
||||||
|
});
|
||||||
|
// 兼容旧内核鼠标输入,并过滤触摸结束后生成的合成鼠标事件。
|
||||||
|
n.addEventListener('mousedown', (event) => {
|
||||||
|
if (source || event.button !== 0 || Date.now() < ignoreMouseUntil) return;
|
||||||
|
source = 'mouse';
|
||||||
|
begin(event);
|
||||||
|
prevent(event);
|
||||||
|
});
|
||||||
|
n.addEventListener('mousemove', (event) => {
|
||||||
|
if (source === 'mouse') move(event);
|
||||||
|
});
|
||||||
|
['mouseup', 'mouseleave'].forEach((name) => {
|
||||||
|
n.addEventListener(name, () => {
|
||||||
|
if (source === 'mouse') end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},this.getContentBase64=function(t,e){if(t){var i=!0,s=!1,a=void 0;try{for(var r,f=document.querySelectorAll('canvas[data-none="1"]')[Symbol.iterator]();!(i=(r=f.next()).done);i=!0){var d=r.value;d.parentNode.removeChild(d)}}catch(t){s=!0,a=t}finally{try{!i&&f.return&&f.return()}finally{if(s)throw a}}var l=document.createElement("canvas");l.dataset.none="1",l.width=n.offsetHeight,l.height=n.offsetWidth,document.body.appendChild(l);var c=document.querySelector('canvas[data-none="1"]').getContext("2d"),u=new Image;u.src=o.getContentBase64(),u.onload=function(){var t=Math.round(n.offsetHeight/2),o=Math.round(n.offsetWidth/2);c.save(),c.translate(t,o),c.rotate(-90*Math.PI/180),c.drawImage(u,-u.width/2,-u.height/2),c.restore(),e&&e(document.querySelector('canvas[data-none="1"]').toDataURL("image/png"))}}else{if(!e)return n.toDataURL("image/png");e(n.toDataURL("image/png"))}},this.getBlob=function(){for(var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.getContentBase64(),e=t.split(","),o=e[0].match(/:(.*?);/)[1],n=atob(e[1]),i=n.length,s=new Uint8Array(i);i--;)s[i]=n.charCodeAt(i);return new Blob([s],{type:o})},this.getFile=function(t){for(var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getContentBase64(),o=t||"file",n=e.split(","),i=n[0].match(/:(.*?);/)[1],s=i.split("/")[1],a=atob(n[1]),r=a.length,f=new Uint8Array(r);r--;)f[r]=a.charCodeAt(r);return new File([f],o+"."+s,{type:i})},this.reset=function(){if(!o.firstStatus)return!1;i.clearRect(0,0,n.offsetWidth,n.offsetHeight);var t=new Image;try{t.src=o.firstStatus,t.addEventListener("load",function(){i.drawImage(t,0,0)}),o.step=-1,o.status=[]}catch(t){}},this.undo=function(t){if(o.step>=0){i.clearRect(0,0,n.offsetWidth,n.offsetHeight);var e=new Image;try{e.src=o.status[o.step],e.addEventListener("load",function(){i.drawImage(e,0,0)})}catch(t){}o.step--}else console.warn("no more undo"),t&&t()},this.redo=function(t){if(o.step<o.status.length-2){o.step++;var e=new Image;o.status[o.step+1]&&(e.src=o.status[o.step+1],e.addEventListener("load",function(){i.clearRect(0,0,n.offsetWidth,n.offsetHeight),i.drawImage(e,0,0)}))}else console.warn("no more redo"),t&&t()}});
|
||||||
+150
-1
@@ -1 +1,150 @@
|
|||||||
"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};!function(t,e){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self,t.Signature=e)}(void 0,function(t){function e(){o.step++,o.step<status.length&&(o.status.length=o.step),o.status.push(o.getContentBase64())}var o=this;this.obj=t&&!Array.isArray(t)?t:{},this.option={el:o.obj.el?o.obj.el:"body",lineWidth:o.obj.lineWidth&&!isNaN(parseInt(o.obj.lineWidth))?o.obj.lineWidth:5,lineColor:o.obj.lineColor&&/^#[1-9|A-F]{6}/.test(o.obj.lineColor.substr(0,7).toLocaleUpperCase())?o.obj.lineColor:"#000000",overflow:!o.obj.direction||"boolean"!=typeof o.obj.direction||o.obj.direction,background:o.obj.background&&/^#[1-9|A-F]{6}/.test(o.obj.background.substr(0,7).toLocaleUpperCase())?o.obj.background:"#ffffff"},this.status=[],this.step=-1,this.firstStatus="";var n=document.querySelector(this.option.el);if("CANVAS"!==n.tagName)throw new Error("Target element must be canvas");n.style.backgroundColor=this.option.background;var i=n.getContext("2d");this.create=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:o.option;i.lineWidth=t.lineWidth,i.strokeStyle=t.lineColor,!0===t.overflow&&(document.querySelector("body").style.overflow="hidden");var s="",a=navigator.userAgent.toLocaleLowerCase();o.firstStatus=o.getContentBase64(),-1!==a.indexOf("iphone")||-1!==a.indexOf("android")?(n.addEventListener("touchstart",function(t){s={x:t.touches[0].clientX-this.offsetLeft,y:t.touches[0].clientY-this.offsetTop}}),n.addEventListener("touchmove",function(t){i.beginPath(),i.moveTo(s.x,s.y),s={x:t.touches[0].clientX-this.offsetLeft,y:t.touches[0].clientY-this.offsetTop},i.lineTo(s.x,s.y),i.fill(),i.closePath(),i.stroke()}),n.addEventListener("touchend",function(){s=""})):(n.addEventListener("mousedown",function(t){e(),s={x:t.clientX-this.offsetLeft,y:t.clientY-this.offsetTop}}),n.addEventListener("mousemove",function(t){s.x&&s.y&&(i.beginPath(),i.moveTo(s.x,s.y),s={x:t.clientX-this.offsetLeft,y:t.clientY-this.offsetTop},i.lineTo(s.x,s.y),i.fill(),i.closePath(),i.stroke())}),n.addEventListener("mouseup",function(){s=""}),n.addEventListener("mouseout",function(){s=""}))},this.getContentBase64=function(t,e){if(t){var i=!0,s=!1,a=void 0;try{for(var r,f=document.querySelectorAll('canvas[data-none="1"]')[Symbol.iterator]();!(i=(r=f.next()).done);i=!0){var d=r.value;d.parentNode.removeChild(d)}}catch(t){s=!0,a=t}finally{try{!i&&f.return&&f.return()}finally{if(s)throw a}}var l=document.createElement("canvas");l.dataset.none="1",l.width=n.offsetHeight,l.height=n.offsetWidth,document.body.appendChild(l);var c=document.querySelector('canvas[data-none="1"]').getContext("2d"),u=new Image;u.src=o.getContentBase64(),u.onload=function(){var t=Math.round(n.offsetHeight/2),o=Math.round(n.offsetWidth/2);c.save(),c.translate(t,o),c.rotate(-90*Math.PI/180),c.drawImage(u,-u.width/2,-u.height/2),c.restore(),e&&e(document.querySelector('canvas[data-none="1"]').toDataURL("image/png"))}}else{if(!e)return n.toDataURL("image/png");e(n.toDataURL("image/png"))}},this.getBlob=function(){for(var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.getContentBase64(),e=t.split(","),o=e[0].match(/:(.*?);/)[1],n=atob(e[1]),i=n.length,s=new Uint8Array(i);i--;)s[i]=n.charCodeAt(i);return new Blob([s],{type:o})},this.getFile=function(t){for(var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getContentBase64(),o=t||"file",n=e.split(","),i=n[0].match(/:(.*?);/)[1],s=i.split("/")[1],a=atob(n[1]),r=a.length,f=new Uint8Array(r);r--;)f[r]=a.charCodeAt(r);return new File([f],o+"."+s,{type:i})},this.reset=function(){if(!o.firstStatus)return!1;i.clearRect(0,0,n.offsetWidth,n.offsetHeight);var t=new Image;try{t.src=o.firstStatus,t.addEventListener("load",function(){i.drawImage(t,0,0)}),o.step=-1,o.status=[]}catch(t){}},this.undo=function(t){if(o.step>=0){i.clearRect(0,0,n.offsetWidth,n.offsetHeight);var e=new Image;try{e.src=o.status[o.step],e.addEventListener("load",function(){i.drawImage(e,0,0)})}catch(t){}o.step--}else console.warn("no more undo"),t&&t()},this.redo=function(t){if(o.step<o.status.length-2){o.step++;var e=new Image;o.status[o.step+1]&&(e.src=o.status[o.step+1],e.addEventListener("load",function(){i.clearRect(0,0,n.offsetWidth,n.offsetHeight),i.drawImage(e,0,0)}))}else console.warn("no more redo"),t&&t()}});
|
"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};!function(t,e){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self,t.Signature=e)}(void 0,function(t){function e(){o.step++,o.step<status.length&&(o.status.length=o.step),o.status.push(o.getContentBase64())}var o=this;this.obj=t&&!Array.isArray(t)?t:{},this.option={el:o.obj.el?o.obj.el:"body",lineWidth:o.obj.lineWidth&&!isNaN(parseInt(o.obj.lineWidth))?o.obj.lineWidth:5,lineColor:o.obj.lineColor&&/^#[1-9|A-F]{6}/.test(o.obj.lineColor.substr(0,7).toLocaleUpperCase())?o.obj.lineColor:"#000000",overflow:"boolean"===typeof o.obj.overflow?o.obj.overflow:true,background:o.obj.background&&/^#[1-9|A-F]{6}/.test(o.obj.background.substr(0,7).toLocaleUpperCase())?o.obj.background:"#ffffff"},this.status=[],this.step=-1,this.firstStatus="";var n=document.querySelector(this.option.el);if("CANVAS"!==n.tagName)throw new Error("Target element must be canvas");n.style.backgroundColor=this.option.background;var i=n.getContext("2d");// 根据浏览器输入能力绑定事件;不依赖 Android/iPhone 标识,兼容鸿蒙触屏。
|
||||||
|
// overflow=false 仅限制画布手势,不禁止整页滚动;未传时保持原默认行为。
|
||||||
|
this.create = (options = o.option) => {
|
||||||
|
i.lineWidth = options.lineWidth;
|
||||||
|
i.strokeStyle = options.lineColor;
|
||||||
|
if (options.overflow === true) document.body.style.overflow = 'hidden';
|
||||||
|
n.style.touchAction = 'none';
|
||||||
|
try {
|
||||||
|
o.firstStatus = o.getContentBase64();
|
||||||
|
} catch (error) {
|
||||||
|
// 空白快照失败时,清空仍可直接擦除画布,不依赖图片解码。
|
||||||
|
o.reset = () => {
|
||||||
|
i.clearRect(0, 0, n.width, n.height);
|
||||||
|
o.step = -1;
|
||||||
|
o.status = [];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let point = null;
|
||||||
|
let activeId = null;
|
||||||
|
// client 坐标按画布实际显示尺寸换算,避免边框、缩放造成笔迹偏移。
|
||||||
|
const position = (event) => {
|
||||||
|
const rect = n.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
x: (event.clientX - rect.left - n.clientLeft) * n.width / n.clientWidth,
|
||||||
|
y: (event.clientY - rect.top - n.clientTop) * n.height / n.clientHeight
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const begin = (event) => {
|
||||||
|
point = position(event);
|
||||||
|
try {
|
||||||
|
const snapshot = o.getContentBase64();
|
||||||
|
o.step++;
|
||||||
|
o.status.length = o.step;
|
||||||
|
o.status.push(snapshot);
|
||||||
|
} catch (error) {
|
||||||
|
// 历史记录仅用于撤销;快照失败不能阻断当前笔画。
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const move = (event) => {
|
||||||
|
if (!point) return;
|
||||||
|
const next = position(event);
|
||||||
|
i.beginPath();
|
||||||
|
i.moveTo(point.x, point.y);
|
||||||
|
i.lineTo(next.x, next.y);
|
||||||
|
i.stroke();
|
||||||
|
point = next;
|
||||||
|
};
|
||||||
|
let source = '';
|
||||||
|
let pointerType = '';
|
||||||
|
let captured = false;
|
||||||
|
let ignoreMouseUntil = 0;
|
||||||
|
// 仅阻止画布内可取消的手势,避免被动监听异常和浏览器滚动抢占笔画。
|
||||||
|
const prevent = (event) => {
|
||||||
|
if (event.cancelable) event.preventDefault();
|
||||||
|
};
|
||||||
|
const end = () => {
|
||||||
|
point = null;
|
||||||
|
activeId = null;
|
||||||
|
source = '';
|
||||||
|
pointerType = '';
|
||||||
|
captured = false;
|
||||||
|
};
|
||||||
|
const suppressMouse = () => { ignoreMouseUntil = Date.now() + 800; };
|
||||||
|
// changedTouches 是 TouchList,不要求内核实现迭代器;只处理当前落笔手指。
|
||||||
|
const findTouch = (list) => {
|
||||||
|
for (let index = 0; list && index < list.length; index++) {
|
||||||
|
if (list[index].identifier === activeId) return list[index];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
n.addEventListener('pointerdown', (event) => {
|
||||||
|
const type = event.pointerType || '';
|
||||||
|
if (source || (type === 'mouse' && (event.button !== 0 || Date.now() < ignoreMouseUntil))) return;
|
||||||
|
source = 'pointer';
|
||||||
|
pointerType = type;
|
||||||
|
activeId = event.pointerId;
|
||||||
|
begin(event);
|
||||||
|
if (type !== 'mouse') suppressMouse();
|
||||||
|
// 部分内核提供 PointerEvent 却不支持捕获;捕获失败仍允许在画布内绘制。
|
||||||
|
try {
|
||||||
|
if (typeof n.setPointerCapture === 'function') {
|
||||||
|
n.setPointerCapture(event.pointerId);
|
||||||
|
captured = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
captured = false;
|
||||||
|
}
|
||||||
|
prevent(event);
|
||||||
|
});
|
||||||
|
n.addEventListener('pointermove', (event) => {
|
||||||
|
if (source !== 'pointer' || event.pointerId !== activeId) return;
|
||||||
|
move(event);
|
||||||
|
prevent(event);
|
||||||
|
});
|
||||||
|
['pointerup', 'pointercancel', 'lostpointercapture'].forEach((name) => {
|
||||||
|
n.addEventListener(name, (event) => {
|
||||||
|
if (source !== 'pointer' || event.pointerId !== activeId) return;
|
||||||
|
if (pointerType !== 'mouse') suppressMouse();
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
n.addEventListener('pointerleave', () => {
|
||||||
|
if (source === 'pointer' && !captured) end();
|
||||||
|
});
|
||||||
|
// 始终绑定触摸兜底。若只收到 pointerdown 而没有 pointermove,touchstart 可接管该笔画。
|
||||||
|
n.addEventListener('touchstart', (event) => {
|
||||||
|
suppressMouse();
|
||||||
|
prevent(event);
|
||||||
|
const touch = event.changedTouches && event.changedTouches[0];
|
||||||
|
if (!touch || source === 'touch' || source === 'mouse') return;
|
||||||
|
if (source === 'pointer') {
|
||||||
|
if (pointerType === 'mouse' || pointerType === 'pen') return;
|
||||||
|
// 接管不再保存第二份历史,也不重复画线;后续指针事件由来源锁忽略。
|
||||||
|
point = position(touch);
|
||||||
|
} else {
|
||||||
|
begin(touch);
|
||||||
|
}
|
||||||
|
source = 'touch';
|
||||||
|
activeId = touch.identifier;
|
||||||
|
}, {passive: false});
|
||||||
|
n.addEventListener('touchmove', (event) => {
|
||||||
|
suppressMouse();
|
||||||
|
prevent(event);
|
||||||
|
if (source !== 'touch') return;
|
||||||
|
const touch = findTouch(event.changedTouches);
|
||||||
|
if (touch) move(touch);
|
||||||
|
}, {passive: false});
|
||||||
|
['touchend', 'touchcancel'].forEach((name) => {
|
||||||
|
n.addEventListener(name, (event) => {
|
||||||
|
suppressMouse();
|
||||||
|
prevent(event);
|
||||||
|
if (source === 'touch' && findTouch(event.changedTouches)) end();
|
||||||
|
}, {passive: false});
|
||||||
|
});
|
||||||
|
// 兼容旧内核鼠标输入,并过滤触摸结束后生成的合成鼠标事件。
|
||||||
|
n.addEventListener('mousedown', (event) => {
|
||||||
|
if (source || event.button !== 0 || Date.now() < ignoreMouseUntil) return;
|
||||||
|
source = 'mouse';
|
||||||
|
begin(event);
|
||||||
|
prevent(event);
|
||||||
|
});
|
||||||
|
n.addEventListener('mousemove', (event) => {
|
||||||
|
if (source === 'mouse') move(event);
|
||||||
|
});
|
||||||
|
['mouseup', 'mouseleave'].forEach((name) => {
|
||||||
|
n.addEventListener(name, () => {
|
||||||
|
if (source === 'mouse') end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},this.getContentBase64=function(t,e){if(t){var i=!0,s=!1,a=void 0;try{for(var r,f=document.querySelectorAll('canvas[data-none="1"]')[Symbol.iterator]();!(i=(r=f.next()).done);i=!0){var d=r.value;d.parentNode.removeChild(d)}}catch(t){s=!0,a=t}finally{try{!i&&f.return&&f.return()}finally{if(s)throw a}}var l=document.createElement("canvas");l.dataset.none="1",l.width=n.offsetHeight,l.height=n.offsetWidth,document.body.appendChild(l);var c=document.querySelector('canvas[data-none="1"]').getContext("2d"),u=new Image;u.src=o.getContentBase64(),u.onload=function(){var t=Math.round(n.offsetHeight/2),o=Math.round(n.offsetWidth/2);c.save(),c.translate(t,o),c.rotate(-90*Math.PI/180),c.drawImage(u,-u.width/2,-u.height/2),c.restore(),e&&e(document.querySelector('canvas[data-none="1"]').toDataURL("image/png"))}}else{if(!e)return n.toDataURL("image/png");e(n.toDataURL("image/png"))}},this.getBlob=function(){for(var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.getContentBase64(),e=t.split(","),o=e[0].match(/:(.*?);/)[1],n=atob(e[1]),i=n.length,s=new Uint8Array(i);i--;)s[i]=n.charCodeAt(i);return new Blob([s],{type:o})},this.getFile=function(t){for(var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getContentBase64(),o=t||"file",n=e.split(","),i=n[0].match(/:(.*?);/)[1],s=i.split("/")[1],a=atob(n[1]),r=a.length,f=new Uint8Array(r);r--;)f[r]=a.charCodeAt(r);return new File([f],o+"."+s,{type:i})},this.reset=function(){if(!o.firstStatus)return!1;i.clearRect(0,0,n.offsetWidth,n.offsetHeight);var t=new Image;try{t.src=o.firstStatus,t.addEventListener("load",function(){i.drawImage(t,0,0)}),o.step=-1,o.status=[]}catch(t){}},this.undo=function(t){if(o.step>=0){i.clearRect(0,0,n.offsetWidth,n.offsetHeight);var e=new Image;try{e.src=o.status[o.step],e.addEventListener("load",function(){i.drawImage(e,0,0)})}catch(t){}o.step--}else console.warn("no more undo"),t&&t()},this.redo=function(t){if(o.step<o.status.length-2){o.step++;var e=new Image;o.status[o.step+1]&&(e.src=o.status[o.step+1],e.addEventListener("load",function(){i.clearRect(0,0,n.offsetWidth,n.offsetHeight),i.drawImage(e,0,0)}))}else console.warn("no more redo"),t&&t()}});
|
||||||
|
|||||||
@@ -8,23 +8,26 @@
|
|||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
||||||
<title>签字</title>
|
<title>签字</title>
|
||||||
<!-- 引入样式文件 -->
|
<!-- 引入样式文件 -->
|
||||||
<link rel="stylesheet" href="https://cdn.staticfile.org/vant/2.12.5/index.min.css">
|
<link rel="stylesheet" href="/assets/platform/plugins/vant/index.min.css">
|
||||||
<link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.2.0/weui.min.css">
|
|
||||||
|
|
||||||
<!-- 引入 Vue 和 Vant 的 JS 文件 -->
|
<!-- 引入 Vue 和 Vant 的 JS 文件 -->
|
||||||
<script src="https://cdn.staticfile.org/vue/2.6.9/vue.min.js"></script>
|
<script src="/assets/platform/plugins/vue/vue.min.js"></script>
|
||||||
<script src="https://cdn.staticfile.org/vant/2.12.5/vant.min.js"></script>
|
<script src="/assets/platform/plugins/vant/vant.min.js"></script>
|
||||||
<script src="/assets/platform/plugins/jquery/jquery-1.11.1.min.js"></script>
|
<script src="/assets/platform/plugins/jquery/jquery-1.11.1.min.js"></script>
|
||||||
<script src="/assets/platform/plugins/moment/moment.min.js"></script>
|
|
||||||
<script src="/assets/platform/js/signature.min.js"></script>
|
<script src="/assets/platform/js/signature-20261112-4.js?v=20261112-5"></script>
|
||||||
</head>
|
</head>
|
||||||
<style>
|
<style>
|
||||||
[v-clock] {
|
html, body { height: 100%; overflow: hidden; }
|
||||||
|
#canvas { touch-action: none; }
|
||||||
|
[v-cloak] {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<body style="height: 100%;width: 100%">
|
<body style="height: 100%;width: 100%">
|
||||||
<div v-clock id="app" style="height: 100%;width: 100%">
|
<p id="sign-status" role="status" style="padding: 24px;text-align: center">签字页面正在加载;若长时间未显示,请刷新页面重试。</p>
|
||||||
|
<div v-cloak id="app" style="height: 100%;width: 100%">
|
||||||
<van-row style="margin-top: 10vh;text-align: center">
|
<van-row style="margin-top: 10vh;text-align: center">
|
||||||
<h3 style="color: #1989fa">提示:请在下方区域签字!</h3>
|
<h3 style="color: #1989fa">提示:请在下方区域签字!</h3>
|
||||||
</van-row>
|
</van-row>
|
||||||
@@ -38,7 +41,7 @@
|
|||||||
<van-button @click="reset" type="default" size="large">清 空</van-button>
|
<van-button @click="reset" type="default" size="large">清 空</van-button>
|
||||||
</van-col>
|
</van-col>
|
||||||
<van-col span="11">
|
<van-col span="11">
|
||||||
<van-button type="info" @click="doSub" size="large" :disabled="subDis">提 交</van-button>
|
<van-button type="info" @click="doSub" size="large" :disabled="subDis || formLoading" :loading="formLoading">提 交</van-button>
|
||||||
</van-col>
|
</van-col>
|
||||||
</van-row>
|
</van-row>
|
||||||
</van-col>
|
</van-col>
|
||||||
@@ -46,110 +49,75 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.body.addEventListener('touchmove', function (e) {
|
// prefix 为业务标识、id 为二维码会话标识,原样提交以保持 PC 轮询对应关系。
|
||||||
e.preventDefault(); //阻止默认的处理方式(阻止下拉滑动的效果)
|
const getQueryVariable = (name) => {
|
||||||
}, {passive: false}); //passive 参数不能省略,用来兼容ios和android
|
const parts = window.location.search.substring(1).split('&');
|
||||||
|
for (let index = 0; index < parts.length; index++) {
|
||||||
function getQueryVariable(variable) {
|
const separator = parts[index].indexOf('=');
|
||||||
var query = window.location.search.substring(1);
|
if (parts[index].substring(0, separator) === name) {
|
||||||
var vars = query.split("&");
|
return decodeURIComponent(parts[index].substring(separator + 1));
|
||||||
for (var i = 0; i < vars.length; i++) {
|
|
||||||
var pair = vars[i].split("=");
|
|
||||||
if (pair[0] == variable) {
|
|
||||||
return pair[1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
};
|
||||||
|
let qm;
|
||||||
var qmoption = {
|
try {
|
||||||
el: '#canvas',
|
if (!window.Vue || !window.vant || !window.Signature || !window.jQuery) {
|
||||||
lineWidth: 4,
|
throw new Error('签字页面依赖未加载');
|
||||||
lineColor: '#000',
|
}
|
||||||
overflow: false,
|
new Vue({
|
||||||
background: '#fff'
|
el: '#app',
|
||||||
}
|
data() {
|
||||||
var qm
|
return {prefix: '', id: '', subDis: true, formLoading: false};
|
||||||
|
|
||||||
var
|
|
||||||
vue = new Vue({
|
|
||||||
el: "#app",
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
prefix: '',
|
|
||||||
id: '',
|
|
||||||
base64: '',
|
|
||||||
subDis: false,
|
|
||||||
isNull: function (val) {
|
|
||||||
if (val === null || val === undefined || val === '') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
none2str: function (value) {
|
|
||||||
if (vue.isNull(value)) {
|
|
||||||
return '暂无'
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reset() {
|
reset() {
|
||||||
qm.reset()
|
if (qm) qm.reset();
|
||||||
},
|
},
|
||||||
doSub() {
|
doSub() {
|
||||||
qm.getContentBase64(false, (val) => {
|
if (!qm || this.subDis || this.formLoading) return;
|
||||||
this.base64 = val
|
// 提交 PNG Base64;接口返回 {code, msg},code=0 表示可由 PC 获取签名。
|
||||||
})
|
this.formLoading = true;
|
||||||
|
$.post('/signature/doSub', {
|
||||||
$.post("/signature/doSub", {
|
|
||||||
prefix: this.prefix,
|
prefix: this.prefix,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
base64: this.base64
|
base64: qm.getContentBase64()
|
||||||
}, (data) => {
|
}).then((data) => {
|
||||||
if (data.code == 0) {
|
if (data && data.code === 0) {
|
||||||
this.$notify({
|
this.$notify({message: data.msg || '签字提交成功', type: 'success'});
|
||||||
message: data.msg,
|
this.subDis = true;
|
||||||
type: 'success'
|
|
||||||
});
|
|
||||||
this.subDis = true
|
|
||||||
} else {
|
} else {
|
||||||
this.$notify({
|
this.$notify({message: (data && data.msg) || '提交失败,请重试', type: 'danger'});
|
||||||
message: data.msg,
|
|
||||||
type: 'danger'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, "json");
|
}, () => {
|
||||||
}
|
this.$notify({message: '提交失败,请检查网络后重试', type: 'danger'});
|
||||||
},
|
}).always(() => {
|
||||||
mounted: () => {
|
this.formLoading = false;
|
||||||
canvas = document.getElementById("canvas");
|
|
||||||
if (canvas.width < window.innerWidth) {
|
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canvas.height < window.innerHeight) {
|
|
||||||
canvas.height = window.innerHeight * 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
qm = new Signature(qmoption);
|
|
||||||
qm.create();
|
|
||||||
},
|
|
||||||
created: function () {
|
|
||||||
if (this.isNull(getQueryVariable("id")) || this.isNull(getQueryVariable("prefix"))) {
|
|
||||||
this.$notify({
|
|
||||||
message: '缺少参数!',
|
|
||||||
type: 'danger'
|
|
||||||
});
|
});
|
||||||
this.subDis = true
|
|
||||||
}
|
}
|
||||||
this.id = getQueryVariable("id")
|
},
|
||||||
this.prefix = getQueryVariable("prefix")
|
mounted() {
|
||||||
|
try {
|
||||||
|
this.id = getQueryVariable('id');
|
||||||
|
this.prefix = getQueryVariable('prefix');
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
canvas.width = Math.max(1, window.innerWidth - 2);
|
||||||
|
canvas.height = Math.max(1, Math.floor(window.innerHeight * 0.5));
|
||||||
|
qm = new Signature({el: '#canvas', lineWidth: 4, lineColor: '#000', overflow: true, background: '#fff'});
|
||||||
|
qm.create();
|
||||||
|
this.subDis = !this.id || !this.prefix;
|
||||||
|
const status = document.getElementById('sign-status');
|
||||||
|
status.textContent = this.subDis ? '缺少签字参数,请重新扫描二维码。' : '';
|
||||||
|
status.style.display = this.subDis ? 'block' : 'none';
|
||||||
|
} catch (error) {
|
||||||
|
// 初始化异常仍保留可见提示,避免用户只能看到白屏。
|
||||||
|
document.getElementById('sign-status').textContent = '签字初始化失败,请刷新页面重试。';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById('sign-status').textContent = '签字页面加载失败,请检查网络并刷新页面重试。';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user