123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- <template>
- <view class="signature-wrap">
- <view class="img-wrap" @tap="showSignature()" @touchstart="touchSignature()">
- <image :src="absPrevView" mode="scaleToFill"></image>
- </view>
- <view v-if="!disabled" v-show="show" class="signature-contain">
- <view class="signature-main" style="z-index: 3000;">
- <view class="signature-title"><text v-for="t in titles">{{t}}</text></view>
- <canvas disable-scroll="true" class="signature" :class="cid" canvas-id="cvs" @touchstart="touchstart"
- @touchmove="touchmove" @touchend="touchend"></canvas>
- <view class="signature-btns">
- <view class="btn btn-cancel cu-btn bg-main margin-tb-sm text-white" @tap="cancelSignature()">
- <text>取</text><text>消</text>
- </view>
- <view class="btn btn-clear cu-btn bg-main margin-tb-sm text-white" @tap="clearSignature();">
- <text>清</text><text>空</text>
- </view>
- <view class="btn btn-ok cu-btn bg-main margin-tb-sm text-white" @tap="onOK()">
- <text>确</text><text>定</text>
- </view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- let _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
- var _utf8_encode = function(string) {
- string = string.replace(/\r\n/g, "\n");
- var utftext = "";
- for (var n = 0; n < string.length; n++) {
- var c = string.charCodeAt(n);
- if (c < 128) {
- utftext += String.fromCharCode(c);
- } else if ((c > 127) && (c < 2048)) {
- utftext += String.fromCharCode((c >> 6) | 192);
- utftext += String.fromCharCode((c & 63) | 128);
- } else {
- utftext += String.fromCharCode((c >> 12) | 224);
- utftext += String.fromCharCode(((c >> 6) & 63) | 128);
- utftext += String.fromCharCode((c & 63) | 128);
- }
- }
- return utftext;
- }
- let base64encode = function(input) {
- var output = "";
- var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
- var i = 0;
- input = _utf8_encode(input);
- while (i < input.length) {
- chr1 = input.charCodeAt(i++);
- chr2 = input.charCodeAt(i++);
- chr3 = input.charCodeAt(i++);
- enc1 = chr1 >> 2;
- enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
- enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
- enc4 = chr3 & 63;
- if (isNaN(chr2)) {
- enc3 = enc4 = 64;
- } else if (isNaN(chr3)) {
- enc4 = 64;
- }
- output = output +
- _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
- _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
- }
- return output;
- }
- export default {
- cxt: null,
- data() {
- return {
- VERSION: '1.0.0',
- cid: 'cvs',
- show: false,
- ctrl: null,
- listeners: [],
- prevView: '',
- draws: [],
- lines: [],
- line: null,
- };
- },
- props: {
- value: {
- default: '',
- },
- title: {
- type: String,
- default: '请签字',
- },
- disabled: {
- type: Boolean,
- default: false,
- }
- },
- watch: {
- value() {
- this.prevView = this.value;
- }
- },
- computed: {
- titles() {
- return this.title.split('')
- },
- absPrevView() {
- var pv = this.prevView;
- // if(pv){
- // pv = this.$wrapUrl(pv)
- // }
- return pv;
- }
- },
- mounted() {
- this.prevView = this.value;
- console.log('dx')
- },
- methods: {
- onOK() {
- let data = this.ctrl.getValue();
- this.$emit('input', data);
- this.prevView = data;
- this.hideSignature();
- let f = this.listeners.shift();
- if (f) {
- f(data);
- }
- },
- touchSignature() {
- let sig = this.prevView
- if (!sig || !sig.length) {
- this.showSignature()
- }
- },
- showSignature() {
- if (this.disabled)
- return;
- if (!this.ctrl) {
- this.initCtrl();
- } else if (!this.show) {
- this.clearSignature();
- this.show = true;
- }
- },
- async getSyncSignature() {
- this.showSignature();
- return await new Promise(async (resolve, reject) => {
- this.listeners.push((res) => {
- resolve(res);
- });
- });
- },
- cancelSignature() {
- this.listeners.map((f) => {
- f(null);
- })
- this.hideSignature();
- },
- hideSignature() {
- this.ctrl && this.ctrl.clear();
- this.show = false;
- },
- clearSignature() {
- this.ctrl && this.ctrl.clear();
- },
- async initCtrl() {
- this.show = true;
- let cxt = uni.createCanvasContext(this.cid, this);
- this.cxt = cxt;
- // cxt.clearRect(0,0,c.width,c.height);
- this.ctrl = {
- width: 0,
- height: 0,
- clear: () => {
- this.lines = [];
- let info = uni.createSelectorQuery().in(this).select("." + this.cid);
- info.boundingClientRect((data) => {
- if (data) {
- cxt.clearRect(0, 0, data.width, data.height);
- if (data.width && data.height) {
- this.ctrl.width = data.width;
- this.ctrl.height = data.height;
- }
- }
- }).exec();
- this.redraw();
- },
- getValue: () => {
- if (!this.lines.length)
- return '';
- let svg = this._get_svg();
- // new Buff
- let b64 = base64encode(svg);
- let data = 'data:image/svg+xml;base64,' + b64;
- // console.log(svg);
- // console.log(data);
- return data;
- },
- };
- this.$nextTick(function() {
- this.ctrl.clear();
- })
- },
- _get_svg() {
- let r = -90;
- let paths = [];
- let raww = this.ctrl.width;
- let rawh = this.ctrl.height;
- let width = Math.abs(r) != 90 ? raww : rawh;
- let height = Math.abs(r) == 90 ? raww : rawh;
- let cx = raww / 2;
- let cy = rawh / 2;
- let PI = Math.PI;
- let R = (r || 0) % 360;
- let cosv = Math.cos(R * PI / 180);
- let sinv = Math.sin(R * PI / 180);
- let dcx = (width - raww) / 2;
- let dcy = (height - rawh) / 2;
- let trans = function(p) {
- if (!R) {
- return p;
- } else {
- let nx = (p.x - cx) * cosv - (p.y - cy) * sinv + cx;
- let ny = (p.x - cx) * sinv + (p.y - cy) * cosv + cy;
- return {
- x: nx + dcx,
- y: ny + dcy
- };
- }
- return p;
- }
- this.lines.map(l => {
- if (l.points.length < 2) {
- return;
- }
- let sp = trans(l.start)
- let pts = [`M ${sp.x} ${Number(sp.y)}`];
- l.points.map(p => {
- let np = trans(p)
- pts.push(`L ${np.x} ${Number(np.y)}`);
- });
- paths.push(
- `<path stroke-linejoin="round" stroke-linecap="round" stroke-width="3" stroke="rgb(0,0,0)" fill="none" d="${pts.join(' ')}"/>`
- );
- })
- let svg =
- `<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="${width}" height="${height}">${paths.join('\n')}</svg>`;
- return svg;
- },
- _get_svg_raw() {
- let paths = [];
- this.lines.map(l => {
- if (l.points.length < 2) {
- return;
- }
- let pts = [`M ${l.start.x} ${Number(l.start.y)}`];
- l.points.map(p => {
- pts.push(`L ${p.x} ${Number(p.y)}`);
- });
- paths.push(
- `<path stroke-linejoin="round" stroke-linecap="round" stroke-width="3" stroke="rgb(0,0,0)" fill="none" d="${pts.join(' ')}"/>`
- );
- })
- let width = this.ctrl.width;
- let height = this.ctrl.height;
- let svg =
- `<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="${width}" height="${height}" transform="rotate(-90)">${paths.join('\n')}</svg>`;
- return svg;
- },
- _get_point(e) {
- return {
- x: e.changedTouches[0].x.toFixed(1),
- y: e.changedTouches[0].y.toFixed(1),
- }
- },
- touchstart(e) {
- let p = this._get_point(e);
- this.line = {
- start: p,
- points: [p],
- }
- this.lines.push(this.line);
- },
- touchmove(e) {
- let p = this._get_point(e);
- this.line.points.push(p)
- if (!this.tm) {
- this.tm = setTimeout(() => {
- this.redraw();
- this.tm = 0;
- }, 10)
- }
- },
- touchend(e) {
- let p = this._get_point(e);
- this.line.points.push(p)
- this.line.end = p
- this.redraw()
- },
- redraw() {
- let cxt = this.cxt;
- cxt.setStrokeStyle("#000");
- cxt.setLineWidth(3);
- var last = null;
- this.lines.map(l => {
- cxt.beginPath();
- if (l.points.length < 2) {
- return;
- }
- cxt.moveTo(l.start.x, l.start.y);
- l.points.map(p => {
- cxt.lineTo(p.x, p.y)
- })
- cxt.stroke()
- })
- cxt.draw()
- },
- canvasIdErrorCallback: function(e) {
- console.error(e.detail.errMsg)
- }
- }
- }
- </script>
- <style lang="scss">
- .signature-wrap {
- height: 100%;
- width: 100%;
- // padding: 0 5px;
- // min-width: 60vw;
- .img-wrap {
- width: 100%;
- min-height: 200rpx;
- display: flex;
- align-items: center;
- text-align: center;
- align-content: center;
- justify-content: center;
- image {
- width: 100%;
- }
- // background: red;
- }
- }
- .signature-contain {
- z-index: 9000;
- position: fixed;
- left: 0;
- top: 0;
- width: 100%;
- .signature-main {
- background: white;
- flex-direction: row-reverse;
- display: flex;
- align-items: stretch;
- height: 101%;
- overflow: scroll;
- }
- .signature-title {
- font-weight: bold;
- font-size: 18px;
- display: flex;
- padding: 0 20rpx;
- flex-direction: column;
- justify-content: center;
- height: 100vh;
- color: $uni-text-color;
- text {
- transform: rotate(90deg);
- }
- }
- .signature {
- border: 1px dotted black;
- border-bottom: 1px dotted black;
- background: #FFF;
- margin: 10px 0;
- width: 90vw;
- height: 90vh;
- align-self: center;
- // pointer-events:none;
- }
- .signature-btns {
- display: flex;
- padding: 2px;
- // margin-right: 5px;
- flex-direction: column;
- .btn {
- flex-grow: 1;
- flex-shrink: 0;
- padding: 20rpx;
- font-size: 20px;
- margin: 0;
- text-align: center;
- text-decoration: none;
- height: 30vh;
- display: flex;
- align-content: center;
- justify-content: center;
- flex-direction: column;
- text {
- transform: rotate(90deg);
- }
- &+.btn {
- border-top: 1px solid #eee;
- }
- &.btn-clear {
- // background-color: #fc2a07;
- color: $uni-color-success;
- }
- &.btn-cancel {
- // background-color: #eff4f4;
- color: $uni-color-warning;
- }
- &.btn-ok {
- // background-color: $uni-color-success;
- color: $uni-color-primary;
- }
- }
- }
- }
- </style>
|