uniapp实现一个(横屏)签字板(小程序端+app端),并进行压缩,输出为base64串

参考:

(样式层面实现的)代码:

下面代码,可以看着二开

<template>
   <view class="wrapper">
      <view class="handCenter">
         <view class="ats">
            <canvas
               class="canvasOne"
               :disable-scroll="true"
               @touchstart="uploadScaleStart"
               @touchmove="uploadScaleMove"
               @touchend="uploadScaleEnd"
               canvas-id="canvasOne"
               id="canvasOne"
            >
            </canvas>
            <canvas class="handWriting2" id="canvas" canvas-id="canvas"></canvas>
         </view>
         <view class="handBtn">
            <button @click="subCanvas" class="subBtn">完成</button>
            <button @click="retDraw" class="delBtn">重置</button>
         </view>
      </view>
      <view class="handRight">
         <text class="handTitle">签名</text>
      </view>
      <u-overlay :show="overlayShow">
         <view class="rotate-container">
            <view>没有签字内容</view>
         </view>
      </u-overlay>
   </view>
</template>

<script>
   import UOverlay from '../../../uni_modules/uview-ui/components/u-overlay/u-overlay.vue';

   export default {
      name: 'signatureBoard',
      components: {UOverlay},
      data() {
         return {
            canvasName: 'canvasOne',
            ctx: '',
            canvasWidth: 0,
            canvasHeight: 0,
            transparent: 1, // 透明度
            selectColor: 'black',
            lineColor: '#1A1A1A', // 颜色
            lineSize: 1.5,
            lineMin: 0.5,
            lineMax: 4,
            pressure: 1,
            smoothness: 60,
            currentPoint: {},
            currentLine: [],
            firstTouch: true,
            radius: 1,
            cutArea: {top: 0, right: 0, bottom: 0, left: 0},
            bethelPoint: [],
            lastPoint: 0,
            chirography: [],
            currentChirography: {},
            linePrack: [],
            overlayShow: false,
         };
      },
      onLoad: function (options) {
         setTimeout(() => {
            let canvasName = this.canvasName;
            let newCtx = uni.createCanvasContext(canvasName);
            this.ctx = newCtx;
            const query = uni.createSelectorQuery();
            query
               .select('.handCenter')
               .boundingClientRect((rect) => {
                  this.canvasWidth = rect.width;
                  this.canvasHeight = rect.height;

                  this.setCanvasBg('#fff');
               })
               .exec();
         }, 300);
      },
      methods: {
         // 笔迹开始
         uploadScaleStart(e) {
            if (e.type !== 'touchstart') return false;
            let ctx = this.ctx;
            ctx.setFillStyle(this.lineColor); // 初始线条设置颜色
            ctx.setGlobalAlpha(this.transparent); // 设置半透明
            let newCurrentPoint = {
               x: e.touches[0].x,
               y: e.touches[0].y,
            };
            let currentLine = this.currentLine;
            currentLine.unshift({
               time: new Date().getTime(),
               dis: 0,
               x: newCurrentPoint.x,
               y: newCurrentPoint.y,
            });

            this.currentPoint = newCurrentPoint;

            if (this.firstTouch) {
               this.cutArea = {
                  top: newCurrentPoint.y,
                  right: newCurrentPoint.x,
                  bottom: newCurrentPoint.y,
                  left: newCurrentPoint.x,
               };

               this.cutArea = false;
            }
            this.pointToLine(currentLine);
         },
         // 笔迹移动
         uploadScaleMove(e) {
            if (e.type !== 'touchmove') return false;
            if (e.cancelable) {
               // 判断默认行为是否已经被禁用
               if (!e.defaultPrevented) {
                  e.preventDefault();
               }
            }
            let point = {
               x: e.touches[0].x,
               y: e.touches[0].y,
            };

            //测试裁剪
            if (point.y < this.cutArea.top) {
               this.cutArea.top = point.y;
            }
            if (point.y < 0) this.cutArea.top = 0;

            if (point.x > this.cutArea.right) {
               this.cutArea.right = point.x;
            }
            if (this.canvasWidth - point.x <= 0) {
               this.cutArea.right = this.canvasWidth;
            }
            if (point.y > this.cutArea.bottom) {
               this.cutArea.bottom = point.y;
            }
            if (this.canvasHeight - point.y <= 0) {
               this.cutArea.bottom = this.canvasHeight;
            }
            if (point.x < this.cutArea.left) {
               this.cutArea.left = point.x;
            }
            if (point.x < 0) this.cutArea.left = 0;

            this.lastPoint = this.currentPoint;
            this.currentPoint = point;

            let currentLine = this.currentLine;
            currentLine.unshift({
               time: new Date().getTime(),
               dis: this.distance(this.currentPoint, this.lastPoint),
               x: point.x,
               y: point.y,
            });

            this.pointToLine(currentLine);
         },

         // 笔迹结束
         uploadScaleEnd(e) {
            if (e.type !== 'touchend') return 0;
            let point = {
               x: e.changedTouches[0].x,
               y: e.changedTouches[0].y,
            };
            this.lastPoint = this.currentPoint;
            this.currentPoint = point;
            let currentLine = this.currentLine;
            currentLine.unshift({
               time: new Date().getTime(),
               dis: this.distance(this.currentPoint, this.lastPoint),
               x: point.x,
               y: point.y,
            });

            // if (currentLine.length > 2) {
            //    const info =
            //       (currentLine[0].time - currentLine[currentLine.length - 1].time) /
            //       currentLine.length;
            //    //$("#info").text(info.toFixed(2));
            // }
            //增加判断是否在手写区域;
            console.log('结束时额', currentLine);
            this.pointToLine(currentLine);
            const currentChirography = {
               lineSize: this.lineSize,
               lineColor: this.lineColor,
            };
            const chirography = this.chirography;
            chirography.unshift(currentChirography);
            this.chirography = chirography;
            const linePrack = this.linePrack;
            linePrack.unshift(this.currentLine);
            this.linePrack = linePrack;
            this.currentLine = [];
         },
         retDraw() {
            this.ctx.clearRect(0, 0, 700, 730);
            this.ctx.draw();

            //设置canvas背景
            this.setCanvasBg('#fff');
         },

         //画两点之间的线条;参数为:line,会绘制最近的开始的两个点;
         pointToLine(line) {
            this.calcBethelLine(line);
            return;
         },
         //计算插值的方式;
         calcBethelLine(line) {
            if (line.length <= 1) {
               line[0].r = this.radius;
               return;
            }
            let x0,
               x1,
               x2,
               y0,
               y1,
               y2,
               r0,
               r1,
               r2,
               len,
               lastRadius,
               dis = 0,
               time = 0,
               curveValue = 0.5;
            if (line.length <= 2) {
               x0 = line[1].x;
               y0 = line[1].y;
               x2 = line[1].x + (line[0].x - line[1].x) * curveValue;
               y2 = line[1].y + (line[0].y - line[1].y) * curveValue;
               //x2 = line[1].x;
               //y2 = line[1].y;
               x1 = x0 + (x2 - x0) * curveValue;
               y1 = y0 + (y2 - y0) * curveValue;
            } else {
               x0 = line[2].x + (line[1].x - line[2].x) * curveValue;
               y0 = line[2].y + (line[1].y - line[2].y) * curveValue;
               x1 = line[1].x;
               y1 = line[1].y;
               x2 = x1 + (line[0].x - x1) * curveValue;
               y2 = y1 + (line[0].y - y1) * curveValue;
            }
            //从计算公式看,三个点分别是(x0,y0),(x1,y1),(x2,y2) ;(x1,y1)这个是控制点,控制点不会落在曲线上;实际上,这个点还会手写获取的实际点,却落在曲线上
            len = this.distance({x: x2, y: y2}, {x: x0, y: y0});
            lastRadius = this.radius;
            for (let n = 0; n < line.length - 1; n++) {
               dis += line[n].dis;
               time += line[n].time - line[n + 1].time;
               if (dis > this.smoothness) break;
            }
            this.radius =
               Math.min((time / len) * this.pressure + this.lineMin, this.lineMax) *
               this.lineSize;
            line[0].r = this.radius;
            //计算笔迹半径;
            if (line.length <= 2) {
               r0 = (lastRadius + this.radius) / 2;
               r1 = r0;
               r2 = r1;
               //return;
            } else {
               r0 = (line[2].r + line[1].r) / 2;
               r1 = line[1].r;
               r2 = (line[1].r + line[0].r) / 2;
            }
            let n = 5;
            let point = [];
            for (let i = 0; i < n; i++) {
               let t = i / (n - 1);
               let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2;
               let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2;
               let r = lastRadius + ((this.radius - lastRadius) / n) * i;
               point.push({x: x, y: y, r: r});
               if (point.length == 3) {
                  let a = this.ctaCalc(
                     point[0].x,
                     point[0].y,
                     point[0].r,
                     point[1].x,
                     point[1].y,
                     point[1].r,
                     point[2].x,
                     point[2].y,
                     point[2].r,
                  );
                  a[0].color = this.lineColor;

                  this.bethelDraw(a, 1);
                  point = [{x: x, y: y, r: r}];
               }
            }
            this.currentLine = line;
         },

         //求两点之间距离
         distance(a, b) {
            let x = b.x - a.x;
            let y = b.y - a.y;
            return Math.sqrt(x * x + y * y);
         },
         ctaCalc(x0, y0, r0, x1, y1, r1, x2, y2, r2) {
            let a = [],
               vx01,
               vy01,
               norm,
               n_x0,
               n_y0,
               vx21,
               vy21,
               n_x2,
               n_y2;
            vx01 = x1 - x0;
            vy01 = y1 - y0;
            norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2;
            vx01 = (vx01 / norm) * r0;
            vy01 = (vy01 / norm) * r0;
            n_x0 = vy01;
            n_y0 = -vx01;
            vx21 = x1 - x2;
            vy21 = y1 - y2;
            norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2;
            vx21 = (vx21 / norm) * r2;
            vy21 = (vy21 / norm) * r2;
            n_x2 = -vy21;
            n_y2 = vx21;
            a.push({mx: x0 + n_x0, my: y0 + n_y0, color: '#1A1A1A'});
            a.push({
               c1x: x1 + n_x0,
               c1y: y1 + n_y0,
               c2x: x1 + n_x2,
               c2y: y1 + n_y2,
               ex: x2 + n_x2,
               ey: y2 + n_y2,
            });
            a.push({
               c1x: x2 + n_x2 - vx21,
               c1y: y2 + n_y2 - vy21,
               c2x: x2 - n_x2 - vx21,
               c2y: y2 - n_y2 - vy21,
               ex: x2 - n_x2,
               ey: y2 - n_y2,
            });
            a.push({
               c1x: x1 - n_x2,
               c1y: y1 - n_y2,
               c2x: x1 - n_x0,
               c2y: y1 - n_y0,
               ex: x0 - n_x0,
               ey: y0 - n_y0,
            });
            a.push({
               c1x: x0 - n_x0 - vx01,
               c1y: y0 - n_y0 - vy01,
               c2x: x0 + n_x0 - vx01,
               c2y: y0 + n_y0 - vy01,
               ex: x0 + n_x0,
               ey: y0 + n_y0,
            });
            a[0].mx = a[0].mx.toFixed(1);
            a[0].mx = parseFloat(a[0].mx);
            a[0].my = a[0].my.toFixed(1);
            a[0].my = parseFloat(a[0].my);
            for (let i = 1; i < a.length; i++) {
               a[i].c1x = a[i].c1x.toFixed(1);
               a[i].c1x = parseFloat(a[i].c1x);
               a[i].c1y = a[i].c1y.toFixed(1);
               a[i].c1y = parseFloat(a[i].c1y);
               a[i].c2x = a[i].c2x.toFixed(1);
               a[i].c2x = parseFloat(a[i].c2x);
               a[i].c2y = a[i].c2y.toFixed(1);
               a[i].c2y = parseFloat(a[i].c2y);
               a[i].ex = a[i].ex.toFixed(1);
               a[i].ex = parseFloat(a[i].ex);
               a[i].ey = a[i].ey.toFixed(1);
               a[i].ey = parseFloat(a[i].ey);
            }
            return a;
         },
         bethelDraw(point, is_fill, color) {
            let ctx = this.ctx;
            ctx.beginPath();
            ctx.moveTo(point[0].mx, point[0].my);
            if (color !== undefined) {
               ctx.setFillStyle(color);
               ctx.setStrokeStyle(color);
            } else {
               ctx.setFillStyle(point[0].color);
               ctx.setStrokeStyle(point[0].color);
            }
            for (let i = 1; i < point.length; i++) {
               ctx.bezierCurveTo(
                  point[i].c1x,
                  point[i].c1y,
                  point[i].c2x,
                  point[i].c2y,
                  point[i].ex,
                  point[i].ey,
               );
            }
            ctx.stroke();
            if (is_fill !== undefined) {
               ctx.fill();
            }
            ctx.draw(true);
         },

         //完成
         subCanvas() {
            if (this.linePrack.length === 0) {
               this.overlayShow = true;
               setTimeout(() => {
                  this.overlayShow = false;
               }, 1500);
            } else {
               this.getImage();
            }
         },

         //设置canvas背景色
         setCanvasBg(color) {
            this.ctx.rect(0, 0, this.canvasWidth, this.canvasHeight - 4);
            this.ctx.setFillStyle(color);
            this.ctx.fill(); //设置填充
            this.ctx.draw(); //开画
         },

         // 最终的图片
         getImage() {
            uni.canvasToTempFilePath({
               canvasId: 'canvasOne',
               success: (res) => {
                  console.log('生成的图片', res);
                  this.drowImage(res.tempFilePath);
               },
               fail: (err) => {
                  console.error('绘制失败');
               },
            });
         },

         drowImage(path) {
            const canvasContext = uni.createCanvasContext('canvas');
            canvasContext.scale(0.5, 0.5);
            canvasContext.translate(0, 300);
            canvasContext.rotate((-90 * Math.PI) / 180);
            canvasContext.drawImage(path, 0, 0, 300, 600);
            canvasContext.draw(true);
            setTimeout(() => {
               uni.canvasToTempFilePath({
                  canvasId: 'canvas',
                  success(res) {
                     uni.$emit('signImg', res?.tempFilePath);
                     uni.navigateBack({
                        delta: 1,
                     });
                  },
               });
            }, 1000);
         },
      },
   };
</script>

<style scoped lang="scss">
   .wrapper {
      width: 100%;
      height: 95vh;
      margin: 30rpx 0;
      overflow: hidden;
      display: flex;
      align-content: center;
      flex-direction: row;
      justify-content: center;
      font-size: 28rpx;
      margin-left: 20rpx;
   }
   //第一个canvas
   .canvasOne {
      position: relative;
      background: #fff;
      width: 98%;
      z-index: 99;
      height: 83vh;
      border: 4rpx dashed #e9e9e9;
   }
   .ats {
      position: relative;
   }
   //第二个canvas
   .handWriting2 {
      position: absolute;
      z-index: 9;
      width: 600rpx;
      height: 300rpx;
      top: 0;
      left: 0;
      /* transform: rotate(90deg); */
      background-color: #fff;
   }

   .handRight {
      display: flex;
      height: 100%;
      align-items: center;
      justify-content: center;
      text-align: center;
      width: 100rpx;
   }

   .handCenter {
      display: flex;
      width: 620rpx;
      flex-direction: column;
      overflow: hidden;
      box-sizing: border-box;
   }

   .handTitle {
      display: flex;
      color: #000000;
      font-size: 33rpx;
      font-weight: bold;
      transform: rotate(90deg);
      align-items: center;
      justify-content: center;
      text-align: center;
   }

   .handBtn button {
      font-size: 28rpx;
   }

   .handBtn {
      display: inline-flex;
      flex-direction: row;
      justify-content: space-between;
      align-content: space-between;
      text-align: center;
      flex: 1;
   }

   .delBtn {
      display: flex;
      width: 120rpx;
      height: 75rpx;
      transform: rotate(90deg);
      color: #666;
      align-items: center;
      justify-content: center;
      margin-top: 60rpx;
   }

   .delBtn image {
      position: absolute;
      top: 13rpx;
      left: 25rpx;
   }
   .subBtn {
      width: 120rpx;
      height: 75rpx;
      display: inline-flex;
      transform: rotate(90deg);
      background: linear-gradient(133deg, #5e69ff 0%, #2e37a8 100%);
      box-shadow: 0rpx 6rpx 12rpx rgba(0, 0, 0, 0.16);
      opacity: 1;
      border-radius: 13rpx;
      color: #fff;
      align-items: center;
      justify-content: center;
      margin-top: 60rpx;
   }

   .rotate-container {
      position: relative;
      top: 50%;
      left: 50%;
      transform: rotate(90deg) translate(-50%, 57%);
      width: 200rpx;
      height: auto;
      color: #ffffff;
      font-size: 28rpx;
   }
</style>

效果图:

问题:

有人可能会问,为什么要通过css实现页面横屏,这样会多做很多事...只能说问的好,原因是自己通过配置使用框架的配置项(从系统层)已经实现了页面横屏,但无奈组里负责发版的人不会发版,只能删代码,推倒重来...做了无用功!

(系统层面实现的)代码: 

这种方式实现的话可能需要让用户去应用商店下载新版应用;

// 安卓机型横竖屏切换
//页面显示时切换为横屏配置
onShow() {
    // #ifdef APP-PLUS
    uni.showLoading({
        title: "加载中..."
    })
    setTimeout(() => {
        plus.screen.unlockOrientation();
        plus.screen.lockOrientation('landscape-primary');
        uni.hideLoading();
    }, 1200)
    //#endif
},
//页面卸载时切换为竖屏配置
onUnload() {
    // #ifdef APP-PLUS
    plus.screen.lockOrientation('portrait-primary'); //锁死屏幕方向为竖屏
    // #endif
}



// **************************************************



// 小程序横竖屏配置只需要在page.json中相关页面的style中配置"pageOrientation": "landscape" 
{
   "path": "collection/index",
   "style": {
        "navigationBarTitleText": "我的收藏",
        "pageOrientation": "landscape"
    }
 }



// *****************************************************


// ios想要横竖屏切换需要配置manifest.json文件中的app-plus对象中的screenOrientation属性,值为['rtrait-primary','andscape-secondary'];distribute对象的distribute属性的orientation属性的值['portrait-primary', 'andscape-secondary']

压缩图片以及转base64相应的代码:

// 注意转base64时,需要获取文件管理器,然后就是注意app与小程序的差异性,这一点很重要;

          
            // 获取文件系统管理器
            // #ifdef MP-WEIXIN
            const fs = uni.getFileSystemManager();
            // #endif


            // 压缩
            uni.compressImage({
               src: this.autographedPhotoList[0].thumb,
               width: '50%',
               height: '50%',
               quality: 10,
               success: (res) => {
                  const tempAddress = (
                     this.addressInfo.region + this.addressInfo.address
                  ).trim();

                  // #ifdef APP-PLUS
                  plus.io.resolveLocalFileSystemURL(
                     res.tempFilePath,
                     (entry) => {
                        // 可通过entry对象操作test.html文件
                        entry.file((file) => {
                           const fileReader = new plus.io.FileReader();
                           fileReader.readAsDataURL(file, 'base64');

                           fileReader.onloadend = (e) => {
                              base64Url = `${e.target.result}`;
                              console.log('最终的base64', base64Url);

                              if (base64Url) {
                                 this.helpFunc(
                                    tempAddress,
                                    this.removeBase64Header(base64Url),
                                 );
                              }
                           };
                        });
                     },
                     function (e) {
                        uni.showToast({
                           title: e.message,
                           duration: 1000,
                        });
                        throw new Error(e.message);
                     },
                  );
                  // #endif

                  // 转成base64
                  // #ifdef MP-WEIXIN
                  fs.readFile({
                     filePath: res.tempFilePath,
                     encoding: 'base64',
                     success: (readRes) => {
                        base64Url = `${readRes.data}`;

                        this.helpFunc(tempAddress, base64Url);
                     },
                     fail(err) {
                        uni.showToast({
                           title:
                              typeof err === 'object'
                                 ? JSON.stringify(err)
                                 : err ?? '签约错误',
                           duration: 1000,
                        });
                        throw new Error(JSON.stringify(err));
                     },
                  });
                  // #endif
               },
            });

对于https链接的图片转换为base64的方法:(app\小程序通用)

// https://cloud.tencent.com/developer/article/1597234

uni.request({
  url: "https://s0.2mdn.net/simgad/10657937226496242109",
  method: 'GET',
  responseType: 'arraybuffer',
  success: (res) => {
    let base64 = wx.arrayBufferToBase64(res.data);
    let userImageBase64 = 'data:image/jpg;base64,' + base64;
    console.log(userImageBase64); // 打印base64格式图片
    // 如果需要使用本地缓存图片,请参照第一步
  }
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值