uniapp makes a watermark camera to add a watermark to the picture and save the picture to the local

Three ways to save files in uniapp

  • Documents are mainly divided into two categories:

1. Code package file: The code package file refers to the file added in the project directory.

2. Local files: Locally generated by calling the interface, or downloaded through the network, and stored locally.

  • There are three types of local files:

1. Local temporary files: files that are generated temporarily and will be recycled at any time. It can store up to 4GB when running. After running, if more than 2GB has been used, it will take the file as the dimension and clean up according to the latest usage time from far to near to less than 2GB.
2. Local cache file: the file generated after the applet caches the local temporary file through the interface, and the directory and file name cannot be customized. Together with local user files, mini-programs (including mini-games) can store up to 200MB.
3. Local user files: files generated by the applet after caching local temporary files through the interface, allowing custom directories and file names. Together with the local cache files, mini-programs (including mini-games) can store up to 200MB.
Note: No matter which storage method is used, as long as the applet is deleted, all the information stored locally by the user will be cleared! ! !
insert image description here

The official detailed introduction to file storage can be viewed by clicking here.
Since the picture needs to be saved locally and then the content of the picture will be echoed here, the way of local temporary file storage is not considered directly. Next, I will cache files locally and users The two angles of local files respectively demonstrate how to save the watermark added to the image locally.

code description

1. Since the positioning of the WeChat applet can only obtain the latitude and longitude, but not the cities and streets in the region, the inverse analysis of the latitude and longitude of the Tencent map is used here . I suggest that you read this article first, otherwise I will introduce the QQMapWX below. , you may feel some doubts.
2. There is still a lot of room for optimization in the code: for example, every time you click to take a photo, you have to restart the camera. Here, you can use unaipp’s camera to solve this problem with its own watermark camera, instead of using the camera that comes with WeChat. Shooting, you can solve these problems by yourself according to your needs.
3. This article is just a guide to provide you with some private solutions to the problem. There may be many mistakes in the code. I hope you can take the essence and discard the dross.

Add watermark to pictures

The following code just adds a watermark, see the effect of the watermark below

    // 添加水印
    setImageText() {
    
    
      var that = this;
      // 绘制前清空画布
      this.context.clearRect(
        0,
        0,
        this.canvasView.width,
        this.canvasView.height
      );
      // 将图片src放到cancas内,宽高必须为图片大小
      this.context.drawImage(
        this.canvasView.canvasImg,
        0,
        0,
        this.canvasView.width,
        this.canvasView.height,
        this.canvasView.width,
        this.canvasView.height
      );
      // 设置上下两个边框的透明度
      this.context.setGlobalAlpha(0.5);

      this.context.beginPath();
      // 1.绘制顶部的白色背景
      this.context.rect(
        0,
        0,
        this.canvasView.width,
        this.canvasView.contentHeight
      );
      this.context.setFillStyle("white");
      this.context.fill();

      // 2.绘制底部的白色背景
      this.context.rect(
        0,
        this.canvasView.height - this.canvasView.contentHeight,
        this.canvasView.width,
        this.canvasView.contentHeight
      );
      // this.context.setFillStyle("white");
      this.context.fill("white");
      // 设置文字的透明度
      this.context.setGlobalAlpha(1);
      // 3.绘制顶部的文字
      this.context.setFontSize(26);
      this.context.setTextAlign("left");
      this.context.setFillStyle("black");
      this.context.fillText("拍摄人:李帅豪", 50, 60);
      this.context.fillText(
        "拍摄时间:" + this.$u.timeFormat(new Date(), "yyyy-mm-dd hh:MM:ss"),
        50,
        110
      );

      // 4.绘制顶部的图片
      that.context.drawImage(
        "/static/image/lshlogo.png",
        that.canvasView.width - that.canvasView.logoWidth - 10,
        20,
        that.canvasView.logoWidth,
        that.canvasView.contentHeight - 40
      );
      // 5.绘制底部的文字
      // 5.1绘制定位以及网络状态
      this.context.setFontSize(33);
      this.context.setFillStyle("black");
      this.context.fillText(
        "定位:" + this.positionInfo.formatLongitude,
        50,
        this.canvasView.height - 100
      );
      this.context.fillText(
        this.positionInfo.formatLatitude,
        this.canvasView.width - 450,
        this.canvasView.height - 100
      );
      var systemimg = "";
      var networkimg = "";
      if (that.systemInfo.platform == "android") {
    
    
        systemimg = "anzhuo";
      } else if (that.systemInfo.platform == "ios") {
    
    
        systemimg = "ios";
      } else {
    
    
        systemimg = "anzhuo";
      }
      if (that.networkType == "unknown" || that.networkType == "none") {
    
    
        networkimg = "network1";
      } else if (that.networkType == "none") {
    
    
        networkimg = "flyMode";
      } else {
    
    
        networkimg = "network2";
      }
      console.log("systemimg,networkimg", systemimg, networkimg);
      that.context.drawImage(
        "/static/image/" + systemimg + ".png",
        this.canvasView.width - 150,
        this.canvasView.height - 140,
        50,
        50
      );
      that.context.drawImage(
        "/static/image/" + networkimg + ".png",
        this.canvasView.width - 90,
        this.canvasView.height - 140,
        50,
        50
      );
      // 5.2绘制所在村位置
      // 5.3绘制拍摄地址
      this.context.setFontSize(33);
      this.context.setFillStyle("black");
      this.context.fillText(
        "拍摄地址:北京市xxxxxx",
        50,
        this.canvasView.height - 50,
        this.canvasView.width - 100
      );

      // 绘制图片并保存至相册
      this.drawSave();
    },

Use the local cache file to save the watermark image to the local

Advantages: You can directly read pictures without converting them to base64 to display pictures, the reading efficiency is fast, and there will be no performance problems.
Disadvantage: Although this method can save the picture locally, its disadvantage is that it is not flexible enough. If we want to store some business information of the current picture locally during the process of saving the current picture locally, it will change. very difficult. And we will read all the files saved using saveFileSync, which cannot be read in batches.
The renderings are as follows:
insert image description here
insert image description here

code show as below:

<template>
  <view class="pages">
    <!-- 这个canvas一定不可以将一出的部分隐藏掉,否则就会有一些部分加载不出来 -->
    <canvas
      id="mycanvas"
      :style="
        'width:' + canvasView.width + 'px;height:' + canvasView.height + 'px;'
      "
      canvas-id="Canvas"
    />
    <!-- <u-loading :show="isLoading"></u-loading> -->
    <u-loadmore status="loading" v-if="isLoading" />
    <view class="imgContainer">
      <view v-for="(item1, index1) in imgList" :key="index1" class="imgDayItem">
        <view class="imgTime"
          >拍摄时间:{
    
    {
    
     item1[0].time }}({
    
    {
    
     item1.length }})</view
        >
        <div class="imgGroup">
          <view
            class="itemImg"
            v-for="(item2, index2) in item1"
            @click="previewImage(item2, index2, index1)"
            :key="index2"
          >
            <image :src="item2.path" alt="" srcset="" />
            <view class="imgName">图片{
    
    {
    
     index2 }}</view>
            <view
              class="removeImg"
              @click.stop.prevent="removeCurrentImg(item2)"
            >
              x
            </view>
          </view>
        </div>
      </view>
    </view>
    <!-- 定位的拍摄图片 -->
    <image
      src="@/static/image/cameraImg.png"
      class="cameraBtn"
      @click="TakeAPicture()"
      mode=""
    ></image>
    <!-- 测试用的删除本地文件 -->
    <button @click="deleteImg">删除本地所有图片</button>
  </view>
</template>

<script>
import QQMapWX from "../../../../js_sdk/qqmap-wx-jssdk1.2/qqmap-wx-jssdk";

export default {
    
    
  data() {
    
    
    return {
    
    
      // canvas的一些信息
      canvasView: {
    
    
        // 当前要绘制的图片
        canvasImg: undefined,
        // 将要被绘制的图片的宽度
        width: undefined,
        // 将要被绘制的图片的高度
        height: undefined,
        // 将要被绘制到图像中的上下两个矩形的高度(px)
        contentHeight: 150,
        // 将要被绘制到图像中的顶部logo的高度(px)
        logoWidth: 400,
      },
      // 当前的经纬度信息
      positionInfo: {
    
    
        mapAddress: "", // 所在村位置
        address: "", // 拍摄地址
        longitude: "", //经度(坐标)
        latitude: "", //纬度(坐标)
        formatLongitude: "", //转变过后的经度(度分秒形式)
        formatLatitude: "", //转变过后的纬度(度分秒形式)
      },
      // 当前要被绘制的图片的信息
      fileUrl: undefined,
      // canvas对象
      context: undefined,
      // 图片列表
      imgList: [],
      // 是否是正在加载中
      isLoading: true,
      // 网络类型
      networkType: "",
      // 系统信息
      systemInfo: "",
    };
  },
  created() {
    
    
    this.getCurrentLocation();
    this.getNetworkType();
    this.getSystemInfo();
  },
  mounted() {
    
    
    uni.showLoading({
    
    
      title: "正在加载图片",
    });
    this.isLoading = true;
    this.context = uni.createCanvasContext("Canvas", this);
    this.getSavedFileList();
  },
  methods: {
    
    
    // 获取本地的图片列表
    getSavedFileList() {
    
    
      var that = this;
      uni.getFileSystemManager().getSavedFileList({
    
    
        success: function (res) {
    
    
          console.log("获取本地小程序下载的文件如下:", res);

          that.imgList = res.fileList.map((item) => {
    
    
            console.log("res.fileList", item);
            return {
    
    
              path: item.filePath,
              time: that.$u.timeFormat(
                item.createTime * 1000,
                "yyyy年mm月dd日"
              ),
            };
          });
          console.log("========222==", that.imgList);
          that.formatImgList(that.imgList);
          uni.hideLoading();
          that.isLoading = false;
        },
      });
    },
    // 通过微信小程序自带的方法获取到当前的经纬度,然后通过腾讯sdk获取详细位置
    getCurrentLocation() {
    
    
      let that = this; //在uniapp中药定义一下this才能使用
      uni.getLocation({
    
    
        type: "gcj02",
        altitude: true,
        geocode: true,
        isHighAccuracy: true,
        success: function (res) {
    
    
          console.log("微信小程序定位信息", res);
          that.positionInfo.longitude = res.longitude;
          that.positionInfo.latitude = res.latitude;
          that.positionInfo.formatLongitude = that.convertToDMS(
            res.latitude,
            res.longitude
          ).longitude;
          that.positionInfo.formatLatitude = that.convertToDMS(
            res.latitude,
            res.longitude
          ).latitude;
          that.loAcquire(
            that.positionInfo.longitude,
            that.positionInfo.latitude
          );
        },
      });
    },
    // 获取网络类型
    getNetworkType() {
    
    
      var that = this;
      uni.getNetworkType({
    
    
        success: function (res) {
    
    
          console.log(res);
          that.networkType = res.networkType;
        },
      });
    },
    // 获取系统信息
    getSystemInfo() {
    
    
      var that = this;
      uni.getSystemInfo({
    
    
        success: function (res) {
    
    
          // console.log(res.appName)
          that.systemInfo = res;
          console.log("当前使用的系统是:", res.platform);
        },
      });
    },
    // 获取当前地址
    loAcquire(longitude, latitude) {
    
    
      let that = this;
      // 腾讯地图Api
      const qqmapsdk = new QQMapWX({
    
    
        key: "5IPBZ-2OA3Q-7UR5Y-4XVMJ-IIEDQ-EDFRN", //这里填写自己申请的key
      });
      console.log({
    
     longitude, latitude });
      qqmapsdk.reverseGeocoder({
    
    
        location: {
    
     longitude, latitude },
        success(response) {
    
    
          uni.hideLoading();
          that.isLoading = false;
          console.log("腾讯sdk获取的定位信息:", response);
          that.positionInfo.mapAddress =
            response.result.address_component.province +
            response.result.address_component.city +
            response.result.address_component.district +
            response.result.address_component.street;
          response.result.address_component.street_number;
          that.positionInfo.address =
            response.result.address_component.city +
            response.result.formatted_addresses.recommend;
        },
        fail() {
    
    
          that.isLoading = false;
          uni.showToast({
    
    
            icon: "none",
            title: "定位失败请检查网络状态",
            duration: 4000,
          });
        },
      });
    },
    // 添加水印
    setImageText(image) {
    
    
      var that = this;
      // 绘制前清空画布
      this.context.clearRect(
        0,
        0,
        this.canvasView.width,
        this.canvasView.height
      );
      // 将图片src放到cancas内,宽高必须为图片大小
      this.context.drawImage(
        this.canvasView.canvasImg,
        0,
        0,
        this.canvasView.width,
        this.canvasView.height,
        this.canvasView.width,
        this.canvasView.height
      );
      // 设置上下两个边框的透明度
      this.context.setGlobalAlpha(0.5);

      this.context.beginPath();
      // 1.绘制顶部的白色背景
      this.context.rect(
        0,
        0,
        this.canvasView.width,
        this.canvasView.contentHeight
      );
      this.context.setFillStyle("white");
      this.context.fill();

      // 2.绘制底部的白色背景
      this.context.rect(
        0,
        this.canvasView.height - this.canvasView.contentHeight,
        this.canvasView.width,
        this.canvasView.contentHeight
      );
      // this.context.setFillStyle("white");
      this.context.fill("white");
      // 设置文字的透明度
      this.context.setGlobalAlpha(1);
      // 3.绘制顶部的文字
      this.context.setFontSize(26);
      this.context.setTextAlign("left");
      this.context.setFillStyle("black");
      this.context.fillText("拍摄人:李帅豪", 50, 60);
      this.context.fillText(
        "拍摄时间:" + this.$u.timeFormat(new Date(), "yyyy-mm-dd hh:MM:ss"),
        50,
        110
      );

      // 4.绘制顶部的图片
      that.context.drawImage(
        "/static/image/lshlogo.png",
        that.canvasView.width - that.canvasView.logoWidth - 10,
        20,
        that.canvasView.logoWidth,
        that.canvasView.contentHeight - 40
      );
      // 5.绘制底部的文字
      // 5.1绘制定位以及网络状态
      this.context.setFontSize(33);
      this.context.setFillStyle("black");
      console.log('下面需要填写具体的位置信息');
      this.context.fillText(
        "定位:" + this.positionInfo.formatLongitude,
        50,
        this.canvasView.height - 100
      );
      this.context.fillText(
        this.positionInfo.formatLatitude,
        this.canvasView.width - 450,
        this.canvasView.height - 100
      );
      var systemimg = "";
      var networkimg = "";
      if (that.systemInfo.platform == "android") {
    
    
        systemimg = "anzhuo";
      } else if (that.systemInfo.platform == "ios") {
    
    
        systemimg = "ios";
      } else {
    
    
        systemimg = "anzhuo";
      }
      if (that.networkType == "unknown" || that.networkType == "none") {
    
    
        networkimg = "network1";
      } else if (that.networkType == "none") {
    
    
        networkimg = "flyMode";
      } else {
    
    
        networkimg = "network2";
      }
      console.log("systemimg,networkimg", systemimg, networkimg);
      that.context.drawImage(
        "/static/image/" + systemimg + ".png",
        this.canvasView.width - 150,
        this.canvasView.height - 140,
        50,
        50
      );
      that.context.drawImage(
        "/static/image/" + networkimg + ".png",
        this.canvasView.width - 90,
        this.canvasView.height - 140,
        50,
        50
      );
      // 5.3绘制拍摄地址
      this.context.setFontSize(33);
      this.context.setFillStyle("black");
      console.log('在这里填写拍摄的地址',this.positionInfo.address);
      this.context.fillText(
        "拍摄地址:北京市xxxxxxxxx" ,
        50,
        this.canvasView.height - 50,
        this.canvasView.width - 100
      );

      this.drawSave();
    },
    // 绘制图片并保存至相册
    drawSave() {
    
    
      var that = this;
      // 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
      setTimeout(() => {
    
    
        // 本次回画完重开开始绘画,并且在绘画完毕之后再保存图片,不然页面可能会出现白屏等情况
        this.context.draw(false, () => {
    
    
          console.log(
            "!!!!!!!!!1开始绘画",
            this.canvasView.width,
            this.canvasView.height
          );

          uni.canvasToTempFilePath(
            {
    
    
              canvasId: "Canvas",
              fileType: "png",
              width: this.canvasView.width,
              height: this.canvasView.height,
              destWidth: this.canvasView.width,
              destHeight: this.canvasView.height,
              success: async (path) => {
    
    
                console.log("path", path);

                let imageInfo = await that.getImageInfo({
    
    
                  imgSrc: path.tempFilePath,
                });
                console.log("==========", imageInfo);
                that.fileUrl = imageInfo.path;

                // console.log("这一步如果换成png那么就可以显示出真是的图片");
                // console.log(
                //   "lshlsh",
                //   `data:image/png;base64,${uni
                //     .getFileSystemManager()
                //     .readFileSync(imageInfo.path, "base64")}`
                // );
                var respath = uni
                  .getFileSystemManager()
                  .saveFileSync(imageInfo.path);
                console.log("拥挤存储的路径saveFileSync", respath);
                uni.saveImageToPhotosAlbum({
    
    
                  filePath: respath,
                  complete: (res) => {
    
    
                    console.log(res);
                  },
                });
                that.imgList = [];
                that.getSavedFileList();
              },
            },
            that
          );
        });
      }, 1000);
    },
    //检查相机权限
    TakeAPicture() {
    
    
      var that = this;
      uni.authorize({
    
    
        scope: "scope.camera",
        success() {
    
    
          console.log("scope.camera用户授权成功小程序");

          uni.getSetting({
    
    
            success(res) {
    
    
              console.log("authSetting", res);
              if (res.authSetting["scope.camera"] == true) {
    
    
                console.log("111当前用户已经个授权过了scope.camera");
                that.openCamera();
              } else {
    
    
                console.log("22当前用户没用授权过scope.camera");
                uni.openSetting({
    
    
                  success(res) {
    
    
                    console.log("openSetting", res);
                    if (res.authSetting["scope.camera"] == true) {
    
    
                      console.log("openSetting打开设置成功打开了摄像头权限");
                      that.openCamera();
                    } else {
    
    
                      uni.showToast({
    
    
                        title: "未获取到相机权限",
                      });
                    }
                  },
                });
              }
            },
          });
        },
        fail() {
    
    
          console.log("拒绝");
          uni.showToast({
    
    
            icon: "none",
            title: "您已拒绝相机权限,暂无法使用水印相机",
            duration: 4000,
          });
        },
      });
    },
    //获取图片信息
    async getImageInfo({
     
      imgSrc }) {
    
    
      return new Promise((resolve, errs) => {
    
    
        uni.getImageInfo({
    
    
          src: imgSrc,
          success: function (image) {
    
    
            resolve(image);
          },
          fail(err) {
    
    
            errs(err);
          },
        });
      });
    },
    //打开相机
    async openCamera() {
    
    
      var that = this;
      // 一定要使用chooseMedia,如果使用chooseImage那么部分IOS手机就没法正常使用了
      uni.chooseMedia({
    
    
        count: 1,
        mediaType: ["image"],
        sourceType: ["camera"],
        camera: "back",
        success: async (res) => {
    
    
          uni.showLoading({
    
    
            title: "正在加载图片资源",
          });
          that.isLoading = true;
          console.log(res);
          let imgInfo = await that.getImageInfo({
    
    
            imgSrc: res.tempFiles[0].tempFilePath,
          });
          console.log("这一步可以获取到真正的base图片不过还没有转化");
          // console.log(
          //   `data:image/jpg;base64,${uni
          //     .getFileSystemManager()
          //     .readFileSync(res.tempFilePaths[0], "base64")}`
          // );
          console.log("==========imgInfo", imgInfo);
          that.canvasView.canvasImg = imgInfo.path;
          that.canvasView.width = imgInfo.width;
          that.canvasView.height = imgInfo.height;
          console.log(this.canvasView);
          console.log(
            "!!!!!!!!!!当前图片信息canvasView",
            that.canvasView
          );
          that.setImageText();
        },
        fail: function (res) {
    
    
          console.log("选择图片失败", res);
        },
      });
    },
    // 删除当前的图片(测试节点使用的)
    removeCurrentImg(item) {
    
    
      // 等会儿在处理这个问题,这个需要二维为数组中寻找并且删除元素
      var that = this;
      uni.getFileSystemManager().removeSavedFile({
    
    
        filePath: item.path,
        success(res) {
    
    
          console.log("单个删除成功", res);
          that.getSavedFileList();
        },
      });
    },
    // 删除所有的图片(测试节点使用的)
    deleteImg() {
    
    
      var that = this;
      this.imgList.forEach((item) => {
    
    
        item.forEach((item2, index2) => {
    
    
          uni.getFileSystemManager().removeSavedFile({
    
    
            filePath: item2.path,
            success(res) {
    
    
              console.log("全部删除成功", res);
              that.getSavedFileList();
            },
          });
        });
      });
    },
    // 预览图片
    // 第一个参数是当前项的图片信息,
    // 第二个参数为当前图片的索引
    // 第三个参数为当前图片所在时间组的索引
    previewImage(item, index2, index1) {
    
    
      console.log("图片预览", item, index2, index1);
      var that = this;
      // 预览图片
      uni.previewImage({
    
    
        current: index2,
        urls: that.imgList[index1].map((item) => item.path),
      });
    },
    // 将获取到的图片按照日期进行格式化
    formatImgList(arr) {
    
    
      // this.imgList = Object.values(
      //   arr.reduce((obj, item) => {
    
    
      //     obj[item.time] = obj[item.time] || [item];
      //     obj[item.time].push(item);
      //     return obj;
      //   }, {})
      // ).filter((item) => item.length > 1);
      this.imgList = arr.reduce((prev, item) => {
    
    
        const index = prev.findIndex((value) => {
    
    
          return value.length > 0 && value[0].time === item.time;
        });

        if (index !== -1) {
    
    
          prev[index].push(item);
        } else {
    
    
          prev.push([item]);
        }

        return prev;
      }, []);
      console.log("formatImgList", this.imgList);
    },
    // 将经纬度转化为度分秒
    convertToDMS(lat, lng) {
    
    
      let latDirection = lat >= 0 ? "N" : "S";
      let lngDirection = lng >= 0 ? "E" : "W";

      lat = Math.abs(lat);
      lng = Math.abs(lng);

      let latDegrees = Math.floor(lat);
      let latMinutes = Math.floor((lat - latDegrees) * 60);
      let latSeconds = ((lat - latDegrees - latMinutes / 60) * 3600).toFixed(4);

      let lngDegrees = Math.floor(lng);
      let lngMinutes = Math.floor((lng - lngDegrees) * 60);
      let lngSeconds = ((lng - lngDegrees - lngMinutes / 60) * 3600).toFixed(4);

      return {
    
    
        latitude: `${
      
      latDirection}:${
      
      latDegrees}°${
      
      latMinutes}'${
      
      latSeconds}"`,
        longitude: `${
      
      lngDirection}:${
      
      lngDegrees}°${
      
      lngMinutes}'${
      
      lngSeconds}"`,
      };
    },
  },
};
</script>

<style lang="scss" scoped>
.pages {
    
    
  overflow: scroll;
}
.imgContainer {
    
    
  .imgDayItem {
    
    
    padding: 30rpx 30rpx 0;
    .imgTime {
    
    
      padding: 20rpx 0 20rpx 0;
      margin-bottom: 20rpx;
      background-color: rgb(249, 249, 249);
    }
    .imgGroup {
    
    
      // border: 2rpx solid red;
      display: flex;
      align-items: center;
      justify-content: flex-start;
      flex-wrap: wrap;

      .itemImg {
    
    
        width: 30%;
        border-radius: 25rpx;
        // border: 1rpx solid red;
        position: relative;
        margin-right: 35rpx;
        margin-bottom: 35rpx;
        &:nth-child(3n) {
    
    
          margin-right: 0rpx;
        }
        image {
    
    
          width: 100%;
          height: 200rpx;
          display: block;
        }
        .imgName {
    
    
          text-align: center;
          position: absolute;
          left: 50%;
          transform: translateX(-50%);
          bottom: 0;
        }
        .removeImg {
    
    
          color: white;
          border-radius: 50%;
          background-color: red;
          width: 40rpx;
          height: 40rpx;
          line-height: 40rpx;
          text-align: center;
          position: absolute;
          top: -24rpx;
          right: -20rpx;
        }
      }
    }
  }
}

.cameraBtn {
    
    
  position: fixed;
  right: 40rpx;
  bottom: 50rpx;
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  z-index: 30;
}
#mycanvas {
    
    
  position: fixed;
  left: 9999rpx;
  // opacity: 0;
  // visibility: hidden;
  // opacity: 0;
}
</style>

Use the local user file to save the watermark image to the local

Before reading the following code, it is recommended to read the official uni.getFileSystemManager() API of the applet first , otherwise there may be some doubts.
Advantages: You can save some information locally when saving pictures (not shown here, just demonstrate its most basic usage), for example, we can create a folder where the user saves the latitude and longitude. At any time, you can save the latitude and longitude of the current picture to the local, and its file name is the same as the name when saving the picture. Later, when we get all the information of this picture, we directly read the folder where the picture is saved and the folder where the latitude and longitude are saved, so that all the information of a picture is read out.
Advantages: We can create folders by ourselves, and then name and save the files by ourselves (note that this is not to create a visible folder directly in the user's local file management, and I don't know where to create the new folder directly) . When the business increases, his strength is reflected.
The effect diagram is as follows:
insert image description here
insert image description here
the code is as follows:

<template>
  <view class="pages">
    <!-- 这个canvas一定不可以将一出的部分隐藏掉,否则就会有一些部分加载不出来 -->
    <canvas
      id="mycanvas"
      :style="
        'width:' + canvasView.width + 'px;height:' + canvasView.height + 'px;'
      "
      canvas-id="Canvas"
    />
    <!-- <u-loading :show="isLoading"></u-loading> -->
    <u-loadmore status="loading" v-if="isLoading" />
    <view class="imgContainer">
      <view v-for="(item1, index1) in imgList" :key="index1" class="imgDayItem">
        <view class="imgTime"
          >拍摄时间:{
    
    {
    
     item1[0].time }}({
    
    {
    
     item1.length }})</view
        >
        <div class="imgGroup">
          <view
            class="itemImg"
            v-for="(item2, index2) in item1"
            @click="previewImage(item2, index2, index1)"
            :key="index2"
          >
            <image :src="item2.path" alt="" srcset="" />
            <view class="imgName">{
    
    {
    
     item2.detailTime }}</view>
            <view
              class="removeImg"
              @click.stop.prevent="removeCurrentImg(item2)"
            >
              x
            </view>
          </view>
        </div>
      </view>
    </view>
    <!-- 定位的拍摄图片 -->
    <image
      src="@/static/image/cameraImg.png"
      class="cameraBtn"
      @click="TakeAPicture()"
      mode=""
    ></image>
    <!-- 测试用的删除本地文件 -->
    <!-- <button @click="deleteImg">删除本地所有图片</button> -->

    <u-back-top
      :scroll-top="scrollTop"
      :custom-style="backTopStyle"
    ></u-back-top>
  </view>
</template>

<script>
import QQMapWX from "../../../../js_sdk/qqmap-wx-jssdk1.2/qqmap-wx-jssdk";

export default {
    
    
  data() {
    
    
    return {
    
    
      // canvas的一些信息
      // 照片即将要被保存到的系统文件路径
      savePath: `${
      
      wx.env.USER_DATA_PATH}/watermarkCameraImages`,
      canvasView: {
    
    
        // 当前要绘制的图片
        canvasImg: undefined,
        // 将要被绘制的图片的宽度
        width: undefined,
        // 将要被绘制的图片的高度
        height: undefined,
        // 将要被绘制到图像中的上下两个矩形的高度(px)
        contentHeight: 150,
        // 将要被绘制到图像中的顶部logo的高度(px)
        logoWidth: 400,
      },
      // 当前的经纬度信息
      positionInfo: {
    
    
        mapAddress: "", // 所在村位置
        address: "", // 拍摄地址
        longitude: "", //经度(坐标)
        latitude: "", //纬度(坐标)
        formatLongitude: "", //转变过后的经度(度分秒形式)
        formatLatitude: "", //转变过后的纬度(度分秒形式)
      },
      // 当前要被绘制的图片的信息
      fileUrl: undefined,
      // canvas对象
      context: undefined,
      // 文件管理对象
      systemManager: "",
      // 图片列表
      imgList: [],
      // 是否是正在加载中
      isLoading: true,
      // 网络类型
      networkType: "",
      // 系统信息
      systemInfo: "",
      // 页面滚动值顶部
      scrollTop: 0,

      backTopStyle: {
    
    
        right: "60rpx",
      },
    };
  },
  created() {
    
    
    this.systemManager = uni.getFileSystemManager();
    // 判断需不需要穿件新的文件夹
    this.addDir();
    // 获取所有的文件
    this.getSavedFileList();

    this.getCurrentLocation();
    this.getNetworkType();
    this.getSystemInfo();
  },
  mounted() {
    
    
    // uni.showLoading({
    
    
    //   title: "资源加载中请稍后",
    // });
    this.isLoading = true;
    this.context = uni.createCanvasContext("Canvas", this);
  },
  onPageScroll(e) {
    
    
    this.scrollTop = e.scrollTop;
  },
  methods: {
    
    
    // 获取所有的文件
    getSavedFileList() {
    
    
      var that = this;
      that.imgList = [];
      console.log(`${
      
      wx.env.USER_DATA_PATH}`);
      var res = uni.getFileSystemManager().readdir({
    
    
        dirPath: that.savePath,
        success(res) {
    
    
          console.log("获取文件列表成功aaaaaaa", res);
          res.files.forEach((item, index) => {
    
    
            var timeStr = Number(item.slice(0, -4));
            console.log(timeStr);
            that.imgList.push({
    
    
              path: `${
      
      that.savePath}/${
      
      item}`,
              time: that.$u.timeFormat(timeStr, "yyyy年mm月dd日"),
              detailTime: that.$u.timeFormat(timeStr, "hh时MM分ss秒"),
              url: "/" + item,
            });
          });
          that.formatImgList(that.imgList);
          uni.hideLoading();
          that.isLoading = false;
        },
        fail() {
    
    
          uni.showLoading({
    
    
            title: "图片加载失败请退出后重试",
          });
        },
      });
    },
    // 判断需不需要添加文件夹
    addDir() {
    
    
      var that = this;
      uni.getFileSystemManager().stat({
    
    
        path: that.savePath,
        success: function (res) {
    
    
          console.log(res);
          console.log("已经有这个文件了");
        },
        fail() {
    
    
          console.log("需要传建一个这样的文件夹");
          // 创建一个文件夹用于存储图片
          var res = uni.getFileSystemManager().mkdir({
    
    
            dirPath: that.savePath,
            success(res) {
    
    
              console.log("aaaaaaa", res);
            },
          });
        },
      });
    },
    // 通过微信小程序自带的方法获取到当前的经纬度,然后通过腾讯sdk获取详细位置
    getCurrentLocation() {
    
    
      let that = this; //在uniapp中药定义一下this才能使用
      uni.getLocation({
    
    
        type: "gcj02",
        altitude: true,
        geocode: true,
        isHighAccuracy: true,
        success: function (res) {
    
    
          console.log("微信小程序定位信息", res);
          that.positionInfo.longitude = res.longitude;
          that.positionInfo.latitude = res.latitude;
          that.positionInfo.formatLongitude = that.convertToDMS(
            res.latitude,
            res.longitude
          ).longitude;
          that.positionInfo.formatLatitude = that.convertToDMS(
            res.latitude,
            res.longitude
          ).latitude;
          that.loAcquire(
            that.positionInfo.longitude,
            that.positionInfo.latitude
          );
        },
        fail: function (res) {
    
    
          console.log("定位失败信息res", res);
          uni.showToast({
    
    
            icon: "none",
            duration: 4000,
            title: "请检查手机是否开启定位",
          });
        },
      });
    },
    // 获取网络类型
    getNetworkType() {
    
    
      var that = this;
      uni.getNetworkType({
    
    
        success: function (res) {
    
    
          console.log(res);
          that.networkType = res.networkType;
        },
      });
    },
    // 获取系统信息
    getSystemInfo() {
    
    
      var that = this;
      uni.getSystemInfo({
    
    
        success: function (res) {
    
    
          // console.log(res.appName)
          that.systemInfo = res;
          console.log("当前使用的系统是:", res.platform);
        },
      });
    },
    // 获取当前地址
    loAcquire(longitude, latitude) {
    
    
      let that = this;
      // 腾讯地图Api
      const qqmapsdk = new QQMapWX({
    
    
        key: "5IPBZ-2OA3Q-7UR5Y-4XVMJ-IIEDQ-EDFRN", //这里填写自己申请的key
      });
      console.log({
    
     longitude, latitude });
      qqmapsdk.reverseGeocoder({
    
    
        location: {
    
     longitude, latitude },
        success(response) {
    
    
          uni.hideLoading();
          that.isLoading = false;
          console.log("腾讯sdk获取的定位信息:", response);
          that.positionInfo.mapAddress =
            response.result.address_component.province +
            response.result.address_component.city +
            response.result.address_component.district +
            response.result.address_component.street;
          response.result.address_component.street_number;
          that.positionInfo.address =
            response.result.address_component.city +
            response.result.formatted_addresses.recommend;
        },
        fail() {
    
    
          that.isLoading = false;
          uni.showToast({
    
    
            icon: "none",
            title: "定位失败请检查网络状态",
            duration: 4000,
          });
        },
      });
    },
    // 添加水印
    setImageText(image) {
    
    
      var that = this;
      // 绘制前清空画布
      this.context.clearRect(
        0,
        0,
        this.canvasView.width,
        this.canvasView.height
      );
      // 将图片src放到cancas内,宽高必须为图片大小
      this.context.drawImage(
        this.canvasView.canvasImg,
        0,
        0,
        this.canvasView.width,
        this.canvasView.height,
        this.canvasView.width,
        this.canvasView.height
      );
      // 设置上下两个边框的透明度
      this.context.setGlobalAlpha(0.5);

      this.context.beginPath();
      // 1.绘制顶部的白色背景
      this.context.rect(
        0,
        0,
        this.canvasView.width,
        this.canvasView.contentHeight
      );
      this.context.setFillStyle("white");
      this.context.fill();

      // 2.绘制底部的白色背景
      this.context.rect(
        0,
        this.canvasView.height - this.canvasView.contentHeight,
        this.canvasView.width,
        this.canvasView.contentHeight
      );
      // this.context.setFillStyle("white");
      this.context.fill("white");
      // 设置文字的透明度
      this.context.setGlobalAlpha(1);
      // 3.绘制顶部的文字
      this.context.setFontSize(26);
      this.context.setTextAlign("left");
      this.context.setFillStyle("black");
      this.context.fillText("拍摄人:李帅豪", 50, 60);
      this.context.fillText(
        "拍摄时间:" + this.$u.timeFormat(new Date(), "yyyy-mm-dd hh:MM:ss"),
        50,
        110
      );

      // 4.绘制顶部的图片
      that.context.drawImage(
        "/static/image/lshlogo.png",
        that.canvasView.width - that.canvasView.logoWidth - 10,
        20,
        that.canvasView.logoWidth,
        that.canvasView.contentHeight - 40
      );
      // 5.绘制底部的文字
      // 5.1绘制定位以及网络状态
      this.context.setFontSize(33);
      this.context.setFillStyle("black");
      this.context.fillText(
        "定位:" + this.positionInfo.formatLongitude,
        50,
        this.canvasView.height - 100
      );
      this.context.fillText(
        this.positionInfo.formatLatitude,
        this.canvasView.width - 450,
        this.canvasView.height - 100
      );
      var systemimg = "";
      var networkimg = "";
      if (that.systemInfo.platform == "android") {
    
    
        systemimg = "anzhuo";
      } else if (that.systemInfo.platform == "ios") {
    
    
        systemimg = "ios";
      } else {
    
    
        systemimg = "anzhuo";
      }
      if (that.networkType == "unknown" || that.networkType == "none") {
    
    
        networkimg = "network1";
      } else if (that.networkType == "none") {
    
    
        networkimg = "flyMode";
      } else {
    
    
        networkimg = "network2";
      }
      console.log("systemimg,networkimg", systemimg, networkimg);
      that.context.drawImage(
        "/static/image/" + systemimg + ".png",
        this.canvasView.width - 150,
        this.canvasView.height - 140,
        50,
        50
      );
      that.context.drawImage(
        "/static/image/" + networkimg + ".png",
        this.canvasView.width - 90,
        this.canvasView.height - 140,
        50,
        50
      );
      // 5.2绘制所在村位置
      // 5.3绘制拍摄地址
      this.context.setFontSize(33);
      this.context.setFillStyle("black");
      this.context.fillText(
        "拍摄地址:北京市xxxxxx",
        50,
        this.canvasView.height - 50,
        this.canvasView.width - 100
      );

      this.drawSave();
    },
    // 绘制图片并保存至相册
    drawSave() {
    
    
      var that = this;
      // 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
      setTimeout(() => {
    
    
        // 本次回画完重开开始绘画,并且在绘画完毕之后再保存图片,不然页面可能会出现白屏等情况
        this.context.draw(false, () => {
    
    
          console.log(
            "!!!!!!!!!1开始绘画",
            this.canvasView.width,
            this.canvasView.height
          );

          uni.canvasToTempFilePath(
            {
    
    
              canvasId: "Canvas",
              fileType: "jpg",
              width: this.canvasView.width,
              height: this.canvasView.height,
              destWidth: this.canvasView.width,
              destHeight: this.canvasView.height,
              success: async (path) => {
    
    
                console.log("path", path);

                let imageInfo = await that.getImageInfo({
    
    
                  imgSrc: path.tempFilePath,
                });
                console.log("==========", imageInfo);
                that.fileUrl = imageInfo.path;
                // var respath = uni
                //   .getFileSystemManager()
                //   .saveFileSync(imageInfo.path);
                // console.log("拥挤存储的路径saveFileSync", respath);

                // 压缩图片
                uni.compressImage({
    
    
                  src: imageInfo.path,
                  quality: 30,
                  success: (res) => {
    
    
                    console.log(res.tempFilePath);

                    var fileName = Date.now() + ".jpg";
                    var bufferData = that.systemManager.readFileSync(
                      res.tempFilePath,
                      "base64"
                    );
                    var a1 = that.systemManager.writeFileSync(
                      `${
      
      that.savePath}/${
      
      fileName}`,
                      bufferData,
                      "base64"
                    );
                    console.log(a1);
                    uni.saveImageToPhotosAlbum({
    
    
                      filePath: res.tempFilePath,
                      complete: (res) => {
    
    
                        console.log(res);
                      },
                    });

                    // 有性能问题
                    // that.getSavedFileList();
                    // 优化方向:不重新获取列表
                    that.addImgToList({
    
     fileName });
                  },
                });
              },
            },
            that
          );
        });
      }, 1000);
    },
    // 将当前图片添加至图片列表
    addImgToList({
     
      fileName }) {
    
    
      console.log("@@@@@@", "addImgToList");
      var that = this;
      var path = `${
      
      that.savePath}/${
      
      fileName}`;
      var time = that.$u.timeFormat(
        Number(fileName.slice(0, -4)),
        "yyyy年mm月dd日"
      );

      var detailTime = that.$u.timeFormat(
        Number(fileName.slice(0, -4)),
        "hh时MM分ss秒"
      );
      var url = "/" + fileName;
      var obj = {
    
    
        path,
        time,
        url,
        detailTime,
      };

      if (that.imgList.length > 0) {
    
    
        // 当前需要被添加的数组
        var currentArr = [];
        // 当前需要被添加的数组的索引
        var currentArrIndex = "";
        console.log(obj);
        that.imgList.forEach((item, index) => {
    
    
          item.forEach((item2, index2) => {
    
    
            if (item2.time == obj.time) {
    
    
              currentArr = item;
              currentArrIndex = index;
            }
          });
        });
        if (currentArr.length > 0) {
    
    
          currentArr.push(obj);
          console.log("currentArr", currentArr);
          that.$set(that.imgList, currentArrIndex, currentArr);
        } else {
    
    
          that.imgList.push([obj]);
        }
        console.log("可能会出问题的地方", currentArr, currentArrIndex);
        console.log("currentArr", currentArr);
      } else {
    
    
        that.imgList.push([obj]);
      }
      uni.hideLoading();
      that.isLoading = false;
    },
    //检查相机权限
    TakeAPicture() {
    
    
      var that = this;
      uni.authorize({
    
    
        scope: "scope.camera",
        success() {
    
    
          console.log("scope.camera用户授权成功小程序");

          uni.getSetting({
    
    
            success(res) {
    
    
              console.log("authSetting", res);
              if (res.authSetting["scope.camera"] == true) {
    
    
                console.log("111当前用户已经个授权过了scope.camera");
                that.openCamera();
              } else {
    
    
                console.log("22当前用户没用授权过scope.camera");
                uni.openSetting({
    
    
                  success(res) {
    
    
                    console.log("openSetting", res);
                    if (res.authSetting["scope.camera"] == true) {
    
    
                      console.log("openSetting打开设置成功打开了摄像头权限");
                      that.openCamera();
                    } else {
    
    
                      uni.showToast({
    
    
                        title: "未获取到相机权限",
                      });
                    }
                  },
                });
              }
            },
          });
        },
        fail() {
    
    
          console.log("拒绝");
          uni.showToast({
    
    
            icon: "none",
            title: "您已拒绝相机权限,暂无法使用水印相机",
            duration: 4000,
          });
        },
      });
    },
    //获取图片信息
    async getImageInfo({
     
      imgSrc }) {
    
    
      return new Promise((resolve, errs) => {
    
    
        uni.getImageInfo({
    
    
          src: imgSrc,
          success: function (image) {
    
    
            resolve(image);
          },
          fail(err) {
    
    
            errs(err);
          },
        });
      });
    },
    //打开相机
    async openCamera() {
    
    
      var that = this;
      // 一定要使用chooseMedia,如果使用chooseImage那么部分IOS手机就没法正常使用了
      uni.chooseMedia({
    
    
        count: 1,
        mediaType: ["image"],
        sourceType: ["camera"],
        camera: "back",
        success: async (res) => {
    
    
          uni.showLoading({
    
    
            title: "正在加载图片资源",
          });
          that.isLoading = true;
          console.log(res);
          let imgInfo = await that.getImageInfo({
    
    
            imgSrc: res.tempFiles[0].tempFilePath,
          });
          console.log("这一步可以获取到真正的base图片不过还没有转化");
          // console.log(
          //   `data:image/jpg;base64,${uni
          //     .getFileSystemManager()
          //     .readFileSync(res.tempFilePaths[0], "base64")}`
          // );
          console.log("==========imgInfo", imgInfo);
          that.canvasView.canvasImg = imgInfo.path;
          that.canvasView.width = imgInfo.width;
          that.canvasView.height = imgInfo.height;
          console.log(this.canvasView);
          console.log(
            "!!!!!!!!!!当前图片信息canvasView",
            that.canvasView
          );
          that.setImageText();
        },
        fail: function (res) {
    
    
          console.log("选择图片失败", res);
        },
      });
    },
    // 删除当前的图片(测试节点使用的)
    removeCurrentImg(item) {
    
    
      // 等会儿在处理这个问题,这个需要二维为数组中寻找并且删除元素
      var that = this;
      // uni.getFileSystemManager().removeSavedFile({
    
    
      //   filePath: item.path,
      //   success(res) {
    
    
      //     console.log("单个删除成功", res);
      //     that.getSavedFileList();
      //   },
      // });

      this.systemManager.unlink({
    
    
        filePath: this.savePath + item.url,
        success(res) {
    
    
          console.log("单个删除成功", res);
          that.getSavedFileList();
        },
        fail(res) {
    
    
          console.error(res);
        },
      });
    },
    // 删除所有的图片(这种方式不支持,不发对非空文件夹直接删除)
    deleteImg() {
    
    },
    // 预览图片
    // 第一个参数是当前项的图片信息,
    // 第二个参数为当前图片的索引
    // 第三个参数为当前图片所在时间组的索引
    previewImage(item, index2, index1) {
    
    
      console.log("图片预览", item, index2, index1);
      var that = this;
      // 预览图片
      uni.previewImage({
    
    
        current: index2,
        urls: that.imgList[index1].map((item) => item.path),
      });
    },
    // 将获取到的图片按照日期进行格式化
    formatImgList(arr) {
    
    
      // this.imgList = Object.values(
      //   arr.reduce((obj, item) => {
    
    
      //     obj[item.time] = obj[item.time] || [item];
      //     obj[item.time].push(item);
      //     return obj;
      //   }, {})
      // ).filter((item) => item.length > 1);
      this.imgList = arr.reduce((prev, item) => {
    
    
        const index = prev.findIndex((value) => {
    
    
          return value.length > 0 && value[0].time === item.time;
        });

        if (index !== -1) {
    
    
          prev[index].push(item);
        } else {
    
    
          prev.push([item]);
        }

        return prev;
      }, []);
      console.log("formatImgList", this.imgList);
    },
    // 将经纬度转化为度分秒
    convertToDMS(lat, lng) {
    
    
      let latDirection = lat >= 0 ? "N" : "S";
      let lngDirection = lng >= 0 ? "E" : "W";

      lat = Math.abs(lat);
      lng = Math.abs(lng);

      let latDegrees = Math.floor(lat);
      let latMinutes = Math.floor((lat - latDegrees) * 60);
      let latSeconds = ((lat - latDegrees - latMinutes / 60) * 3600).toFixed(4);

      let lngDegrees = Math.floor(lng);
      let lngMinutes = Math.floor((lng - lngDegrees) * 60);
      let lngSeconds = ((lng - lngDegrees - lngMinutes / 60) * 3600).toFixed(4);

      return {
    
    
        latitude: `${
      
      latDirection}:${
      
      latDegrees}°${
      
      latMinutes}'${
      
      latSeconds}"`,
        longitude: `${
      
      lngDirection}:${
      
      lngDegrees}°${
      
      lngMinutes}'${
      
      lngSeconds}"`,
      };
    },
  },
};
</script>

<style lang="scss" scoped>
.pages {
    
    
  overflow: scroll;
}
.imgContainer {
    
    
  .imgDayItem {
    
    
    padding: 30rpx 30rpx 0;
    .imgTime {
    
    
      padding: 20rpx 0 20rpx 0;
      margin-bottom: 20rpx;
      background-color: rgb(249, 249, 249);
    }
    .imgGroup {
    
    
      // border: 2rpx solid red;
      display: flex;
      align-items: center;
      justify-content: flex-start;
      flex-wrap: wrap;

      .itemImg {
    
    
        width: 30%;
        border-radius: 25rpx;
        // border: 1rpx solid red;
        position: relative;
        margin-right: 35rpx;
        margin-bottom: 35rpx;
        &:nth-child(3n) {
    
    
          margin-right: 0rpx;
        }
        image {
    
    
          width: 100%;
          height: 200rpx;
          display: block;
        }
        .imgName {
    
    
          width: 100%;
          text-align: center;
          position: absolute;
          left: 50%;
          transform: translateX(-50%);
          bottom: 0;
        }
        .removeImg {
    
    
          color: white;
          border-radius: 50%;
          background-color: red;
          width: 40rpx;
          height: 40rpx;
          line-height: 40rpx;
          text-align: center;
          position: absolute;
          top: -24rpx;
          right: -20rpx;
        }
      }
    }
  }
}

.cameraBtn {
    
    
  position: fixed;
  right: 40rpx;
  bottom: 50rpx;
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  z-index: 30;
}
#mycanvas {
    
    
  position: fixed;
  left: 9999rpx;
  // opacity: 0;
  // visibility: hidden;
  // opacity: 0;
}
</style>

Guess you like

Origin blog.csdn.net/ksjdbdh/article/details/130669206