微信小程序+七牛云水印功能 实现自定义图片分享

需求说明 :

一个社交 小程序,用户发表动态之后,要求实现点击分享按钮,根据动态内容 生成图片并自定义分享。如果 动态是 文字的话,需要将动态文字 和 ui 给定的背景图 合并生成新的图片,如果动态有图片的情况,需要 将动态第一张图,和ui 给定的背景 合并生成新的图片,替换 onShareAppMessage return 方法中 imageUrl字段。

WechatIMG206.jpeg

WechatIMG204.jpeg

WechatIMG203.jpeg

踩坑 :

小程序页面分享很容易实现,只需要在 onShareAppMessagereturn自定义 分享内容就好了,但是如果使用 button 按钮 的 开放能力open-type:share去自定义分享,就会出现问题:button按钮的share 能力,点击button 立即调起 好友列表,并同时触发 onShareAppMessage方法,且事件不可控,无法做延迟操作。也就是说 无法!先本地生成图片,然后替换 自定义图片地址!并且,如果在本地点一个 分享 就生成一个分享图 ,canvas绘制性能太慢,大大影响体验。

寻找实现方式:

首先想到的是,在 发表 动态的时候,本地生成分享图,然后 在发表动态接口传给后端,然后前端在 点击 share button 的时候 直接 拿后端 shareimg 地址 替换 onShareAppMessageimgUrl字段。这样都是同步操作,便可以实现 不同动态,不同的分享图片。但是经过后端交涉,认为这种方案,白白浪费服务器资源,每次都生成一个无用的分享图。不值得。

于是,我就去 找业内 别人的实现方式。 我找的 小打卡小程序。当我用charles 抓包他们点击动态分享时候请求的数据,看到了一个 分享图链接 如下 :

WechatIMG302.png

分享图链接:
http://img-union-cdn.sharedaka.com/noteshare/txt_bg_0.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s=,size_28,text_5omT5Y2h56ys5Zub5aSpICAgIDnmnIgxOOaXpSAg,color_FFFFFF,shadow_0,t_100,g_nw,x_42,y_62/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s=,size_28,text_6ZuoCgrpmIXor7vkuabnsY3vvJrjgIrpnZ7mmrTlipvmsp8=,color_FFFFFF,shadow_0,t_100,g_nw,x_42,y_106/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s=,size_28,text_6YCa44CLCgrnq6DoioLvvJrnrKzkuInnq6DigJzljLrliIY=,color_FFFFFF,shadow_0,t_100,g_nw,x_42,y_150/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s=,size_28,text_6KeC5a-f5ZKM6K-E6K664oCdCgrmkZjlvZXvvJrpnZ7mmrQ=,color_FFFFFF,shadow_0,t_100,g_nw,x_42,y_194/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s=,size_28,text_5Yqb5rKf6YCa55qE56ys5LiA5Liq6KaB57Sg5piv6KeCLi4u,color_FFFFFF,shadow_0,t_100,g_nw,x_28,y_238
于是我看到http://img-union-cdn.sharedaka.com/noteshare/txt_bg_0.png后面 跟着 一大堆参数,x-oss-process=image/watermark 原来是图片水印。

于是 我就找到了七牛云 生成水印的地方---图片处理 https://portal.qiniu.com/dora/fop/imageprocess

image.png

实现:

  1. 将图片背景图 上传至七牛云
  2. 用可视化编辑器 将 文字 作为水印合成到图片上
  3. 图片同理
    技术文档:https://developer.qiniu.com/dora/api/1316/image-watermarking-processing-watermark
    4 、注意 文字 需要 base64 转码并替换 + /
    image.png

实现代码 :

<button data-courseid="{{homeworkData.courseId}}" data-taskid="{{homeworkData.id}}" data-notetext="{{homeworkData.content}}" data-noteimg="{{homeworkData.imgList}}" data-taskname='{{homeworkData.wxNickName}}' open-type="share" hover-start-time="100" class="shareComment" plain="{{true}}" style="border:none">
        <image class="shareIcon" src="./img/work_share.png"></image>
        <text class="samllText">分享</text>
 </button>
  onShareAppMessage: function (res) {
    if (res.from == 'button') {


      let noteImg = res.target.dataset.noteimg
      let notetext = res.target.dataset.notetext
      let shareImgUrl = ''


      if ((noteImg.length > 0 && notetext) || (notetext && noteImg.length == 0)) {
        // 没有图的情况
        console.log(notetext)
        shareImgUrl = base64Until.dealShareText(notetext)
      } else {
        // 有图的情况
        shareImgUrl = base64Until.dealShareImg(noteImg[0].imgPath)
      }

     let shareUrl = `pages/homework/homeworkShare?taskId=${res.target.dataset.taskid}&courseid=${res.target.dataset.courseid}&campClassStudentId=${this.data.indexData.campClassStudentId}`
 
      return {
        title: `${res.target.dataset.taskname}的作业` || '训练营主页',
        path: shareUrl,
        imageUrl: shareImgUrl || ''
      }
    } 

  },

/*
  *  base64编码(编码,配合encodeURIComponent使用)
  *  @parm : str 传入的字符串
  *  使用:
        1、引入util.js(路径更改) :const util  = require('../../utils/util.js');
        2、util.base64_encode(util.utf16to8('base64 编码'));
 */
function base64_encode(str) {
  //下面是64个基本的编码
  var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var out, i, len;
  var c1, c2, c3;
  len = str.length;
  i = 0;
  out = "";
  while (i < len) {
    c1 = str.charCodeAt(i++) & 0xff;
    if (i == len) {
      out += base64EncodeChars.charAt(c1 >> 2);
      out += base64EncodeChars.charAt((c1 & 0x3) << 4);
      out += "==";
      break;
    }
    c2 = str.charCodeAt(i++);
    if (i == len) {
      out += base64EncodeChars.charAt(c1 >> 2);
      out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
      out += base64EncodeChars.charAt((c2 & 0xF) << 2);
      out += "=";
      break;
    }
    c3 = str.charCodeAt(i++);
    out += base64EncodeChars.charAt(c1 >> 2);
    out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
    out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
    out += base64EncodeChars.charAt(c3 & 0x3F);
  }
  return out;
}
/*
  *  base64编码(编码,配合encodeURIComponent使用)
  *  @parm : str 传入的字符串
 */
function utf16to8(str) {
  var out, i, len, c;
  out = "";
  len = str.length;
  for (i = 0; i < len; i++) {
    c = str.charCodeAt(i);
    if ((c >= 0x0001) && (c <= 0x007F)) {
      out += str.charAt(i);
    } else if (c > 0x07FF) {
      out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
      out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
    } else {
      out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
    }
  }
  return out;
}

/*
  *  base64解码(配合decodeURIComponent使用)
  *  @parm : input 传入的字符串
  *  使用:
        1、引入util.js(路径更改) :const util  = require('../../utils/util.js');
        2、util.base64_decode('YmFzZTY0IOe8lueggQ==');
 */
function base64_decode(input) {
  var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  var output = "";
  var chr1, chr2, chr3;
  var enc1, enc2, enc3, enc4;
  var i = 0;
  input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  while (i < input.length) {
    enc1 = base64EncodeChars.indexOf(input.charAt(i++));
    enc2 = base64EncodeChars.indexOf(input.charAt(i++));
    enc3 = base64EncodeChars.indexOf(input.charAt(i++));
    enc4 = base64EncodeChars.indexOf(input.charAt(i++));
    chr1 = (enc1 << 2) | (enc2 >> 4);
    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
    chr3 = ((enc3 & 3) << 6) | enc4;
    output = output + String.fromCharCode(chr1);
    if (enc3 != 64) {
      output = output + String.fromCharCode(chr2);
    }
    if (enc4 != 64) {
      output = output + String.fromCharCode(chr3);
    }
  }
  return utf8_decode(output);
}

/*
  *  utf-8解码
  *  @parm : utftext 传入的字符串
 */
function utf8_decode(utftext) {
  var string = '';
  let i = 0;
  let c = 0;
  let c1 = 0;
  let c2 = 0;
  while (i < utftext.length) {
    c = utftext.charCodeAt(i);
    if (c < 128) {
      string += String.fromCharCode(c);
      i++;
    } else if ((c > 191) && (c < 224)) {
      c1 = utftext.charCodeAt(i + 1);
      string += String.fromCharCode(((c & 31) << 6) | (c1 & 63));
      i += 2;
    } else {
      c1 = utftext.charCodeAt(i + 1);
      c2 = utftext.charCodeAt(i + 2);
      string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63));
      i += 3;
    }
  }
  return string;
}

/*
    * base64编码函数封装
    * @parm: str(传入要编成base64的内容)
    * 使用:
        1、引入util.js(路径更改) :const util  = require('../../utils/util.js');
        2、util.baseEncode('base64 编码');
*/
function baseEncode(str) {
  return base64_encode(utf16to8(str));
}



function dealShareText(text){
  // 先看一下 text 的长度,如果大于 30 返回  三段  ,
  console.log(text)
  let length = text.length
  let str1 = ''
  let str2 = ''
  let str3 = ''
  let  url = ''
  console.log(length)
  let baseUrl = 'http://img.yi-chuangxin.com/Fn64f5WddnKWXTy5tu1kCdCmcAan?imageView2/1/w/500/h/400/q/100'
  if(length>30){
    let str = text.substr(0,27)+'...'
    str1  = str.substr(0,10)
    str2 = str.substr(10,10)
    str3 = str.substr(20,10)
    str1 = baseEncode(str1).replace(/\+/g,'-').replace(/\//g,'_')
    str2 = baseEncode(str2).replace(/\+/g,'-').replace(/\//g,'_')
    str3 = baseEncode(str3).replace(/\+/g,'-').replace(/\//g,'_')
     url  = `${baseUrl}|watermark/2/text/${str1}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/100|watermark/2/text/${str2}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/160|watermark/2/text/${str3}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/220`
  }else if(length>20&&length<=30){
    str1  = text.substr(0,10)
    str2 = text.substr(10,10)
    str3 = text.substr(20,10)
     url  = `${baseUrl}|watermark/2/text/${baseEncode(str1).replace(/\+/g,'-').replace(/\//g,'_')}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/100|watermark/2/text/${baseEncode(str2).replace(/\+/g,'-').replace(/\//g,'_')}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/160|watermark/2/text/${baseEncode(str3).replace(/\+/g,'-').replace(/\//g,'_')}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/220`
  }else if(length>10&&length<=20){
    str1  = text.substr(0,10)
    str2 = text.substr(10,10)
    url  = `${baseUrl}|watermark/2/text/${baseEncode(str1).replace(/\+/g,'-').replace(/\//g,'_')}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/100|watermark/2/text/${baseEncode(str2).replace(/\+/g,'-').replace(/\//g,'_')}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/160`
  }else if(length<=10){
    str1  = text.substr(0,10)
    url  = `${baseUrl}|watermark/2/text/${baseEncode(str1).replace(/\+/g,'-').replace(/\//g,'_')}/font/6buR5L2T/fontsize/600/fill/IzE4M0Y4Qg==/dissolve/100/gravity/North/dx/10/dy/100`
  }

  return url 

}

function dealShareImg(imgUrl){
  let imgurl = `${imgUrl}?imageView2/1/w/460/h/364/q/75`

  let baseUrl = 'http://img.yi-chuangxin.com/Fihxh0GqlfTrWC45qfGP9Z_lT5U0?imageView2/1/w/500/h/400/q/75'
  let url = `${baseUrl}|watermark/1/image/${baseEncode(imgurl).replace(/\+/g,'-').replace(/\//g,'_')}/dissolve/100/gravity/Center/dx/10/dy/10`

  return url
}



/*
    * base64解码函数封装
    * @parm: str(传入要解为正常字体)
    * 使用:
        1、引入util.js(路径更改) :const util  = require('../../utils/util.js');
        2、util.baseDecode(util.baseEncode('base64 编码'))
*/
function baseDecode(str) {
  return base64_decode(str);
}// 抛出函数使用
module.exports = {
  baseEncode: baseEncode,
  baseDecode: baseDecode,
  dealShareText:dealShareText,
  dealShareImg:dealShareImg
}

总结:
用同样的方法 ,可以生产任何海报内容,无需canvas 生成,生成速度快,不占用服务器资源

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