前言:需要做一个微信端的h5小活动,最后要生成一个包含用户微信头像、昵称及一些其他图片的页面,并将页面以图片的形式保存下来(长按保存)。
实现效果:
因为是微信内置浏览器,保存功能还是需要用户长按图片,调起微信内置菜单来完成,所以,在页面记载完成时,图片就已经生成了(因为我这边要求最后保存到手机的图和用户当前看的结果图并不相同,所以我另外做了个层级遮盖,欸,狸猫换太子。这里就不做赘述了)。
用到的当然还是受到广泛欢迎的html2canvas,但是坑好多。。。
遇到的坑
- border
在头像设置了圆角后,在外面加border,没有效果。解决方法很容易,在头像的img标签外套一层div,将div设置背景色(就是你想要的border颜色),然后加padding(就是border的宽度),就可以啦。。。。这个比较简单,就不贴代码啦 - 清晰度问题
这个网上有好多教程,教你设置canvas的scale,然后保存,但是这样不够灵活。我是这样做的,原来的html部分的css该什么样还是什么样,但是我会再写一份scale版本的,基本是放大2倍(有时可能不是刚好2倍,可以根据实际保存效果做调整)。css如下,html会在下面贴上,因为html过程中还有下面的一个坑
/*要存的区域*/
.pic-content{
position: relative;
width: 100%;
height:100%;
background-image: url(community-ad/community-certificate-bg.png);
background-size: 100% 100%;
padding: 80px 24% 0;
}
/*选中的照片*/
.pic-content .choosed-pic{
width: 100%;
background-image: url(community-ad/community-frame.png);
background-size: 100% 100%;
padding: 13px 17px;
margin-bottom: 10px;
}
/*一些其他元素*/
...
/*放大图*/
.scale-content {
position: relative;
width: 200%;
height:200%;
background-image: url(community-ad/community-certificate-bg.png);
background-size: 100% 100%;
padding: 150px 28% 0;
}
/*选中的照片*/
.scale-content .choosed-pic{
width: 100%;
background-image: url(community-ad/community-frame.png);
background-size: 100% 100%;
padding: 26px 28px;
margin-bottom: 20px;
}
/*一些其他元素*/
...
- html2canvas默认以屏幕的宽为canvas的宽,当需要转化的html超出屏幕范围时就只能转化当前可视部分,即上面scale-content部分设置width 200% 会超出。
html2canvas之所以获取不到超出部分是因为对象element的父元素宽高不够,也就是说只要将父元素的宽高设为和需要转换为canvas的子元素的宽高一样大就不会出现这个问题啦
所以,html页面上,要把这块拎出来,不能和pic-content放在同一父元素下。这边做的是直接放body中
<div class="wb-content" v-cloak>
<div id="shareContent" class="pic-content hide">
<div class="choosed-pic">
<img id="img1" :src="sliders[pic]" />
</div>
...
</div>
<!--生成的图片-->
<img v-if="url" class="new-pic" :src="url" />
<!--这里还可以写一些其他需要的,可能要遮盖的内容,这里就不赘述了-->
...
</div>
<!--放大版canvas-->
<div id="scaleContent" class="scale-content"></div>
<script type="text/javascript">
//在转之前要先这样
var scaleContent=document.getElementById("scaleContent");
scaleContent.innerHTML=document.getElementById("shareContent").innerHTML;
</script>
- 当然了,图片跨域问题
在调用方法canvas.toDataURL("image/jpeg")时会遇到跨域的图片,然后画布就被tainted污染了。两种:公司用来存图片的是七牛,所以就是七牛服务器跨域;另一种是微信头像的
网上关于这两个的解决方式基本是图片服务端设置允许跨域(返回 CORS 头),html2canvas设置useCORS:true,//(图片跨域相关)allowTaint:false,//允许跨域(图片跨域相关);对于微信头像,可通过配置服务端代理转发(forward)实现,此处不赘述。(关键是我不大懂服务器端的东西,总是觉得高大上)
这里用的方法是将文件读入到blob文件对象,然后用URL.createObjectURL()方法转换成img src可用的地址,然后再转canvas。这样不管是哪种跨域都可以直接解决
getImage:function (url,imgId) {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
if (this.status == 200) {
document.getElementById(imgId).src = URL.createObjectURL(this.response);
}
};
xhr.send();
},
最后贴上主要部分的完整代码
<style type="text/css">
/*禁止滑动*/
body{overflow: hidden;}
.wb-content{width: 100%;height:100%;position: relative;}
.pic-content{
position: relative;
width: 100%;
height:100%;
background-image: url(img/community-certificate-bg.png);
background-size: 100% 100%;
padding: 60px 50px 0;
}
/*选中的照片*/
.pic-content .choosed-pic{
width: 100%;
background-image: url(img/community-frame.png);
background-size: 100% 100%;
padding: 13px 17px;
margin-bottom: 10px;
}
/*其他*/
...
/*放大图*/
.scale-content {
position: relative;
width: 200%;
height:200%;
background-image: url(img/community-certificate-bg.png);
background-size: 100% 100%;
padding: 100px 100px 0;
}
/*选中的照片*/
.scale-content .choosed-pic{
width: 100%;
background-image: url(img/community-frame.png);
background-size: 100% 100%;
padding: 26px 34px;
margin-bottom: 10px;
}
...
</style>
<body>
<div class="wb-content" v-cloak>
<div id="shareContent" class="pic-content hide" >
<div class="choosed-pic">
<img id="img1" :src="getImage(sliders[pic],'img1')" />
</div>
...
</div>
<img v-if="url" class="new-pic" :src="url" />
...
</div>
<!--放大版canvas-->
<div id="scaleContent" class="scale-content"></div>
</body>
<script type="text/javascript" charset="utf-8">
var vm;
$(function() {
vm = new Vue({
el:".wb-content",
data:{
pic:getUrlParam('pic'),//选中的爱心画
url:'',//生成的图片链接
sliders:sliperChild,//轮播图
},
methods:{
//直接读成blob文件对象
getImage:function (url,imgId) {
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
if (this.status == 200) {
document.getElementById(imgId).src = URL.createObjectURL(this.response);
}
};
xhr.send(null);
}
},
mounted:function(){
var self = this;
self.$nextTick(function(){
//生成图片
htmlToImg(function(url) {
self.url=url
})
});
}
});
});
//转存图片
function htmlToImg(success){
//进度条
showWaiting();
//延时1000ms,等待图片读入到blob文件对象,然后使用URL.createObjectURL转换成src可用的地址
setTimeout(function () {
//放大版html
var scaleContent=document.getElementById("scaleContent");
scaleContent.innerHTML=document.getElementById("shareContent").innerHTML;
//延时300ms,等待放大版html加载图片完毕
setTimeout(function () {
//转存图片
html2canvas(scaleContent, {
useCORS: true, // 【重要】开启跨域配置
allowTaint: true,//允许跨域图片
taintTest: false,//是否在渲染前测试图片
onrendered: function(canvas) {
scaleContent.innerHTML="";
scaleContent.remove();
var dataUrl = canvas.toDataURL("image/jpeg");
closeWaiting();
success&&success(dataUrl);
}
});
},300);
},1000);
}
</script>
还有需要改进的地方,请指出哈,不胜感激~~
网友评论