实现一个小程序电子名片功能

1693636892404.jpg

微信截图_20230902144254.png

思路:
1:创建一个canvas对象
2:引入名片背景图
3:定位各个数据位置
4:保存生成的名片图片临时地址
5:创建一个新的canvas对象用来生成分享图
6:创建纯色背景,引入生成的名片临时图片地址
7:导出分享图

<view class="p20">
  <view class="canvas-box">
    //电子名片
    <canvas type="2d" style="width:695rpx;height:370rpx;" id="myCanvas"></canvas>
  </view>
  <!-- <image src="{{shareImage}}"></image> -->
  <view class="flex toolsBtnBox j-center a-items-c ">
    <!-- <view class="toolsBtn" >
      <button data-name="shareBtn" size="mini" open-type="share">
        <view class="icon">
          <image src="../../image/share.png"></image>
        </view>
      </button>
      <button style="font-size: 24rpx;color: #303030;">发名片</button>
    </view> -->
    <view class="toolsBtn"  catchtap="phoneClick">
      <view class="icon">
        <image src="../../image/phone.png"></image>
      </view>
      <button>打电话</button>
    </view>
    <view class="toolsBtn flex f-column " catchtap="copyBtn" data-type="email" data-str="{{employeeInfo.email}}">
      <view class="icon">
        <image  src="../../image/email.png"></image>
      </view>
      <button>发邮件</button>
    </view>
    <view class="toolsBtn flex f-column " catchtap="copyBtn" data-type="wxNo" data-str="{{employeeInfo.wxNo}}">
      <view class="icon">
        <image  src="../../image/email.png"></image>
      </view>
      <button>加微信</button>
    </view>
    <view class="toolsBtn flex f-column " catchtap="showEmployeeCard">
      <view class="icon">
        <image  src="../../image/mingpian.png"></image>
      </view>
      <button>纸质名片</button>
    </view>
    <view class="toolsBtn flex f-column " catchtap="visitHandle">
      <view class="icon">
        <image  src="../../image/loc.png"></image>
      </view>
      <button>去拜访</button>
    </view>
  </view>
  <view class="basicInformation introBox">
      <view class="fw mb20 title">公司简介</view>
  </view>
  <view class="bottomBox flex a-items-c j-center">
     <view class="submit flex a-items-c j-around" >
      <button class="mr20" data-name="shareBtn" size="mini" open-type="share">
        分享名片
      </button>
      <button  bindtap="editEmployeeCard">
        编辑名片
      </button>
    </view>
  </view>
//创建分享用的图片
  <canvas class="shareImageCanvas" type="2d" id="myCanvas1"></canvas>
</view>

// pages/employeeCard/employeeCard.js
const util = require('../../utils/util.js');
const api = require('../../config/api.js');
const app = getApp();
var canvas=null;
var canvas1=null;
Page({

  /**
   * 页面的初始数据
   */
  data: {
    workCode:'',
    employeeInfo:{},
    markers:[],
    windowW: 0,
    windowH: 0,
    scale: 1,
    isToggle:false,
    current:0,
    cardNum:2,
    addressInfo:'',
    employeeImage:'',
    shareImage:'',
    isShowCard:true,
    videoCtx:null,
    pauseFlag:true,
    storageMobile:''
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {

    this.setData({
      storageMobile
    })

    if(options){
      // console.log('options',options);
      let {workcode}=options
      this.setData({
        workCode:workcode
      })
    }else{
      this.setData({
        workCode:stoWorkCode
      })
    }
    this.getSystemInfo();
    this.getEmployeeCardInfo()
  },

  // 获取名片信息
  getEmployeeCardInfo(){
    let that=this
    util.request(api.employeeCardGetInfo, 
      {workCode:this.data.workCode},'POST').then(function (res) {
        console.log('getEmployeeCardInfo',res.data);
        if (res.code == '1') {
        that.setData({
          employeeInfo:res.data
        })
        wx.setNavigationBarTitle({
          title:`${res.data.name}的电子名片`,
          success: (result)=>{
          },
          fail: ()=>{},
          complete: ()=>{}
        });
        that.createdEmployeeCard().then(imageUrl=>{
          setTimeout(() => {
            //因生成分享图需依赖上面刚生成的电子名片,因为机型性能不同生成速度不同,为确保生成分享图时有数据,所以在异步后再加上一个定时器
            that.createdShareImage(imageUrl)
          }, 1500);
        })
      } 
    })
  },
  getSystemInfo () {
    var that = this;
    wx.getSystemInfo({
      success: function (res) {
        that.setData({
          windowW: res.windowWidth,
          windowH: res.windowHeight
        })
      },
    })
  },
  //主要代码------ 生成名片
  createdEmployeeCard() {
    return new Promise((resolve, reject) => {
      let that = this
      let {employeeInfo:{name,companyName,job,phone,email}}=that.data
      let canvasH = that.data.windowH
      let canvasW  = that.data.windowW
      wx.createSelectorQuery()
      .select('#myCanvas') 
      .fields({ node: true, size: true })
      .exec((res) => {
          // Canvas 对象
          canvas = res[0].node
          const context = canvas.getContext('2d')
          // Canvas 画布的实际绘制宽高
          const height = res[0].height 
          const width = res[0].width
          console.log('height---',height);
          console.log('width---',width);
            // 计算像素比 初始化画布大小
          const dpr = wx.getWindowInfo().pixelRatio
          canvas.width = width * dpr
          canvas.height = height * dpr
          context.scale(dpr, dpr)
          // 开始绘制
          context.clearRect(0, 0, canvas.width, canvas.height)
          // 若干绘制调用
          context.fillStyle = 'rgb(256, 256, 256)';
          context.fillRect(0, 0, canvasW, canvasH)
          const bgImage = canvas.createImage()
          // const logoImage = canvas.createImage()
          bgImage.src = '/image/mingpian_bg.png'
          bgImage.onload = () => {
            context.drawImage(
              bgImage,
              0,
              0,
              360,
              190,
            )
            context.textAlign ='left'
            context.fillStyle = "#000";
            context.font = 'normal bold 28px 微软雅黑' // 设置字体大小
            // context.textBaseline = 'top'
            context.fillText(name, 30, 40)
            context.font = 'normal 500 12px 微软雅黑' // 设置字体大小
            context.fillStyle = "#000";
            context.fillText('某有限公司', 30,65)
            context.fillText(job, 30, 85)
            context.fillText(phone, 50, 131)
            context.fillText(email, 50, 153)
            wx.canvasToTempFilePath({
              canvas: canvas,
              success(res) {
                  that.setData({
                  //临时路径保存
                    employeeImage:res.tempFilePath
                  }) 
                  resolve(res.tempFilePath)
                }
            })
          }
        })
    })
  },
  // saveEmployeeCard(){
  //   let {employeeImage}=this.data
  //   wx.saveImageToPhotosAlbum({
  //     filePath:employeeImage,
  //     success(res) {
  //       wx.showToast({
  //         title: '保存成功',
  //         icon: 'none',
  //         image: '',
  //         duration: 1500,
  //         mask: false,
  //         success: (result)=>{
  //         },
  //         fail: ()=>{},
  //         complete: ()=>{}
  //       });
  //     }
  //   })
  // },
   // 生成分享图 
  createdShareImage(imageUrl) {
    let that = this
    let canvasH = that.data.windowH
    let canvasW  = that.data.windowW
    wx.createSelectorQuery()
    .select('#myCanvas1') 
    .fields({ node: true, size: true })
    .exec((res) => {
        // Canvas 对象
        canvas1 = res[0].node
        const employeeImage = canvas1.createImage()
        const iconImage = canvas1.createImage()
        const context = canvas1.getContext('2d')
        // Canvas 画布的实际绘制宽高
        const width = res[0].width
        const height = res[0].height
          // 初始化画布大小 计算像素比
        const dpr = wx.getWindowInfo().pixelRatio 
        canvas1.width = width * dpr
        canvas1.height = height * dpr
        context.scale(dpr, dpr)
    
        context.clearRect(0, 0, canvas1.width, canvas1.height)
        const grd = context.createLinearGradient(0, 0,500, 0, canvasH)
        grd.addColorStop(0, '#443319')
        grd.addColorStop(0.7, '#70552C')
        grd.addColorStop(1, '#C89A53')
        context.fillStyle = grd;
        context.fillRect(0, 0, canvasW, canvasH)
        employeeImage.src = imageUrl
        iconImage.src = '/image/open.png'
        iconImage.onload = () => {
          context.drawImage(iconImage,230,30,30,30)
          context.textAlign ='left'
          context.fillStyle = "#fff";
          context.font = 'normal bold 32px 微软雅黑' // 设置字体大小
          context.textBaseline = 'top'
          context.fillText('点击查看名片', 15, 30)
        }
        employeeImage.onload = () => {
          context.drawImage(
            employeeImage,
            5,
            90,
            290,
            140,
          )
          that.saveShareImage()
        }
    })
  },
  // 保存
  saveShareImage () {
    let that = this;
    wx.canvasToTempFilePath({
      canvas: canvas1,
      success(res) {
          console.log('分享的图片',res.tempFilePath);
          that.setData({
            shareImage:res.tempFilePath
          }) 
          // shareImage=res.tempFilePath
          // wx.saveImageToPhotosAlbum({
          //   filePath:res.tempFilePath,
          //   success(res) {}
          // })
        }
    })
  },
  phoneClick(e){
    wx.makePhoneCall({
      phoneNumber: this.data.employeeInfo.phone,
      success(){},
      fail(){}
    })
  },
  copyBtn(e){
    console.log('e',e);
    let type=e.currentTarget.dataset.type
    let str=e.currentTarget.dataset.str
    wx.showModal({
      title:type=='wxNo'?'微信号':'电子邮箱',
      content: str,
      showCancel: true,
      cancelText: '取消',
      cancelColor: '#ccc',
      confirmText: '复制',
      confirmColor: '#00a5dd',
      success: (result) => {
        if(result.confirm){
          wx.setClipboardData({
            data: str,
            success: function (res) {
              wx.showToast({
                title:(type=='wxNo'?'微信号':'电子邮箱')+'已复制',
                icon:"none",
                mask:"true"
            })
          }})
        }
      },
      fail: ()=>{},
      complete: ()=>{}
    });
   
  },
  showEmployeeCard(){
    wx.previewImage({
      current: this.data.employeeImage, // 图片的地址url
      urls: [this.data.employeeImage] // 预览的地址url
    })
  },
  submitHandle(){
    wx.showToast({
      title: '保存成功',
      icon: 'none',
      image: '',
      duration: 1500,
      mask: false,
      success: (result)=>{
        
      },
      fail: ()=>{},
      complete: ()=>{}
    });
  },
  backHandle(){
   wx.navigateBack({
    delta: 1
   });
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {

  },



  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },
  // 拜访
  visitHandle(e){
    let {addressLat,addressLng}=this.data.employeeInfo
    wx.openLocation({
      latitude:+addressLat,
      longitude:+addressLng,
      scale: 17
    })
  },
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage(options) {
    let that = this;
    let {shareImage,employeeInfo,storageMobile}= that.data
    // 设置菜单中的转发按钮触发转发事件时的转发内容
    let shareObj = {
      // title:storageMobile==employeeInfo.phone?"您好,这是我的名片":`这是${employeeInfo.name}的电子名片` ,    // 默认是小程序的名称(可以写slogan等)
      title:`这是${employeeInfo.name}的电子名片` ,    // 默认是小程序的名称(可以写slogan等)
      path: `/pages/employeeCardPreview/employeeCardPreview?workcode=${that.data.workCode}`,    // 默认是当前页面,必须是以‘/'开头的完整路径
      imageUrl: shareImage,   //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径,支持PNG及JPG,不传入 imageUrl 则使用默认截图。显示图片长宽比是 5:4
      success(res){
        if(res.errMsg == 'shareAppMessage:ok'){}
      },
      fail(){
        if(res.errMsg == 'shareAppMessage:fail cancel'){
        }else if(res.errMsg == 'shareAppMessage:fail'){}
      },
      complete(){
      }
    }
    // 来自页面内的按钮的转发
    if( options.from == 'button' ){
      var eData = options.target.dataset;
      console.log('---------------', eData.name ); 
      // 此处可以修改 shareObj 中的内容
      // shareObj.path = '/pages/btnname/btnname?btn_name='+eData.name;
    }
    return shareObj;
  },

})
/* pages/employeeCard/employeeCard.wxss */
page {
  background-color: #f2f2f2;
  padding-bottom: 150rpx;
}

.p20 {
  padding: 20rpx;
}

.employeeCardClass {
  background: linear-gradient(90deg, rgba(255, 109, 85, 1) 0%, rgba(255, 31, 31, 1) 100%);
  margin: 20rpx;
  height: 300rpx;
  padding: 20rpx;
}

.f32-ff {
  font-size: 32rpx;
  color: #fff;
}

.f32-5d {
  font-size: 32rpx;
  color: #5d5d5d;
}

.fw {
  font-weight: 700;
}

.red {
  color: red;
  margin-left: 10rpx;
  font-size: 42rpx;
  position: absolute;
  top: 5rpx;
}

.pr {
  position: relative;
}

.title {
  font-size: 32rpx;
  padding-bottom: 20rpx;
  border-bottom: 1px solid #f2f2f2;
}

.ml20 {
  margin-left: 20rpx;
}

.mb10 {
  margin-bottom: 10rpx;
}

.mb20 {
  margin-bottom: 20rpx;
}

.mr100 {
  margin-right: 100rpx;
}

.flex {
  display: flex;
}

.basicInformation {
  padding: 20rpx;
  margin-top: 10rpx;
  background-color: #fff;
  border-radius: 10rpx;
  margin-bottom: 10rpx;
}



.nonInsiderClass {
  position: fixed;
  background-color: #fff;
  z-index: 99999;
  height: 100vh;
  width: 100vw;
  line-height: 100vh;
  top: 0rpx;
  left: 0rpx;
  margin: 0 auto;
  text-align: center;
  font-size: 36rpx;
  font-weight: 700;
}

.formInput {
  width: 450rpx;
  padding: 0 20rpx;
}

.f-column {
  flex-direction: column;
}

.a-items-c {
  align-items: center;
}

.j-around {
  justify-content: around;
}

.j-center {
  justify-content: center;
}

.loginInput {
  padding: 20rpx;
}

.loginInput_item {
  height: 100rpx;
  width: 80%;
  margin-bottom: 20rpx;
  background-color: #fff;
}

.loginInput_item text {
  font-size: 28rpx;
  color: #303030;
  width: 150rpx;
}

.loginInput_item input {
  font-size: 28rpx;
  width: 200rpx;
  padding: 20rpx 0;
  padding-left: 20rpx;
  border-bottom: 1rpx solid #F5f5f5;
}

.canvas-box {
  margin-left: 10rpx;
  height: 370rpx;
}

.loginButton {
  height: 80rpx;
}

.login-button-custom {
  margin-top: 30rpx;
  width: 340rpx;
  height: 70rpx;
  font-size: 32rpx;
  background-color: #00a5dd !important;
  border-radius: 10rpx !important;
  color: #fff !important;
}

.shareImageCanvas {
  margin-left: 20px;
  width: 300px;
  height: 240px;
  position: fixed;
  top: -9999rpx;
}

.formInput_d {
  /* background-color: #f5f5f5; */
  color: #999;
}

.toolsBtnBox {
  background-color: #fff;
  margin-top: 10rpx;
  box-sizing: border-box;
  border-radius: 10rpx;
}

.toolsBtn {
  width: 200rpx;
  padding: 10rpx 20rpx;
  color: #fff;
  text-align: center;
}

button {
  margin: 0 !important;
  padding: 0 !important;
  white-space: nowrap;
  color: #fff;
  font-size: 30rpx !important;
  background-color: transparent;
  border-radius: 0;
  height: 70rpx;
  line-height: 70rpx;
  text-align: center;
  background-color: #00a5dd;
  width: 250rpx;
}

.toolsBtn button {
  margin: 0 !important;
  padding: 0 !important;
  white-space: nowrap;
  color: #333;
  width: 100rpx !important;
  height: 60rpx !important;
  line-height: 60rpx !important;
  font-size: 24rpx !important;
  background-color: #fff;
}

.icon image {
  height: 50rpx;
  width: 50rpx;
}


button::after {
  border: none;
}

.bottomBox {
  box-shadow: 1rpx solid 10px 10px 5px rgba(0, 0, 0, 0.9);
  background-color: #fff;
  width: 100vw;
  padding: 10rpx;
  color: #fff;
  position: fixed;
  bottom: 0rpx;
  left: 50%;
  z-index: 9999;
  transform: translateX(-50%);
}


.preview {
  height: 70rpx;
  line-height: 70rpx;
  text-align: center;
  background-color: #00a5dd;
  width: 300rpx;
  margin-right: 20rpx;
}

.mapClass {
  width: 85vw;
  overflow: hidden;
  margin-left: 3vw;
  transform: translateY(0);
  box-sizing: border-box;
  height: 280rpx;
  border-radius: 10rpx;
}

.addressBox {
  width: 500rpx;
  padding-left: 20rpx;
  padding-right: 120rpx;
}

.addressStr {
  color: #00a5dd;
}

.chooseLoc {
  background-color: #fff;
  color: #00a5dd;
  margin-left: 15rpx;
  white-space: nowrap;
  font-size: 26rpx;
}

map {
  width: 100%;
  background-color: #f6f9f9;
}

.placeholderClass {
  font-size: 26rpx;
  color: #ccc;
}

.introBox {
  padding: 20rpx;
}

.introBox video {
  width: 100%;
}

.introBox image {
  width: 100vw;
  height: 100%;
}

.mr20 {
  margin-right: 20rpx !important;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,188评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,464评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,562评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,893评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,917评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,708评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,430评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,342评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,801评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,976评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,115评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,804评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,458评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,008评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,135评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,365评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,055评论 2 355

推荐阅读更多精彩内容