纯前端canvas手绘海报
作者:互联网
源码:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" name="viewport">
<!-- 解决ios HTML img标签 src为网络地址无法显示图片问题解决(https) -->
<meta name="referrer" content="no-referrer">
<title>分享赚钱</title>
.poster-wrap img {
width: 100%;
}
</style>
</head>
<body>
<div class="" style="background: #ffbb53;">
<div class="poster-wrap" style="width:100%;">
<!-- 设置海报背景图 以动态获取海报高度 -->
<img src="img/img-hb.png" class="hb-img" style="width: 100%;position: absolute;z-index: -1;">
<!-- 创建canvas -->
<canvas id="posterWrap" style="width: 100%;height: 675px;"></canvas>
</div>
<div class="">
<div class="option-wrap">
<a class="item _savePoster" href="javascript:;">
<span class="icon download"></span>
<span class="title">点击生成<br>分享海报</span>
</a>
<a class="item _shareTimeLine" href="javascript:;">
<span class="icon moments"></span>
<span class="title">点击右上角<br>分享到朋友圈</span>
</a>
</div>
</div>
</div>
<script src="../../static/js/jquery.js"></script>
<script src="../../static/js/layer_mobile/layer.js"></script>
<script>
$(function(){
//接口获取数据
function getCode(){
$.get(_api + '/user/getCode', function(result){
//海报背景
var img_big = 'img/img-hb.png';
//解决ios 图片不显示问题 链接后追加?x-oss-process=image/format,jpg
var img_code = result.data.path+'?x-oss-process=image/format,jpg';
//获取海报图片高度
var hb_height = $('.hb-img').height()
//将获取海报图片高度hb_height,动态赋值给canvas的高度,保证图片完整展示
$('#posterWrap').height(hb_height)
//开始绘制canvas
var poster = document.getElementById("posterWrap");
var cxt = poster.getContext("2d");
//做移动端不同dpr适配,解决Canvas在移动端绘制模糊的问题
let dpr = window.devicePixelRatio;
// 获取css的宽高
let { width: cssWidth, height: cssHeight } = poster.getBoundingClientRect();
// 根据dpr,扩大canvas画布的像素,使1个canvas像素和1个物理像素相等
poster.width = dpr * cssWidth;
poster.height = dpr * cssHeight;
// 由于画布扩大,canvas的坐标系也跟着扩大,如果按照原先的坐标系绘图内容会缩小
// 所以需要将绘制比例放大
cxt.scale(dpr,dpr);
//此处宽高为css中的width和height属性 与 canvas的width和height属性 的
//不同之处参考https://segmentfault.com/a/1190000019007037
//定义变量方便后面调用
var w = document.body.clientWidth;//可见区域宽,动态适配不同分辨率
console.log(w)
var h = hb_height;//海报高度,此处海报过长使用的是图片本身的高度
//如果只做一屏展示的海报可以用
//var h = document.body.clientHeight;
console.log(h)
// 底部背景色
cxt.fillStyle="#ffbb53";
cxt.fillRect(0, 0 ,w ,h );
// 海报图
var imgbig = new Image;
imgbig.src = img_big;
imgbig.onload = function () {
cxt.drawImage(imgbig, 0,0,w ,h );
// 二维码
var imgcode = new Image;
imgcode.src = img_code;
imgcode.onload = function () {
//canvas中 图形水平居中
//参考:https://blog.csdn.net/wzy890312/article/details/8722538
//公式: (外层图形宽度-需要居中图形宽度)/2
cxt.drawImage(imgcode, (w-160)/2 ,h-230 ,160 ,160 );
}
}
//点击按钮生成海报
$("._savePoster").click(function() {
//添加延时器,避免ios渲染失败问题
setTimeout(function() {
var img = convertCanvasToImage(poster);
$('.poster-wrap').html(img);
//提示弹框
layer.open({content:'海报已生成,长按海报保存到本地', skin:'msg', time: 2});
console.log(img);
} ,1000)
})
//canvas生成base64图片
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
});
};
getCode();
});
</script>
</body>
</html>
第二张海报,部分功能做一下记录
<div class="poster-wrap" style="width:100%;height: 100vh;">
<canvas id="posterWrap" style="width: 100%;height: 100%;"></canvas>
</div>
<script>
var img_big = 'https://***/img-lzj.jpg';
var img_code = 'https://***/img-code.png';
console.log(img_code)
var poster = document.getElementById("posterWrap");
var cxt = poster.getContext("2d");
let dpr = window.devicePixelRatio; // 假设dpr为2
// 获取css的宽高
let { width: cssWidth, height: cssHeight } = poster.getBoundingClientRect();
// 根据dpr,扩大canvas画布的像素,使1个canvas像素和1个物理像素相等
poster.width = dpr * cssWidth;
poster.height = dpr * cssHeight;
// 由于画布扩大,canvas的坐标系也跟着扩大,如果按照原先的坐标系绘图内容会缩小
// 所以需要将绘制比例放大
cxt.scale(dpr,dpr);
// 可见区域宽
var w = document.body.clientWidth;
// 可见区域高
var h = document.body.clientHeight;
console.log(w)
console.log(h)
var imgbig = new Image;
imgbig.src = img_big;
imgbig.onload = function () {
// 海报图
cxt.drawImage(imgbig, 0,0,w ,h-80 );
// 底部背景色
cxt.fillStyle="#ab141b";
cxt.fillRect(0,h-80 ,w ,80 );
// 二维码
var imgcode = new Image;
imgcode.src = img_code;
imgcode.onload = function () {
cxt.drawImage(imgcode, 5 ,h-75 ,70 ,70 );
}
// 长按图标
var iconca = new Image;
iconca.src = 'img/icon-ca.png';
iconca.onload = function () {
cxt.drawImage(iconca, 85 ,h-75 ,8 ,10 );
}
// 长按文字
cxt.beginPath()//注意此处
cxt.fillStyle = '#fff'; // 文字填充颜色
cxt.font = '12px Adobe Ming Std';
cxt.fillText('长按图片购买',98 ,h-65 );
cxt.closePath()//注意此处
// 爆品产值限时购
var imgTips = new Image;
imgTips.src = 'img/img-tips.png';
imgTips.onload = function () {
cxt.drawImage(imgTips, (w-128),h-80 ,128 ,20 );
}
// 城市文字
cxt.beginPath()//注意此处
cxt.fillStyle = '#fff'; // 文字填充颜色
cxt.font = '16px Adobe Ming Std';
cxt.fillText('济南 |',85 ,h-46 );
cxt.closePath()//注意此处
// 商品名称
cxt.beginPath()//注意此处
cxt.fillStyle = '#fff'; // 文字填充颜色
cxt.font = '16px Adobe Ming Std';
cxt.fillText('醉美枣庄辣子鸡',135 ,h-46 );
cxt.closePath()//注意此处
// 地址图标
var imgAddr = new Image;
imgAddr.src = 'img/icon-dw.png';
imgAddr.onload = function () {
cxt.drawImage(imgAddr, 85 ,h-37 ,5 ,8);
}
// 地址标题文字
cxt.beginPath()//注意此处
cxt.fillStyle = '#fff'; // 文字填充颜色
cxt.font = '10px Adobe Ming Std';
cxt.fillText('地址:',92 ,h-30 );
cxt.closePath()//注意此处
// 地址详情文字
cxt.beginPath()//注意此处
cxt.fillStyle = '#fff'; // 文字填充颜色
cxt.font = '10px Adobe Ming Std';
// cxt.fillText('万科中心401室经十路与奥体东路交叉口东北角',120,578);
cxt.lineWidth=1;
var str = "万科中心401室经十路与奥体东路交叉口东北角(浪潮东门,万科麓山南邻)距奥体东路经十路交叉口180m"
var lineWidth = 0;
var canvasWidth = w -140//计算canvas的宽度
var initHeight = h-30;//绘制字体距离canvas顶部初始的高度
var lastSubStrIndex= 0; //每次开始截取的字符串的索引
for(let i=0;i<str.length;i++){
lineWidth+=cxt.measureText(str[i]).width;
if(lineWidth>canvasWidth){
cxt.fillText(str.substring(lastSubStrIndex,i),120,initHeight);//绘制截取部分
initHeight+=11;//40为字体的高度
lineWidth=25;
lastSubStrIndex=i;
}
// if(i==str.length-1){//绘制剩余部分
// cxt.fillText(str.substring(lastSubStrIndex,i+1),120,initHeight);
// }
}
cxt.closePath()//注意此处
// 商家电话文字
cxt.beginPath()//注意此处
cxt.fillStyle = '#fff'; // 文字填充颜色
cxt.font = '11px Adobe Ming Std';
cxt.fillText('商家电话:15555555555',85 ,h-5 );
cxt.closePath()//注意此处
}
setTimeout(function() {
var img = convertCanvasToImage(poster);
$('.poster-wrap').html(img);
layer.open({content:'海报已生成,长按海报保存到本地', skin:'msg', time: 2});
console.log(img);
}, 1000);
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
</script>
实现方式基本相同,多了一个文字超出换行功能:
主要参考https://www.cnblogs.com/hejun26/p/10239649.html
// 地址详情文字
cxt.beginPath()//注意此处
cxt.fillStyle = '#fff'; // 文字填充颜色
cxt.font = '10px Adobe Ming Std';
// cxt.fillText('万科中心401室经十路与奥体东路交叉口东北角',120,578);
cxt.lineWidth=1;
var str = "万科中心401室经十路与奥体东路交叉口东北角(浪潮东门,万科麓山南邻)距奥体东路经十路交叉口180m"
var lineWidth = 0;
var canvasWidth = w -140//计算canvas的宽度
var initHeight = h-30;//绘制字体距离canvas顶部初始的高度
var lastSubStrIndex= 0; //每次开始截取的字符串的索引
for(let i=0;i<str.length;i++){
lineWidth+=cxt.measureText(str[i]).width;
if(lineWidth>canvasWidth){
cxt.fillText(str.substring(lastSubStrIndex,i),120,initHeight);//绘制截取部分
initHeight+=11;//40为字体的高度
lineWidth=25;
lastSubStrIndex=i;
}
// if(i==str.length-1){//绘制剩余部分
// cxt.fillText(str.substring(lastSubStrIndex,i+1),120,initHeight);
// }
}
cxt.closePath()//注意此处
另外 beginPath() 和 closePath()的使用:
主要参考:https://blog.csdn.net/qq_33974741/article/details/84474878
canvas 绘制可以通过 JS 代码来控制(其实应该说 HTML 5 只是提供了个容器,绘制只能在 JS 里完成),而 JS 提供了两个函数,beginPath() 和 closePath() ,这两个函数可以起到类似 div 的作用,用它来把每个圆圈包围,就可以绘制不同颜色的图形了。
另外,在测试中,笔者还发现,当去掉所有的 closePath() 时,只保留 beginPath() ,一样可以达到目的,也就是说,当遇到beginPath() 时,会自动重新开始下一个图形的绘制(保留 closePath() ,去掉 beginPath() 时,效果相当于不添加这两个函数)。但是笔者个人认为,最好两个函数都添加,这样会提高代码可读性,使代码更规范。
官方解释 :
beginPath() 方法开始一条路径,或重置当前的路径。
closePath() 方法创建从当前点到开始点的路径。
个人理解 beginPath()相当于开始的点,closePath()相当于结束的点,搭配使用应该可以形成一个封闭的路径,这样在一个父图形中可以绘制多个不同的子图形。
海报主要绘制参考:
https://www.cnblogs.com/hejun26/p/10239649.html
Canvas在移动端绘制模糊的原因与解决办法参考:
https://segmentfault.com/a/1190000019007037
标签:function,canvas,cxt,img,poster,海报,var,手绘 来源: https://blog.csdn.net/qq_43588387/article/details/115010179