纯正的代码水文--多码警告
写这个文章主要是为了记录开发中用到的相关方法,避免以后再遇到的时候再去一通度娘搜索(虽然以后遇到还是很大概率直接去搜索...),同时也希望能帮助有需要的小伙伴们。
本文主要记录三个内容,一个是前端生成带印章和文字的图片;二是微信和支付宝内h5分享;三是微信和支付宝内点击图片查看大图功能。
一、前端生成带公章的图片
此处需求是:前端生成一个带有文字和公章的图片,并能在微信和支付宝里长按保存,其中各种文字和公章内容都是动态的。
先上代码,看下整体的结构,此处数据均为实例数据,真实需求中均为接口获取。
<!-- 转化为图片内容 -->
<div class="zy_my_certificate_end_box">
// 最终生成的图片
<img :src="dataObj.imgSrc" />
</div>
<!-- 实体html内容 -->
<div class="zy_my_certificate_box" id="ImgRef">
<img :src="getAssets('gongyi/certificate.png')" />
<p class="certificate_number">证书编号:123456789</p>
<div class="certificate_content">
<p>亲爱的张三同学</p>
<p>恭喜你获得全班第一名的好成绩,仅此奖状,以资鼓励</p>
</div>
<div class="certificate_bottom">
// 此处为公章位置
<div class="certificate_icon">
<canvas id="certificate_icon_id" width="80" height="80"></canvas>
</div>
<p>东南西北第一实验小学</p>
<p>2023年5月9日</p>
</div>
</div>
针对这个结构,就只说一点,实体的html内容,也就是用来生成图片的内容,不能设置任何opacity:0, visibility: hidden
之类的属性,那样生成的图片都是大白纸一张,此处只能把它fixed、absolute
到屏幕之外的地方。
接下来看关于公章的生成,主要涉及环形文字的生成和五角星的绘制,相关说明在代码注释里有写明。
// 绘制印章
const createSeal = () => {
return new Promise((resolve)=>{
var canvas = document.getElementById('certificate_icon_id');
var context = canvas.getContext('2d');
// 绘制印章边框
var width = canvas.width / 2;
var height = canvas.height / 2;
context.lineWidth = 2; // 边框宽度
context.strokeStyle = "#f00"; // 边框颜色
context.beginPath();
context.arc(width, height, 38, 0, Math.PI * 2); // 绘制个圆圈,不理解的可以查下api
context.stroke();
//画五角星,此处的第四个参数,根据自己需要的大小进行修改
create5star(context, width, height, 10, "#f00", 0);
// 绘制印章名称,就是公章星星下边那一排小字
context.font = '7px SimSun';
context.textBaseline = 'middle'; //设置文本的垂直对齐方式
context.textAlign = 'center'; //设置文本的水平对对齐方式
context.lineWidth = 1;
context.fillStyle = '#f00';
let name = "学校专用章"
context.fillText(name, width, height + 24); // 此处最后一个参数表示距离底部位置,可以根据情况自行设定
// 绘制印章单位
context.translate(width, height); // 平移到此位置,
context.font = '8px SimSun'
let count = dataObj.certificateData!.merName.length; // 字数
let angle = 4 * Math.PI / (3 * (count - 1)); // 字间角度
let chars = dataObj.certificateData!.merName.split("");
let c;
for (let i = 0; i < count; i++) {
c = chars[i]; // 需要绘制的字符
if (i == 0){
context.rotate(5 * Math.PI / 6);
} else {
context.rotate(angle);
}
context.save();
context.translate(31, 0); // 平移到此位置,此时字和x轴垂直,关于这个位置,个人也是多次尝试确定好的,考虑可能和绘制半径有一定的关系。这里一定要根据自己图章大小修改,否则可能看不到任何文字!
context.rotate(Math.PI / 2); // 旋转90度,让字平行于x轴
context.fillText(c, 0, 0); // 此点为字的中心点
context.restore();
}
resolve(true)
//绘制五角星,这部分不用修改,可以拿来直接用
/**
* 创建一个五角星形状. 该五角星的中心坐标为(sx,sy),中心到顶点的距离为radius,rotate=0时一个顶点在对称轴上
* rotate:绕对称轴旋转rotate弧度
*/
function create5star(context, sx, sy, radius, color, rotato) {
context.save();
context.fillStyle = color;
context.translate(sx, sy); //移动坐标原点
context.rotate(Math.PI + rotato); //旋转
context.beginPath(); //创建路径
var x = Math.sin(0);
var y = Math.cos(0);
var dig = Math.PI / 5 * 4;
for (var i = 0; i < 5; i++) {//画五角星的五条边
var x = Math.sin(i * dig);
var y = Math.cos(i * dig);
context.lineTo(x * radius, y * radius);
}
context.closePath();
context.stroke();
context.fill();
context.restore();
}
})
}
此处使用了promise,因为后需要将文字和图章一起转化为图片,需要确保图章已经生成完成才去转换。
后边的内容就比较简单了,直接用html2canvas
转换就可以了,当然此处没有做缩放的处理,因为下载出来的图片效果还是可以的,如果图片模糊,可能需要先放大再缩小的操作。
// 将html转化成图片
const htmlToImg = ()=>{
let imgid = document.getElementById('ImgRef')!;
html2canvas(imgid, {
allowTaint: true,
useCORS: true,
logging: true,
}).then((canvas) => {
// 转成图片,生成图片地址
let imgUrl = canvas.toDataURL('image/jpeg')
dataObj.imgSrc = imgUrl
})
}
// 调用绘制公章和转换图片
createSeal().then(()=>{
htmlToImg()
})
最后来看下生成的图片最终效果,数据和上边的数据不一样哦,只是看下整体效果~
二、微信和支付宝h5页面的分享
这个应该属于老生常谈的问题了,相关功能,可以使用微信/支付宝环境内置JSBridge,也可以使用相关的jssdk,这里使用的是sdk方法,JSBridge方法在下边的图片查看里有使用。
此处以微信为例,如果你问为啥不用支付宝做例子,因为支付宝h5根本不允许自定义分享
,调用sdk的share方法,会报错,提示无权限使用,配置也不会生效;而支付宝JSBridge里根本没有相关的方法。
// main.js
import wx from 'weixin-js-sdk'
let app = createApp(App)
app.use(router)
app.config.globalProperties.$wx = wx
// utils/index.js
//判断微信、支付宝环境。
export const isWxOrZfb = () => {
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
return 'wx';
}
if (ua.match(/AlipayClient/i) == "alipayclient") {
return "zfb";
}
}
// 组件内使用
import { getCurrentInstance } from 'vue'
const proxy = getCurrentInstance().proxy;
// 分享
const shareFun = ()=>{
const shareUrl = window.location.href;
if(isWxOrZfb() === 'wx'){
const params = {
shareUrl,
debug: false,
jsApiList: "updateAppMessageShareData,onMenuShareAppMessage,updateTimelineShareData,onMenuShareTimeline,previewImage"
}
// 此处调用后端接口,因为使用jssdk方法前,一定要用config进行配置,并在ready里才可以使用
sceneHttp.getWxShare(params).then((res) => {
if (res.code === 200 && res.data) {
proxy.$wx.config({
debug: false,
appId: res.data.appId, // 必填,公众号的唯一标识
timestamp: res.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
signature: res.data.signature, // 必填,签名
jsApiList: ["updateAppMessageShareData", "onMenuShareAppMessage","updateTimelineShareData","onMenuShareTimeline","previewImage"]
});
setTimeout(() => {
proxy.$wx.ready(() => {
//分享给朋友的
proxy.$wx.updateAppMessageShareData({
title: '我是标题', // 分享标题
desc: '我是描述文字',
link: 'https://www.aaaaa.com', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '', // 分享出去所展示的小图
success: function () {
console.log('分享到朋友成功')
},
});
//分享给朋友圈
proxy.$wx.updateTimelineShareData({
title: '我是标题', // 分享标题
link: 'https://www.aaaaa.com', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '', // 分享出去所展示的小图
success: function () {
console.log('分享到朋友圈成功')
},
fail: function () { }
});
});
}, 1000)
} else {
console.log('准备失败');
}
}, function (err: any) {
console.log('准备失败', err);
});
} else if(isWxOrZfb() === 'zfb'){
console.log('对不起,支付宝不支持自定义,再见吧!👋🏻')
}
}
三、微信和支付宝点击小图打开大图预览
这个功能也是属于微信/支付宝内置的功能,可以通过api调用直接实现。此处主要说一下,如果一篇文章中有很多图片,而文章的内容又是通过html格式从接口里获取的,那此时要想预览图片该如何操作呢?
我们知道js原生可以通过事件冒泡或者事件捕获,获取到目标元素,此处我们可以使用这个方法。
同时通过查看两端的相关api我们发现,除了获取到当前需要展示的图片外,还有一个数组用来存储所有的需要查看的图片,此处我们就在点击事件开始时,顺带把所有的图片都加到数组里,避免了后续某些诡异的情况(这里主要说的是支付宝的init参数,如果值不正确的话,可能会出现,点击第二个图但是弹出来的确实第一张图,滑动才能看到第二张图)
// 文字加图片内容,数据从接口里获取html片段
<div class="zy_contribution_content_mes" id="zy_contribution_parent">
<div v-html="dataObj.contributionMes.graphicDetails" @click="mesShowImg"></div>
</div>
// 详情点击中的图片查看大图
const mesShowImg = (e)=>{
// 只有点击的元素是img时,才进行后边的操作
if(e.target.tagName.toLowerCase() === 'img'){
let imgUrl = e.target.getAttribute('src')
// 获取所有图片的列表
let imgList = document.getElementById('zy_contribution_parent')!.getElementsByTagName('img')
// 当前打开的是第几张图,支付宝使用
let idx = 0
if(isWxOrZfb() === 'wx'){
if(dataObj.contentImgList.includes(imgUrl)){
dataObj.contentImgList.push(imgUrl)
}
wxJsReady(function(){
WeixinJSBridge.invoke("imagePreview", {
urls: dataObj.contentImgList,
current: imgUrl,
})
})
} else if(isWxOrZfb() === 'zfb'){
// 如果数组里没有图,就循环添加进去
if(!dataObj.contentImgList.length){
for(let i = 0; i < imgList.length; i++){
let zfbObj: ObjAny = {}
let item = imgList[i].src
zfbObj.u = item
zfbObj.t = ''
dataObj.contentImgList.push(zfbObj)
if(imgList[i].src === imgUrl){
idx = i
}
}
} else {
// 如果有图,就看展示第几张
for(let i = 0; i < imgList.length; i++){
if(imgList[i].src === imgUrl){
idx = i
}
}
}
alJsReady(function() {
AlipayJSBridge.call('imageViewer', {
images: dataObj.contentImgList,
init: idx
},
function(result: any) {
console.log('imgview', result);
});
})
}
}
}
此处顺带附上,关于JSBridge是否成功注入的方法,调用api之前最好确定已经成功注入。
// 阿里jsbridge是否注入
export const alJsReady = (callback: Function) => {
// 如果jsbridge已经注入则直接调用
if ((window as any).AlipayJSBridge) {
callback && callback();
} else {
// 如果没有注入则监听注入的事件
(document as any).addEventListener('AlipayJSBridgeReady', callback, false);
}
}
// 微信jsbridge是否注入
export const wxJsReady = (callback: Function) => {
if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") {
callback && callback();
} else {
if ((document as any).addEventListener) {
(document as any).addEventListener("WeixinJSBridgeReady", callback, false);
} else if ((document as any).attachEvent) {
(document as any).attachEvent("WeixinJSBridgeReady", callback);
(document as any).attachEvent("onWeixinJSBridgeReady", callback);
}
}
}
水文结束,有错误的地方,欢迎指正,反正我也不会改😁😄😄