uniapp水印相机(水印照片,图片加水印)

在实际开发的项目中,我们有时候会遇到相机拍照上传照片的时候需要带有水印的功能。下面整理了我在自己的项目中做的水印相机(完整源码)功能实战分享给大家。

水印中内容包含如下(实际包含的内容根据你的实际需求而定,这里只是以我的项目需求为例):

  • 具体日期时间,如:2023-05-25 12:00:00 星期四
  • 地理位置,如:江苏省南京市雨花台区软件大道19号
  • 经纬度,如:纬度: 32.08405200000000,经度: 118.76358600000000

------↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓---上源码---↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓------

1. 申请功能(父组件)

<template>
  <view class="container">
    <!-- 背景渐变 -->
    <view class="bg_gradient"></view>
    <!-- 申报信息 -->
    <view class="applyInfo">
      <view class="applyTable">
        <u-form :model="model1" ref="form1">
          <u-form-item label="申请单位(人)" labelWidth="130rpx" borderBottom prop="name">
            <u-input
              clearable
              placeholder="请输入申请单位(人)"
              v-model="model1.name"
            ></u-input>
          </u-form-item>
          <u-form-item label="通讯地址" labelWidth="130rpx" borderBottom prop="address">
            <u-input
              clearable
              placeholder="请输入通讯地址"
              v-model="model1.address"
            ></u-input>
          </u-form-item>
          <u-form-item label="联系电话" labelWidth="130rpx" borderBottom prop="tel">
            <u-input
              clearable
              placeholder="请输入联系电话"
              v-model="model1.tel"
            ></u-input>
          </u-form-item>
          <u-form-item label="调入地点" labelWidth="130rpx" borderBottom prop="addressIn" @click="addressShow = true">
            <u-input disabled disabledColor="#fff" placeholder="请选择省市区" v-model="model1.addressIn"></u-input>
            <!-- 省市区选择器 -->
            <u-picker 
              ref="uPicker"
              :show="addressShow" 
              :columns="addressColumn" 
              @change="addressChange" 
              @confirm="addressConfirm" 
              @cancel="addressShow = false"
            ></u-picker>
          </u-form-item>
          <u-form-item label="收货单位(人)" labelWidth="130rpx" borderBottom prop="nameOut">
            <u-input
              clearable
              placeholder="请输入收货单位(人)"
              v-model="model1.nameOut"
            ></u-input>
          </u-form-item>
          <u-form-item label="收货人" labelWidth="130rpx" borderBottom prop="nameOut2">
            <u-input
              clearable
              placeholder="请输入收货人姓名"
              v-model="model1.nameOut2"
            ></u-input>
          </u-form-item>
          <u-form-item label="身份证号" labelWidth="130rpx" borderBottom prop="idCard">
            <u-input
              clearable
              placeholder="请输入身份证号码"
              v-model="model1.idCard"
            ></u-input>
          </u-form-item>
          <u-form-item label="手机号" labelWidth="130rpx" borderBottom prop="telOut">
            <u-input
              clearable
              placeholder="请输入手机号"
              v-model="model1.telOut"
            ></u-input>
          </u-form-item>
          <u-form-item label="调入时间" labelWidth="130rpx" borderBottom prop="applyTime" @click="datetimeShow=true">
            <u-input disabled disabledColor="#fff" placeholder="请选择时间" v-model="model1.applyTime"></u-input>
            <!-- 时间选择器 -->
            <u-datetime-picker
              ref="datetimePicker"
              mode="datetime"
              :formatter="formatter2"
              :show="datetimeShow"
              v-model="model1.timeIn"
              @confirm="datetimeConfirm"
              @cancel="datetimeShow=false"
            ></u-datetime-picker>
          </u-form-item>
          <u-form-item label="车牌号" labelWidth="130rpx" borderBottom prop="carNo">
            <u-input
              clearable
              placeholder="请输入车牌号"
              v-model="model1.carNo"
            ></u-input>
          </u-form-item>
          <button style="margin-top: 10px;" @click="addShow=true">新增产品</button>
          <view class="addProduct" v-for="it,index in model1.treeInfoVO" :key="index">
            <view class="deleteProduct" @click="delProduct(it)">x</view>
            <view class="p">
              <view>产品名称:</view>
              <view>{{ it.tname }}</view>
            </view>
            <view class="p">
              <view>品名/材种:</view>
              <view>{{ it.kind }}</view>
            </view>
            <view class="p">
              <view>规格:</view>
              <view>{{ it.spec }}</view>
            </view>
            <view class="p">
              <view>数量:</view>
              <view>{{ it.num }}</view>
            </view>
            <view class="p">
              <view>备注:</view>
              <view>{{ it.remark }}</view>
            </view>
          </view>
        </u-form>
      </view>
      <view class="title">
        <view class="up">
          <b>*</b><view>照片说明</view>
        </view>
        <view style="margin: 20rpx 0;color: #444;">
          <text>车头正面照、车头左侧45度照、车头右侧45度照、车尾正面照、车尾左侧45度照、车尾右侧45度照、细节照1、细节照2</text>
        </view>
      </view>
      <view style="margin: 3px 0 10px;"><u-line></u-line></view>
      <view class="photo">
        <view class="up">
          <b>*</b><view>照片列表</view>
        </view>
        <view class="down">
          <watermark @cameraCloseEvent="cameraCloseBtn" @cameraEvent="cameraBtn" @finishEvent="afterReadHandler" @deleteEvent="deletePhoto" :imgUrls="imgUrls" :submitDisabled="submitDisabled"></watermark>
        </view>
      </view>
      <!-- 提交按钮 -->
      <view class="submitBtn">
        <view>
          <u-button type="primary" shape="circle" color="#00c39c" text="提交" :disabled="submitDisabled" @click="submit"></u-button>
        </view>
      </view>
    </view>
    <!-- 新增产品模态框 -->
    <u-modal 
      :show="addShow" 
      title="植物产品"
      showCancelButton 
      @cancel="addCancel"
      @confirm="addConfirm"
    >
    <view style="display: flex;flex-direction: column;">
      <u-form :model="model2" ref="form2">
        <u-form-item label="产品名称" labelWidth="120rpx" borderBottom prop="tname">
          <u-input placeholder="请输入植物产品名称" v-model="model2.tname"></u-input>
        </u-form-item>
        <u-form-item label="品名/材种" labelWidth="130rpx" borderBottom prop="kind">
          <u-input placeholder="请输入品名/材种" v-model="model2.kind"></u-input>
        </u-form-item>
        <u-form-item label="规格类型" labelWidth="120rpx" borderBottom prop="" @click="specShow=true">
          <u-input disabled disabledColor="#fff" placeholder="请选择规格类型" v-model="specVal"></u-input>
          <u-picker 
            title="规格类型"
            :show="specShow" 
            :columns="specColumns"
            @confirm="specSelect"
            @cancel="specShow = false"
          ></u-picker>
        </u-form-item>
        <u-form-item label="规格" labelWidth="120rpx" borderBottom prop="spec">
          <u-input placeholder="请输入规格内容" v-model="model2.spec"></u-input>
        </u-form-item>
        <u-form-item label="数量" labelWidth="120rpx" borderBottom prop="num">
          <u-input placeholder="请输入数量" v-model="model2.num"></u-input>
        </u-form-item>
        <u-form-item label="备注" labelWidth="100rpx" borderBottom prop="remark">
          <u-textarea count maxlength="80" height="75" placeholder="请输入备注内容" v-model="model2.remark"></u-textarea>
        </u-form-item>
      </u-form>
    </view>
    </u-modal>
    <!-- 提示信息 -->
    <u-toast ref="uToast"></u-toast>
  </view>
</template>

<script>
import {sendApplication,getAddress,getInformation,getImgsByIds} from '../../../api/applicant/index.js'
import watermark from './watermark.vue'
export default {
  components: {watermark},
  data() {
    return {
      model1: {
        name: '',      // 申请单位(人)
        address: '',   // 通讯地址
        tel: '',       // 联系电话
        addressIn: '', // 调入地点
        nameOut: '',   // 收货单位(人)
        nameOut2: '',  // 收货人
        idCard: '',    // 身份证号
        telOut: '',    // 手机号
        timeIn: '',    // 调入时间
        carNo: '',     // 车牌号
        treeInfoVO: [],// 新增产品内容 
        title: '',     // 标题内容
        rid: '',       // 上传照片
        applyTime: '', // 时间选择器选择时间后页面展示的调入时间
      },
      rules1: {
        'name': {
          whitespace: true,
          required: true,
          message: '请输入申请单位(人)',
          trigger: ['blur', 'change']
        },
        'address': {
          whitespace: true,
          required: true,
          message: '请输入通讯地址',
          trigger: ['blur', 'change']
        },
        'tel': [
          {
            required: true,
            message: '请输入联系电话',
            trigger: ['blur', 'change']
				  },
          {
            pattern: /(^\d{3}-\d{8}$|^\d{4}-\d{7}$)|(^1[3-9]\d{9}$)/,
            message: `电话号码规则错误,正确格式:
            XXXX-XXXXXXX、XXX-XXXXXXXX
            或者11位手机号码`,
            trigger: ['blur']
          }
				],
        'addressIn': {
          required: true,
          message: '请选择省市区',
          trigger: ['blur', 'change']
        },
        'nameOut': {
          whitespace: true,
          required: true,
          message: '请输入收货单位(人)',
          trigger: ['blur', 'change']
        },
        'nameOut2': {
          whitespace: true,
          required: true,
          message: '请输入收货人',
          trigger: ['blur', 'change']
        },
        'idCard': [
          {
            required: true,
            message: '请输入身份证号',
            trigger: ['blur', 'change']
				  },
          {
            pattern: /^[1-9]\d{5}(19\d{2}|20[0-2][0-9])(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[0-9Xx]$/,
            message: `请输入正确的身份证号码`,
            trigger: ['blur']
          }
				],
        'telOut': [
          {
            required: true,
            message: '请输入手机号',
            trigger: ['blur', 'change']
				  },
          {
            pattern: /^1[3-9]\d{9}$/,
            message: "请输入合法的手机号码",
            trigger: ['blur']
          }
				],
        'carNo': [
          {
            pattern: /^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[a-zA-Z](([DF]((?![IO])[a-zA-Z0-9](?![IO]))[0-9]{4})|([0-9]{5}[DF]))|[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1})$/,
            message: "请输入合法的车牌号码",
            trigger: ['blur']
          }
				],
        'applyTime': {
          required: true,
          message: '请选择时间',
          trigger: ['blur', 'change']
        },
      },
      model2: {
        tname: '', // 植物产品名称
        kind: '',  // 品名/材种
        spec: '',  // 规格
        num: '',   // 数量
        remark: '',// 备注
      },
      rules2: {
        'tname': {
          whitespace: true,
          required: true,
          message: '请输入植物产品名称',
          trigger: ['blur', 'change']
        },
        'kind': {
          whitespace: true,
          required: true,
          message: '请输入品名/材种',
          trigger: ['blur', 'change']
        },
        'spec': [
          {
            whitespace: true,
            required: true,
            message: '请输入规格内容',
            trigger: ['blur', 'change']
          }
        ],
        'num': [
          {
            required: true,
            message: '请输入数量',
            trigger: ['blur', 'change']
          },
          {
            pattern: /^[0-9]+$/,
            message: "数量必须是非负整数",
            trigger: ['blur']
          }
        ]
      },
      datetimeShow: false,  // 是否显示 时间选择器
      addressShow: false,   // 是否显示 省市区选择器
      addressColumn: [['请选择'],['请选择'],['请选择']],// 省市区
      province: [],         // 省
      city: [],             // 市
      area: [],             // 区
      addShow: false,       // 是否显示 新增产品模态框
      specShow: false,      // 是否显示 规格类型选择器
      specColumns: [['胸径','地径','冠幅','其他']],
      specVal: '胸径',      // 规格类型
      imgsRelation: [],     // 存放(水印照片图片路径与rid对应关系)数组
      submitDisabled: false,// 提交按钮是否禁用
      imgUrls: [],          // 照片url
      imgIds: [],           // 照片id
      isSubmit: false,      // 是否执行了(提交按钮)
    }
  },
  methods: {
    /* 通过传进来的rid获取照片 */
    getImgs() {
      getImgsByIds({ids: this.imgIds}).then(res=>{
        if(res.code==200) {
          this.imgUrls = [];
          let resData = res.data;
          for(let i=0;i<resData.length;i++) {
            this.imgUrls.push(resData[i].url);
            for(let ii=0;ii<this.imgIds.length;ii++) {
              if(i==ii) {
                let relation = {[resData[i].url]: this.imgIds[ii]}; //水印照片图片路径与rid对应关系
                this.imgsRelation.push(relation);
              }
            }
          }
        }
      })
    },
    /* 时间选择器确认 */
    datetimeConfirm(val) {
      this.model1.timeIn = val.value;
      let d = new Date(this.model1.timeIn*1);
      this.model1.applyTime = d.getFullYear() +'年'+ (d.getMonth() + 1) +'月'+ d.getDate() +'日'+ d.getHours() +'时'+ d.getMinutes() +'分';
      this.$refs.form1.validateField('applyTime');
      this.datetimeShow = false;
    },
    /* 时间格式化 */
    formatter2(type, value) {
      if (type === 'year') {return `${value}年`};
      if (type === 'month') {return `${value}月`};
      if (type === 'day') {return `${value}日`};
      if (type === 'hour') {return `${value}时`};
      if (type === 'minute') {return `${value}分`};
      return value;
    },
    /* 省市区选择器确认 */
    addressConfirm(e) {
      if(e.value[0]=='请选择') {
        this.addressShow = false;
      } else {
        if(e.value[1] == '请选择' || e.value[2] == '请选择') {
          this.$refs.uToast.show({
            type: 'error',
            message: '省市区不完整',
            duration: 2500,
          })
        } else {
          this.addressShow = false;
          this.model1.addressIn = e.value[0] + e.value[1] + e.value[2];
          this.$refs.form1.validateField('addressIn');
        }
      }
    },
    /* 省市区选择器改变 */
    async addressChange(e) {
      // 当第一列(省)值发生变化时
      if (e.columnIndex === 0) {
        this.addressColumn[1].splice(1);
        this.addressColumn[2].splice(1);
        if(e.index==0) return;
        this.$refs.uPicker.setColumnValues(1, this.addressColumn[1][e.index])
        // 选中的省份数据
        let selectProvince = this.province.find(item=>{
          return item.address == e.value[0];
        })
        // 获取城市列表
        await getAddress({addressType: 2,code: selectProvince.code}).then(res=>{
          if(res.code==200) {
            this.city = res.data;
            res.data.forEach(item=>{
              this.addressColumn[1].push(item.address);
            })
          }
        })
      }
      // 当第二列(市)值发生变化时
      if (e.columnIndex === 1) {
        this.addressColumn[2].splice(1);
        if(e.index==0) return;
        this.$refs.uPicker.setColumnValues(2, this.addressColumn[2][e.index])
        // 选中的城市数据
        let selectCity = this.city.find(item=>{
          return item.address == e.value[1];
        })
        // 获取区域列表
        await getAddress({addressType: 3,code: selectCity.code}).then(res=>{
          if(res.code==200) {
            this.area = res.data;
            res.data.forEach(item=>{
              this.addressColumn[2].push(item.address);
            })
          }
        })
      }
    },
    /* 关闭水印相机触发 */
    cameraCloseBtn() {
      this.submitDisabled = false;
    },
    /* 点击水印相机触发 */
    cameraBtn() {
      uni.setStorageSync('cacheApplyInfo',JSON.stringify(this.model1));
      this.submitDisabled = true;
    },
    /* 照片完成后的处理函数 */
    afterReadHandler(e) {
      uni.uploadFile({
        url: `${this.$upload}`,
        filePath: e,
        name: 'file',
        header: {
          Authorization: uni.getStorageSync('token'),
          'Content-Type': 'multipart/form-data',
        },
        success: res => {
          if(res.statusCode==200) {
            if(this.model1.rid=='') {
              this.model1.rid += JSON.parse(res.data).data.id;
            } else {
              this.model1.rid += ',' + JSON.parse(res.data).data.id;
            }
            let relation = {[e]: JSON.parse(res.data).data.id}; //水印照片图片路径与rid对应关系
            this.imgsRelation.push(relation);
            this.submitDisabled = false;
          }
        }
      })
    },
    /* 删除照片 */
    deletePhoto(srcVal) {
      let tempArr = this.model1.rid.split(',');
      let tempRid = '';
      this.imgsRelation.forEach(outIt=>{
        for(let inIt in outIt) {
          if(inIt == srcVal) {
            tempRid = outIt[inIt];
          }
        }
      })
      tempArr = tempArr.filter(item=>{
        if(item!=tempRid) return item;
      })
      this.model1.rid = tempArr.join(',');
    },
    /* 规格类型下拉选择触发 */
    specSelect(val) {
      this.specShow = false;
      this.specVal = val.value[0];
    },
    /* 新增产品取消 */
    addCancel() {
      this.addShow = false;
      this.model2.tname = "";
      this.model2.kind = "";
      this.model2.spec = "";
      this.model2.num = "";
      this.model2.remark = "";
      this.$refs.form2.clearValidate();
    },
    /* 新增产品确定 */
    addConfirm() {
      this.$refs.form2.validate().then(() => {
        this.model2.spec = this.specVal +":"+ this.model2.spec;
        this.model1.treeInfoVO.push({...this.model2});
        this.addCancel();
      }).catch(()=>{});
    },
    /* 删除产品 */
    delProduct(itVal) {
      this.model1.treeInfoVO = this.model1.treeInfoVO.filter(item=>{
        if(itVal!=item) return item;
      })
    },
    /* 提交 */
    submit() {
      this.$refs.form1.validate().then(() => {
        if(this.model1.treeInfoVO.length==0) {
          this.$refs.uToast.show({type: 'error',message: '请新增产品'});
        } else if(this.model1.rid.split(',').length<2) {
          this.$refs.uToast.show({type: 'error',message: '照片不得少于2张'});
        } else {
          this.submitDisabled = true;
          this.model1.title = '';
          this.model1.treeInfoVO.forEach(it=>{//每次的新增品种都给title一份,用来搜索查询
            if(this.model1.title=='') {
              this.model1.title += it.kind;
            } else {
              this.model1.title += ',' + it.kind;
            }
          })
          sendApplication(this.model1).then(res=>{
            if(res.code==200) {
              this.isSubmit = true;
              this.$refs.uToast.show({
                type: 'success',
                message: '提交成功',
                duration: 1000,
                complete() {
                  uni.removeStorageSync('cacheApplyInfo');
                  uni.setStorageSync('submitEvent',1);
                  uni.navigateBack({delta: 1});
                }
              });
            }
          })
        }
      }).catch(()=>{});
    }
  },
  beforeDestroy() {
    if(!this.isSubmit) {
      uni.setStorageSync('cacheApplyInfo',JSON.stringify(this.model1));
    }
  },
  onShow() {
    /* 获取省份列表 */
    getAddress({addressType: 1}).then(res=>{
      if(res.code==200) {
        this.province = res.data;
        res.data.forEach(item=>{
          this.addressColumn[0].push(item.address);
        })
      }
    })
    this.model1.timeIn = new Date().getTime();
    /* 获取申请人备案信息 */
    if(uni.getStorageSync('cacheApplyInfo')=="") {
      getInformation().then(res=>{
        if(res.code==200) {
          let data = res.data;
          if(data.perType==1) {
            this.model1.name = data.name;
            this.model1.address = data.addr;
            this.model1.tel = data.tel;
          } else if(data.perType==2) {
            this.model1.name = data.businessName;
            this.model1.address = data.addr;
            this.model1.tel = data.tel;
          }
        }
      })
    } else {
      this.model1 = JSON.parse(uni.getStorageSync('cacheApplyInfo'));
      let d = new Date(this.model1.timeIn*1);
      this.model1.timeIn = this.model1.timeIn*1;
      this.model1.applyTime = d.getFullYear() +'年'+ (d.getMonth() + 1) +'月'+ d.getDate() +'日'+ d.getHours() +'时'+ d.getMinutes() +'分';
      this.imgIds = this.model1.rid.split(',');
      this.getImgs();
    }
  },
  onReady() {
    this.$refs.form1.setRules(this.rules1);// 如果需要兼容微信小程序,并且校验规则中含有方法等,只能通过setRules方法设置规则。
    this.$refs.form2.setRules(this.rules2);// 如果需要兼容微信小程序,并且校验规则中含有方法等,只能通过setRules方法设置规则。
	this.$refs.datetimePicker.setFormatter(this.formatter2);// 微信小程序需要用此写法(时间格式化)
  }
}
</script>

<style lang="scss" scoped>
.container {
  /deep/.u-input {
    padding-left: 0 !important;
  }
  /deep/.u-form-item__body__left__content__label,
  /deep/.u-input__content__field-wrapper__field,
  /deep/.u-textarea__field {
    font-size: 13px !important;
  }
  .bg_gradient {
    width: 100%;
    height: 100px;
    background-image: linear-gradient(to bottom,#00c39c,#F9F9F9);
    position: fixed;
    top: 0;
    z-index: -1;
  }
  .applyInfo {
    background-color: #fff;
    border-radius: 8px;
    margin: 16px 16px 0;
    padding: 12px;
    .applyTable {
      .addProduct {
        border: 1px solid #EAEBEC;margin: 5px 0; padding: 3px; color: #303133;
        position: relative;
        .deleteProduct {
          width: 20px;
          height: 20px;
          line-height: 15px;
          text-align: center;
          font-size: 15px;
          background-color: #373737;
          color: #fff;
          border-bottom-left-radius: 20px;
          z-index: 3;
          position: absolute;
          right: 0;
          top: 0;
        }
        .p {
          margin: 0 0 3px 0;
          display: flex;
          view:nth-child(1) {
            width: 70px;
          }
          view:nth-child(2) {
            margin: 0 15px 0 3px;
            width: calc(100% - 80px);
            word-wrap: break-word;
          }
        }
      }
    }
    .title {
      margin: 10px 0 0 0;
    }
    .title,.photo {
      .up {
        font-size: 13px;
        color: #666666;
        display: flex;
        b {
          margin-right: 6px;
          color: #f10000;
        }
      }
      .down {
        margin-top: 10px;
      }
    }
    .submitBtn {
      margin: 20px 0 0 0;
    }
  }
}
</style>

2. 水印相机(子组件)

<template>
  <view class="container">
    <button @click="openCamera" :disabled="submitDisabled">水印相机</button>
    <view class="myImgs">
      <view class="screenImgs" v-for="(it,index) in imgSrcArr" :key="index">
        <view class="deleteImg" @click="delImg(it)">x</view>
        <u-album :urls="[it]"></u-album>
      </view>
    </view>
    <canvas 
      canvas-id="myCanvasId"
      :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }" 
      v-show="canvasShow"
    ></canvas>
  </view>
</template>
<script>
export default {
  props: {
    imgUrls: {
      type: Array,
      default() {
        return [];
      }
    },
    submitDisabled: {
      type: Boolean,
      default: false,
    }
  },
  data() {
    return {
      context: null,     // canvas上下文
      canvasWidth: 0,    // canvas容器宽度
      canvasHeight: 0,   // canvas容器高度
      canvasShow: true,  // canvas容器是否存在
      imgSrcArr: [],     // 本地展示的图片路径(canvas容器图片)
      addressInfo: '',   // 位置
      longitude: '',     // 经度 
      latitude: '',      // 纬度
      imageInfo: '',     // 拍照得到的照片信息
    }
  },
  methods: {
    /* 删除照片 */
    delImg(srcVal) {
      this.$emit('deleteEvent',srcVal);
      this.imgSrcArr = this.imgSrcArr.filter(item=>{
        if(srcVal!=item) return item;
      })
    },
    /* 点击水印相机按钮 */
    async openCamera() {
      this.$emit('cameraEvent');
      if(this.imgSrcArr.length>7) {
        uni.showToast({icon: 'none',duration: 2500,title: `最多上传【8】张`});
        this.$emit('cameraCloseEvent');
        return;
      }
      try {
        const res = await uni.chooseImage({
          count: 1,
          sizeType: ['compressed'],
          sourceType: ['camera'],
        })
        if(res[1]===undefined) {
          this.$emit('cameraCloseEvent');
        }
        this.imageInfo = await uni.getImageInfo({
          src: res[1].tempFilePaths[0],
        })
        const { width, height } = this.imageInfo[1];
        // 计算canvas宽高,照片横拍与竖拍的比例自适应规则
        const ratio = width / height;
        const maxWidth = uni.upx2px(960); // 最大宽度为960rpx
        const maxHeight = uni.upx2px(720);// 最大高度为720rpx
        /**
         * 规则:1.照片宽度大于最大宽度,以最大宽度为准
         * 规则:2.照片高度大于最大高度,以最大高度为准
         * 规则:3.照片高度或照片宽度任意一个超出最值范围,以最大高度和宽度为准
         */
        if (width > maxWidth) {
          this.canvasWidth = maxWidth;
          this.canvasHeight = this.canvasWidth / ratio;
        } else if (height > maxHeight) {
          this.canvasHeight = maxHeight;
          this.canvasWidth = this.canvasHeight * ratio;
        } else {
          this.canvasWidth = width;
          this.canvasHeight = height;
        }
        this.locationInfo(); // 获取定位信息
      } catch (err) {}
    },
    /* 当前定位信息 */
    locationInfo() {
      uni.getLocation({
        type: 'gcj02',
        success: res => {
          this.latitude = res.latitude;
          this.longitude = res.longitude;
          this.placeInfo(); // 获取位置信息
        },
        fail: () => {
          uni.showToast({ icon: 'none', duration: 2500, title: `获取位置信息失败` });
        },
      })
    },
    /* 当前位置信息 */
    placeInfo() {
      let getAddressUrl = `https://apis.map.qq.com/ws/geocoder/v1/?location=${this.latitude},${this.longitude}&key=你的key值`;
      uni.$u.http.get(getAddressUrl).then(res=>{
        this.addressInfo = res.result.address;
        this.createCanvas(); // 创建canvas容器
      })
    },
    /* canvas容器 */
    createCanvas() {
      this.context = uni.createCanvasContext('myCanvasId',this);
      this.context.drawImage(this.imageInfo[1].path, 0, 0, this.canvasWidth, this.canvasHeight);
      this.context.setFontSize(16);
      this.context.setFillStyle('#ffffff');
      this.context.fillText(this.currentTime(), 10, this.canvasHeight - 30);
      this.context.fillText(`${this.addressInfo}`, 10, this.canvasHeight - 60);
      this.context.fillText(`经度: ${this.longitude}`, 10, this.canvasHeight - 90);
      this.context.fillText(`纬度: ${this.latitude}`, 10, this.canvasHeight - 120);
      this.canvasShow = false;
      this.watermarkPhoto(); // 获取水印照片
    },
    /* 水印照片 */
    watermarkPhoto() {
      let that = this;
      this.context.draw(false, () => {
        // 将canvas转成图片路径
        uni.canvasToTempFilePath({
          canvasId: 'myCanvasId',
          success: function(res) {
            // 将图片路径赋值给image标签的src属性
            that.imgSrcArr.push(res.tempFilePath);
            that.canvasShow = true;
            that.canvasWidth = 0;
            that.canvasHeight = 0;
            that.$emit('finishEvent',res.tempFilePath);
          }
        },this);
      });
    },
    /* 当前日期时间 */
    currentTime() {
      let d = new Date();
      // 年
      let year = d.getFullYear();
      // 月
      let month = d.getMonth() + 1;
      month = month > 9 ? month : '0'+month;
      // 日
      let date = d.getDate();
      date = date > 9 ? date : '0'+date;
      // 时
      let hours = d.getHours();
      hours = hours > 9 ? hours : '0'+hours;
      // 分
      let minutes = d.getMinutes();
      minutes = minutes > 9 ? minutes : '0'+minutes;
      // 秒
      let seconds = d.getSeconds();
      seconds = seconds > 9 ? seconds : '0'+seconds;
      // 星期
      let weekdayArr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
      let weekday = weekdayArr[d.getDay()];
      return year+"-"+month+"-"+date+" "+hours+":"+minutes+":"+seconds+" "+weekday;
    },
  },
  watch: {
    imgUrls: {
      deep: true,
      handler() {
        this.imgSrcArr = [...this.imgUrls];
      }
    }
  }
}
</script>

<style lang="scss" scoped>
/deep/[role=img] {
  width: 80px !important;
  height: 80px !important;
}
.container {
  .myImgs {
    margin: 10px 0 0 0;
    display: flex;
    flex-wrap: wrap;
    .screenImgs {
      margin: 0 4px 4px 0;
      position: relative;
      .deleteImg {
        width: 20px;
        height: 20px;
        line-height: 15px;
        text-align: center;
        font-size: 15px;
        background-color: #373737;
        color: #fff;
        border-bottom-left-radius: 20px;
        z-index: 3;
        position: absolute;
        right: 0;
        top: 0;
      }
    }
  }
}
</style>

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【资源说明】 基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar 基于JavaScript的智能水印相机微信小程序源码+项目说明.tar 基于JavaScript的智能水印相机微信小程序源码+项目说明.tar基于JavaScript的智能水印相机微信小程序源码+项目说明.tar 基于JavaScript的智能水印相机微信小程序源码+项目说明.tar 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,也适用于小白学习入门进阶。当然也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或者热爱钻研,亦可在此项目代码基础上进行修改添,实现其他不同功能。 欢迎下载,沟通交流,互相学习,共同进步!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值