CD's blog CD's blog
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

CD_wOw

内卷的行情,到不了的梦
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础理论

    • 小程序机制
    • 小程序使用笔记
      • 小程序开发实战篇
    • 框架篇

    • 小程序笔记
    • 基础理论
    CD
    2023-03-29
    目录

    小程序使用笔记

    小程序开发经常遇到的业务记录。

    # 小程序如何生成海报分享

    小程序生成海报并分享的实现方式如下:

    (1)在 canvas 中绘制海报:

    小程序可以使用<canvas>标签绘制海报,如下所示:

    <canvas canvas-id="poster" style="width: 100%; height: 100%;"></canvas>
    
    1
    const context = wx.createCanvasContext("poster");
    // 绘制文本、图片等内容
    context.setFillStyle("#ffffff");
    context.fillRect(0, 0, 750, 1200);
    context.drawImage("/images/avatar.png", 30, 30, 300, 300);
    context.setFontSize(48);
    context.setFillStyle("#333333");
    context.fillText("小程序海报", 360, 90);
    // ...
    
    context.draw();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    (2)将 canvas 转换为图片:

    通过wx.canvasToTempFilePath方法可以将 canvas 转换成图片。具体实现如下:

    wx.canvasToTempFilePath({
      canvasId: "poster",
      success: function (res) {
        const posterPath = res.tempFilePath; // 生成的海报图片路径
        // ...后续操作
      },
      fail: function (res) {
        // 异常处理逻辑
      },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    (3)将图片保存到相册:

    将生成的海报图片保存到相册中,可以使用wx.saveImageToPhotosAlbum方法,代码实现如下:

    wx.saveImageToPhotosAlbum({
      filePath: posterPath,
      success: function () {
        // 保存成功处理逻辑
      },
      fail: function (res) {
        // 保存失败处理逻辑
      },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    (4)分享海报:

    将生成的海报分享到朋友圈或者好友,可以使用wx.shareAppMessage或者wx.updateShareMenu方法,代码实现如下:

    wx.shareAppMessage({
      imageUrl: posterPath,
      success: function () {
        // 分享成功处理逻辑
      },
      fail: function (res) {
        // 分享失败处理逻辑
      },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # 小程序内如何生成小程序二维码?

    在小程序中生成小程序码,可以通过调用wx.createQRCode方法来实现。具体操作如下:

    1. 在页面中引入wx对象:

      const wx = getApp().wx;
      
      1
    2. 使用wx.createQRCode方法生成小程序码:

      wx.createQRCode({
        path: "pages/index/index", // 小程序页面路径
        width: 280, // 二维码宽度,单位为 px
        // 如果需要背景色,则可以设置 backgroundColor 字段,例如:backgroundColor: '#ffffff'
        success: function (res) {
          // res.path 就是生成的小程序码图片路径
        },
        fail: function (res) {
          // 生成小程序码失败处理逻辑
        },
      });
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
    3. 在回调函数中,可以获取到生成的小程序码图片路径,可以通过<image>标签来显示该图片。如果需要保存该图片,则可以使用wx.saveImageToPhotosAlbum方法将图片保存到用户的相册中。

    # 如何阻止弹窗滚动穿透

    1. 倘若可控制页面根元素,则在弹窗弹出时,设置页面的 overflow:hidden 样式

    在弹窗区域添加 touchmove 事件,通过event.preventDefault()方法,阻止触摸事件的默认行为,如下所示:

    <view class="popup-container" bindtouchmove="preventScroll">
      <!-- 弹窗内容 -->
    </view>
    
    1
    2
    3
    Page({
      preventScroll(event) {
        // 阻止touchmove事件的默认行为
        event.preventDefault();
      },
    });
    
    1
    2
    3
    4
    5
    6

    倘若在 taro 组件中,可尝试:

    const cathTouchMove = (e) => {
      e.stopPropagation();
      e.preventDefault();
    };
    
    <View catchMove catchtouchmove onTouchMove={cathTouchMove}></View>;
    
    1
    2
    3
    4
    5
    6

    处理滚动

    # 小程序如何发起订阅授权

    小程序发起订阅授权需要以下步骤:

    1. 在小程序的app.json文件中设置订阅消息配置:
    "subPackages": [
      {
        "root": "packageA/",
        "name": "packageA",
        "plugins": {
          "wx2d8f88e869071b94": {
            "version": "1.0.4",
            "provider": "wx2d8f88e869071b94"
          }
        },
        "usingComponents": {
          // ...
        }
      }
    ],
    "permission": {
      "scope.userLocation": {
        "desc": "你的位置信息将用于小程序位置接口的效果展示"
      },
      "scope.userInfo": {
        "desc": "你的用户信息将用于小程序内部逻辑的处理,请放心授权"
      }
    },
    "subpackages": [
      {
        "root": "subscribe-message/",
        "name": "subscribe-message",
        "pages": [
          {
            "path": "index",
            "config": {
              "navigationBarTitleText": "订阅消息"
            }
          }
        ]
      }
    ]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    1. 在小程序的subscribe-message页面中引入相关的组件和方法:
    <view>
      <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>
      <button bindtap="requestMessageTemplate">订阅授权</button>
      <template is="wx-subscribe-message" data="{{...templateData}}"></template>
    </view>
    
    1
    2
    3
    4
    5
    Page({
      data: {
        templateData: {
          subscribes: [
            {
              id: "A1B2C3D4E5",
              title: "订阅消息标题",
              desc: "订阅消息描述",
              iconUrl: "https://cdn.xxx.com/xxx.jpg",
              extJson: JSON.stringify({
                appid: "xxx", // 小程序 id
                page: "pages/home", //  路径
                params: {}, // 额外参数
              }),
              version: 1,
            },
          ],
          subscribes2: [], // 如果有多个模板可以添加多个对象
        },
      },
      getUserInfo: function (e) {
        console.log(e.detail.userInfo);
      },
      requestMessageTemplate() {
        wx.requestSubscribeMessage({
          tmplIds: ["A1B2C3D4E5"],
          success(res) {
            console.log("订阅成功:", res);
          },
          fail(res) {
            console.log("订阅失败:", res);
          },
        });
      },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    1. 在requestSubscribeMessage方法中调用订阅消息接口wx.requestSubscribeMessage来发起授权请求。需要注意的是,订阅消息接口需要在用户主动点击的事件中触发,否则会被腾讯审核拒绝。

    2. 用户点击「订阅授权」按钮后,如果用户未授权订阅,则会弹出授权页面,用户点击允许后,即可成功订阅该模板消息。如果用户拒绝授权,则需要在相关页面给出提示或引导用户去设置中进行授权。

    # 小程序如何实现图片裁切功能

    小程序中实现图片裁剪功能需要使用canvas来进行裁剪并生成新的图片,需要以下步骤:

    1. 在页面中引入canvas标签用于展示要裁剪的图片和裁剪后的图片:
    <view class="container">
      <view class="canvas-container">
        <canvas
          canvas-id="cropCanvas"
          style="cursor: move;"
          bindtouchmove="touchMove"
        ></canvas>
      </view>
      <view class="btn-container">
        <button class="btn-cancel" bindtap="cancelCrop">取消</button>
        <button class="btn-crop" bindtap="startCrop">裁剪</button>
      </view>
    </view>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    1. 在页面的onLoad或onShow生命周期中,加载要裁剪的图片并绘制到canvas上。图片的绘制需要在图片加载完成的回调函数中进行:
    onLoad: function(options) {
      this.setData({
        imagePath: options.imagePath // 从上一个页面传递过来的图片路径
      });
    
      let self = this;
      let ctx = wx.createCanvasContext('cropCanvas', self);
      wx.getImageInfo({
        src: self.data.imagePath,
        success: function(res) {
          let scale = res.width / res.height;
          let canvasWidth = self.data.screenWidth - 20;
          let canvasHeight = canvasWidth / scale;
    
          self.setData({
            canvasWidth: canvasWidth,
            canvasHeight: canvasHeight
          })
    
          self.resetCanvas();
    
          let x = 0,
            y = 0,
            width = res.width,
            height = res.height;
    
          if (canvasHeight < canvasWidth) {
            // 竖图
            height = canvasHeight;
            width = height * scale;
            x = -(width - canvasWidth) / 2;
          } else {
            // 横图
            width = canvasWidth;
            height = width / scale;
            y = -(height - canvasHeight) / 2;
          }
    
          ctx.drawImage(self.data.imagePath, x, y, width, height);
          ctx.draw();
        }
      })
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    1. 实现拖动和缩放功能。拖动需要记录每次触摸移动的距离,并使用translate方法来移动画布。缩放功能可使用两根手指识别,记录每个手指的坐标,并计算两根手指之间的距离和角度,然后使用scale和rotate方法来进行缩放和旋转:
    data: {
      isMoving: false,
      isScaling: false,
      touchCount: 0,
      touchHistory: [],
      scale: 1,
      rotate: 0,
      translateX: 0,
      translateY: 0,
      startX: 0,
      startY: 0,
      screenScale: 1,
      screenWidth: 1,
      canvasWidth: 1,
      canvasHeight: 1,
      imagePath: ''
    },
    
    touchStart(e) {
      let touchCount = this.data.touchCount + 1;
      let touchHistory = this.data.touchHistory;
      touchHistory.push({
        x: e.touches[0].x,
        y: e.touches[0].y
      });
      if (touchCount === 1) {
        this.setData({
          isMoving: true,
          touchCount: touchCount,
          touchHistory: touchHistory,
          startX: e.touches[0].x,
          startY: e.touches[0].y
        });
      } else if (touchCount === 2) {
        let distance = Math.sqrt(Math.pow(touchHistory[1].x - touchHistory[0].x, 2) + Math.pow(touchHistory[1].y - touchHistory[0].y, 2));
        let angle = this.computeRotateAngle(touchHistory[0], touchHistory[1]);
        this.setData({
          isScaling: true,
          touchCount: touchCount,
          touchHistory: touchHistory,
          startDistance: distance,
          startAngle: angle
        });
      }
    },
    
    touchMove(e) {
      let touchHistory = this.data.touchHistory.concat();
      let touchCount = touchHistory.length;
      touchHistory.push({
        x: e.touches[0].x,
        y: e.touches[0].y
      });
      if (touchCount === 1) {
        let distanceX = e.touches[0].x - this.data.startX;
        let distanceY = e.touches[0].y - this.data.startY;
        this.setData({
          isMoving: true,
          touchHistory: touchHistory,
          startX: e.touches[0].x,
          startY: e.touches[0].y,
          translateX: this.data.translateX + distanceX,
          translateY: this.data.translateY + distanceY
        }, () => {
          this.drawCanvas();
        });
      } else if (touchCount === 2) {
        let distance = Math.sqrt(Math.pow(touchHistory[1].x - touchHistory[0].x, 2) + Math.pow(touchHistory[1].y - touchHistory[0].y, 2));
        let angle = this.computeRotateAngle(touchHistory[0], touchHistory[1]);
        this.setData({
          isScaling: true,
          touchHistory: touchHistory,
          currentDistance: distance,
          currentAngle: angle
        }, () => {
          this.drawCanvas();
        });
      }
    },
    
    touchEnd(e) {
      if (this.data.touchCount === 1) {
        this.setData({
          isMoving: false,
          touchCount: 0
        });
      } else if (this.data.touchCount === 2) {
        this.setData({
          isScaling: false,
          touchCount: 0,
          scale: this.data.scale * (this.data.currentDistance / this.data.startDistance),
          rotate: this.data.rotate + (this.data.currentAngle - this.data.startAngle)
        }, () => {
          this.drawCanvas();
        });
      }
    },
    
    computeRotateAngle(p1, p2) {
      let x1 = p1.x,
        y1 = p1.y,
        x2 = p2.x,
        y2 = p2.y;
      let disX = x2 - x1,
        disY = y2 - y1,
        angle;
      if (disY === 0) {
        angle = disX > 0 ? 0 : 180;
      } else if (disX === 0) {
        angle = disY > 0 ? 90 : 270;
      } else {
        angle = Math.atan(disY / disX) * 180 / Math.PI;
        if (disX > 0 && disY > 0) {
          // 第一象限
        } else if (disX < 0 && disY > 0) {
          // 第二象限
          angle = 180 + angle;
        } else if (disX < 0 && disY < 0) {
          // 第三象限
          angle = 180 + angle;
        } else {
          // 第四象限
          angle += 360;
        }
      }
      return angle;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    1. 实现裁剪功能。首先根据缩放和旋转的信息计算出裁剪的起始坐标以及裁剪区域的尺寸,然后将这些参数传递给canvas的drawImage方法,并在绘制完裁剪后的图片后,将图片转化为base64格式,方便后续的上传或保存:
    cropImage() {
      let self = this;
      let ctx = wx.createCanvasContext('cropCanvas', self);
      let canvasWidth = self.data.canvasWidth;
      let canvasHeight = self.data.canvasHeight;
      let scale = self.data.screenScale;
      let rotate = self.data.rotate;
      let scaleX = self.data.scale;
      let scaleY = self.data.scale;
      let translateX = self.data.translateX / scale;
      let translateY = self.data.translateY / scale;
      let x = 0,
        y = 0,
        width = canvasWidth / scaleX,
        height = canvasHeight / scaleY;
      // 裁剪区域超出画布的情况特殊处理
      if (canvasWidth < canvasHeight && Math.abs(translateY) + height > canvasHeight / 2) {
        translateY = canvasHeight / 2 - height;
      } else if (canvasHeight <= canvasWidth && Math.abs(translateX) + width > canvasWidth / 2) {
        translateX = canvasWidth / 2 - width;
      }
      ctx.translate(canvasWidth / 2 + translateX, canvasHeight / 2 + translateY);
      ctx.rotate(rotate * Math.PI / 180);
      ctx.scale(scaleX, scaleY);
      x = (-width) / 2;
      y = (-height) / 2;
      ctx.drawImage(self.data.imagePath, x, y,
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    编辑 (opens new window)
    #小程序
    上次更新: 2023/05/03, 18:26:16
    小程序机制
    小程序开发实战篇

    ← 小程序机制 小程序开发实战篇→

    最近更新
    01
    gsap动画库学习笔记 - 持续~
    06-05
    02
    远程组件加载方案笔记
    05-03
    03
    Nestjs如何做登陆鉴权
    03-27
    更多文章>
    Theme by Vdoing | Copyright © 2020-2023 CD | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式